pumpfun-swap-sdk
Version:
SDK for interacting with the PumpFun AMM on Solana
367 lines (366 loc) • 22.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PumpAmmInternalSdk = void 0;
const anchor_1 = require("@coral-xyz/anchor");
const web3_js_1 = require("@solana/web3.js");
const pda_1 = require("./pda");
const spl_token_1 = require("@solana/spl-token");
const deposit_1 = require("./deposit");
const system_1 = require("@coral-xyz/anchor/dist/cjs/native/system");
const util_1 = require("./util");
const withdraw_1 = require("./withdraw");
const buy_1 = require("./buy");
const sell_1 = require("./sell");
class PumpAmmInternalSdk {
constructor(connection, programId = pda_1.PUMP_AMM_PROGRAM_ID) {
this.connection = connection;
this.program = (0, util_1.getPumpAmmProgram)(connection, programId);
this.globalConfig = (0, pda_1.globalConfigPda)(this.program.programId)[0];
}
programId() {
return this.program.programId;
}
globalConfigKey() {
return this.globalConfig;
}
poolKey(index, creator, baseMint, quoteMint) {
return (0, pda_1.poolPda)(index, creator, baseMint, quoteMint, this.program.programId);
}
lpMintKey(pool) {
return (0, pda_1.lpMintPda)(pool, this.program.programId);
}
fetchGlobalConfigAccount() {
return this.program.account.globalConfig.fetch(this.globalConfig);
}
fetchPool(pool) {
return this.program.account.pool.fetch(pool);
}
async createPoolInstructionsInternal(index, creator, baseMint, quoteMint, baseIn, quoteIn, userBaseTokenAccount = undefined, userQuoteTokenAccount = undefined) {
const [baseTokenProgram, quoteTokenProgram] = await this.getMintTokenPrograms(baseMint, quoteMint);
if (userBaseTokenAccount === undefined) {
userBaseTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(baseMint, creator, true, baseTokenProgram);
}
if (userQuoteTokenAccount === undefined) {
userQuoteTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, creator, true, quoteTokenProgram);
}
return await this.withWsolAccounts(creator, baseMint, userBaseTokenAccount, baseIn, quoteMint, userQuoteTokenAccount, quoteIn, async () => {
const [pool] = (0, pda_1.poolPda)(index, creator, baseMint, quoteMint, this.program.programId);
const instructions = [];
const poolBaseTokenAccountPDA = (0, spl_token_1.getAssociatedTokenAddressSync)(baseMint, pool, true, baseTokenProgram);
if (!(await this.accountExists(poolBaseTokenAccountPDA))) {
instructions.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(creator, poolBaseTokenAccountPDA, pool, baseMint, baseTokenProgram));
}
const poolQuoteTokenAccountPDA = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, pool, true, quoteTokenProgram);
if (!(await this.accountExists(poolQuoteTokenAccountPDA))) {
instructions.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(creator, poolQuoteTokenAccountPDA, pool, quoteMint, quoteTokenProgram));
}
instructions.push(await this.program.methods
.createPool(index, baseIn, quoteIn)
.accountsPartial({
globalConfig: this.globalConfig,
baseMint,
quoteMint,
creator,
userBaseTokenAccount,
userQuoteTokenAccount,
baseTokenProgram,
quoteTokenProgram,
})
.instruction());
return instructions;
});
}
async depositInstructionsInternal(pool, lpToken, maxBase, maxQuote, user, userBaseTokenAccount = undefined, userQuoteTokenAccount = undefined, userPoolTokenAccount = undefined) {
const poolData = await this.program.account.pool.fetch(pool);
const { baseMint, quoteMint, lpMint } = poolData;
const [baseTokenProgram, quoteTokenProgram] = await this.getMintTokenPrograms(baseMint, quoteMint);
const liquidityAccounts = await this.liquidityAccounts(pool, poolData, baseTokenProgram, quoteTokenProgram, user, userBaseTokenAccount, userQuoteTokenAccount, userPoolTokenAccount);
return await this.withWsolAccounts(user, baseMint, liquidityAccounts.userBaseTokenAccount, maxBase, quoteMint, liquidityAccounts.userQuoteTokenAccount, maxQuote, async () => {
const instructions = [];
if (!(await this.accountExists(liquidityAccounts.userPoolTokenAccount))) {
instructions.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(user, liquidityAccounts.userPoolTokenAccount, user, lpMint, spl_token_1.TOKEN_2022_PROGRAM_ID));
}
instructions.push(await this.program.methods
.deposit(lpToken, maxBase, maxQuote)
.accountsPartial(liquidityAccounts)
.instruction());
return instructions;
});
}
async withWsolAccounts(user, baseMint, userBaseAta, baseAmount, quoteMint, userQuoteAta, quoteAmount, block) {
return await this.withWsolAccount(user, baseMint, userBaseAta, baseAmount, () => this.withWsolAccount(user, quoteMint, userQuoteAta, quoteAmount, block));
}
async withWsolAccount(user, mint, ata, amount, block) {
const instructions = [];
let wsolAccountCreated = false;
if (mint.equals(spl_token_1.NATIVE_MINT)) {
if (!(await this.accountExists(ata))) {
instructions.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(user, ata, user, spl_token_1.NATIVE_MINT));
wsolAccountCreated = true;
}
instructions.push(web3_js_1.SystemProgram.transfer({
fromPubkey: user,
toPubkey: ata,
lamports: BigInt(amount.toString()),
}), (0, spl_token_1.createSyncNativeInstruction)(ata));
}
const blockInstructions = await block();
instructions.push(...blockInstructions);
if (wsolAccountCreated) {
instructions.push((0, spl_token_1.createCloseAccountInstruction)(ata, user, user, undefined, spl_token_1.TOKEN_PROGRAM_ID));
}
return instructions;
}
async accountExists(account) {
const accountInfo = await this.connection.getAccountInfo(account);
return accountInfo !== null && !accountInfo.owner.equals(system_1.SYSTEM_PROGRAM_ID);
}
async depositBaseInputInternal(pool, base, slippage) {
const { fetchedPool, poolBaseAmount, poolQuoteAmount } = await this.getPoolBaseAndQuoteAmounts(pool);
const { token1, lpToken, maxToken0, maxToken1 } = (0, deposit_1.depositToken0Internal)(base, slippage, poolBaseAmount, poolQuoteAmount, fetchedPool.lpSupply);
return {
quote: token1,
lpToken,
maxBase: maxToken0,
maxQuote: maxToken1,
};
}
async depositQuoteInputInternal(pool, quote, slippage) {
const { fetchedPool, poolBaseAmount, poolQuoteAmount } = await this.getPoolBaseAndQuoteAmounts(pool);
const { token1, lpToken, maxToken0, maxToken1 } = (0, deposit_1.depositToken0Internal)(quote, slippage, poolQuoteAmount, poolBaseAmount, fetchedPool.lpSupply);
return {
base: token1,
lpToken,
maxBase: maxToken1,
maxQuote: maxToken0,
};
}
async withdrawInstructionsInternal(pool, lpTokenAmountIn, minBaseAmountOut, minQuoteAmountOut, user, userBaseTokenAccount = undefined, userQuoteTokenAccount = undefined, userPoolTokenAccount = undefined) {
const poolData = await this.program.account.pool.fetch(pool);
const { baseMint, quoteMint } = poolData;
const [baseTokenProgram, quoteTokenProgram] = await this.getMintTokenPrograms(baseMint, quoteMint);
const liquidityAccounts = await this.liquidityAccounts(pool, poolData, baseTokenProgram, quoteTokenProgram, user, userBaseTokenAccount, userQuoteTokenAccount, userPoolTokenAccount);
const instructions = [];
let baseWsolAtaCreated = false;
if (!(await this.accountExists(liquidityAccounts.userBaseTokenAccount))) {
instructions.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(user, liquidityAccounts.userBaseTokenAccount, user, liquidityAccounts.baseMint, baseTokenProgram));
if (baseMint.equals(spl_token_1.NATIVE_MINT)) {
baseWsolAtaCreated = true;
}
}
let quoteWsolAtaCreated = false;
if (!(await this.accountExists(liquidityAccounts.userQuoteTokenAccount))) {
instructions.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(user, liquidityAccounts.userQuoteTokenAccount, user, liquidityAccounts.quoteMint, quoteTokenProgram));
if (quoteMint.equals(spl_token_1.NATIVE_MINT)) {
quoteWsolAtaCreated = true;
}
}
instructions.push(await this.program.methods
.withdraw(lpTokenAmountIn, minBaseAmountOut, minQuoteAmountOut)
.accountsPartial(liquidityAccounts)
.instruction());
if (baseWsolAtaCreated) {
instructions.push((0, spl_token_1.createCloseAccountInstruction)(liquidityAccounts.userBaseTokenAccount, user, user, undefined, spl_token_1.TOKEN_PROGRAM_ID));
}
if (quoteWsolAtaCreated) {
instructions.push((0, spl_token_1.createCloseAccountInstruction)(liquidityAccounts.userQuoteTokenAccount, user, user, undefined, spl_token_1.TOKEN_PROGRAM_ID));
}
return instructions;
}
async withdrawInputsInternal(pool, lpAmount, slippage) {
const { fetchedPool, poolBaseAmount, poolQuoteAmount } = await this.getPoolBaseAndQuoteAmounts(pool);
return (0, withdraw_1.withdrawInternal)(lpAmount, slippage, poolBaseAmount, poolQuoteAmount, fetchedPool.lpSupply);
}
async getPoolBaseAndQuoteAmounts(pool) {
const fetchedPool = await this.fetchPool(pool);
const [baseTokenProgram, quoteTokenProgram] = await this.getMintTokenPrograms(fetchedPool.baseMint, fetchedPool.quoteMint);
const poolBaseTokenAccount = await (0, spl_token_1.getAccount)(this.connection, fetchedPool.poolBaseTokenAccount, undefined, baseTokenProgram);
const poolQuoteTokenAccount = await (0, spl_token_1.getAccount)(this.connection, fetchedPool.poolQuoteTokenAccount, undefined, quoteTokenProgram);
const poolBaseAmount = new anchor_1.BN(poolBaseTokenAccount.amount.toString());
const poolQuoteAmount = new anchor_1.BN(poolQuoteTokenAccount.amount.toString());
return { fetchedPool, poolBaseAmount, poolQuoteAmount };
}
async liquidityAccounts(pool, { baseMint, quoteMint, lpMint, poolBaseTokenAccount, poolQuoteTokenAccount, }, baseTokenProgram, quoteTokenProgram, user, userBaseTokenAccount, userQuoteTokenAccount, userPoolTokenAccount) {
if (userBaseTokenAccount === undefined) {
userBaseTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(baseMint, user, true, baseTokenProgram);
}
if (userQuoteTokenAccount === undefined) {
userQuoteTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, user, true, quoteTokenProgram);
}
if (userPoolTokenAccount === undefined) {
userPoolTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(lpMint, user, true, spl_token_1.TOKEN_2022_PROGRAM_ID);
}
return {
pool,
globalConfig: this.globalConfig,
user,
baseMint,
quoteMint,
lpMint,
userBaseTokenAccount,
userQuoteTokenAccount,
userPoolTokenAccount,
poolBaseTokenAccount,
poolQuoteTokenAccount,
};
}
async buyInstructionsInternal(pool, baseOut, maxQuoteIn, user, protocolFeeRecipient = undefined, userBaseTokenAccount = undefined, userQuoteTokenAccount = undefined) {
const { index, creator, baseMint, quoteMint } = await this.program.account.pool.fetch(pool);
return await this.buyInstructionsInternalNoPool(index, creator, baseMint, quoteMint, baseOut, maxQuoteIn, user, protocolFeeRecipient, userBaseTokenAccount, userQuoteTokenAccount);
}
async buyInstructionsInternalNoPool(index, creator, baseMint, quoteMint, baseOut, maxQuoteIn, user, protocolFeeRecipient = undefined, userBaseTokenAccount = undefined, userQuoteTokenAccount = undefined) {
const [pool] = this.poolKey(index, creator, baseMint, quoteMint);
const swapAccounts = await this.swapAccounts(pool, baseMint, quoteMint, user, protocolFeeRecipient, userBaseTokenAccount, userQuoteTokenAccount);
return this.withWsolAccount(user, quoteMint, swapAccounts.userQuoteTokenAccount, maxQuoteIn, async () => {
const instructions = [];
let baseWsolAtaCreated = false;
if (!(await this.accountExists(swapAccounts.userBaseTokenAccount))) {
instructions.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(user, swapAccounts.userBaseTokenAccount, user, swapAccounts.baseMint, swapAccounts.baseTokenProgram));
if (baseMint.equals(spl_token_1.NATIVE_MINT)) {
baseWsolAtaCreated = true;
}
}
instructions.push(await this.program.methods
.buy(baseOut, maxQuoteIn)
.accountsPartial(swapAccounts)
.instruction());
if (baseWsolAtaCreated) {
instructions.push((0, spl_token_1.createCloseAccountInstruction)(swapAccounts.userBaseTokenAccount, user, user, undefined, spl_token_1.TOKEN_PROGRAM_ID));
}
return instructions;
});
}
async buyBaseInput(pool, base, slippage, user, protocolFeeRecipient = undefined, userBaseTokenAccount = undefined, userQuoteTokenAccount = undefined) {
const { maxQuote } = await this.buyBaseInputInternal(pool, base, slippage);
return this.buyInstructionsInternal(pool, base, maxQuote, user, protocolFeeRecipient, userBaseTokenAccount, userQuoteTokenAccount);
}
async buyQuoteInput(pool, quote, slippage, user, protocolFeeRecipient = undefined, userBaseTokenAccount = undefined, userQuoteTokenAccount = undefined) {
const { base, maxQuote } = await this.buyQuoteInputInternal(pool, quote, slippage);
return this.buyInstructionsInternal(pool, base, maxQuote, user, protocolFeeRecipient, userBaseTokenAccount, userQuoteTokenAccount);
}
async buyAutocompleteQuoteFromBase(pool, base, slippage) {
const { uiQuote } = await this.buyBaseInputInternal(pool, base, slippage);
return uiQuote;
}
async buyAutocompleteBaseFromQuote(pool, quote, slippage) {
const { base } = await this.buyQuoteInputInternal(pool, quote, slippage);
return base;
}
async buyBaseInputInternal(pool, base, slippage) {
const { poolBaseAmount, poolQuoteAmount } = await this.getPoolBaseAndQuoteAmounts(pool);
const globalConfig = await this.fetchGlobalConfigAccount();
return (0, buy_1.buyBaseInputInternal)(base, slippage, poolBaseAmount, poolQuoteAmount, globalConfig.lpFeeBasisPoints, globalConfig.protocolFeeBasisPoints);
}
async buyQuoteInputInternal(pool, quote, slippage) {
const { poolBaseAmount, poolQuoteAmount } = await this.getPoolBaseAndQuoteAmounts(pool);
return this.buyQuoteInputInternalNoPool(quote, slippage, poolBaseAmount, poolQuoteAmount);
}
async buyQuoteInputInternalNoPool(quote, slippage, poolBaseAmount, poolQuoteAmount) {
const globalConfig = await this.fetchGlobalConfigAccount();
return (0, buy_1.buyQuoteInputInternal)(quote, slippage, poolBaseAmount, poolQuoteAmount, globalConfig.lpFeeBasisPoints, globalConfig.protocolFeeBasisPoints);
}
async sellInstructionsInternal(pool, baseAmountIn, minQuoteAmountOut, user, protocolFeeRecipient = undefined, userBaseTokenAccount = undefined, userQuoteTokenAccount = undefined) {
const { index, creator, baseMint, quoteMint } = await this.program.account.pool.fetch(pool);
return await this.sellInstructionsInternalNoPool(index, creator, baseMint, quoteMint, baseAmountIn, minQuoteAmountOut, user, protocolFeeRecipient, userBaseTokenAccount, userQuoteTokenAccount);
}
async sellInstructionsInternalNoPool(index, creator, baseMint, quoteMint, baseAmountIn, minQuoteAmountOut, user, protocolFeeRecipient = undefined, userBaseTokenAccount = undefined, userQuoteTokenAccount = undefined) {
const [pool] = this.poolKey(index, creator, baseMint, quoteMint);
const swapAccounts = await this.swapAccounts(pool, baseMint, quoteMint, user, protocolFeeRecipient, userBaseTokenAccount, userQuoteTokenAccount);
return this.withWsolAccount(user, baseMint, swapAccounts.userBaseTokenAccount, baseAmountIn, async () => {
const instructions = [];
let quoteWsolAtaCreated = false;
if (!(await this.accountExists(swapAccounts.userQuoteTokenAccount))) {
instructions.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(user, swapAccounts.userQuoteTokenAccount, user, swapAccounts.quoteMint, swapAccounts.quoteTokenProgram));
if (quoteMint.equals(spl_token_1.NATIVE_MINT)) {
quoteWsolAtaCreated = true;
}
}
instructions.push(await this.program.methods
.sell(baseAmountIn, minQuoteAmountOut)
.accountsPartial(swapAccounts)
.instruction());
if (quoteWsolAtaCreated) {
instructions.push((0, spl_token_1.createCloseAccountInstruction)(swapAccounts.userQuoteTokenAccount, user, user, undefined, spl_token_1.TOKEN_PROGRAM_ID));
}
return instructions;
});
}
async sellBaseInput(pool, base, slippage, user, protocolFeeRecipient = undefined, userBaseTokenAccount = undefined, userQuoteTokenAccount = undefined) {
const { minQuote } = await this.sellBaseInputInternal(pool, base, slippage);
return this.sellInstructionsInternal(pool, base, minQuote, user, protocolFeeRecipient, userBaseTokenAccount, userQuoteTokenAccount);
}
async sellQuoteInput(pool, quote, slippage, user, protocolFeeRecipient = undefined, userBaseTokenAccount = undefined, userQuoteTokenAccount = undefined) {
const { base, minQuote } = await this.sellQuoteInputInternal(pool, quote, slippage);
return this.sellInstructionsInternal(pool, base, minQuote, user, protocolFeeRecipient, userBaseTokenAccount, userQuoteTokenAccount);
}
async sellAutocompleteQuoteFromBase(pool, base, slippage) {
const { uiQuote } = await this.sellBaseInputInternal(pool, base, slippage);
return uiQuote;
}
async sellAutocompleteBaseFromQuote(pool, quote, slippage) {
const { base } = await this.sellQuoteInputInternal(pool, quote, slippage);
return base;
}
async sellBaseInputInternal(pool, base, slippage) {
const { poolBaseAmount, poolQuoteAmount } = await this.getPoolBaseAndQuoteAmounts(pool);
return this.sellBaseInputInternalNoPool(base, slippage, poolBaseAmount, poolQuoteAmount);
}
async sellBaseInputInternalNoPool(base, slippage, poolBaseAmount, poolQuoteAmount) {
const globalConfig = await this.fetchGlobalConfigAccount();
return (0, sell_1.sellBaseInputInternal)(base, slippage, poolBaseAmount, poolQuoteAmount, globalConfig.lpFeeBasisPoints, globalConfig.protocolFeeBasisPoints);
}
async sellQuoteInputInternal(pool, quote, slippage) {
const { poolBaseAmount, poolQuoteAmount } = await this.getPoolBaseAndQuoteAmounts(pool);
const globalConfig = await this.fetchGlobalConfigAccount();
return (0, sell_1.sellQuoteInputInternal)(quote, slippage, poolBaseAmount, poolQuoteAmount, globalConfig.lpFeeBasisPoints, globalConfig.protocolFeeBasisPoints);
}
async extendAccount(account, user) {
return this.program.methods
.extendAccount()
.accountsPartial({
account,
user,
})
.instruction();
}
async swapAccounts(pool, baseMint, quoteMint, user, protocolFeeRecipient, userBaseTokenAccount, userQuoteTokenAccount) {
if (protocolFeeRecipient === undefined) {
const { protocolFeeRecipients } = await this.program.account.globalConfig.fetch(this.globalConfig);
protocolFeeRecipient =
protocolFeeRecipients[Math.floor(Math.random() * protocolFeeRecipients.length)];
}
const [baseTokenProgram, quoteTokenProgram] = await this.getMintTokenPrograms(baseMint, quoteMint);
if (userBaseTokenAccount === undefined) {
userBaseTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(baseMint, user, true, baseTokenProgram);
}
if (userQuoteTokenAccount === undefined) {
userQuoteTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, user, true, quoteTokenProgram);
}
return {
pool,
globalConfig: this.globalConfig,
user,
baseMint,
quoteMint,
userBaseTokenAccount,
userQuoteTokenAccount,
poolBaseTokenAccount: (0, spl_token_1.getAssociatedTokenAddressSync)(baseMint, pool, true, baseTokenProgram),
poolQuoteTokenAccount: (0, spl_token_1.getAssociatedTokenAddressSync)(quoteMint, pool, true, quoteTokenProgram),
protocolFeeRecipient,
baseTokenProgram,
quoteTokenProgram,
};
}
async getMintTokenPrograms(baseMint, quoteMint) {
const baseMintAccountInfo = await this.connection.getAccountInfo(baseMint);
if (baseMintAccountInfo === null) {
throw new Error(`baseMint=${baseMint} not found`);
}
const quoteMintAccountInfo = await this.connection.getAccountInfo(quoteMint);
if (quoteMintAccountInfo === null) {
throw new Error(`quoteMint=${quoteMint} not found`);
}
return [baseMintAccountInfo.owner, quoteMintAccountInfo.owner];
}
}
exports.PumpAmmInternalSdk = PumpAmmInternalSdk;