import {
PublicKey,
TransactionInstruction,
SystemProgram,
} from "@solana/web3.js";
import { TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync } from "@solana/spl-token";
import bs58 from "bs58";
export const PUMP_PROGRAM_ID = new PublicKey(
"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"
);
export const PUMP_FEE_PROGRAM_ID = new PublicKey(
"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"
);
export const GLOBAL = new PublicKey(
"4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf"
);
const BUY_DISC = Buffer.from([102, 6, 61, 18, 1, 218, 235, 234]);
const SELL_DISC = Buffer.from([51, 230, 133, 164, 1, 127, 131, 173]);
function u64le(n) {
const b = Buffer.alloc(8);
b.writeBigUInt64LE(BigInt(n));
return b;
}
export function bondingCurvePda(mint) {
return PublicKey.findProgramAddressSync(
[Buffer.from("bonding-curve"), mint.toBuffer()],
PUMP_PROGRAM_ID
)[0];
}
export function eventAuthorityPda() {
return PublicKey.findProgramAddressSync(
[Buffer.from("__event_authority")],
PUMP_PROGRAM_ID
)[0];
}
export function creatorVaultPda(creator) {
return PublicKey.findProgramAddressSync(
[Buffer.from("creator-vault"), creator.toBuffer()],
PUMP_PROGRAM_ID
)[0];
}
export function feeConfigPda() {
// seeds: ["fee_config", pump_program_id] on the fee program
return PublicKey.findProgramAddressSync(
[Buffer.from("fee_config"), PUMP_PROGRAM_ID.toBuffer()],
PUMP_FEE_PROGRAM_ID
)[0];
}
/** Build a Pump.fun bonding-curve buy instruction */
export function buildBuyIx({
mint,
user,
feeRecipient,
creator, // bonding_curve.creator
amount, // tokens out
maxSolCost, // lamports
trackVolume = false,
}) {
const bondingCurve = bondingCurvePda(mint);
const associatedBondingCurve = getAssociatedTokenAddressSync(
mint,
bondingCurve,
true
);
const associatedUser = getAssociatedTokenAddressSync(mint, user);
const [globalVolume] = PublicKey.findProgramAddressSync(
[Buffer.from("global_volume_accumulator")],
PUMP_PROGRAM_ID
);
const [userVolume] = PublicKey.findProgramAddressSync(
[Buffer.from("user_volume_accumulator"), user.toBuffer()],
PUMP_PROGRAM_ID
);
const data = Buffer.concat([
BUY_DISC,
u64le(amount),
u64le(maxSolCost),
Buffer.from([trackVolume ? 1 : 0]),
]);
return new TransactionInstruction({
programId: PUMP_PROGRAM_ID,
keys: [
{ pubkey: GLOBAL, isSigner: false, isWritable: false },
{ pubkey: feeRecipient, isSigner: false, isWritable: true },
{ pubkey: mint, isSigner: false, isWritable: false },
{ pubkey: bondingCurve, isSigner: false, isWritable: true },
{ pubkey: associatedBondingCurve, isSigner: false, isWritable: true },
{ pubkey: associatedUser, isSigner: false, isWritable: true },
{ pubkey: user, isSigner: true, isWritable: true },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: creatorVaultPda(creator), isSigner: false, isWritable: true },
{ pubkey: eventAuthorityPda(), isSigner: false, isWritable: false },
{ pubkey: PUMP_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: globalVolume, isSigner: false, isWritable: false },
{ pubkey: userVolume, isSigner: false, isWritable: true },
{ pubkey: feeConfigPda(), isSigner: false, isWritable: false },
{ pubkey: PUMP_FEE_PROGRAM_ID, isSigner: false, isWritable: false },
],
data,
});
}
/** Build a Pump.fun bonding-curve sell instruction */
export function buildSellIx({
mint,
user,
feeRecipient,
creator,
amount,
minSolOutput,
}) {
const bondingCurve = bondingCurvePda(mint);
const associatedBondingCurve = getAssociatedTokenAddressSync(
mint,
bondingCurve,
true
);
const associatedUser = getAssociatedTokenAddressSync(mint, user);
const data = Buffer.concat([SELL_DISC, u64le(amount), u64le(minSolOutput)]);
return new TransactionInstruction({
programId: PUMP_PROGRAM_ID,
keys: [
{ pubkey: GLOBAL, isSigner: false, isWritable: false },
{ pubkey: feeRecipient, isSigner: false, isWritable: true },
{ pubkey: mint, isSigner: false, isWritable: false },
{ pubkey: bondingCurve, isSigner: false, isWritable: true },
{ pubkey: associatedBondingCurve, isSigner: false, isWritable: true },
{ pubkey: associatedUser, isSigner: false, isWritable: true },
{ pubkey: user, isSigner: true, isWritable: true },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
{ pubkey: creatorVaultPda(creator), isSigner: false, isWritable: true },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: eventAuthorityPda(), isSigner: false, isWritable: false },
{ pubkey: PUMP_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: feeConfigPda(), isSigner: false, isWritable: false },
{ pubkey: PUMP_FEE_PROGRAM_ID, isSigner: false, isWritable: false },
],
data,
});
}
// Identify buy/sell from a confirmed transaction
export function parsePumpInstructionData(dataBase58) {
const data = Buffer.from(bs58.decode(dataBase58));
const disc = data.subarray(0, 8);
if (disc.equals(BUY_DISC)) {
return {
name: "buy",
amount: data.readBigUInt64LE(8),
maxSolCost: data.readBigUInt64LE(16),
};
}
if (disc.equals(SELL_DISC)) {
return {
name: "sell",
amount: data.readBigUInt64LE(8),
minSolOutput: data.readBigUInt64LE(16),
};
}
return null;
}