@pump-fun/pump-swap-sdk
Version:
Official SDK for interacting with Pump Swap AMM protocol on Solana
189 lines (168 loc) • 5.83 kB
text/typescript
import { expect } from "chai";
import BN from "bn.js";
import { clusterApiUrl, Connection, PublicKey } from "@solana/web3.js";
import { RawMint } from "@solana/spl-token";
import { sellBaseInput } from "../sdk/sell";
import { createFeeConfigFromGlobalConfig } from "./utils";
import { GlobalConfig, Pool } from "../types/sdk";
import { OnlinePumpAmmSdk } from "../sdk/onlinePumpAmm";
import { PUMP_AMM_SDK } from "../sdk/offlinePumpAmm";
describe("sellBaseInput", () => {
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
const sdk = new OnlinePumpAmmSdk(connection);
const user = new PublicKey("4kBH5H5p9oRkZPGLSx8R4WKoDsmXnEpmzsgkebkKvzSg");
const baseMintAccount: RawMint = {
mintAuthorityOption: 0,
mintAuthority: PublicKey.unique(),
supply: BigInt(1),
decimals: 9,
isInitialized: false,
freezeAuthorityOption: 0,
freezeAuthority: PublicKey.unique(),
};
const pool: Pool = {
poolBump: 1,
index: 0,
creator: PublicKey.unique(),
baseMint: PublicKey.unique(),
quoteMint: PublicKey.unique(),
lpMint: PublicKey.unique(),
poolBaseTokenAccount: PublicKey.unique(),
poolQuoteTokenAccount: PublicKey.unique(),
lpSupply: new BN(0),
coinCreator: PublicKey.default,
isMayhemMode: false,
isCashbackCoin: false,
};
const globalConfig: GlobalConfig = {
admin: PublicKey.unique(),
lpFeeBasisPoints: new BN(30),
protocolFeeBasisPoints: new BN(20),
disableFlags: 0,
protocolFeeRecipients: [],
coinCreatorFeeBasisPoints: new BN(0),
adminSetCoinCreatorAuthority: PublicKey.unique(),
whitelistPda: PublicKey.unique(),
reservedFeeRecipient: PublicKey.unique(),
mayhemModeEnabled: false,
reservedFeeRecipients: [],
};
const feeConfig = createFeeConfigFromGlobalConfig(globalConfig);
it("should compute final quote and minQuote correctly with typical inputs", () => {
// Example pool reserves
const baseReserve = new BN(1_000_000); // base tokens in pool
const quoteReserve = new BN(2_000_000); // quote tokens in pool
// The user wants to sell 50,000 base tokens
const base = new BN(50_000);
// Slippage = 1% => the user will accept at least 99% of finalQuote
const slippage = 1;
const result = sellBaseInput({
base,
slippage,
baseReserve,
quoteReserve,
globalConfig,
baseMintAccount,
baseMint: pool.baseMint,
coinCreator: pool.coinCreator,
creator: pool.creator,
feeConfig,
});
console.log("Final quote received:", result.uiQuote.toString());
console.log("Min quote after slippage:", result.minQuote.toString());
// Replace these placeholder values with the actual results once you confirm them offline:
// For example, if you do the math manually or from a reference, set them here:
const expectedFinalQuote = new BN(94761); // Example placeholder
const expectedMinQuote = new BN(93813); // Example placeholder
expect(result.uiQuote.toString()).to.equal(
expectedFinalQuote.toString(),
"Incorrect final quote"
);
expect(result.minQuote.toString()).to.equal(
expectedMinQuote.toString(),
"Incorrect min quote"
);
});
it("should throw an error if 'baseReserve' or 'quoteReserve' is zero", () => {
const slippage = 1;
// baseReserve = 0
expect(() =>
sellBaseInput({
base: new BN(1000),
slippage,
baseReserve: new BN(0),
quoteReserve: new BN(2_000_000),
globalConfig,
baseMintAccount,
baseMint: pool.baseMint,
coinCreator: pool.coinCreator,
creator: pool.creator,
feeConfig,
})
).to.throw(
"Invalid input: 'baseReserve' or 'quoteReserve' cannot be zero."
);
// quoteReserve = 0
expect(() =>
sellBaseInput({
base: new BN(1000),
slippage,
baseReserve: new BN(1_000_000),
quoteReserve: new BN(0),
globalConfig,
baseMintAccount,
baseMint: pool.baseMint,
coinCreator: pool.coinCreator,
creator: pool.creator,
feeConfig,
})
).to.throw(
"Invalid input: 'baseReserve' or 'quoteReserve' cannot be zero."
);
});
it("should throw an error if fees exceed total output (finalQuote negative)", () => {
// We want quoteAmountOut > 0 but finalQuote < 0 after subtracting fees.
const base = new BN(1);
const baseReserve = new BN(1);
const quoteReserve = new BN(2);
const slippage = 1;
const highFeeGlobalConfig: GlobalConfig = {
admin: PublicKey.unique(),
lpFeeBasisPoints: new BN(9000),
protocolFeeBasisPoints: new BN(2000),
disableFlags: 0,
protocolFeeRecipients: [],
coinCreatorFeeBasisPoints: new BN(0),
adminSetCoinCreatorAuthority: PublicKey.unique(),
whitelistPda: PublicKey.unique(),
reservedFeeRecipient: PublicKey.unique(),
mayhemModeEnabled: false,
reservedFeeRecipients: [],
};
const highFeeConfig = createFeeConfigFromGlobalConfig(highFeeGlobalConfig);
expect(() =>
sellBaseInput({
base,
slippage,
baseReserve,
quoteReserve,
globalConfig,
baseMintAccount,
baseMint: pool.baseMint,
coinCreator: pool.coinCreator,
creator: pool.creator,
feeConfig: highFeeConfig,
})
).to.throw("Fees exceed total output; final quote is negative.");
});
it("should build the instruction successfully", async () => {
const pool = new PublicKey("Fzrac7XDX29dYBfMeoPBG18zB2BYFxR5v9fV9zFH7fnV");
expect(async () => {
return await PUMP_AMM_SDK.sellBaseInput(
await sdk.swapSolanaState(pool, user),
new BN(10),
10
);
}).to.not.throw();
});
});