apps/web/lib/solana/pump.ts
pump.fun launch instructions and the lookup table contents. Shown whole, as it was in the repository when this site was built. Line numbers link: add #L12 to the address.
apps/web/lib/solana/pump.ts213 lines
1// pump.fun instruction builders for the launch, with @solana/web3.js only. Account lists and data mirror the official2// @pump-fun/pump-sdk 2.0.0 (`createV2Instruction`, `createFeeSharingConfig`, `updateFeeShares`, and `buy`'s account3// list for `buy_exact_sol_in`); checked byte for byte against the SDK and simulated read-only on mainnet (sigVerify off).4// Isomorphic: no React, no server-only.5import '../launch/polyfill';6import { PublicKey, TransactionInstruction, type AccountMeta } from '@solana/web3.js';7import { createAssociatedTokenAccountIdempotentInstruction, getAssociatedTokenAddressSync } from '@solana/spl-token';8import {9 PUMP_BUYBACK_FEE_RECIPIENTS,10 PUMP_FEE_RECIPIENTS,11 PUMP_IDS,12 PUMP_SEEDS,13 buyExactSolInData,14 createFeeSharingConfigData,15 createV2Data,16 updateFeeSharesData,17} from '@solary/core/pump';18 19export const PUMP_PROGRAM = new PublicKey(PUMP_IDS.pump);20export const PUMP_AMM_PROGRAM = new PublicKey(PUMP_IDS.pumpAmm);21export const PUMP_FEES_PROGRAM = new PublicKey(PUMP_IDS.pumpFees);22export const MAYHEM_PROGRAM = new PublicKey(PUMP_IDS.mayhem);23export const TOKEN_PROGRAM = new PublicKey(PUMP_IDS.token);24export const TOKEN_2022_PROGRAM = new PublicKey(PUMP_IDS.token2022);25export const ATA_PROGRAM = new PublicKey(PUMP_IDS.ata);26export const SYSTEM_PROGRAM = new PublicKey(PUMP_IDS.system);27export const WSOL_MINT = new PublicKey(PUMP_IDS.wsol);28 29const enc = (s: string) => new TextEncoder().encode(s);30const pda = (seeds: Array<string | PublicKey>, program: PublicKey) =>31 PublicKey.findProgramAddressSync(32 seeds.map((s) => (typeof s === 'string' ? enc(s) : s.toBytes())),33 program,34 )[0];35 36export const pumpPda = {37 global: () => pda([PUMP_SEEDS.global], PUMP_PROGRAM),38 mintAuthority: () => pda([PUMP_SEEDS.mintAuthority], PUMP_PROGRAM),39 bondingCurve: (mint: PublicKey) => pda([PUMP_SEEDS.bondingCurve, mint], PUMP_PROGRAM),40 bondingCurveV2: (mint: PublicKey) => pda([PUMP_SEEDS.bondingCurveV2, mint], PUMP_PROGRAM),41 creatorVault: (creator: PublicKey) => pda([PUMP_SEEDS.creatorVault, creator], PUMP_PROGRAM),42 eventAuthority: (program: PublicKey) => pda([PUMP_SEEDS.eventAuthority], program),43 globalVolumeAccumulator: () => pda([PUMP_SEEDS.globalVolumeAccumulator], PUMP_PROGRAM),44 userVolumeAccumulator: (user: PublicKey) => pda([PUMP_SEEDS.userVolumeAccumulator, user], PUMP_PROGRAM),45 feeConfig: () => pda([PUMP_SEEDS.feeConfig, PUMP_PROGRAM], PUMP_FEES_PROGRAM),46 sharingConfig: (mint: PublicKey) => pda([PUMP_SEEDS.sharingConfig, mint], PUMP_FEES_PROGRAM),47 ammCreatorVaultAuthority: (creator: PublicKey) => pda([PUMP_SEEDS.ammCreatorVault, creator], PUMP_AMM_PROGRAM),48 mayhemGlobalParams: () => pda([PUMP_SEEDS.mayhemGlobalParams], MAYHEM_PROGRAM),49 mayhemSolVault: () => pda([PUMP_SEEDS.mayhemSolVault], MAYHEM_PROGRAM),50 mayhemState: (mint: PublicKey) => pda([PUMP_SEEDS.mayhemState, mint], MAYHEM_PROGRAM),51};52 53const ro = (pubkey: PublicKey): AccountMeta => ({ pubkey, isSigner: false, isWritable: false });54const w = (pubkey: PublicKey): AccountMeta => ({ pubkey, isSigner: false, isWritable: true });55const signerW = (pubkey: PublicKey): AccountMeta => ({ pubkey, isSigner: true, isWritable: true });56 57/** create_v2: a Token-2022 pump.fun coin quoted in SOL; `creator` receives the creator fees until fee sharing is set. */58export function createV2Ix(a: { mint: PublicKey; user: PublicKey; creator: PublicKey; name: string; symbol: string; uri: string }): TransactionInstruction {59 const bondingCurve = pumpPda.bondingCurve(a.mint);60 const solVault = pumpPda.mayhemSolVault();61 return new TransactionInstruction({62 programId: PUMP_PROGRAM,63 keys: [64 signerW(a.mint),65 ro(pumpPda.mintAuthority()),66 w(bondingCurve),67 w(getAssociatedTokenAddressSync(a.mint, bondingCurve, true, TOKEN_2022_PROGRAM)),68 ro(pumpPda.global()),69 signerW(a.user),70 ro(SYSTEM_PROGRAM),71 ro(TOKEN_2022_PROGRAM),72 ro(ATA_PROGRAM),73 w(MAYHEM_PROGRAM),74 ro(pumpPda.mayhemGlobalParams()),75 w(solVault),76 w(pumpPda.mayhemState(a.mint)),77 w(getAssociatedTokenAddressSync(a.mint, solVault, true, TOKEN_2022_PROGRAM)),78 ro(pumpPda.eventAuthority(PUMP_PROGRAM)),79 ro(PUMP_PROGRAM),80 ],81 data: Buffer.from(createV2Data({ name: a.name, symbol: a.symbol, uri: a.uri, creator: a.creator.toBase58() })),82 });83}84 85/** pump_fees.create_fee_sharing_config for an ungraduated coin; the payer must be the coin's creator. */86export function createFeeSharingConfigIx(a: { mint: PublicKey; creator: PublicKey }): TransactionInstruction {87 return new TransactionInstruction({88 programId: PUMP_FEES_PROGRAM,89 keys: [90 ro(pumpPda.eventAuthority(PUMP_FEES_PROGRAM)),91 ro(PUMP_FEES_PROGRAM),92 signerW(a.creator),93 ro(pumpPda.global()),94 ro(a.mint),95 w(pumpPda.sharingConfig(a.mint)),96 ro(SYSTEM_PROGRAM),97 w(pumpPda.bondingCurve(a.mint)),98 ro(PUMP_PROGRAM),99 ro(pumpPda.eventAuthority(PUMP_PROGRAM)),100 // `pool` is optional: Anchor's "none" is the instruction's own program id (read-only)101 ro(PUMP_FEES_PROGRAM),102 ro(PUMP_AMM_PROGRAM),103 ro(pumpPda.eventAuthority(PUMP_AMM_PROGRAM)),104 ],105 data: Buffer.from(createFeeSharingConfigData()),106 });107}108 109/** pump_fees.update_fee_shares: set the (one-time, then locked) reward split. */110export function updateFeeSharesIx(a: {111 mint: PublicKey;112 authority: PublicKey;113 currentShareholders: PublicKey[];114 shareholders: Array<{ address: PublicKey; bps: number }>;115}): TransactionInstruction {116 const cfg = pumpPda.sharingConfig(a.mint);117 const vaultAuthority = pumpPda.ammCreatorVaultAuthority(cfg);118 return new TransactionInstruction({119 programId: PUMP_FEES_PROGRAM,120 keys: [121 ro(pumpPda.eventAuthority(PUMP_FEES_PROGRAM)),122 ro(PUMP_FEES_PROGRAM),123 { pubkey: a.authority, isSigner: true, isWritable: false },124 ro(pumpPda.global()),125 ro(a.mint),126 w(cfg),127 ro(pumpPda.bondingCurve(a.mint)),128 w(pumpPda.creatorVault(cfg)),129 ro(SYSTEM_PROGRAM),130 ro(PUMP_PROGRAM),131 ro(pumpPda.eventAuthority(PUMP_PROGRAM)),132 ro(PUMP_AMM_PROGRAM),133 ro(pumpPda.eventAuthority(PUMP_AMM_PROGRAM)),134 ro(WSOL_MINT),135 ro(TOKEN_PROGRAM),136 ro(ATA_PROGRAM),137 w(vaultAuthority),138 w(getAssociatedTokenAddressSync(WSOL_MINT, vaultAuthority, true, TOKEN_PROGRAM)),139 ...a.currentShareholders.map(w),140 ],141 data: Buffer.from(updateFeeSharesData(a.shareholders.map((s) => ({ address: s.address.toBase58(), bps: s.bps })))),142 });143}144 145const pick = <T,>(list: readonly T[]) => list[Math.floor(Math.random() * list.length)]!;146 147/**148 * The deployer's first buy, in the launch transaction: spend exactly `lamports` (fees included). Atomic with the149 * create, so nothing trades in between and `minTokensOut` can stay at 1. `creator` is the curve's creator at the150 * time of the buy: the sharing config once fee sharing is set.151 */152export function buyExactSolInIxs(a: { mint: PublicKey; user: PublicKey; creator: PublicKey; lamports: bigint; minTokensOut?: bigint }): TransactionInstruction[] {153 const bondingCurve = pumpPda.bondingCurve(a.mint);154 const userAta = getAssociatedTokenAddressSync(a.mint, a.user, true, TOKEN_2022_PROGRAM);155 return [156 createAssociatedTokenAccountIdempotentInstruction(a.user, userAta, a.user, a.mint, TOKEN_2022_PROGRAM),157 new TransactionInstruction({158 programId: PUMP_PROGRAM,159 keys: [160 ro(pumpPda.global()),161 w(new PublicKey(pick(PUMP_FEE_RECIPIENTS))),162 ro(a.mint),163 w(bondingCurve),164 w(getAssociatedTokenAddressSync(a.mint, bondingCurve, true, TOKEN_2022_PROGRAM)),165 w(userAta),166 signerW(a.user),167 ro(SYSTEM_PROGRAM),168 ro(TOKEN_2022_PROGRAM),169 w(pumpPda.creatorVault(a.creator)),170 ro(pumpPda.eventAuthority(PUMP_PROGRAM)),171 ro(PUMP_PROGRAM),172 ro(pumpPda.globalVolumeAccumulator()),173 w(pumpPda.userVolumeAccumulator(a.user)),174 ro(pumpPda.feeConfig()),175 ro(PUMP_FEES_PROGRAM),176 // remaining accounts, as the SDK's buy passes them177 ro(pumpPda.bondingCurveV2(a.mint)),178 w(new PublicKey(pick(PUMP_BUYBACK_FEE_RECIPIENTS))),179 ],180 data: Buffer.from(buyExactSolInData(a.lamports, a.minTokensOut ?? 1n)),181 }),182 ];183}184 185/**186 * Every account a Solary launch uses that is the same for every launch: what a lookup table should hold so the187 * whole launch (create + fee sharing + first buy + priority fee) fits one transaction.188 */189export function launchLookupAddresses(): PublicKey[] {190 return [191 PUMP_PROGRAM,192 PUMP_FEES_PROGRAM,193 PUMP_AMM_PROGRAM,194 MAYHEM_PROGRAM,195 SYSTEM_PROGRAM,196 TOKEN_PROGRAM,197 TOKEN_2022_PROGRAM,198 ATA_PROGRAM,199 WSOL_MINT,200 new PublicKey(PUMP_IDS.computeBudget),201 pumpPda.global(),202 pumpPda.mintAuthority(),203 pumpPda.eventAuthority(PUMP_PROGRAM),204 pumpPda.eventAuthority(PUMP_FEES_PROGRAM),205 pumpPda.eventAuthority(PUMP_AMM_PROGRAM),206 pumpPda.mayhemGlobalParams(),207 pumpPda.mayhemSolVault(),208 pumpPda.globalVolumeAccumulator(),209 pumpPda.feeConfig(),210 ...PUMP_FEE_RECIPIENTS.map((k) => new PublicKey(k)),211 ...PUMP_BUYBACK_FEE_RECIPIENTS.map((k) => new PublicKey(k)),212 ];213}