From 8f2f99f3d990ef94e135732ef8aebc85d59779e5 Mon Sep 17 00:00:00 2001 From: 0xfnzero <0xfnzero@users.noreply.github.com> Date: Fri, 10 Apr 2026 15:37:53 +0800 Subject: [PATCH] chore: remove duplicate nodejs and python SDK subdirectories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These incomplete SDK implementations in the Rust repository were outdated and redundant. The complete versions are maintained in separate repositories: - sol-trade-sdk-ts (Node.js) - sol-trade-sdk-python (Python) 🤖 Generated with [Qoder](https://qoder.com) --- sol-trade-sdk-nodejs/package.json | 43 - sol-trade-sdk-nodejs/src/index.ts | 7 - .../src/instruction/bonk_builder.ts | 646 --------------- sol-trade-sdk-nodejs/src/instruction/index.ts | 21 - .../instruction/meteora_damm_v2_builder.ts | 437 ---------- .../src/instruction/pumpfun_builder.ts | 515 ------------ .../src/instruction/raydium_amm_v4_builder.ts | 476 ----------- .../src/instruction/raydium_cpmm_builder.ts | 556 ------------- sol-trade-sdk-nodejs/tsconfig.json | 29 - sol-trade-sdk-python/README.md | 80 -- sol-trade-sdk-python/pyproject.toml | 53 -- sol-trade-sdk-python/src/__init__.py | 9 - .../src/instruction/__init__.py | 366 --------- .../src/instruction/bonk_builder.py | 533 ------------ .../src/instruction/common.py | 216 ----- .../instruction/meteora_damm_v2_builder.py | 325 -------- .../src/instruction/pumpfun_builder.py | 560 ------------- .../src/instruction/pumpswap_builder.py | 760 ------------------ .../src/instruction/raydium_amm_v4_builder.py | 374 --------- .../src/instruction/raydium_cpmm_builder.py | 454 ----------- 20 files changed, 6460 deletions(-) delete mode 100644 sol-trade-sdk-nodejs/package.json delete mode 100644 sol-trade-sdk-nodejs/src/index.ts delete mode 100644 sol-trade-sdk-nodejs/src/instruction/bonk_builder.ts delete mode 100644 sol-trade-sdk-nodejs/src/instruction/index.ts delete mode 100644 sol-trade-sdk-nodejs/src/instruction/meteora_damm_v2_builder.ts delete mode 100644 sol-trade-sdk-nodejs/src/instruction/pumpfun_builder.ts delete mode 100644 sol-trade-sdk-nodejs/src/instruction/raydium_amm_v4_builder.ts delete mode 100644 sol-trade-sdk-nodejs/src/instruction/raydium_cpmm_builder.ts delete mode 100644 sol-trade-sdk-nodejs/tsconfig.json delete mode 100644 sol-trade-sdk-python/README.md delete mode 100644 sol-trade-sdk-python/pyproject.toml delete mode 100644 sol-trade-sdk-python/src/__init__.py delete mode 100644 sol-trade-sdk-python/src/instruction/__init__.py delete mode 100644 sol-trade-sdk-python/src/instruction/bonk_builder.py delete mode 100644 sol-trade-sdk-python/src/instruction/common.py delete mode 100644 sol-trade-sdk-python/src/instruction/meteora_damm_v2_builder.py delete mode 100644 sol-trade-sdk-python/src/instruction/pumpfun_builder.py delete mode 100644 sol-trade-sdk-python/src/instruction/pumpswap_builder.py delete mode 100644 sol-trade-sdk-python/src/instruction/raydium_amm_v4_builder.py delete mode 100644 sol-trade-sdk-python/src/instruction/raydium_cpmm_builder.py diff --git a/sol-trade-sdk-nodejs/package.json b/sol-trade-sdk-nodejs/package.json deleted file mode 100644 index e8e3a87..0000000 --- a/sol-trade-sdk-nodejs/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "sol-trade-sdk", - "version": "1.0.0", - "description": "Production-grade instruction builders for Solana DEX protocols", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "scripts": { - "build": "tsc", - "clean": "rm -rf dist", - "prepublishOnly": "npm run build" - }, - "keywords": [ - "solana", - "dex", - "pumpfun", - "bonk", - "raydium", - "meteora", - "blockchain", - "crypto" - ], - "author": "", - "license": "MIT", - "dependencies": { - "@solana/web3.js": "^1.95.0", - "@solana/spl-token": "^0.4.0" - }, - "devDependencies": { - "typescript": "^5.0.0", - "@types/node": "^20.0.0" - }, - "files": [ - "dist", - "README.md" - ], - "repository": { - "type": "git", - "url": "https://github.com/sol-trade/sol-trade-sdk-nodejs" - }, - "engines": { - "node": ">=18.0.0" - } -} diff --git a/sol-trade-sdk-nodejs/src/index.ts b/sol-trade-sdk-nodejs/src/index.ts deleted file mode 100644 index fde9e3d..0000000 --- a/sol-trade-sdk-nodejs/src/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Sol Trade SDK - Node.js - * - * Production-grade instruction builders for Solana DEX protocols. - */ - -export * from "./instruction"; diff --git a/sol-trade-sdk-nodejs/src/instruction/bonk_builder.ts b/sol-trade-sdk-nodejs/src/instruction/bonk_builder.ts deleted file mode 100644 index d328d25..0000000 --- a/sol-trade-sdk-nodejs/src/instruction/bonk_builder.ts +++ /dev/null @@ -1,646 +0,0 @@ -/** - * Bonk Protocol Instruction Builder - * - * Production-grade instruction builder for Bonk AMM protocol. - * Supports buy and sell operations with WSOL and USD1 pools. - */ - -import { - PublicKey, - Keypair, - AccountMeta, - Instruction, - SystemProgram, -} from "@solana/web3.js"; -import { - getAssociatedTokenAddressSync, - createAssociatedTokenAccountInstruction, - TOKEN_PROGRAM_ID, - createCloseAccountInstruction, - NATIVE_MINT, - createSyncNativeInstruction, - createAccount, - getAccount, - getMint, - Account as TokenAccount, -} from "@solana/spl-token"; - -// ============================================ -// Program IDs and Constants -// ============================================ - -/** Bonk program ID */ -export const BONK_PROGRAM_ID = new PublicKey( - "LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj" -); - -/** Authority */ -export const AUTHORITY = new PublicKey( - "WLhv2UAZm6z4KyaaELi5pjdbJh6RESMva1Rnn8pJVVh" -); - -/** Global Config */ -export const GLOBAL_CONFIG = new PublicKey( - "6s1xP3hpbAfFoNtUNF8mfHsjr2Bd97JxFJRWLbL6aHuX" -); - -/** USD1 Global Config */ -export const USD1_GLOBAL_CONFIG = new PublicKey( - "EPiZbnrThjyLnoQ6QQzkxeFqyL5uyg9RzNHHAudUPxBz" -); - -/** Event Authority */ -export const EVENT_AUTHORITY = new PublicKey( - "2DPAtwB8L12vrMRExbLuyGnC7n2J5LNoZQSejeQGpwkr" -); - -/** WSOL Token Account (mint) */ -export const WSOL_TOKEN_ACCOUNT = new PublicKey( - "So11111111111111111111111111111111111111112" -); - -/** USD1 Token Account (mint) */ -export const USD1_TOKEN_ACCOUNT = new PublicKey( - "USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB" -); - -/** Fee rates */ -export const PLATFORM_FEE_RATE = 100n; // 1% -export const PROTOCOL_FEE_RATE = 25n; // 0.25% -export const SHARE_FEE_RATE = 0n; // 0% - -// ============================================ -// Discriminators -// ============================================ - -/** Buy exact in instruction discriminator */ -export const BUY_EXACT_IN_DISCRIMINATOR: Buffer = Buffer.from([ - 250, 234, 13, 123, 213, 156, 19, 236, -]); - -/** Sell exact in instruction discriminator */ -export const SELL_EXACT_IN_DISCRIMINATOR: Buffer = Buffer.from([ - 149, 39, 222, 155, 211, 124, 152, 26, -]); - -// ============================================ -// Seeds -// ============================================ - -export const POOL_SEED = Buffer.from("pool"); -export const POOL_VAULT_SEED = Buffer.from("pool_vault"); - -// ============================================ -// PDA Derivation Functions -// ============================================ - -/** - * Derive the pool PDA for given base and quote mints - */ -export function getPoolPda(baseMint: PublicKey, quoteMint: PublicKey): PublicKey { - const [pda] = PublicKey.findProgramAddressSync( - [POOL_SEED, baseMint.toBuffer(), quoteMint.toBuffer()], - BONK_PROGRAM_ID - ); - return pda; -} - -/** - * Derive the vault PDA for a pool and mint - */ -export function getVaultPda(poolState: PublicKey, mint: PublicKey): PublicKey { - const [pda] = PublicKey.findProgramAddressSync( - [POOL_VAULT_SEED, poolState.toBuffer(), mint.toBuffer()], - BONK_PROGRAM_ID - ); - return pda; -} - -/** - * Derive platform associated account - */ -export function getPlatformAssociatedAccount(platformConfig: PublicKey): PublicKey { - const [pda] = PublicKey.findProgramAddressSync( - [platformConfig.toBuffer(), WSOL_TOKEN_ACCOUNT.toBuffer()], - BONK_PROGRAM_ID - ); - return pda; -} - -/** - * Derive creator associated account - */ -export function getCreatorAssociatedAccount(creator: PublicKey): PublicKey { - const [pda] = PublicKey.findProgramAddressSync( - [creator.toBuffer(), WSOL_TOKEN_ACCOUNT.toBuffer()], - BONK_PROGRAM_ID - ); - return pda; -} - -// ============================================ -// Helper Functions -// ============================================ - -/** - * Calculate amount in net after fees - */ -export function getAmountInNet( - amountIn: bigint, - protocolFeeRate: bigint, - platformFeeRate: bigint, - shareFeeRate: bigint -): bigint { - const protocolFee = (amountIn * protocolFeeRate) / 10000n; - const platformFee = (amountIn * platformFeeRate) / 10000n; - const shareFee = (amountIn * shareFeeRate) / 10000n; - return amountIn - protocolFee - platformFee - shareFee; -} - -/** - * Calculate amount out for a swap - */ -export function getAmountOut( - amountIn: bigint, - protocolFeeRate: bigint, - platformFeeRate: bigint, - shareFeeRate: bigint, - virtualBase: bigint, - virtualQuote: bigint, - realBase: bigint, - realQuote: bigint, - slippageBasisPoints: bigint -): bigint { - const amountInNet = getAmountInNet( - amountIn, - protocolFeeRate, - platformFeeRate, - shareFeeRate - ); - - const inputReserve = virtualQuote + realQuote; - const outputReserve = virtualBase - realBase; - - const numerator = amountInNet * outputReserve; - const denominator = inputReserve + amountInNet; - let amountOut = numerator / denominator; - - // Apply slippage - amountOut = amountOut - (amountOut * slippageBasisPoints) / 10000n; - - return amountOut; -} - -/** - * Calculate amount in required for a desired output - */ -export function getAmountIn( - amountOut: bigint, - protocolFeeRate: bigint, - platformFeeRate: bigint, - shareFeeRate: bigint, - virtualBase: bigint, - virtualQuote: bigint, - realBase: bigint, - realQuote: bigint, - slippageBasisPoints: bigint -): bigint { - // Consider slippage, actual required output amount is higher - const amountOutWithSlippage = (amountOut * 10000n) / (10000n - slippageBasisPoints); - - const inputReserve = virtualQuote + realQuote; - const outputReserve = virtualBase - realBase; - - // Reverse calculate using AMM formula - const numerator = amountOutWithSlippage * inputReserve; - const denominator = outputReserve - amountOutWithSlippage; - const amountInNet = numerator / denominator; - - // Calculate total fee rate - const totalFeeRate = protocolFeeRate + platformFeeRate + shareFeeRate; - - const amountIn = (amountInNet * 10000n) / (10000n - totalFeeRate); - - return amountIn; -} - -// ============================================ -// WSOL Helper Functions -// ============================================ - -/** - * Create instructions to wrap SOL into WSOL - */ -export function createWsolInstructions( - payer: PublicKey, - amount: bigint -): Instruction[] { - const instructions: Instruction[] = []; - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payer, true); - - // Create WSOL ATA if needed (using create account for simplicity) - // In production, use createAssociatedTokenAccountInstruction - - // Sync native (wrap SOL) - instructions.push( - createSyncNativeInstruction(wsolAta) - ); - - return instructions; -} - -/** - * Create instruction to close WSOL ATA and unwrap to SOL - */ -export function createCloseWsolInstruction(payer: PublicKey): Instruction { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payer, true); - return createCloseAccountInstruction(wsolAta, payer, payer, [], TOKEN_PROGRAM_ID); -} - -/** - * Create WSOL ATA instruction - */ -export function createWsolAtaInstruction(payer: PublicKey): Instruction { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payer, true); - return createAssociatedTokenAccountInstruction( - payer, - wsolAta, - payer, - NATIVE_MINT, - TOKEN_PROGRAM_ID - ); -} - -// ============================================ -// Types -// ============================================ - -export interface BonkParams { - poolState?: PublicKey; - globalConfig: PublicKey; - platformConfig: PublicKey; - platformAssociatedAccount: PublicKey; - creatorAssociatedAccount: PublicKey; - baseVault?: PublicKey; - quoteVault?: PublicKey; - mintTokenProgram: PublicKey; - virtualBase: bigint; - virtualQuote: bigint; - realBase: bigint; - realQuote: bigint; -} - -export interface BuildBuyInstructionsParams { - payer: Keypair | PublicKey; - outputMint: PublicKey; - inputAmount: bigint; - slippageBasisPoints?: bigint; - fixedOutputAmount?: bigint; - createInputMintAta?: boolean; - createOutputMintAta?: boolean; - closeInputMintAta?: boolean; - protocolParams: BonkParams; -} - -export interface BuildSellInstructionsParams { - payer: Keypair | PublicKey; - inputMint: PublicKey; - inputAmount: bigint; - slippageBasisPoints?: bigint; - fixedOutputAmount?: bigint; - createOutputMintAta?: boolean; - closeOutputMintAta?: boolean; - closeInputMintAta?: boolean; - protocolParams: BonkParams; -} - -// ============================================ -// Instruction Builders -// ============================================ - -/** - * Build buy instructions for Bonk protocol - */ -export function buildBuyInstructions( - params: BuildBuyInstructionsParams -): Instruction[] { - const { - payer, - outputMint, - inputAmount, - slippageBasisPoints = 1000n, - fixedOutputAmount, - createInputMintAta = true, - createOutputMintAta = true, - closeInputMintAta = false, - protocolParams, - } = params; - - if (inputAmount === 0n) { - throw new Error("Amount cannot be zero"); - } - - const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer; - const instructions: Instruction[] = []; - - const { - globalConfig, - platformConfig, - platformAssociatedAccount, - creatorAssociatedAccount, - baseVault, - quoteVault, - mintTokenProgram, - virtualBase, - virtualQuote, - realBase, - realQuote, - } = protocolParams; - - // Check if USD1 pool - const isUsd1Pool = globalConfig.equals(USD1_GLOBAL_CONFIG); - - // Determine quote token mint - const quoteTokenMint = isUsd1Pool ? USD1_TOKEN_ACCOUNT : WSOL_TOKEN_ACCOUNT; - - // Derive pool state - const poolState = protocolParams.poolState && !protocolParams.poolState.equals(PublicKey.default) - ? protocolParams.poolState - : getPoolPda(outputMint, quoteTokenMint); - - // Calculate minimum amount out - const minimumAmountOut = fixedOutputAmount - ? fixedOutputAmount - : getAmountOut( - inputAmount, - PROTOCOL_FEE_RATE, - PLATFORM_FEE_RATE, - SHARE_FEE_RATE, - virtualBase, - virtualQuote, - realBase, - realQuote, - slippageBasisPoints - ); - - // Derive user token accounts - const userBaseTokenAccount = getAssociatedTokenAddressSync( - outputMint, - payerPubkey, - true, - mintTokenProgram - ); - const userQuoteTokenAccount = getAssociatedTokenAddressSync( - quoteTokenMint, - payerPubkey, - true, - TOKEN_PROGRAM_ID - ); - - // Derive vault accounts - const baseVaultAccount = baseVault && !baseVault.equals(PublicKey.default) - ? baseVault - : getVaultPda(poolState, outputMint); - const quoteVaultAccount = quoteVault && !quoteVault.equals(PublicKey.default) - ? quoteVault - : getVaultPda(poolState, quoteTokenMint); - - // Handle WSOL wrapping for non-USD1 pools - if (createInputMintAta && !isUsd1Pool) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createAssociatedTokenAccountInstruction( - payerPubkey, - wsolAta, - payerPubkey, - NATIVE_MINT, - TOKEN_PROGRAM_ID - ) - ); - instructions.push(createSyncNativeInstruction(wsolAta)); - } - - // Create output mint ATA if needed - if (createOutputMintAta) { - instructions.push( - createAssociatedTokenAccountInstruction( - payerPubkey, - userBaseTokenAccount, - payerPubkey, - outputMint, - mintTokenProgram - ) - ); - } - - // Build instruction data - const shareFeeRate = 0n; - const data = Buffer.alloc(32); - BUY_EXACT_IN_DISCRIMINATOR.copy(data, 0); - data.writeBigUInt64LE(inputAmount, 8); - data.writeBigUInt64LE(minimumAmountOut, 16); - data.writeBigUInt64LE(shareFeeRate, 24); - - // Build accounts - const accounts: AccountMeta[] = [ - { pubkey: payerPubkey, isSigner: true, isWritable: true }, - { pubkey: AUTHORITY, isSigner: false, isWritable: false }, - { pubkey: globalConfig, isSigner: false, isWritable: false }, - { pubkey: platformConfig, isSigner: false, isWritable: false }, - { pubkey: poolState, isSigner: false, isWritable: true }, - { pubkey: userBaseTokenAccount, isSigner: false, isWritable: true }, - { pubkey: userQuoteTokenAccount, isSigner: false, isWritable: true }, - { pubkey: baseVaultAccount, isSigner: false, isWritable: true }, - { pubkey: quoteVaultAccount, isSigner: false, isWritable: true }, - { pubkey: outputMint, isSigner: false, isWritable: false }, - { pubkey: quoteTokenMint, isSigner: false, isWritable: false }, - { pubkey: mintTokenProgram, isSigner: false, isWritable: false }, - { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, - { pubkey: EVENT_AUTHORITY, isSigner: false, isWritable: false }, - { pubkey: BONK_PROGRAM_ID, isSigner: false, isWritable: false }, - { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, - { pubkey: platformAssociatedAccount, isSigner: false, isWritable: true }, - { pubkey: creatorAssociatedAccount, isSigner: false, isWritable: true }, - ]; - - instructions.push( - new Instruction({ - keys: accounts, - programId: BONK_PROGRAM_ID, - data, - }) - ); - - // Close WSOL ATA if requested - if (closeInputMintAta && !isUsd1Pool) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createCloseAccountInstruction(wsolAta, payerPubkey, payerPubkey, [], TOKEN_PROGRAM_ID) - ); - } - - return instructions; -} - -/** - * Build sell instructions for Bonk protocol - */ -export function buildSellInstructions( - params: BuildSellInstructionsParams -): Instruction[] { - const { - payer, - inputMint, - inputAmount, - slippageBasisPoints = 1000n, - fixedOutputAmount, - createOutputMintAta = true, - closeOutputMintAta = false, - closeInputMintAta = false, - protocolParams, - } = params; - - if (inputAmount === 0n) { - throw new Error("Amount cannot be zero"); - } - - const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer; - const instructions: Instruction[] = []; - - const { - globalConfig, - platformConfig, - platformAssociatedAccount, - creatorAssociatedAccount, - baseVault, - quoteVault, - mintTokenProgram, - virtualBase, - virtualQuote, - realBase, - realQuote, - } = protocolParams; - - // Check if USD1 pool - const isUsd1Pool = globalConfig.equals(USD1_GLOBAL_CONFIG); - - // Determine quote token mint - const quoteTokenMint = isUsd1Pool ? USD1_TOKEN_ACCOUNT : WSOL_TOKEN_ACCOUNT; - - // Derive pool state - const poolState = protocolParams.poolState && !protocolParams.poolState.equals(PublicKey.default) - ? protocolParams.poolState - : getPoolPda(inputMint, quoteTokenMint); - - // Calculate minimum amount out - const minimumAmountOut = fixedOutputAmount - ? fixedOutputAmount - : getAmountOut( - inputAmount, - PROTOCOL_FEE_RATE, - PLATFORM_FEE_RATE, - SHARE_FEE_RATE, - virtualBase, - virtualQuote, - realBase, - realQuote, - slippageBasisPoints - ); - - // Derive user token accounts - const userBaseTokenAccount = getAssociatedTokenAddressSync( - inputMint, - payerPubkey, - true, - mintTokenProgram - ); - const userQuoteTokenAccount = getAssociatedTokenAddressSync( - quoteTokenMint, - payerPubkey, - true, - TOKEN_PROGRAM_ID - ); - - // Derive vault accounts - const baseVaultAccount = baseVault && !baseVault.equals(PublicKey.default) - ? baseVault - : getVaultPda(poolState, inputMint); - const quoteVaultAccount = quoteVault && !quoteVault.equals(PublicKey.default) - ? quoteVault - : getVaultPda(poolState, quoteTokenMint); - - // Create WSOL ATA for receiving SOL if needed - if (createOutputMintAta && !isUsd1Pool) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createAssociatedTokenAccountInstruction( - payerPubkey, - wsolAta, - payerPubkey, - NATIVE_MINT, - TOKEN_PROGRAM_ID - ) - ); - } - - // Build instruction data - const shareFeeRate = 0n; - const data = Buffer.alloc(32); - SELL_EXACT_IN_DISCRIMINATOR.copy(data, 0); - data.writeBigUInt64LE(inputAmount, 8); - data.writeBigUInt64LE(minimumAmountOut, 16); - data.writeBigUInt64LE(shareFeeRate, 24); - - // Build accounts - const accounts: AccountMeta[] = [ - { pubkey: payerPubkey, isSigner: true, isWritable: true }, - { pubkey: AUTHORITY, isSigner: false, isWritable: false }, - { pubkey: globalConfig, isSigner: false, isWritable: false }, - { pubkey: platformConfig, isSigner: false, isWritable: false }, - { pubkey: poolState, isSigner: false, isWritable: true }, - { pubkey: userBaseTokenAccount, isSigner: false, isWritable: true }, - { pubkey: userQuoteTokenAccount, isSigner: false, isWritable: true }, - { pubkey: baseVaultAccount, isSigner: false, isWritable: true }, - { pubkey: quoteVaultAccount, isSigner: false, isWritable: true }, - { pubkey: inputMint, isSigner: false, isWritable: false }, - { pubkey: quoteTokenMint, isSigner: false, isWritable: false }, - { pubkey: mintTokenProgram, isSigner: false, isWritable: false }, - { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, - { pubkey: EVENT_AUTHORITY, isSigner: false, isWritable: false }, - { pubkey: BONK_PROGRAM_ID, isSigner: false, isWritable: false }, - { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, - { pubkey: platformAssociatedAccount, isSigner: false, isWritable: true }, - { pubkey: creatorAssociatedAccount, isSigner: false, isWritable: true }, - ]; - - instructions.push( - new Instruction({ - keys: accounts, - programId: BONK_PROGRAM_ID, - data, - }) - ); - - // Close WSOL ATA if requested - if (closeOutputMintAta && !isUsd1Pool) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createCloseAccountInstruction(wsolAta, payerPubkey, payerPubkey, [], TOKEN_PROGRAM_ID) - ); - } - - // Close input token ATA if requested - if (closeInputMintAta) { - instructions.push( - createCloseAccountInstruction( - userBaseTokenAccount, - payerPubkey, - payerPubkey, - [], - mintTokenProgram - ) - ); - } - - return instructions; -} diff --git a/sol-trade-sdk-nodejs/src/instruction/index.ts b/sol-trade-sdk-nodejs/src/instruction/index.ts deleted file mode 100644 index f135428..0000000 --- a/sol-trade-sdk-nodejs/src/instruction/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Sol Trade SDK - Instruction Builders - * - * Production-grade instruction builders for Solana DEX protocols. - * Supports: PumpFun, Bonk, Raydium CPMM, Raydium AMM V4, Meteora DAMM V2 - */ - -// PumpFun Protocol -export * from "./pumpfun_builder"; - -// Bonk Protocol -export * from "./bonk_builder"; - -// Raydium CPMM Protocol -export * from "./raydium_cpmm_builder"; - -// Raydium AMM V4 Protocol -export * from "./raydium_amm_v4_builder"; - -// Meteora DAMM V2 Protocol -export * from "./meteora_damm_v2_builder"; diff --git a/sol-trade-sdk-nodejs/src/instruction/meteora_damm_v2_builder.ts b/sol-trade-sdk-nodejs/src/instruction/meteora_damm_v2_builder.ts deleted file mode 100644 index fe66293..0000000 --- a/sol-trade-sdk-nodejs/src/instruction/meteora_damm_v2_builder.ts +++ /dev/null @@ -1,437 +0,0 @@ -/** - * Meteora DAMM V2 Protocol Instruction Builder - * - * Production-grade instruction builder for Meteora DAMM V2 protocol. - * Supports swap operations with WSOL and USDC pools. - */ - -import { - PublicKey, - Keypair, - AccountMeta, - Instruction, - SystemProgram, -} from "@solana/web3.js"; -import { - getAssociatedTokenAddressSync, - createAssociatedTokenAccountInstruction, - TOKEN_PROGRAM_ID, - createCloseAccountInstruction, - NATIVE_MINT, - createSyncNativeInstruction, -} from "@solana/spl-token"; - -// ============================================ -// Program IDs and Constants -// ============================================ - -/** Meteora DAMM V2 program ID */ -export const METEORA_DAMM_V2_PROGRAM_ID = new PublicKey( - "cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG" -); - -/** Authority */ -export const AUTHORITY = new PublicKey( - "HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC" -); - -/** WSOL Token Account (mint) */ -export const WSOL_TOKEN_ACCOUNT = new PublicKey( - "So11111111111111111111111111111111111111112" -); - -/** USDC Token Account (mint) */ -export const USDC_TOKEN_ACCOUNT = new PublicKey( - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" -); - -// ============================================ -// Discriminators -// ============================================ - -/** Swap instruction discriminator */ -export const SWAP_DISCRIMINATOR: Buffer = Buffer.from([ - 248, 198, 158, 145, 225, 117, 135, 200, -]); - -// ============================================ -// Seeds -// ============================================ - -export const EVENT_AUTHORITY_SEED = Buffer.from("__event_authority"); - -// ============================================ -// PDA Derivation Functions -// ============================================ - -/** - * Derive the event authority PDA - */ -export function getEventAuthorityPda(): PublicKey { - const [pda] = PublicKey.findProgramAddressSync( - [EVENT_AUTHORITY_SEED], - METEORA_DAMM_V2_PROGRAM_ID - ); - return pda; -} - -// ============================================ -// Helper Functions -// ============================================ - -/** - * Create instructions to wrap SOL into WSOL - */ -export function createWsolInstructions( - payer: PublicKey, - amount: bigint -): Instruction[] { - const instructions: Instruction[] = []; - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payer, true); - - // Sync native (wrap SOL) - instructions.push(createSyncNativeInstruction(wsolAta)); - - return instructions; -} - -/** - * Create instruction to close WSOL ATA and unwrap to SOL - */ -export function createCloseWsolInstruction(payer: PublicKey): Instruction { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payer, true); - return createCloseAccountInstruction(wsolAta, payer, payer, [], TOKEN_PROGRAM_ID); -} - -/** - * Create WSOL ATA instruction - */ -export function createWsolAtaInstruction(payer: PublicKey): Instruction { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payer, true); - return createAssociatedTokenAccountInstruction( - payer, - wsolAta, - payer, - NATIVE_MINT, - TOKEN_PROGRAM_ID - ); -} - -// ============================================ -// Types -// ============================================ - -export interface MeteoraDammV2Params { - pool: PublicKey; - tokenAMint: PublicKey; - tokenBMint: PublicKey; - tokenAVault: PublicKey; - tokenBVault: PublicKey; - tokenAProgram: PublicKey; - tokenBProgram: PublicKey; -} - -export interface BuildBuyInstructionsParams { - payer: Keypair | PublicKey; - inputMint: PublicKey; - outputMint: PublicKey; - inputAmount: bigint; - slippageBasisPoints?: bigint; - fixedOutputAmount?: bigint; - createInputMintAta?: boolean; - createOutputMintAta?: boolean; - closeInputMintAta?: boolean; - protocolParams: MeteoraDammV2Params; -} - -export interface BuildSellInstructionsParams { - payer: Keypair | PublicKey; - inputMint: PublicKey; - outputMint: PublicKey; - inputAmount: bigint; - slippageBasisPoints?: bigint; - fixedOutputAmount?: bigint; - createOutputMintAta?: boolean; - closeOutputMintAta?: boolean; - closeInputMintAta?: boolean; - protocolParams: MeteoraDammV2Params; -} - -// ============================================ -// Instruction Builders -// ============================================ - -/** - * Build buy instructions for Meteora DAMM V2 protocol - */ -export function buildBuyInstructions( - params: BuildBuyInstructionsParams -): Instruction[] { - const { - payer, - inputMint, - outputMint, - inputAmount, - fixedOutputAmount, - createInputMintAta = true, - createOutputMintAta = true, - closeInputMintAta = false, - protocolParams, - } = params; - - if (inputAmount === 0n) { - throw new Error("Amount cannot be zero"); - } - - if (!fixedOutputAmount) { - throw new Error("fixedOutputAmount must be set for Meteora DAMM V2 swap"); - } - - const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer; - const instructions: Instruction[] = []; - - const { - pool, - tokenAMint, - tokenBMint, - tokenAVault, - tokenBVault, - tokenAProgram, - tokenBProgram, - } = protocolParams; - - // Check pool type - const isWsol = tokenAMint.equals(WSOL_TOKEN_ACCOUNT) || tokenBMint.equals(WSOL_TOKEN_ACCOUNT); - const isUsdc = tokenAMint.equals(USDC_TOKEN_ACCOUNT) || tokenBMint.equals(USDC_TOKEN_ACCOUNT); - - if (!isWsol && !isUsdc) { - throw new Error("Pool must contain WSOL or USDC"); - } - - // Determine swap direction - const isAIn = tokenAMint.equals(WSOL_TOKEN_ACCOUNT) || tokenAMint.equals(USDC_TOKEN_ACCOUNT); - - // Derive user token accounts - const inputTokenAccount = getAssociatedTokenAddressSync( - inputMint, - payerPubkey, - true, - isAIn ? tokenAProgram : tokenBProgram - ); - const outputTokenAccount = getAssociatedTokenAddressSync( - outputMint, - payerPubkey, - true, - isAIn ? tokenBProgram : tokenAProgram - ); - - // Derive event authority - const eventAuthority = getEventAuthorityPda(); - - // Handle WSOL wrapping - if (createInputMintAta && isWsol) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createAssociatedTokenAccountInstruction( - payerPubkey, - wsolAta, - payerPubkey, - NATIVE_MINT, - TOKEN_PROGRAM_ID - ) - ); - instructions.push(createSyncNativeInstruction(wsolAta)); - } - - // Create output mint ATA if needed - if (createOutputMintAta) { - instructions.push( - createAssociatedTokenAccountInstruction( - payerPubkey, - outputTokenAccount, - payerPubkey, - outputMint, - TOKEN_PROGRAM_ID - ) - ); - } - - // Build instruction data - const data = Buffer.alloc(24); - SWAP_DISCRIMINATOR.copy(data, 0); - data.writeBigUInt64LE(inputAmount, 8); - data.writeBigUInt64LE(fixedOutputAmount, 16); - - // Build accounts - const accounts: AccountMeta[] = [ - { pubkey: AUTHORITY, isSigner: false, isWritable: false }, - { pubkey: pool, isSigner: false, isWritable: true }, - { pubkey: inputTokenAccount, isSigner: false, isWritable: true }, - { pubkey: outputTokenAccount, isSigner: false, isWritable: true }, - { pubkey: tokenAVault, isSigner: false, isWritable: true }, - { pubkey: tokenBVault, isSigner: false, isWritable: true }, - { pubkey: tokenAMint, isSigner: false, isWritable: false }, - { pubkey: tokenBMint, isSigner: false, isWritable: false }, - { pubkey: payerPubkey, isSigner: true, isWritable: true }, - { pubkey: tokenAProgram, isSigner: false, isWritable: false }, - { pubkey: tokenBProgram, isSigner: false, isWritable: false }, - { pubkey: METEORA_DAMM_V2_PROGRAM_ID, isSigner: false, isWritable: false }, // Referral Token Account (placeholder) - { pubkey: eventAuthority, isSigner: false, isWritable: false }, - { pubkey: METEORA_DAMM_V2_PROGRAM_ID, isSigner: false, isWritable: false }, - ]; - - instructions.push( - new Instruction({ - keys: accounts, - programId: METEORA_DAMM_V2_PROGRAM_ID, - data, - }) - ); - - // Close WSOL ATA if requested - if (closeInputMintAta && isWsol) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createCloseAccountInstruction(wsolAta, payerPubkey, payerPubkey, [], TOKEN_PROGRAM_ID) - ); - } - - return instructions; -} - -/** - * Build sell instructions for Meteora DAMM V2 protocol - */ -export function buildSellInstructions( - params: BuildSellInstructionsParams -): Instruction[] { - const { - payer, - inputMint, - outputMint, - inputAmount, - fixedOutputAmount, - createOutputMintAta = true, - closeOutputMintAta = false, - closeInputMintAta = false, - protocolParams, - } = params; - - if (inputAmount === 0n) { - throw new Error("Amount cannot be zero"); - } - - if (!fixedOutputAmount) { - throw new Error("fixedOutputAmount must be set for Meteora DAMM V2 swap"); - } - - const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer; - const instructions: Instruction[] = []; - - const { - pool, - tokenAMint, - tokenBMint, - tokenAVault, - tokenBVault, - tokenAProgram, - tokenBProgram, - } = protocolParams; - - // Check pool type - const isWsol = tokenBMint.equals(WSOL_TOKEN_ACCOUNT) || tokenAMint.equals(WSOL_TOKEN_ACCOUNT); - const isUsdc = tokenBMint.equals(USDC_TOKEN_ACCOUNT) || tokenAMint.equals(USDC_TOKEN_ACCOUNT); - - if (!isWsol && !isUsdc) { - throw new Error("Pool must contain WSOL or USDC"); - } - - // Determine swap direction (selling token for WSOL/USDC) - const isAIn = tokenBMint.equals(WSOL_TOKEN_ACCOUNT) || tokenBMint.equals(USDC_TOKEN_ACCOUNT); - - // Derive user token accounts - const inputTokenAccount = getAssociatedTokenAddressSync( - inputMint, - payerPubkey, - true, - isAIn ? tokenAProgram : tokenBProgram - ); - const outputTokenAccount = getAssociatedTokenAddressSync( - outputMint, - payerPubkey, - true, - isAIn ? tokenBProgram : tokenAProgram - ); - - // Derive event authority - const eventAuthority = getEventAuthorityPda(); - - // Create WSOL ATA for receiving if needed - if (createOutputMintAta && isWsol) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createAssociatedTokenAccountInstruction( - payerPubkey, - wsolAta, - payerPubkey, - NATIVE_MINT, - TOKEN_PROGRAM_ID - ) - ); - } - - // Build instruction data - const data = Buffer.alloc(24); - SWAP_DISCRIMINATOR.copy(data, 0); - data.writeBigUInt64LE(inputAmount, 8); - data.writeBigUInt64LE(fixedOutputAmount, 16); - - // Build accounts - const accounts: AccountMeta[] = [ - { pubkey: AUTHORITY, isSigner: false, isWritable: false }, - { pubkey: pool, isSigner: false, isWritable: true }, - { pubkey: inputTokenAccount, isSigner: false, isWritable: true }, - { pubkey: outputTokenAccount, isSigner: false, isWritable: true }, - { pubkey: tokenAVault, isSigner: false, isWritable: true }, - { pubkey: tokenBVault, isSigner: false, isWritable: true }, - { pubkey: tokenAMint, isSigner: false, isWritable: false }, - { pubkey: tokenBMint, isSigner: false, isWritable: false }, - { pubkey: payerPubkey, isSigner: true, isWritable: true }, - { pubkey: tokenAProgram, isSigner: false, isWritable: false }, - { pubkey: tokenBProgram, isSigner: false, isWritable: false }, - { pubkey: METEORA_DAMM_V2_PROGRAM_ID, isSigner: false, isWritable: false }, // Referral Token Account - { pubkey: eventAuthority, isSigner: false, isWritable: false }, - { pubkey: METEORA_DAMM_V2_PROGRAM_ID, isSigner: false, isWritable: false }, - ]; - - instructions.push( - new Instruction({ - keys: accounts, - programId: METEORA_DAMM_V2_PROGRAM_ID, - data, - }) - ); - - // Close WSOL ATA if requested - if (closeOutputMintAta && isWsol) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createCloseAccountInstruction(wsolAta, payerPubkey, payerPubkey, [], TOKEN_PROGRAM_ID) - ); - } - - // Close input token ATA if requested - if (closeInputMintAta) { - instructions.push( - createCloseAccountInstruction( - inputTokenAccount, - payerPubkey, - payerPubkey, - [], - isAIn ? tokenAProgram : tokenBProgram - ) - ); - } - - return instructions; -} diff --git a/sol-trade-sdk-nodejs/src/instruction/pumpfun_builder.ts b/sol-trade-sdk-nodejs/src/instruction/pumpfun_builder.ts deleted file mode 100644 index f7ffc04..0000000 --- a/sol-trade-sdk-nodejs/src/instruction/pumpfun_builder.ts +++ /dev/null @@ -1,515 +0,0 @@ -/** - * PumpFun Protocol Instruction Builder - * - * Production-grade instruction builder for PumpFun bonding curve protocol. - * Supports buy, sell, and cashback claim operations. - */ - -import { - PublicKey, - Keypair, - AccountMeta, - Instruction, - SYSVAR_RENT_PUBKEY, - SystemProgram, -} from "@solana/web3.js"; -import { - getAssociatedTokenAddressSync, - createAssociatedTokenAccountInstruction, - TOKEN_PROGRAM_ID, - TOKEN_2022_PROGRAM_ID, - createCloseAccountInstruction, -} from "@solana/spl-token"; - -// ============================================ -// Program IDs and Constants -// ============================================ - -/** PumpFun program ID */ -export const PUMPFUN_PROGRAM_ID = new PublicKey( - "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" -); - -/** MPL Token Metadata program ID */ -export const MPL_TOKEN_METADATA_PROGRAM_ID = new PublicKey( - "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" -); - -/** Event Authority for PumpFun */ -export const EVENT_AUTHORITY = new PublicKey( - "Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1" -); - -/** Fee Program */ -export const FEE_PROGRAM = new PublicKey( - "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" -); - -/** Global Volume Accumulator */ -export const GLOBAL_VOLUME_ACCUMULATOR = new PublicKey( - "Hq2wp8uJ9jCPsYgNHex8RtqdvMPfVGoYwjvF1ATiwn2Y" -); - -/** Fee Config */ -export const FEE_CONFIG = new PublicKey( - "8Wf5TiAheLUqBrKXeYg2JtAFFMWtKdG2BSFgqUcPVwTt" -); - -/** Global Account */ -export const GLOBAL_ACCOUNT = new PublicKey( - "4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf" -); - -/** Authority */ -export const AUTHORITY = new PublicKey( - "FFWtrEQ4B4PKQoVuHYzZq8FabGkVatYzDpEVHsK5rrhF" -); - -/** Withdraw Authority */ -export const WITHDRAW_AUTHORITY = new PublicKey( - "39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg" -); - -/** Fee Recipient */ -export const FEE_RECIPIENT = new PublicKey( - "62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV" -); - -/** Mayhem Fee Recipients */ -export const MAYHEM_FEE_RECIPIENTS: PublicKey[] = [ - new PublicKey("GesfTA3X2arioaHp8bbKdjG9vJtskViWACZoYvxp4twS"), - new PublicKey("4budycTjhs9fD6xw62VBducVTNgMgJJ5BgtKq7mAZwn6"), - new PublicKey("8SBKzEQU4nLSzcwF4a74F2iaUDQyTfjGndn6qUWBnrpR"), - new PublicKey("4UQeTP1T39KZ9Sfxzo3WR5skgsaP6NZa87BAkuazLEKH"), - new PublicKey("8sNeir4QsLsJdYpc9RZacohhK1Y5FLU3nC5LXgYB4aa6"), - new PublicKey("Fh9HmeLNUMVCvejxCtCL2DbYaRyBFVJ5xrWkLnMH6fdk"), - new PublicKey("463MEnMeGyJekNZFQSTUABBEbLnvMTALbT6ZmsxAbAdq"), - new PublicKey("6AUH3WEHucYZyC61hqpqYUWVto5qA5hjHuNQ32GNnNxA"), -]; - -// ============================================ -// Discriminators -// ============================================ - -/** Buy instruction discriminator */ -export const BUY_DISCRIMINATOR: Buffer = Buffer.from([ - 102, 6, 61, 18, 1, 218, 235, 234, -]); - -/** Buy exact SOL in discriminator */ -export const BUY_EXACT_SOL_IN_DISCRIMINATOR: Buffer = Buffer.from([ - 56, 252, 116, 8, 158, 223, 205, 95, -]); - -/** Sell instruction discriminator */ -export const SELL_DISCRIMINATOR: Buffer = Buffer.from([ - 51, 230, 133, 164, 1, 127, 131, 173, -]); - -/** Claim cashback discriminator */ -export const CLAIM_CASHBACK_DISCRIMINATOR: Buffer = Buffer.from([ - 37, 58, 35, 126, 190, 53, 228, 197, -]); - -// ============================================ -// Seeds -// ============================================ - -export const BONDING_CURVE_SEED = Buffer.from("bonding-curve"); -export const BONDING_CURVE_V2_SEED = Buffer.from("bonding-curve-v2"); -export const CREATOR_VAULT_SEED = Buffer.from("creator-vault"); -export const USER_VOLUME_ACCUMULATOR_SEED = Buffer.from("user_volume_accumulator"); - -// ============================================ -// PDA Derivation Functions -// ============================================ - -/** - * Derive the bonding curve PDA for a given mint - */ -export function getBondingCurvePda(mint: PublicKey): PublicKey { - const [pda] = PublicKey.findProgramAddressSync( - [BONDING_CURVE_SEED, mint.toBuffer()], - PUMPFUN_PROGRAM_ID - ); - return pda; -} - -/** - * Derive the bonding curve v2 PDA for a given mint - */ -export function getBondingCurveV2Pda(mint: PublicKey): PublicKey { - const [pda] = PublicKey.findProgramAddressSync( - [BONDING_CURVE_V2_SEED, mint.toBuffer()], - PUMPFUN_PROGRAM_ID - ); - return pda; -} - -/** - * Derive the creator vault PDA for a given creator - */ -export function getCreatorVaultPda(creator: PublicKey): PublicKey { - const [pda] = PublicKey.findProgramAddressSync( - [CREATOR_VAULT_SEED, creator.toBuffer()], - PUMPFUN_PROGRAM_ID - ); - return pda; -} - -/** - * Derive the user volume accumulator PDA for a given user - */ -export function getUserVolumeAccumulatorPda(user: PublicKey): PublicKey { - const [pda] = PublicKey.findProgramAddressSync( - [USER_VOLUME_ACCUMULATOR_SEED, user.toBuffer()], - PUMPFUN_PROGRAM_ID - ); - return pda; -} - -/** - * Get a random Mayhem fee recipient - */ -export function getRandomMayhemFeeRecipient(): PublicKey { - const index = Math.floor(Math.random() * MAYHEM_FEE_RECIPIENTS.length); - return MAYHEM_FEE_RECIPIENTS[index]; -} - -// ============================================ -// Helper Functions -// ============================================ - -/** - * Calculate buy amount with slippage protection - */ -export function calculateWithSlippageBuy(amount: bigint, basisPoints: bigint): bigint { - const maxBps = 9999n; - const bps = basisPoints > maxBps ? maxBps : basisPoints; - return amount + (amount * bps) / 10000n; -} - -/** - * Calculate sell amount with slippage protection - */ -export function calculateWithSlippageSell(amount: bigint, basisPoints: bigint): bigint { - if (amount <= basisPoints / 10000n) { - return 1n; - } - return amount - (amount * basisPoints) / 10000n; -} - -// ============================================ -// Types -// ============================================ - -export interface BondingCurve { - account: PublicKey; - virtualTokenReserves: bigint; - virtualSolReserves: bigint; - realTokenReserves: bigint; - isMayhemMode: boolean; - isCashbackCoin: boolean; -} - -export interface PumpFunParams { - bondingCurve: BondingCurve; - creatorVault: PublicKey; - tokenProgram: PublicKey; - associatedBondingCurve?: PublicKey; - closeTokenAccountWhenSell?: boolean; -} - -export interface BuildBuyInstructionsParams { - payer: Keypair | PublicKey; - outputMint: PublicKey; - inputAmount: bigint; - slippageBasisPoints?: bigint; - fixedOutputAmount?: bigint; - createOutputMintAta?: boolean; - protocolParams: PumpFunParams; - useExactSolAmount?: boolean; -} - -export interface BuildSellInstructionsParams { - payer: Keypair | PublicKey; - inputMint: PublicKey; - inputAmount: bigint; - slippageBasisPoints?: bigint; - fixedOutputAmount?: bigint; - closeInputMintAta?: boolean; - protocolParams: PumpFunParams; -} - -// ============================================ -// Instruction Builders -// ============================================ - -/** - * Build buy instructions for PumpFun protocol - */ -export function buildBuyInstructions( - params: BuildBuyInstructionsParams -): Instruction[] { - const { - payer, - outputMint, - inputAmount, - slippageBasisPoints = 1000n, - fixedOutputAmount, - createOutputMintAta = true, - protocolParams, - useExactSolAmount = true, - } = params; - - if (inputAmount === 0n) { - throw new Error("Amount cannot be zero"); - } - - const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer; - const instructions: Instruction[] = []; - - const { bondingCurve, creatorVault, tokenProgram, associatedBondingCurve } = protocolParams; - - // Derive bonding curve address - const bondingCurveAddr = - bondingCurve.account.equals(PublicKey.default) || bondingCurve.account === undefined - ? getBondingCurvePda(outputMint) - : bondingCurve.account; - - // Get token program - const tokenProgramId = tokenProgram || TOKEN_PROGRAM_ID; - - // Derive associated bonding curve - const associatedBondingCurveAddr = - associatedBondingCurve && !associatedBondingCurve.equals(PublicKey.default) - ? associatedBondingCurve - : getAssociatedTokenAddressSync(outputMint, bondingCurveAddr, true, tokenProgramId); - - // Derive user token account - const userTokenAccount = getAssociatedTokenAddressSync( - outputMint, - payerPubkey, - true, - tokenProgramId - ); - - // Derive user volume accumulator - const userVolumeAccumulator = getUserVolumeAccumulatorPda(payerPubkey); - - // Create ATA if needed - if (createOutputMintAta) { - instructions.push( - createAssociatedTokenAccountInstruction( - payerPubkey, - userTokenAccount, - payerPubkey, - outputMint, - tokenProgramId - ) - ); - } - - // Determine fee recipient - const feeRecipient = bondingCurve.isMayhemMode - ? getRandomMayhemFeeRecipient() - : FEE_RECIPIENT; - - // Derive bonding curve v2 - const bondingCurveV2 = getBondingCurveV2Pda(outputMint); - - // Track volume for cashback coins - const trackVolume = bondingCurve.isCashbackCoin - ? Buffer.from([1, 1]) - : Buffer.from([1, 0]); - - // Build instruction data - let data: Buffer; - if (useExactSolAmount) { - // buy_exact_sol_in(spendable_sol_in: u64, min_tokens_out: u64, track_volume) - const minTokensOut = fixedOutputAmount - ? fixedOutputAmount - : calculateWithSlippageSell(inputAmount, slippageBasisPoints); - data = Buffer.alloc(26); - BUY_EXACT_SOL_IN_DISCRIMINATOR.copy(data, 0); - data.writeBigUInt64LE(inputAmount, 8); - data.writeBigUInt64LE(minTokensOut, 16); - trackVolume.copy(data, 24); - } else { - // buy(token_amount: u64, max_sol_cost: u64, track_volume) - const maxSolCost = calculateWithSlippageBuy(inputAmount, slippageBasisPoints); - data = Buffer.alloc(26); - BUY_DISCRIMINATOR.copy(data, 0); - data.writeBigUInt64LE(inputAmount, 8); - data.writeBigUInt64LE(maxSolCost, 16); - trackVolume.copy(data, 24); - } - - // Build accounts - const accounts: AccountMeta[] = [ - { pubkey: GLOBAL_ACCOUNT, isSigner: false, isWritable: false }, - { pubkey: feeRecipient, isSigner: false, isWritable: true }, - { pubkey: outputMint, isSigner: false, isWritable: false }, - { pubkey: bondingCurveAddr, isSigner: false, isWritable: true }, - { pubkey: associatedBondingCurveAddr, isSigner: false, isWritable: true }, - { pubkey: userTokenAccount, isSigner: false, isWritable: true }, - { pubkey: payerPubkey, isSigner: true, isWritable: true }, - { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, - { pubkey: tokenProgramId, isSigner: false, isWritable: false }, - { pubkey: creatorVault, isSigner: false, isWritable: true }, - { pubkey: EVENT_AUTHORITY, isSigner: false, isWritable: false }, - { pubkey: PUMPFUN_PROGRAM_ID, isSigner: false, isWritable: false }, - { pubkey: GLOBAL_VOLUME_ACCUMULATOR, isSigner: false, isWritable: true }, - { pubkey: userVolumeAccumulator, isSigner: false, isWritable: true }, - { pubkey: FEE_CONFIG, isSigner: false, isWritable: false }, - { pubkey: FEE_PROGRAM, isSigner: false, isWritable: false }, - { pubkey: bondingCurveV2, isSigner: false, isWritable: false }, - ]; - - instructions.push( - new Instruction({ - keys: accounts, - programId: PUMPFUN_PROGRAM_ID, - data, - }) - ); - - return instructions; -} - -/** - * Build sell instructions for PumpFun protocol - */ -export function buildSellInstructions( - params: BuildSellInstructionsParams -): Instruction[] { - const { - payer, - inputMint, - inputAmount, - slippageBasisPoints = 1000n, - fixedOutputAmount, - closeInputMintAta = false, - protocolParams, - } = params; - - if (inputAmount === 0n) { - throw new Error("Amount cannot be zero"); - } - - const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer; - const instructions: Instruction[] = []; - - const { bondingCurve, creatorVault, tokenProgram, associatedBondingCurve, closeTokenAccountWhenSell } = protocolParams; - - // Derive bonding curve address - const bondingCurveAddr = - bondingCurve.account.equals(PublicKey.default) || bondingCurve.account === undefined - ? getBondingCurvePda(inputMint) - : bondingCurve.account; - - // Get token program - const tokenProgramId = tokenProgram || TOKEN_PROGRAM_ID; - - // Derive associated bonding curve - const associatedBondingCurveAddr = - associatedBondingCurve && !associatedBondingCurve.equals(PublicKey.default) - ? associatedBondingCurve - : getAssociatedTokenAddressSync(inputMint, bondingCurveAddr, true, tokenProgramId); - - // Derive user token account - const userTokenAccount = getAssociatedTokenAddressSync( - inputMint, - payerPubkey, - true, - tokenProgramId - ); - - // Determine fee recipient - const feeRecipient = bondingCurve.isMayhemMode - ? getRandomMayhemFeeRecipient() - : FEE_RECIPIENT; - - // Derive bonding curve v2 - const bondingCurveV2 = getBondingCurveV2Pda(inputMint); - - // Build instruction data (sell: token_amount, min_sol_output) - const minSolOutput = fixedOutputAmount - ? fixedOutputAmount - : calculateWithSlippageSell(inputAmount, slippageBasisPoints); - const data = Buffer.alloc(24); - SELL_DISCRIMINATOR.copy(data, 0); - data.writeBigUInt64LE(inputAmount, 8); - data.writeBigUInt64LE(minSolOutput, 16); - - // Build accounts - const accounts: AccountMeta[] = [ - { pubkey: GLOBAL_ACCOUNT, isSigner: false, isWritable: false }, - { pubkey: feeRecipient, isSigner: false, isWritable: true }, - { pubkey: inputMint, isSigner: false, isWritable: false }, - { pubkey: bondingCurveAddr, isSigner: false, isWritable: true }, - { pubkey: associatedBondingCurveAddr, isSigner: false, isWritable: true }, - { pubkey: userTokenAccount, isSigner: false, isWritable: true }, - { pubkey: payerPubkey, isSigner: true, isWritable: true }, - { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, - { pubkey: creatorVault, isSigner: false, isWritable: true }, - { pubkey: tokenProgramId, isSigner: false, isWritable: false }, - { pubkey: EVENT_AUTHORITY, isSigner: false, isWritable: false }, - { pubkey: PUMPFUN_PROGRAM_ID, isSigner: false, isWritable: false }, - { pubkey: FEE_CONFIG, isSigner: false, isWritable: false }, - { pubkey: FEE_PROGRAM, isSigner: false, isWritable: false }, - ]; - - // Add user volume accumulator for cashback coins - if (bondingCurve.isCashbackCoin) { - const userVolumeAccumulator = getUserVolumeAccumulatorPda(payerPubkey); - accounts.push({ pubkey: userVolumeAccumulator, isSigner: false, isWritable: true }); - } - - // Add bonding curve v2 - accounts.push({ pubkey: bondingCurveV2, isSigner: false, isWritable: false }); - - instructions.push( - new Instruction({ - keys: accounts, - programId: PUMPFUN_PROGRAM_ID, - data, - }) - ); - - // Close token account if requested - if (closeInputMintAta || closeTokenAccountWhenSell) { - instructions.push( - createCloseAccountInstruction( - userTokenAccount, - payerPubkey, - payerPubkey, - [], - tokenProgramId - ) - ); - } - - return instructions; -} - -/** - * Build claim cashback instruction for PumpFun - */ -export function buildClaimCashbackInstruction(payer: PublicKey): Instruction { - const userVolumeAccumulator = getUserVolumeAccumulatorPda(payer); - - const accounts: AccountMeta[] = [ - { pubkey: payer, isSigner: true, isWritable: true }, - { pubkey: userVolumeAccumulator, isSigner: false, isWritable: true }, - { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, - { pubkey: EVENT_AUTHORITY, isSigner: false, isWritable: false }, - { pubkey: PUMPFUN_PROGRAM_ID, isSigner: false, isWritable: false }, - ]; - - return new Instruction({ - keys: accounts, - programId: PUMPFUN_PROGRAM_ID, - data: CLAIM_CASHBACK_DISCRIMINATOR, - }); -} diff --git a/sol-trade-sdk-nodejs/src/instruction/raydium_amm_v4_builder.ts b/sol-trade-sdk-nodejs/src/instruction/raydium_amm_v4_builder.ts deleted file mode 100644 index 2ca3c0b..0000000 --- a/sol-trade-sdk-nodejs/src/instruction/raydium_amm_v4_builder.ts +++ /dev/null @@ -1,476 +0,0 @@ -/** - * Raydium AMM V4 Protocol Instruction Builder - * - * Production-grade instruction builder for Raydium AMM V4 protocol. - * Supports swap operations with WSOL and USDC pools. - */ - -import { - PublicKey, - Keypair, - AccountMeta, - Instruction, - SystemProgram, -} from "@solana/web3.js"; -import { - getAssociatedTokenAddressSync, - createAssociatedTokenAccountInstruction, - TOKEN_PROGRAM_ID, - createCloseAccountInstruction, - NATIVE_MINT, - createSyncNativeInstruction, -} from "@solana/spl-token"; - -// ============================================ -// Program IDs and Constants -// ============================================ - -/** Raydium AMM V4 program ID */ -export const RAYDIUM_AMM_V4_PROGRAM_ID = new PublicKey( - "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8" -); - -/** Authority */ -export const AUTHORITY = new PublicKey( - "5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1" -); - -/** WSOL Token Account (mint) */ -export const WSOL_TOKEN_ACCOUNT = new PublicKey( - "So11111111111111111111111111111111111111112" -); - -/** USDC Token Account (mint) */ -export const USDC_TOKEN_ACCOUNT = new PublicKey( - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" -); - -/** Fee rates */ -export const TRADE_FEE_NUMERATOR = 25n; -export const TRADE_FEE_DENOMINATOR = 10000n; -export const SWAP_FEE_NUMERATOR = 25n; -export const SWAP_FEE_DENOMINATOR = 10000n; - -// ============================================ -// Discriminators -// ============================================ - -/** Swap base in instruction discriminator (single byte) */ -export const SWAP_BASE_IN_DISCRIMINATOR: Buffer = Buffer.from([9]); - -/** Swap base out instruction discriminator (single byte) */ -export const SWAP_BASE_OUT_DISCRIMINATOR: Buffer = Buffer.from([11]); - -// ============================================ -// Seeds -// ============================================ - -export const POOL_SEED = Buffer.from("pool"); - -// ============================================ -// Helper Functions -// ============================================ - -/** - * Compute swap amount for AMM V4 - */ -export function computeSwapAmount( - coinReserve: bigint, - pcReserve: bigint, - isCoinIn: boolean, - amountIn: bigint, - slippageBasisPoints: bigint -): { amountOut: bigint; minAmountOut: bigint } { - // Apply trade fee (0.25%) - const amountInAfterFee = amountIn - (amountIn * TRADE_FEE_NUMERATOR) / TRADE_FEE_DENOMINATOR; - - // Calculate output using constant product formula - let amountOut: bigint; - if (isCoinIn) { - // Selling coin for pc: output = (pcReserve * amountIn) / (coinReserve + amountIn) - const denominator = coinReserve + amountInAfterFee; - amountOut = (pcReserve * amountInAfterFee) / denominator; - } else { - // Selling pc for coin: output = (coinReserve * amountIn) / (pcReserve + amountIn) - const denominator = pcReserve + amountInAfterFee; - amountOut = (coinReserve * amountInAfterFee) / denominator; - } - - // Apply slippage - const minAmountOut = amountOut - (amountOut * slippageBasisPoints) / 10000n; - - return { amountOut, minAmountOut }; -} - -/** - * Create instructions to wrap SOL into WSOL - */ -export function createWsolInstructions( - payer: PublicKey, - amount: bigint -): Instruction[] { - const instructions: Instruction[] = []; - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payer, true); - - // Sync native (wrap SOL) - instructions.push(createSyncNativeInstruction(wsolAta)); - - return instructions; -} - -/** - * Create instruction to close WSOL ATA and unwrap to SOL - */ -export function createCloseWsolInstruction(payer: PublicKey): Instruction { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payer, true); - return createCloseAccountInstruction(wsolAta, payer, payer, [], TOKEN_PROGRAM_ID); -} - -/** - * Create WSOL ATA instruction - */ -export function createWsolAtaInstruction(payer: PublicKey): Instruction { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payer, true); - return createAssociatedTokenAccountInstruction( - payer, - wsolAta, - payer, - NATIVE_MINT, - TOKEN_PROGRAM_ID - ); -} - -// ============================================ -// Types -// ============================================ - -export interface RaydiumAmmV4Params { - amm: PublicKey; - coinMint: PublicKey; - pcMint: PublicKey; - tokenCoin: PublicKey; - tokenPc: PublicKey; - coinReserve: bigint; - pcReserve: bigint; -} - -export interface BuildBuyInstructionsParams { - payer: Keypair | PublicKey; - outputMint: PublicKey; - inputAmount: bigint; - slippageBasisPoints?: bigint; - fixedOutputAmount?: bigint; - createInputMintAta?: boolean; - createOutputMintAta?: boolean; - closeInputMintAta?: boolean; - protocolParams: RaydiumAmmV4Params; -} - -export interface BuildSellInstructionsParams { - payer: Keypair | PublicKey; - inputMint: PublicKey; - inputAmount: bigint; - slippageBasisPoints?: bigint; - fixedOutputAmount?: bigint; - createOutputMintAta?: boolean; - closeOutputMintAta?: boolean; - closeInputMintAta?: boolean; - protocolParams: RaydiumAmmV4Params; -} - -// ============================================ -// Instruction Builders -// ============================================ - -/** - * Build buy instructions for Raydium AMM V4 protocol - */ -export function buildBuyInstructions( - params: BuildBuyInstructionsParams -): Instruction[] { - const { - payer, - outputMint, - inputAmount, - slippageBasisPoints = 1000n, - fixedOutputAmount, - createInputMintAta = true, - createOutputMintAta = true, - closeInputMintAta = false, - protocolParams, - } = params; - - if (inputAmount === 0n) { - throw new Error("Amount cannot be zero"); - } - - const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer; - const instructions: Instruction[] = []; - - const { - amm, - coinMint, - pcMint, - tokenCoin, - tokenPc, - coinReserve, - pcReserve, - } = protocolParams; - - // Check pool type - const isWsol = coinMint.equals(WSOL_TOKEN_ACCOUNT) || pcMint.equals(WSOL_TOKEN_ACCOUNT); - const isUsdc = coinMint.equals(USDC_TOKEN_ACCOUNT) || pcMint.equals(USDC_TOKEN_ACCOUNT); - - if (!isWsol && !isUsdc) { - throw new Error("Pool must contain WSOL or USDC"); - } - - // Determine swap direction - const isBaseIn = coinMint.equals(WSOL_TOKEN_ACCOUNT) || coinMint.equals(USDC_TOKEN_ACCOUNT); - - // Calculate output - const swapResult = computeSwapAmount( - coinReserve, - pcReserve, - isBaseIn, - inputAmount, - slippageBasisPoints - ); - const minimumAmountOut = fixedOutputAmount || swapResult.minAmountOut; - - // Determine input/output mints - const inputMint = isWsol ? WSOL_TOKEN_ACCOUNT : USDC_TOKEN_ACCOUNT; - - // Derive user token accounts - const userSourceTokenAccount = getAssociatedTokenAddressSync( - inputMint, - payerPubkey, - true, - TOKEN_PROGRAM_ID - ); - const userDestinationTokenAccount = getAssociatedTokenAddressSync( - outputMint, - payerPubkey, - true, - TOKEN_PROGRAM_ID - ); - - // Handle WSOL wrapping - if (createInputMintAta && isWsol) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createAssociatedTokenAccountInstruction( - payerPubkey, - wsolAta, - payerPubkey, - NATIVE_MINT, - TOKEN_PROGRAM_ID - ) - ); - instructions.push(createSyncNativeInstruction(wsolAta)); - } - - // Create output mint ATA if needed - if (createOutputMintAta) { - instructions.push( - createAssociatedTokenAccountInstruction( - payerPubkey, - userDestinationTokenAccount, - payerPubkey, - outputMint, - TOKEN_PROGRAM_ID - ) - ); - } - - // Build instruction data (1 byte discriminator + 8 bytes amountIn + 8 bytes minAmountOut) - const data = Buffer.alloc(17); - SWAP_BASE_IN_DISCRIMINATOR.copy(data, 0); - data.writeBigUInt64LE(inputAmount, 1); - data.writeBigUInt64LE(minimumAmountOut, 9); - - // Build accounts (Raydium AMM V4 has a specific account order) - const accounts: AccountMeta[] = [ - { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, - { pubkey: amm, isSigner: false, isWritable: true }, - { pubkey: AUTHORITY, isSigner: false, isWritable: false }, - { pubkey: amm, isSigner: false, isWritable: true }, // Amm Open Orders (same as amm for simplicity) - { pubkey: tokenCoin, isSigner: false, isWritable: true }, // Pool Coin Token Account - { pubkey: tokenPc, isSigner: false, isWritable: true }, // Pool Pc Token Account - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Program (placeholder) - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Market (placeholder) - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Bids (placeholder) - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Asks (placeholder) - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Event Queue (placeholder) - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Coin Vault Account (placeholder) - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Pc Vault Account (placeholder) - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Vault Signer (placeholder) - { pubkey: userSourceTokenAccount, isSigner: false, isWritable: true }, - { pubkey: userDestinationTokenAccount, isSigner: false, isWritable: true }, - { pubkey: payerPubkey, isSigner: true, isWritable: true }, - ]; - - instructions.push( - new Instruction({ - keys: accounts, - programId: RAYDIUM_AMM_V4_PROGRAM_ID, - data, - }) - ); - - // Close WSOL ATA if requested - if (closeInputMintAta && isWsol) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createCloseAccountInstruction(wsolAta, payerPubkey, payerPubkey, [], TOKEN_PROGRAM_ID) - ); - } - - return instructions; -} - -/** - * Build sell instructions for Raydium AMM V4 protocol - */ -export function buildSellInstructions( - params: BuildSellInstructionsParams -): Instruction[] { - const { - payer, - inputMint, - inputAmount, - slippageBasisPoints = 1000n, - fixedOutputAmount, - createOutputMintAta = true, - closeOutputMintAta = false, - closeInputMintAta = false, - protocolParams, - } = params; - - if (inputAmount === 0n) { - throw new Error("Amount cannot be zero"); - } - - const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer; - const instructions: Instruction[] = []; - - const { - amm, - coinMint, - pcMint, - tokenCoin, - tokenPc, - coinReserve, - pcReserve, - } = protocolParams; - - // Check pool type - const isWsol = coinMint.equals(WSOL_TOKEN_ACCOUNT) || pcMint.equals(WSOL_TOKEN_ACCOUNT); - const isUsdc = coinMint.equals(USDC_TOKEN_ACCOUNT) || pcMint.equals(USDC_TOKEN_ACCOUNT); - - if (!isWsol && !isUsdc) { - throw new Error("Pool must contain WSOL or USDC"); - } - - // Determine swap direction (selling token for WSOL/USDC means pc is output) - const isBaseIn = pcMint.equals(WSOL_TOKEN_ACCOUNT) || pcMint.equals(USDC_TOKEN_ACCOUNT); - - // Calculate output - const swapResult = computeSwapAmount( - coinReserve, - pcReserve, - isBaseIn, - inputAmount, - slippageBasisPoints - ); - const minimumAmountOut = fixedOutputAmount || swapResult.minAmountOut; - - // Determine output mint - const outputMint = isWsol ? WSOL_TOKEN_ACCOUNT : USDC_TOKEN_ACCOUNT; - - // Derive user token accounts - const userSourceTokenAccount = getAssociatedTokenAddressSync( - inputMint, - payerPubkey, - true, - TOKEN_PROGRAM_ID - ); - const userDestinationTokenAccount = getAssociatedTokenAddressSync( - outputMint, - payerPubkey, - true, - TOKEN_PROGRAM_ID - ); - - // Create WSOL ATA for receiving if needed - if (createOutputMintAta && isWsol) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createAssociatedTokenAccountInstruction( - payerPubkey, - wsolAta, - payerPubkey, - NATIVE_MINT, - TOKEN_PROGRAM_ID - ) - ); - } - - // Build instruction data - const data = Buffer.alloc(17); - SWAP_BASE_IN_DISCRIMINATOR.copy(data, 0); - data.writeBigUInt64LE(inputAmount, 1); - data.writeBigUInt64LE(minimumAmountOut, 9); - - // Build accounts - const accounts: AccountMeta[] = [ - { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, - { pubkey: amm, isSigner: false, isWritable: true }, - { pubkey: AUTHORITY, isSigner: false, isWritable: false }, - { pubkey: amm, isSigner: false, isWritable: true }, // Amm Open Orders - { pubkey: tokenCoin, isSigner: false, isWritable: true }, // Pool Coin Token Account - { pubkey: tokenPc, isSigner: false, isWritable: true }, // Pool Pc Token Account - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Program - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Market - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Bids - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Asks - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Event Queue - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Coin Vault Account - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Pc Vault Account - { pubkey: amm, isSigner: false, isWritable: false }, // Serum Vault Signer - { pubkey: userSourceTokenAccount, isSigner: false, isWritable: true }, - { pubkey: userDestinationTokenAccount, isSigner: false, isWritable: true }, - { pubkey: payerPubkey, isSigner: true, isWritable: true }, - ]; - - instructions.push( - new Instruction({ - keys: accounts, - programId: RAYDIUM_AMM_V4_PROGRAM_ID, - data, - }) - ); - - // Close WSOL ATA if requested - if (closeOutputMintAta && isWsol) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createCloseAccountInstruction(wsolAta, payerPubkey, payerPubkey, [], TOKEN_PROGRAM_ID) - ); - } - - // Close input token ATA if requested - if (closeInputMintAta) { - instructions.push( - createCloseAccountInstruction( - userSourceTokenAccount, - payerPubkey, - payerPubkey, - [], - TOKEN_PROGRAM_ID - ) - ); - } - - return instructions; -} diff --git a/sol-trade-sdk-nodejs/src/instruction/raydium_cpmm_builder.ts b/sol-trade-sdk-nodejs/src/instruction/raydium_cpmm_builder.ts deleted file mode 100644 index 6d511c8..0000000 --- a/sol-trade-sdk-nodejs/src/instruction/raydium_cpmm_builder.ts +++ /dev/null @@ -1,556 +0,0 @@ -/** - * Raydium CPMM (Concentrated Pool Market Maker) Protocol Instruction Builder - * - * Production-grade instruction builder for Raydium CPMM protocol. - * Supports swap operations with WSOL and USDC pools. - */ - -import { - PublicKey, - Keypair, - AccountMeta, - Instruction, - SystemProgram, -} from "@solana/web3.js"; -import { - getAssociatedTokenAddressSync, - createAssociatedTokenAccountInstruction, - TOKEN_PROGRAM_ID, - createCloseAccountInstruction, - NATIVE_MINT, - createSyncNativeInstruction, -} from "@solana/spl-token"; - -// ============================================ -// Program IDs and Constants -// ============================================ - -/** Raydium CPMM program ID */ -export const RAYDIUM_CPMM_PROGRAM_ID = new PublicKey( - "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C" -); - -/** Authority */ -export const AUTHORITY = new PublicKey( - "GpMZbSM2GgvTKHJirzeGfMFoaZ8UR2X7F4v8vHTvxFbL" -); - -/** WSOL Token Account (mint) */ -export const WSOL_TOKEN_ACCOUNT = new PublicKey( - "So11111111111111111111111111111111111111112" -); - -/** USDC Token Account (mint) */ -export const USDC_TOKEN_ACCOUNT = new PublicKey( - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" -); - -/** Fee rates */ -export const FEE_RATE_DENOMINATOR_VALUE = 1_000_000n; -export const TRADE_FEE_RATE = 2500n; -export const CREATOR_FEE_RATE = 0n; -export const PROTOCOL_FEE_RATE = 120000n; -export const FUND_FEE_RATE = 40000n; - -// ============================================ -// Discriminators -// ============================================ - -/** Swap base in instruction discriminator */ -export const SWAP_BASE_IN_DISCRIMINATOR: Buffer = Buffer.from([ - 143, 190, 90, 218, 196, 30, 51, 222, -]); - -/** Swap base out instruction discriminator */ -export const SWAP_BASE_OUT_DISCRIMINATOR: Buffer = Buffer.from([ - 55, 217, 98, 86, 163, 74, 180, 173, -]); - -// ============================================ -// Seeds -// ============================================ - -export const POOL_SEED = Buffer.from("pool"); -export const POOL_VAULT_SEED = Buffer.from("pool_vault"); -export const OBSERVATION_STATE_SEED = Buffer.from("observation"); - -// ============================================ -// PDA Derivation Functions -// ============================================ - -/** - * Derive the pool PDA for given config and mints - */ -export function getPoolPda( - ammConfig: PublicKey, - mint1: PublicKey, - mint2: PublicKey -): PublicKey { - const [pda] = PublicKey.findProgramAddressSync( - [POOL_SEED, ammConfig.toBuffer(), mint1.toBuffer(), mint2.toBuffer()], - RAYDIUM_CPMM_PROGRAM_ID - ); - return pda; -} - -/** - * Derive the vault PDA for a pool and mint - */ -export function getVaultPda(poolState: PublicKey, mint: PublicKey): PublicKey { - const [pda] = PublicKey.findProgramAddressSync( - [POOL_VAULT_SEED, poolState.toBuffer(), mint.toBuffer()], - RAYDIUM_CPMM_PROGRAM_ID - ); - return pda; -} - -/** - * Derive the observation state PDA for a pool - */ -export function getObservationStatePda(poolState: PublicKey): PublicKey { - const [pda] = PublicKey.findProgramAddressSync( - [OBSERVATION_STATE_SEED, poolState.toBuffer()], - RAYDIUM_CPMM_PROGRAM_ID - ); - return pda; -} - -// ============================================ -// Helper Functions -// ============================================ - -/** - * Compute swap amount for CPMM - */ -export function computeSwapAmount( - baseReserve: bigint, - quoteReserve: bigint, - isBaseIn: boolean, - amountIn: bigint, - slippageBasisPoints: bigint -): { amountOut: bigint; minAmountOut: bigint } { - // Apply trade fee (0.25%) - const feeRate = TRADE_FEE_RATE; - const feeDenominator = FEE_RATE_DENOMINATOR_VALUE; - const amountInAfterFee = amountIn - (amountIn * feeRate) / feeDenominator; - - // Calculate output using constant product formula - let amountOut: bigint; - if (isBaseIn) { - // Selling base for quote: output = (quoteReserve * amountIn) / (baseReserve + amountIn) - const denominator = baseReserve + amountInAfterFee; - amountOut = (quoteReserve * amountInAfterFee) / denominator; - } else { - // Selling quote for base: output = (baseReserve * amountIn) / (quoteReserve + amountIn) - const denominator = quoteReserve + amountInAfterFee; - amountOut = (baseReserve * amountInAfterFee) / denominator; - } - - // Apply slippage - const minAmountOut = amountOut - (amountOut * slippageBasisPoints) / 10000n; - - return { amountOut, minAmountOut }; -} - -/** - * Create instructions to wrap SOL into WSOL - */ -export function createWsolInstructions( - payer: PublicKey, - amount: bigint -): Instruction[] { - const instructions: Instruction[] = []; - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payer, true); - - // Sync native (wrap SOL) - instructions.push(createSyncNativeInstruction(wsolAta)); - - return instructions; -} - -/** - * Create instruction to close WSOL ATA and unwrap to SOL - */ -export function createCloseWsolInstruction(payer: PublicKey): Instruction { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payer, true); - return createCloseAccountInstruction(wsolAta, payer, payer, [], TOKEN_PROGRAM_ID); -} - -/** - * Create WSOL ATA instruction - */ -export function createWsolAtaInstruction(payer: PublicKey): Instruction { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payer, true); - return createAssociatedTokenAccountInstruction( - payer, - wsolAta, - payer, - NATIVE_MINT, - TOKEN_PROGRAM_ID - ); -} - -// ============================================ -// Types -// ============================================ - -export interface RaydiumCpmmParams { - poolState?: PublicKey; - ammConfig: PublicKey; - baseMint: PublicKey; - quoteMint: PublicKey; - baseTokenProgram: PublicKey; - quoteTokenProgram: PublicKey; - baseVault?: PublicKey; - quoteVault?: PublicKey; - baseReserve: bigint; - quoteReserve: bigint; - observationState?: PublicKey; -} - -export interface BuildBuyInstructionsParams { - payer: Keypair | PublicKey; - outputMint: PublicKey; - inputAmount: bigint; - slippageBasisPoints?: bigint; - fixedOutputAmount?: bigint; - createInputMintAta?: boolean; - createOutputMintAta?: boolean; - closeInputMintAta?: boolean; - protocolParams: RaydiumCpmmParams; -} - -export interface BuildSellInstructionsParams { - payer: Keypair | PublicKey; - inputMint: PublicKey; - inputAmount: bigint; - slippageBasisPoints?: bigint; - fixedOutputAmount?: bigint; - createOutputMintAta?: boolean; - closeOutputMintAta?: boolean; - closeInputMintAta?: boolean; - protocolParams: RaydiumCpmmParams; -} - -// ============================================ -// Instruction Builders -// ============================================ - -/** - * Build buy instructions for Raydium CPMM protocol - */ -export function buildBuyInstructions( - params: BuildBuyInstructionsParams -): Instruction[] { - const { - payer, - outputMint, - inputAmount, - slippageBasisPoints = 1000n, - fixedOutputAmount, - createInputMintAta = true, - createOutputMintAta = true, - closeInputMintAta = false, - protocolParams, - } = params; - - if (inputAmount === 0n) { - throw new Error("Amount cannot be zero"); - } - - const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer; - const instructions: Instruction[] = []; - - const { - ammConfig, - baseMint, - quoteMint, - baseTokenProgram, - quoteTokenProgram, - baseVault, - quoteVault, - baseReserve, - quoteReserve, - observationState, - } = protocolParams; - - // Check pool type - const isWsol = baseMint.equals(WSOL_TOKEN_ACCOUNT) || quoteMint.equals(WSOL_TOKEN_ACCOUNT); - const isUsdc = baseMint.equals(USDC_TOKEN_ACCOUNT) || quoteMint.equals(USDC_TOKEN_ACCOUNT); - - if (!isWsol && !isUsdc) { - throw new Error("Pool must contain WSOL or USDC"); - } - - // Determine swap direction - const isBaseIn = baseMint.equals(WSOL_TOKEN_ACCOUNT) || baseMint.equals(USDC_TOKEN_ACCOUNT); - const mintTokenProgram = isBaseIn ? quoteTokenProgram : baseTokenProgram; - - // Derive pool state - const poolState = protocolParams.poolState && !protocolParams.poolState.equals(PublicKey.default) - ? protocolParams.poolState - : getPoolPda(ammConfig, baseMint, quoteMint); - - // Calculate output - const swapResult = computeSwapAmount( - baseReserve, - quoteReserve, - isBaseIn, - inputAmount, - slippageBasisPoints - ); - const minimumAmountOut = fixedOutputAmount || swapResult.minAmountOut; - - // Determine input/output mints - const inputMint = isWsol ? WSOL_TOKEN_ACCOUNT : USDC_TOKEN_ACCOUNT; - - // Derive user token accounts - const inputTokenAccount = getAssociatedTokenAddressSync( - inputMint, - payerPubkey, - true, - TOKEN_PROGRAM_ID - ); - const outputTokenAccount = getAssociatedTokenAddressSync( - outputMint, - payerPubkey, - true, - mintTokenProgram - ); - - // Derive vault accounts - const inputVaultAccount = getVaultPda(poolState, inputMint); - const outputVaultAccount = getVaultPda(poolState, outputMint); - - // Derive observation state - const observationStateAccount = observationState && !observationState.equals(PublicKey.default) - ? observationState - : getObservationStatePda(poolState); - - // Handle WSOL wrapping - if (createInputMintAta && isWsol) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createAssociatedTokenAccountInstruction( - payerPubkey, - wsolAta, - payerPubkey, - NATIVE_MINT, - TOKEN_PROGRAM_ID - ) - ); - instructions.push(createSyncNativeInstruction(wsolAta)); - } - - // Create output mint ATA if needed - if (createOutputMintAta) { - instructions.push( - createAssociatedTokenAccountInstruction( - payerPubkey, - outputTokenAccount, - payerPubkey, - outputMint, - mintTokenProgram - ) - ); - } - - // Build instruction data - const data = Buffer.alloc(24); - SWAP_BASE_IN_DISCRIMINATOR.copy(data, 0); - data.writeBigUInt64LE(inputAmount, 8); - data.writeBigUInt64LE(minimumAmountOut, 16); - - // Build accounts - const accounts: AccountMeta[] = [ - { pubkey: payerPubkey, isSigner: true, isWritable: true }, - { pubkey: AUTHORITY, isSigner: false, isWritable: false }, - { pubkey: ammConfig, isSigner: false, isWritable: false }, - { pubkey: poolState, isSigner: false, isWritable: true }, - { pubkey: inputTokenAccount, isSigner: false, isWritable: true }, - { pubkey: outputTokenAccount, isSigner: false, isWritable: true }, - { pubkey: inputVaultAccount, isSigner: false, isWritable: true }, - { pubkey: outputVaultAccount, isSigner: false, isWritable: true }, - { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, - { pubkey: mintTokenProgram, isSigner: false, isWritable: false }, - { pubkey: inputMint, isSigner: false, isWritable: false }, - { pubkey: outputMint, isSigner: false, isWritable: false }, - { pubkey: observationStateAccount, isSigner: false, isWritable: true }, - ]; - - instructions.push( - new Instruction({ - keys: accounts, - programId: RAYDIUM_CPMM_PROGRAM_ID, - data, - }) - ); - - // Close WSOL ATA if requested - if (closeInputMintAta && isWsol) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createCloseAccountInstruction(wsolAta, payerPubkey, payerPubkey, [], TOKEN_PROGRAM_ID) - ); - } - - return instructions; -} - -/** - * Build sell instructions for Raydium CPMM protocol - */ -export function buildSellInstructions( - params: BuildSellInstructionsParams -): Instruction[] { - const { - payer, - inputMint, - inputAmount, - slippageBasisPoints = 1000n, - fixedOutputAmount, - createOutputMintAta = true, - closeOutputMintAta = false, - closeInputMintAta = false, - protocolParams, - } = params; - - if (inputAmount === 0n) { - throw new Error("Amount cannot be zero"); - } - - const payerPubkey = payer instanceof Keypair ? payer.publicKey : payer; - const instructions: Instruction[] = []; - - const { - ammConfig, - baseMint, - quoteMint, - baseTokenProgram, - quoteTokenProgram, - baseReserve, - quoteReserve, - observationState, - } = protocolParams; - - // Check pool type - const isWsol = baseMint.equals(WSOL_TOKEN_ACCOUNT) || quoteMint.equals(WSOL_TOKEN_ACCOUNT); - const isUsdc = baseMint.equals(USDC_TOKEN_ACCOUNT) || quoteMint.equals(USDC_TOKEN_ACCOUNT); - - if (!isWsol && !isUsdc) { - throw new Error("Pool must contain WSOL or USDC"); - } - - // Determine swap direction - const isQuoteOut = quoteMint.equals(WSOL_TOKEN_ACCOUNT) || quoteMint.equals(USDC_TOKEN_ACCOUNT); - const mintTokenProgram = isQuoteOut ? baseTokenProgram : quoteTokenProgram; - - // Derive pool state - const poolState = protocolParams.poolState && !protocolParams.poolState.equals(PublicKey.default) - ? protocolParams.poolState - : getPoolPda(ammConfig, baseMint, quoteMint); - - // Calculate output - const swapResult = computeSwapAmount( - baseReserve, - quoteReserve, - isQuoteOut, - inputAmount, - slippageBasisPoints - ); - const minimumAmountOut = fixedOutputAmount || swapResult.minAmountOut; - - // Determine output mint - const outputMint = isWsol ? WSOL_TOKEN_ACCOUNT : USDC_TOKEN_ACCOUNT; - - // Derive user token accounts - const inputTokenAccount = getAssociatedTokenAddressSync( - inputMint, - payerPubkey, - true, - mintTokenProgram - ); - const outputTokenAccount = getAssociatedTokenAddressSync( - outputMint, - payerPubkey, - true, - TOKEN_PROGRAM_ID - ); - - // Derive vault accounts - const inputVaultAccount = getVaultPda(poolState, inputMint); - const outputVaultAccount = getVaultPda(poolState, outputMint); - - // Derive observation state - const observationStateAccount = observationState && !observationState.equals(PublicKey.default) - ? observationState - : getObservationStatePda(poolState); - - // Create WSOL ATA for receiving if needed - if (createOutputMintAta && isWsol) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createAssociatedTokenAccountInstruction( - payerPubkey, - wsolAta, - payerPubkey, - NATIVE_MINT, - TOKEN_PROGRAM_ID - ) - ); - } - - // Build instruction data - const data = Buffer.alloc(24); - SWAP_BASE_IN_DISCRIMINATOR.copy(data, 0); - data.writeBigUInt64LE(inputAmount, 8); - data.writeBigUInt64LE(minimumAmountOut, 16); - - // Build accounts - const accounts: AccountMeta[] = [ - { pubkey: payerPubkey, isSigner: true, isWritable: true }, - { pubkey: AUTHORITY, isSigner: false, isWritable: false }, - { pubkey: ammConfig, isSigner: false, isWritable: false }, - { pubkey: poolState, isSigner: false, isWritable: true }, - { pubkey: inputTokenAccount, isSigner: false, isWritable: true }, - { pubkey: outputTokenAccount, isSigner: false, isWritable: true }, - { pubkey: inputVaultAccount, isSigner: false, isWritable: true }, - { pubkey: outputVaultAccount, isSigner: false, isWritable: true }, - { pubkey: mintTokenProgram, isSigner: false, isWritable: false }, - { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, - { pubkey: inputMint, isSigner: false, isWritable: false }, - { pubkey: outputMint, isSigner: false, isWritable: false }, - { pubkey: observationStateAccount, isSigner: false, isWritable: true }, - ]; - - instructions.push( - new Instruction({ - keys: accounts, - programId: RAYDIUM_CPMM_PROGRAM_ID, - data, - }) - ); - - // Close WSOL ATA if requested - if (closeOutputMintAta && isWsol) { - const wsolAta = getAssociatedTokenAddressSync(NATIVE_MINT, payerPubkey, true); - instructions.push( - createCloseAccountInstruction(wsolAta, payerPubkey, payerPubkey, [], TOKEN_PROGRAM_ID) - ); - } - - // Close input token ATA if requested - if (closeInputMintAta) { - instructions.push( - createCloseAccountInstruction( - inputTokenAccount, - payerPubkey, - payerPubkey, - [], - mintTokenProgram - ) - ); - } - - return instructions; -} diff --git a/sol-trade-sdk-nodejs/tsconfig.json b/sol-trade-sdk-nodejs/tsconfig.json deleted file mode 100644 index 04044ae..0000000 --- a/sol-trade-sdk-nodejs/tsconfig.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "lib": ["ES2020"], - "declaration": true, - "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "noImplicitThis": true, - "alwaysStrict": true, - "noUnusedLocals": false, - "noUnusedParameters": false, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": false, - "inlineSourceMap": true, - "inlineSources": true, - "experimentalDecorators": true, - "strictPropertyInitialization": false, - "outDir": "./dist", - "rootDir": "./src", - "skipLibCheck": true, - "esModuleInterop": true, - "resolveJsonModule": true, - "moduleResolution": "node" - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/sol-trade-sdk-python/README.md b/sol-trade-sdk-python/README.md deleted file mode 100644 index 3ffe7b4..0000000 --- a/sol-trade-sdk-python/README.md +++ /dev/null @@ -1,80 +0,0 @@ -# Solana Trading SDK - Python - -Production-grade instruction builders for various Solana DEX protocols. - -## Supported Protocols - -- **PumpFun** - Bonding curve trading -- **PumpSwap** - Pump AMM trading -- **Bonk** - Bonk DEX trading -- **Raydium CPMM** - Raydium Concentrated Product Market Maker -- **Raydium AMM V4** - Raydium Automated Market Maker V4 -- **Meteora DAMM V2** - Meteora Dynamic AMM V2 - -## Installation - -```bash -pip install sol-trade-sdk -``` - -## Quick Start - -```python -from solders.pubkey import Pubkey -from sol_trade_sdk.instruction import ( - PumpFunParams, - build_pumpfun_buy_instructions, - get_bonding_curve_pda, -) - -# Set up parameters -payer = Pubkey.from_string("YOUR_WALLET_ADDRESS") -output_mint = Pubkey.from_string("TOKEN_MINT_ADDRESS") - -# Build parameters -params = PumpFunParams( - virtual_token_reserves=1000000000000, - virtual_sol_reserves=30000000000, - real_token_reserves=793000000000000, - creator_vault=Pubkey.from_string("CREATOR_VAULT_ADDRESS"), - token_program=Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), -) - -# Build buy instructions -instructions = build_pumpfun_buy_instructions( - payer=payer, - output_mint=output_mint, - input_amount=1000000000, # 1 SOL - params=params, - slippage_bps=500, # 5% -) -``` - -## Features - -- All program IDs and constants from Rust implementations -- Correct discriminators from Rust (not placeholder values) -- PDA derivation functions using solders.pubkey -- `build_buy_instructions` function for each protocol -- `build_sell_instructions` function for each protocol -- WSOL handling (wrap/close) -- ATA creation support -- Slippage calculation - -## Development - -```bash -# Install dev dependencies -pip install -e ".[dev]" - -# Run tests -pytest - -# Format code -black src/ -isort src/ -``` - -## License - -MIT License diff --git a/sol-trade-sdk-python/pyproject.toml b/sol-trade-sdk-python/pyproject.toml deleted file mode 100644 index 7c23aed..0000000 --- a/sol-trade-sdk-python/pyproject.toml +++ /dev/null @@ -1,53 +0,0 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[project] -name = "sol-trade-sdk" -version = "1.0.0" -description = "Production-grade instruction builders for Solana DEX protocols" -readme = "README.md" -requires-python = ">=3.8" -license = "MIT" -authors = [ - { name = "Sol Trade SDK Team" } -] -classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", -] -dependencies = [ - "solders>=0.21.0", -] - -[project.optional-dependencies] -dev = [ - "pytest>=7.0.0", - "pytest-asyncio>=0.21.0", - "black>=23.0.0", - "isort>=5.0.0", - "mypy>=1.0.0", -] - -[tool.hatch.build.targets.wheel] -packages = ["src/sol_trade_sdk"] - -[tool.black] -line-length = 100 -target-version = ["py38", "py39", "py310", "py311", "py312"] - -[tool.isort] -profile = "black" -line_length = 100 - -[tool.mypy] -python_version = "3.8" -warn_return_any = true -warn_unused_configs = true diff --git a/sol-trade-sdk-python/src/__init__.py b/sol-trade-sdk-python/src/__init__.py deleted file mode 100644 index ffd0ee4..0000000 --- a/sol-trade-sdk-python/src/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -""" -Solana Trading SDK Python - -Production-grade instruction builders for various Solana DEX protocols. -""" - -from .instruction import * - -__version__ = "1.0.0" diff --git a/sol-trade-sdk-python/src/instruction/__init__.py b/sol-trade-sdk-python/src/instruction/__init__.py deleted file mode 100644 index 141a43a..0000000 --- a/sol-trade-sdk-python/src/instruction/__init__.py +++ /dev/null @@ -1,366 +0,0 @@ -""" -Solana Trading SDK - Instruction Builders - -Production-grade instruction builders for various Solana DEX protocols: -- PumpFun -- PumpSwap -- Bonk -- Raydium CPMM -- Raydium AMM V4 -- Meteora DAMM V2 - -Each module provides: -- Program IDs and constants -- Instruction discriminators -- PDA derivation functions -- build_buy_instructions function -- build_sell_instructions function -- Protocol-specific parameter dataclasses -""" - -from .common import ( - # Program IDs - SYSTEM_PROGRAM, - TOKEN_PROGRAM, - TOKEN_PROGRAM_2022, - ASSOCIATED_TOKEN_PROGRAM, - # Token Mints - SOL_TOKEN_ACCOUNT, - WSOL_TOKEN_ACCOUNT, - USDC_TOKEN_ACCOUNT, - USD1_TOKEN_ACCOUNT, - # Constants - DEFAULT_SLIPPAGE, - # Utility Functions - get_associated_token_address, - create_associated_token_account_idempotent_instruction, - create_wsol_account_instruction, - close_wsol_account_instruction, - close_token_account_instruction, - handle_wsol, - create_wsol_ata, - close_wsol, - # Calculation Functions - calculate_with_slippage_buy, - calculate_with_slippage_sell, -) - -from .pumpfun_builder import ( - # Program IDs and Constants - PUMPFUN_PROGRAM_ID, - FEE_RECIPIENT as PUMPFUN_FEE_RECIPIENT, - GLOBAL_ACCOUNT as PUMPFUN_GLOBAL_ACCOUNT, - EVENT_AUTHORITY as PUMPFUN_EVENT_AUTHORITY, - AUTHORITY as PUMPFUN_AUTHORITY, - FEE_PROGRAM as PUMPFUN_FEE_PROGRAM, - GLOBAL_VOLUME_ACCUMULATOR as PUMPFUN_GLOBAL_VOLUME_ACCUMULATOR, - FEE_CONFIG as PUMPFUN_FEE_CONFIG, - MAYHEM_FEE_RECIPIENTS as PUMPFUN_MAYHEM_FEE_RECIPIENTS, - # Discriminators - BUY_DISCRIMINATOR as PUMPFUN_BUY_DISCRIMINATOR, - BUY_EXACT_SOL_IN_DISCRIMINATOR as PUMPFUN_BUY_EXACT_SOL_IN_DISCRIMINATOR, - SELL_DISCRIMINATOR as PUMPFUN_SELL_DISCRIMINATOR, - CLAIM_CASHBACK_DISCRIMINATOR as PUMPFUN_CLAIM_CASHBACK_DISCRIMINATOR, - # PDA Functions - get_bonding_curve_pda, - get_bonding_curve_v2_pda, - get_creator_vault_pda, - get_user_volume_accumulator_pda as get_pumpfun_user_volume_accumulator_pda, - get_creator, - get_mayhem_fee_recipient_random, - # Params - PumpFunParams, - # Calculation Functions - get_buy_token_amount_from_sol_amount, - get_sell_sol_amount_from_token_amount, - # Instruction Builders - build_buy_instructions as build_pumpfun_buy_instructions, - build_sell_instructions as build_pumpfun_sell_instructions, - claim_cashback_pumpfun_instruction, -) - -from .pumpswap_builder import ( - # Program IDs and Constants - PUMPSWAP_PROGRAM_ID, - FEE_RECIPIENT as PUMPSWAP_FEE_RECIPIENT, - GLOBAL_ACCOUNT as PUMPSWAP_GLOBAL_ACCOUNT, - EVENT_AUTHORITY as PUMPSWAP_EVENT_AUTHORITY, - ASSOCIATED_TOKEN_PROGRAM as PUMPSWAP_ASSOCIATED_TOKEN_PROGRAM, - PUMP_PROGRAM_ID, - FEE_PROGRAM as PUMPSWAP_FEE_PROGRAM, - GLOBAL_VOLUME_ACCUMULATOR as PUMPSWAP_GLOBAL_VOLUME_ACCUMULATOR, - FEE_CONFIG as PUMPSWAP_FEE_CONFIG, - DEFAULT_COIN_CREATOR_VAULT_AUTHORITY, - MAYHEM_FEE_RECIPIENTS as PUMPSWAP_MAYHEM_FEE_RECIPIENTS, - LP_FEE_BASIS_POINTS, - PROTOCOL_FEE_BASIS_POINTS, - COIN_CREATOR_FEE_BASIS_POINTS, - # Discriminators - BUY_DISCRIMINATOR as PUMPSWAP_BUY_DISCRIMINATOR, - BUY_EXACT_QUOTE_IN_DISCRIMINATOR, - SELL_DISCRIMINATOR as PUMPSWAP_SELL_DISCRIMINATOR, - CLAIM_CASHBACK_DISCRIMINATOR as PUMPSWAP_CLAIM_CASHBACK_DISCRIMINATOR, - # PDA Functions - get_pool_v2_pda, - get_pump_pool_authority_pda, - get_canonical_pool_pda, - get_user_volume_accumulator_pda as get_pumpswap_user_volume_accumulator_pda, - get_user_volume_accumulator_wsol_ata, - get_user_volume_accumulator_quote_ata, - coin_creator_vault_authority, - coin_creator_vault_ata, - fee_recipient_ata, - get_mayhem_fee_recipient_random as get_pumpswap_mayhem_fee_recipient_random, - # Params - PumpSwapParams, - # Calculation Functions - buy_quote_input_internal, - sell_base_input_internal, - # Instruction Builders - build_buy_instructions as build_pumpswap_buy_instructions, - build_sell_instructions as build_pumpswap_sell_instructions, - claim_cashback_pumpswap_instruction, -) - -from .bonk_builder import ( - # Program IDs and Constants - BONK_PROGRAM_ID, - AUTHORITY as BONK_AUTHORITY, - GLOBAL_CONFIG, - USD1_GLOBAL_CONFIG, - EVENT_AUTHORITY as BONK_EVENT_AUTHORITY, - PLATFORM_FEE_RATE, - PROTOCOL_FEE_RATE as BONK_PROTOCOL_FEE_RATE, - SHARE_FEE_RATE, - # Discriminators - BUY_EXACT_IN_DISCRIMINATOR, - SELL_EXACT_IN_DISCRIMINATOR, - # PDA Functions - get_pool_pda as get_bonk_pool_pda, - get_vault_pda as get_bonk_vault_pda, - get_platform_associated_account, - get_creator_associated_account, - # Params - BonkParams, - # Calculation Functions - get_amount_in_net, - get_amount_out as get_bonk_amount_out, - get_buy_token_amount_from_sol_amount as get_bonk_buy_token_amount_from_sol_amount, - get_sell_sol_amount_from_token_amount as get_bonk_sell_sol_amount_from_token_amount, - # Instruction Builders - build_buy_instructions as build_bonk_buy_instructions, - build_sell_instructions as build_bonk_sell_instructions, -) - -from .raydium_cpmm_builder import ( - # Program IDs and Constants - RAYDIUM_CPMM_PROGRAM_ID, - AUTHORITY as RAYDIUM_CPMM_AUTHORITY, - FEE_RATE_DENOMINATOR_VALUE, - TRADE_FEE_RATE, - CREATOR_FEE_RATE, - PROTOCOL_FEE_RATE as RAYDIUM_CPMM_PROTOCOL_FEE_RATE, - FUND_FEE_RATE, - # Discriminators - SWAP_BASE_IN_DISCRIMINATOR as RAYDIUM_CPMM_SWAP_BASE_IN_DISCRIMINATOR, - SWAP_BASE_OUT_DISCRIMINATOR as RAYDIUM_CPMM_SWAP_BASE_OUT_DISCRIMINATOR, - # PDA Functions - get_pool_pda as get_raydium_cpmm_pool_pda, - get_vault_pda as get_raydium_cpmm_vault_pda, - get_observation_state_pda, - # Params - RaydiumCpmmParams, - # Calculation Functions - compute_swap_amount as compute_raydium_cpmm_swap_amount, - # Instruction Builders - build_buy_instructions as build_raydium_cpmm_buy_instructions, - build_sell_instructions as build_raydium_cpmm_sell_instructions, -) - -from .raydium_amm_v4_builder import ( - # Program IDs and Constants - RAYDIUM_AMM_V4_PROGRAM_ID, - AUTHORITY as RAYDIUM_AMM_V4_AUTHORITY, - TRADE_FEE_NUMERATOR, - TRADE_FEE_DENOMINATOR, - SWAP_FEE_NUMERATOR, - SWAP_FEE_DENOMINATOR, - # Discriminators - SWAP_BASE_IN_DISCRIMINATOR as RAYDIUM_AMM_V4_SWAP_BASE_IN_DISCRIMINATOR, - SWAP_BASE_OUT_DISCRIMINATOR as RAYDIUM_AMM_V4_SWAP_BASE_OUT_DISCRIMINATOR, - # Params - RaydiumAmmV4Params, - # Calculation Functions - compute_swap_amount as compute_raydium_amm_v4_swap_amount, - # Instruction Builders - build_buy_instructions as build_raydium_amm_v4_buy_instructions, - build_sell_instructions as build_raydium_amm_v4_sell_instructions, -) - -from .meteora_damm_v2_builder import ( - # Program IDs and Constants - METEORA_DAMM_V2_PROGRAM_ID, - AUTHORITY as METEORA_DAMM_V2_AUTHORITY, - # Discriminators - SWAP_DISCRIMINATOR as METEORA_DAMM_V2_SWAP_DISCRIMINATOR, - # PDA Functions - get_event_authority_pda, - # Params - MeteoraDammV2Params, - # Instruction Builders - build_buy_instructions as build_meteora_damm_v2_buy_instructions, - build_sell_instructions as build_meteora_damm_v2_sell_instructions, -) - -# Version -__version__ = "1.0.0" - -__all__ = [ - # Common - "SYSTEM_PROGRAM", - "TOKEN_PROGRAM", - "TOKEN_PROGRAM_2022", - "ASSOCIATED_TOKEN_PROGRAM", - "SOL_TOKEN_ACCOUNT", - "WSOL_TOKEN_ACCOUNT", - "USDC_TOKEN_ACCOUNT", - "USD1_TOKEN_ACCOUNT", - "DEFAULT_SLIPPAGE", - "get_associated_token_address", - "create_associated_token_account_idempotent_instruction", - "create_wsol_account_instruction", - "close_wsol_account_instruction", - "close_token_account_instruction", - "handle_wsol", - "create_wsol_ata", - "close_wsol", - "calculate_with_slippage_buy", - "calculate_with_slippage_sell", - - # PumpFun - "PUMPFUN_PROGRAM_ID", - "PUMPFUN_FEE_RECIPIENT", - "PUMPFUN_GLOBAL_ACCOUNT", - "PUMPFUN_EVENT_AUTHORITY", - "PUMPFUN_AUTHORITY", - "PUMPFUN_FEE_PROGRAM", - "PUMPFUN_GLOBAL_VOLUME_ACCUMULATOR", - "PUMPFUN_FEE_CONFIG", - "PUMPFUN_MAYHEM_FEE_RECIPIENTS", - "PUMPFUN_BUY_DISCRIMINATOR", - "PUMPFUN_BUY_EXACT_SOL_IN_DISCRIMINATOR", - "PUMPFUN_SELL_DISCRIMINATOR", - "PUMPFUN_CLAIM_CASHBACK_DISCRIMINATOR", - "get_bonding_curve_pda", - "get_bonding_curve_v2_pda", - "get_creator_vault_pda", - "get_pumpfun_user_volume_accumulator_pda", - "get_creator", - "get_mayhem_fee_recipient_random", - "PumpFunParams", - "get_buy_token_amount_from_sol_amount", - "get_sell_sol_amount_from_token_amount", - "build_pumpfun_buy_instructions", - "build_pumpfun_sell_instructions", - "claim_cashback_pumpfun_instruction", - - # PumpSwap - "PUMPSWAP_PROGRAM_ID", - "PUMPSWAP_FEE_RECIPIENT", - "PUMPSWAP_GLOBAL_ACCOUNT", - "PUMPSWAP_EVENT_AUTHORITY", - "PUMPSWAP_ASSOCIATED_TOKEN_PROGRAM", - "PUMP_PROGRAM_ID", - "PUMPSWAP_FEE_PROGRAM", - "PUMPSWAP_GLOBAL_VOLUME_ACCUMULATOR", - "PUMPSWAP_FEE_CONFIG", - "DEFAULT_COIN_CREATOR_VAULT_AUTHORITY", - "PUMPSWAP_MAYHEM_FEE_RECIPIENTS", - "LP_FEE_BASIS_POINTS", - "PROTOCOL_FEE_BASIS_POINTS", - "COIN_CREATOR_FEE_BASIS_POINTS", - "PUMPSWAP_BUY_DISCRIMINATOR", - "BUY_EXACT_QUOTE_IN_DISCRIMINATOR", - "PUMPSWAP_SELL_DISCRIMINATOR", - "PUMPSWAP_CLAIM_CASHBACK_DISCRIMINATOR", - "get_pool_v2_pda", - "get_pump_pool_authority_pda", - "get_canonical_pool_pda", - "get_pumpswap_user_volume_accumulator_pda", - "get_user_volume_accumulator_wsol_ata", - "get_user_volume_accumulator_quote_ata", - "coin_creator_vault_authority", - "coin_creator_vault_ata", - "fee_recipient_ata", - "get_pumpswap_mayhem_fee_recipient_random", - "PumpSwapParams", - "buy_quote_input_internal", - "sell_base_input_internal", - "build_pumpswap_buy_instructions", - "build_pumpswap_sell_instructions", - "claim_cashback_pumpswap_instruction", - - # Bonk - "BONK_PROGRAM_ID", - "BONK_AUTHORITY", - "GLOBAL_CONFIG", - "USD1_GLOBAL_CONFIG", - "BONK_EVENT_AUTHORITY", - "PLATFORM_FEE_RATE", - "BONK_PROTOCOL_FEE_RATE", - "SHARE_FEE_RATE", - "BUY_EXACT_IN_DISCRIMINATOR", - "SELL_EXACT_IN_DISCRIMINATOR", - "get_bonk_pool_pda", - "get_bonk_vault_pda", - "get_platform_associated_account", - "get_creator_associated_account", - "BonkParams", - "get_amount_in_net", - "get_bonk_amount_out", - "get_bonk_buy_token_amount_from_sol_amount", - "get_bonk_sell_sol_amount_from_token_amount", - "build_bonk_buy_instructions", - "build_bonk_sell_instructions", - - # Raydium CPMM - "RAYDIUM_CPMM_PROGRAM_ID", - "RAYDIUM_CPMM_AUTHORITY", - "FEE_RATE_DENOMINATOR_VALUE", - "TRADE_FEE_RATE", - "CREATOR_FEE_RATE", - "RAYDIUM_CPMM_PROTOCOL_FEE_RATE", - "FUND_FEE_RATE", - "RAYDIUM_CPMM_SWAP_BASE_IN_DISCRIMINATOR", - "RAYDIUM_CPMM_SWAP_BASE_OUT_DISCRIMINATOR", - "get_raydium_cpmm_pool_pda", - "get_raydium_cpmm_vault_pda", - "get_observation_state_pda", - "RaydiumCpmmParams", - "compute_raydium_cpmm_swap_amount", - "build_raydium_cpmm_buy_instructions", - "build_raydium_cpmm_sell_instructions", - - # Raydium AMM V4 - "RAYDIUM_AMM_V4_PROGRAM_ID", - "RAYDIUM_AMM_V4_AUTHORITY", - "TRADE_FEE_NUMERATOR", - "TRADE_FEE_DENOMINATOR", - "SWAP_FEE_NUMERATOR", - "SWAP_FEE_DENOMINATOR", - "RAYDIUM_AMM_V4_SWAP_BASE_IN_DISCRIMINATOR", - "RAYDIUM_AMM_V4_SWAP_BASE_OUT_DISCRIMINATOR", - "RaydiumAmmV4Params", - "compute_raydium_amm_v4_swap_amount", - "build_raydium_amm_v4_buy_instructions", - "build_raydium_amm_v4_sell_instructions", - - # Meteora DAMM V2 - "METEORA_DAMM_V2_PROGRAM_ID", - "METEORA_DAMM_V2_AUTHORITY", - "METEORA_DAMM_V2_SWAP_DISCRIMINATOR", - "get_event_authority_pda", - "MeteoraDammV2Params", - "build_meteora_damm_v2_buy_instructions", - "build_meteora_damm_v2_sell_instructions", -] diff --git a/sol-trade-sdk-python/src/instruction/bonk_builder.py b/sol-trade-sdk-python/src/instruction/bonk_builder.py deleted file mode 100644 index 8444384..0000000 --- a/sol-trade-sdk-python/src/instruction/bonk_builder.py +++ /dev/null @@ -1,533 +0,0 @@ -""" -Bonk instruction builder for Solana trading SDK. -Production-grade implementation with all constants, discriminators, and PDA derivation functions. -""" - -from typing import List, Optional, Tuple -from dataclasses import dataclass -from solders.pubkey import Pubkey -from solders.instruction import Instruction, AccountMeta -import struct - -from .common import ( - SYSTEM_PROGRAM, - TOKEN_PROGRAM, - WSOL_TOKEN_ACCOUNT, - USD1_TOKEN_ACCOUNT, - USDC_TOKEN_ACCOUNT, - DEFAULT_SLIPPAGE, - get_associated_token_address, - create_associated_token_account_idempotent_instruction, - handle_wsol, - close_wsol, - close_token_account_instruction, - calculate_with_slippage_sell, -) - -# ============================================ -# Bonk Program ID -# ============================================ - -BONK_PROGRAM_ID: Pubkey = Pubkey.from_string("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj") - -# ============================================ -# Bonk Constants -# ============================================ - -# Authority -AUTHORITY: Pubkey = Pubkey.from_string("WLHv2UAZm6z4KyaaELi5pjdbJh6RESMva1Rnn8pJVVh") - -# Global Config (SOL pools) -GLOBAL_CONFIG: Pubkey = Pubkey.from_string("6s1xP3hpbAfFoNtUNF8mfHsjr2Bd97JxFJRWLbL6aHuX") - -# USD1 Global Config -USD1_GLOBAL_CONFIG: Pubkey = Pubkey.from_string("EPiZbnrThjyLnoQ6QQzkxeFqyL5uyg9RzNHHAudUPxBz") - -# Event Authority -EVENT_AUTHORITY: Pubkey = Pubkey.from_string("2DPAtwB8L12vrMRExbLuyGnC7n2J5LNoZQSejeQGpwkr") - -# Fee Rates -PLATFORM_FEE_RATE: int = 100 # 1% -PROTOCOL_FEE_RATE: int = 25 # 0.25% -SHARE_FEE_RATE: int = 0 # 0% - -# ============================================ -# Instruction Discriminators -# ============================================ - -BUY_EXACT_IN_DISCRIMINATOR: bytes = bytes([250, 234, 13, 123, 213, 156, 19, 236]) -SELL_EXACT_IN_DISCRIMINATOR: bytes = bytes([149, 39, 222, 155, 211, 124, 152, 26]) - -# ============================================ -# Seeds -# ============================================ - -POOL_SEED = b"pool" -POOL_VAULT_SEED = b"pool_vault" - -# ============================================ -# PDA Derivation Functions -# ============================================ - -def get_pool_pda(base_mint: Pubkey, quote_mint: Pubkey) -> Pubkey: - """ - Derive the pool PDA for a given base and quote mint. - Seeds: ["pool", base_mint, quote_mint] - """ - seeds = [POOL_SEED, bytes(base_mint), bytes(quote_mint)] - (pda, _) = Pubkey.find_program_address(seeds, BONK_PROGRAM_ID) - return pda - - -def get_vault_pda(pool_state: Pubkey, mint: Pubkey) -> Pubkey: - """ - Derive the vault PDA for a given pool and mint. - Seeds: ["pool_vault", pool_state, mint] - """ - seeds = [POOL_VAULT_SEED, bytes(pool_state), bytes(mint)] - (pda, _) = Pubkey.find_program_address(seeds, BONK_PROGRAM_ID) - return pda - - -def get_platform_associated_account(platform_config: Pubkey) -> Pubkey: - """ - Derive the platform associated account PDA. - Seeds: [platform_config, WSOL_TOKEN_ACCOUNT] - """ - seeds = [bytes(platform_config), bytes(WSOL_TOKEN_ACCOUNT)] - (pda, _) = Pubkey.find_program_address(seeds, BONK_PROGRAM_ID) - return pda - - -def get_creator_associated_account(creator: Pubkey) -> Pubkey: - """ - Derive the creator associated account PDA. - Seeds: [creator, WSOL_TOKEN_ACCOUNT] - """ - seeds = [bytes(creator), bytes(WSOL_TOKEN_ACCOUNT)] - (pda, _) = Pubkey.find_program_address(seeds, BONK_PROGRAM_ID) - return pda - - -# ============================================ -# Bonk Parameters Dataclass -# ============================================ - -@dataclass -class BonkParams: - """Parameters for Bonk protocol trading.""" - virtual_base: int = 0 - virtual_quote: int = 0 - real_base: int = 0 - real_quote: int = 0 - pool_state: Optional[Pubkey] = None # If None, will derive from mints - base_vault: Optional[Pubkey] = None - quote_vault: Optional[Pubkey] = None - mint_token_program: Pubkey = TOKEN_PROGRAM - platform_config: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - platform_associated_account: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - creator_associated_account: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - global_config: Pubkey = GLOBAL_CONFIG # Use USD1_GLOBAL_CONFIG for USD1 pools - - @property - def is_usd1_pool(self) -> bool: - """Check if this is a USD1 pool.""" - return self.global_config == USD1_GLOBAL_CONFIG - - -# ============================================ -# Bonk Calculation Functions -# ============================================ - -def get_amount_in_net( - amount_in: int, - protocol_fee_rate: int, - platform_fee_rate: int, - share_fee_rate: int, -) -> int: - """Calculate net input amount after fees.""" - protocol_fee = (amount_in * protocol_fee_rate) // 10000 - platform_fee = (amount_in * platform_fee_rate) // 10000 - share_fee = (amount_in * share_fee_rate) // 10000 - return amount_in - protocol_fee - platform_fee - share_fee - - -def get_amount_out( - amount_in: int, - protocol_fee_rate: int, - platform_fee_rate: int, - share_fee_rate: int, - virtual_base: int, - virtual_quote: int, - real_base: int, - real_quote: int, - slippage_bps: int, -) -> int: - """ - Calculate output amount for a given input amount on Bonk. - """ - amount_in_net = get_amount_in_net( - amount_in, protocol_fee_rate, platform_fee_rate, share_fee_rate - ) - - input_reserve = virtual_quote + real_quote - output_reserve = virtual_base - real_base - - numerator = amount_in_net * output_reserve - denominator = input_reserve + amount_in_net - amount_out = numerator // denominator - - # Apply slippage - amount_out = amount_out - (amount_out * slippage_bps) // 10000 - return amount_out - - -def get_buy_token_amount_from_sol_amount( - amount_in: int, - virtual_base: int, - virtual_quote: int, - real_base: int, - real_quote: int, - slippage_bps: int, -) -> int: - """ - Calculate the token amount received for a given SOL amount on Bonk. - """ - return get_amount_out( - amount_in, - PROTOCOL_FEE_RATE, - PLATFORM_FEE_RATE, - SHARE_FEE_RATE, - virtual_base, - virtual_quote, - real_base, - real_quote, - slippage_bps, - ) - - -def get_sell_sol_amount_from_token_amount( - amount_in: int, - virtual_base: int, - virtual_quote: int, - real_base: int, - real_quote: int, - slippage_bps: int, -) -> int: - """ - Calculate the SOL amount received for a given token amount on Bonk. - For sell, we swap base -> quote. - """ - # For sell: base is input, quote is output - # So we swap virtual_base with virtual_quote roles - return get_amount_out( - amount_in, - PROTOCOL_FEE_RATE, - PLATFORM_FEE_RATE, - SHARE_FEE_RATE, - virtual_quote, # Swapped - virtual_base, # Swapped - real_quote, # Swapped - real_base, # Swapped - slippage_bps, - ) - - -# ============================================ -# Build Buy Instructions -# ============================================ - -def build_buy_instructions( - payer: Pubkey, - output_mint: Pubkey, - input_amount: int, - params: BonkParams, - slippage_bps: int = DEFAULT_SLIPPAGE, - create_input_ata: bool = True, - create_output_ata: bool = True, - close_input_ata: bool = False, - fixed_output_amount: Optional[int] = None, -) -> List[Instruction]: - """ - Build Bonk buy instructions. - - Args: - payer: The wallet paying for the swap - output_mint: The token mint to buy - input_amount: Amount of SOL/quote to spend - params: Bonk protocol parameters - slippage_bps: Slippage tolerance in basis points - create_input_ata: Whether to create WSOL ATA if needed - create_output_ata: Whether to create output token ATA if needed - close_input_ata: Whether to close WSOL ATA after swap - fixed_output_amount: If set, use this as exact output amount - - Returns: - List of instructions for the buy operation - """ - if input_amount == 0: - raise ValueError("Amount cannot be zero") - - instructions = [] - is_usd1_pool = params.is_usd1_pool - - # Determine quote mint (WSOL or USD1) - quote_mint = USD1_TOKEN_ACCOUNT if is_usd1_pool else WSOL_TOKEN_ACCOUNT - quote_token_mint_meta = AccountMeta(quote_mint, False, False) - - # Get pool state - pool_state = params.pool_state - if pool_state is None: - pool_state = get_pool_pda(output_mint, quote_mint) - - # Get global config - global_config = USD1_GLOBAL_CONFIG if is_usd1_pool else GLOBAL_CONFIG - global_config_meta = AccountMeta(global_config, False, False) - - # Calculate minimum output amount - if fixed_output_amount is not None: - minimum_amount_out = fixed_output_amount - else: - minimum_amount_out = get_buy_token_amount_from_sol_amount( - input_amount, - params.virtual_base, - params.virtual_quote, - params.real_base, - params.real_quote, - slippage_bps, - ) - - share_fee_rate = 0 - - # Get user token accounts - user_base_token_account = get_associated_token_address( - payer, output_mint, params.mint_token_program - ) - user_quote_token_account = get_associated_token_address( - payer, quote_mint, TOKEN_PROGRAM - ) - - # Get vaults - base_vault = params.base_vault - if base_vault is None: - base_vault = get_vault_pda(pool_state, output_mint) - - quote_vault = params.quote_vault - if quote_vault is None: - quote_vault = get_vault_pda(pool_state, quote_mint) - - # Handle WSOL for non-USD1 pools - if create_input_ata and not is_usd1_pool: - instructions.extend(handle_wsol(payer, input_amount)) - - # Create output ATA if needed - if create_output_ata: - instructions.append( - create_associated_token_account_idempotent_instruction( - payer, payer, output_mint, params.mint_token_program - ) - ) - - # Build instruction data - data = BUY_EXACT_IN_DISCRIMINATOR + struct.pack(" List[Instruction]: - """ - Build Bonk sell instructions. - - Args: - payer: The wallet paying for the swap - input_mint: The token mint to sell - input_amount: Amount of tokens to sell - params: Bonk protocol parameters - slippage_bps: Slippage tolerance in basis points - create_output_ata: Whether to create WSOL ATA for receiving SOL - close_output_ata: Whether to close WSOL ATA after swap - close_input_ata: Whether to close token ATA after swap - fixed_output_amount: If set, use this as exact output amount - - Returns: - List of instructions for the sell operation - """ - if input_amount == 0: - raise ValueError("Amount cannot be zero") - - instructions = [] - is_usd1_pool = params.is_usd1_pool - - # Determine quote mint (WSOL or USD1) - quote_mint = USD1_TOKEN_ACCOUNT if is_usd1_pool else WSOL_TOKEN_ACCOUNT - quote_token_mint_meta = AccountMeta(quote_mint, False, False) - - # Get pool state - pool_state = params.pool_state - if pool_state is None: - pool_state = get_pool_pda(input_mint, quote_mint) - - # Get global config - global_config = USD1_GLOBAL_CONFIG if is_usd1_pool else GLOBAL_CONFIG - global_config_meta = AccountMeta(global_config, False, False) - - # Calculate minimum output amount - share_fee_rate = 0 - if fixed_output_amount is not None: - minimum_amount_out = fixed_output_amount - else: - minimum_amount_out = get_sell_sol_amount_from_token_amount( - input_amount, - params.virtual_base, - params.virtual_quote, - params.real_base, - params.real_quote, - slippage_bps, - ) - - # Get user token accounts - user_base_token_account = get_associated_token_address( - payer, input_mint, params.mint_token_program - ) - user_quote_token_account = get_associated_token_address( - payer, quote_mint, TOKEN_PROGRAM - ) - - # Get vaults - base_vault = params.base_vault - if base_vault is None: - base_vault = get_vault_pda(pool_state, input_mint) - - quote_vault = params.quote_vault - if quote_vault is None: - quote_vault = get_vault_pda(pool_state, quote_mint) - - # Create WSOL ATA if needed for receiving SOL - if create_output_ata and not is_usd1_pool: - wsol_ata = get_associated_token_address(payer, WSOL_TOKEN_ACCOUNT, TOKEN_PROGRAM) - instructions.append( - create_associated_token_account_idempotent_instruction( - payer, payer, WSOL_TOKEN_ACCOUNT, TOKEN_PROGRAM - ) - ) - - # Build instruction data - data = SELL_EXACT_IN_DISCRIMINATOR + struct.pack(" Pubkey: - """ - Derive the associated token account address for a given owner and mint. - """ - from solders.pubkey import Pubkey as SolderPubkey - - seeds = [ - bytes(owner), - bytes(token_program), - bytes(mint), - ] - (ata, _) = SolderPubkey.find_program_address(seeds, ASSOCIATED_TOKEN_PROGRAM) - return ata - - -def create_associated_token_account_idempotent_instruction( - payer: Pubkey, - owner: Pubkey, - mint: Pubkey, - token_program: Pubkey = TOKEN_PROGRAM, -) -> Instruction: - """ - Create an idempotent instruction to create an associated token account. - This instruction will succeed even if the account already exists. - """ - ata = get_associated_token_address(owner, mint, token_program) - - # Discriminator for create_idempotent (Anchor IDL) - data = b"\x01" # create_idempotent discriminator (different from create) - - accounts = [ - AccountMeta(payer, True, True), # payer (signer, writable) - AccountMeta(ata, False, True), # ata (writable) - AccountMeta(owner, False, False), # owner (readonly) - AccountMeta(mint, False, False), # mint (readonly) - AccountMeta(SYSTEM_PROGRAM, False, False), # system_program (readonly) - AccountMeta(token_program, False, False), # token_program (readonly) - ] - - return Instruction(ASSOCIATED_TOKEN_PROGRAM, data, accounts) - - -def create_wsol_account_instruction( - payer: Pubkey, - amount: int, -) -> List[Instruction]: - """ - Create instructions to: - 1. Create WSOL account if needed (via ATA idempotent) - 2. Transfer SOL to the WSOL account - 3. Sync the WSOL account (close will be done separately) - - Returns a list of instructions for handling WSOL. - """ - instructions = [] - - wsol_ata = get_associated_token_address(payer, WSOL_TOKEN_ACCOUNT, TOKEN_PROGRAM) - - # Create idempotent WSOL ATA - instructions.append( - create_associated_token_account_idempotent_instruction( - payer, payer, WSOL_TOKEN_ACCOUNT, TOKEN_PROGRAM - ) - ) - - # Transfer SOL to WSOL ATA (system transfer) - # The amount is transferred to the WSOL account - from solders.system_program import transfer, TransferParams - - transfer_ix = transfer( - TransferParams( - from_pubkey=payer, - to_pubkey=wsol_ata, - lamports=amount, - ) - ) - instructions.append(transfer_ix) - - # Sync native token account (needed for wrapped SOL) - # Sync instruction: 17, 2, 218, 95, 237, 188, 186, 205 (sync_native discriminator) - sync_data = bytes([17, 2, 218, 95, 237, 188, 186, 205]) - sync_accounts = [AccountMeta(wsol_ata, False, True)] - sync_ix = Instruction(TOKEN_PROGRAM, sync_data, sync_accounts) - instructions.append(sync_ix) - - return instructions - - -def close_wsol_account_instruction( - payer: Pubkey, -) -> Instruction: - """ - Create instruction to close WSOL account and reclaim rent. - """ - wsol_ata = get_associated_token_address(payer, WSOL_TOKEN_ACCOUNT, TOKEN_PROGRAM) - - # Close account discriminator - close_data = bytes([153, 228, 76, 56, 218, 79, 98, 4]) # close_account - - accounts = [ - AccountMeta(wsol_ata, False, True), # account to close (writable) - AccountMeta(payer, False, True), # destination (writable) - AccountMeta(payer, True, False), # owner (signer) - ] - - return Instruction(TOKEN_PROGRAM, close_data, accounts) - - -def close_token_account_instruction( - token_program: Pubkey, - account: Pubkey, - destination: Pubkey, - owner: Pubkey, -) -> Instruction: - """ - Create instruction to close a token account and reclaim rent. - """ - close_data = bytes([153, 228, 76, 56, 218, 79, 98, 4]) # close_account - - accounts = [ - AccountMeta(account, False, True), # account to close (writable) - AccountMeta(destination, False, True), # destination (writable) - AccountMeta(owner, True, False), # owner (signer) - ] - - return Instruction(token_program, close_data, accounts) - - -def handle_wsol(payer: Pubkey, amount: int) -> List[Instruction]: - """ - Handle WSOL operations: create ATA, transfer SOL, sync. - This is the Python equivalent of the Rust handle_wsol function. - """ - return create_wsol_account_instruction(payer, amount) - - -def create_wsol_ata(payer: Pubkey) -> List[Instruction]: - """ - Create WSOL ATA without funding (for receiving WSOL). - """ - return [ - create_associated_token_account_idempotent_instruction( - payer, payer, WSOL_TOKEN_ACCOUNT, TOKEN_PROGRAM - ) - ] - - -def close_wsol(payer: Pubkey) -> List[Instruction]: - """ - Close WSOL ATA and reclaim rent. - """ - return [close_wsol_account_instruction(payer)] - - -# ============================================ -# Calculation Functions -# ============================================ - -def calculate_with_slippage_buy(amount: int, slippage_bps: int) -> int: - """ - Calculate maximum input amount with slippage for buy operations. - Returns amount * (10000 + slippage_bps) / 10000 - """ - return amount * (10000 + slippage_bps) // 10000 - - -def calculate_with_slippage_sell(amount: int, slippage_bps: int) -> int: - """ - Calculate minimum output amount with slippage for sell operations. - Returns amount * (10000 - slippage_bps) / 10000 - """ - return amount * (10000 - slippage_bps) // 10000 diff --git a/sol-trade-sdk-python/src/instruction/meteora_damm_v2_builder.py b/sol-trade-sdk-python/src/instruction/meteora_damm_v2_builder.py deleted file mode 100644 index cd5ef6a..0000000 --- a/sol-trade-sdk-python/src/instruction/meteora_damm_v2_builder.py +++ /dev/null @@ -1,325 +0,0 @@ -""" -Meteora DAMM V2 instruction builder for Solana trading SDK. -Production-grade implementation with all constants, discriminators, and PDA derivation functions. -""" - -from typing import List, Optional -from dataclasses import dataclass -from solders.pubkey import Pubkey -from solders.instruction import Instruction, AccountMeta -import struct - -from .common import ( - TOKEN_PROGRAM, - WSOL_TOKEN_ACCOUNT, - USDC_TOKEN_ACCOUNT, - DEFAULT_SLIPPAGE, - get_associated_token_address, - create_associated_token_account_idempotent_instruction, - handle_wsol, - close_wsol, - close_token_account_instruction, -) - -# ============================================ -# Meteora DAMM V2 Program ID -# ============================================ - -METEORA_DAMM_V2_PROGRAM_ID: Pubkey = Pubkey.from_string("cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG") - -# ============================================ -# Meteora DAMM V2 Constants -# ============================================ - -# Pool Authority -AUTHORITY: Pubkey = Pubkey.from_string("HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC") - -# ============================================ -# Instruction Discriminators -# ============================================ - -SWAP_DISCRIMINATOR: bytes = bytes([248, 198, 158, 145, 225, 117, 135, 200]) - -# ============================================ -# Seeds -# ============================================ - -EVENT_AUTHORITY_SEED = b"__event_authority" - - -# ============================================ -# PDA Derivation Functions -# ============================================ - -def get_event_authority_pda() -> Pubkey: - """ - Derive the event authority PDA. - Seeds: ["__event_authority"] - """ - seeds = [EVENT_AUTHORITY_SEED] - (pda, _) = Pubkey.find_program_address(seeds, METEORA_DAMM_V2_PROGRAM_ID) - return pda - - -# ============================================ -# Meteora DAMM V2 Parameters Dataclass -# ============================================ - -@dataclass -class MeteoraDammV2Params: - """Parameters for Meteora DAMM V2 protocol trading.""" - pool: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - token_a_vault: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - token_b_vault: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - token_a_mint: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - token_b_mint: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - token_a_program: Pubkey = TOKEN_PROGRAM - token_b_program: Pubkey = TOKEN_PROGRAM - - @property - def is_wsol(self) -> bool: - """Check if the pool contains WSOL.""" - return self.token_a_mint == WSOL_TOKEN_ACCOUNT or self.token_b_mint == WSOL_TOKEN_ACCOUNT - - @property - def is_usdc(self) -> bool: - """Check if the pool contains USDC.""" - return self.token_a_mint == USDC_TOKEN_ACCOUNT or self.token_b_mint == USDC_TOKEN_ACCOUNT - - -# ============================================ -# Build Buy Instructions -# ============================================ - -def build_buy_instructions( - payer: Pubkey, - output_mint: Pubkey, - input_amount: int, - params: MeteoraDammV2Params, - slippage_bps: int = DEFAULT_SLIPPAGE, - create_input_ata: bool = True, - create_output_ata: bool = True, - close_input_ata: bool = False, - fixed_output_amount: Optional[int] = None, -) -> List[Instruction]: - """ - Build Meteora DAMM V2 buy instructions. - - Args: - payer: The wallet paying for the swap - output_mint: The token mint to buy - input_amount: Amount of SOL/USDC to spend - params: Meteora DAMM V2 protocol parameters - slippage_bps: Slippage tolerance in basis points - create_input_ata: Whether to create WSOL ATA if needed - create_output_ata: Whether to create output token ATA if needed - close_input_ata: Whether to close WSOL ATA after swap - fixed_output_amount: MUST be set for Meteora DAMM V2 swaps - - Returns: - List of instructions for the buy operation - """ - if input_amount == 0: - raise ValueError("Amount cannot be zero") - - instructions = [] - - # Validate pool contains WSOL or USDC - if not params.is_wsol and not params.is_usdc: - raise ValueError("Pool must contain WSOL or USDC") - - # Determine if token A is input (WSOL/USDC) - is_a_in = params.token_a_mint == WSOL_TOKEN_ACCOUNT or params.token_a_mint == USDC_TOKEN_ACCOUNT - - # Meteora DAMM V2 requires fixed_output_amount - if fixed_output_amount is None: - raise ValueError("fixed_output_amount must be set for MeteoraDammV2 swap") - - minimum_amount_out = fixed_output_amount - - # Determine input/output mints and programs - input_mint = WSOL_TOKEN_ACCOUNT if params.is_wsol else USDC_TOKEN_ACCOUNT - - input_token_program = params.token_a_program if is_a_in else params.token_b_program - output_token_program = params.token_b_program if is_a_in else params.token_a_program - - # Get user token accounts - input_token_account = get_associated_token_address(payer, input_mint, TOKEN_PROGRAM) - output_token_account = get_associated_token_address(payer, output_mint, TOKEN_PROGRAM) - - # Get event authority PDA - event_authority = get_event_authority_pda() - - # Handle WSOL if needed - if create_input_ata and params.is_wsol: - instructions.extend(handle_wsol(payer, input_amount)) - - # Create output ATA if needed - if create_output_ata: - instructions.append( - create_associated_token_account_idempotent_instruction( - payer, payer, output_mint, TOKEN_PROGRAM - ) - ) - - # Build instruction data - data = SWAP_DISCRIMINATOR + struct.pack(" List[Instruction]: - """ - Build Meteora DAMM V2 sell instructions. - - Args: - payer: The wallet paying for the swap - input_mint: The token mint to sell - input_amount: Amount of tokens to sell - params: Meteora DAMM V2 protocol parameters - slippage_bps: Slippage tolerance in basis points - create_output_ata: Whether to create WSOL ATA for receiving SOL - close_output_ata: Whether to close WSOL ATA after swap - close_input_ata: Whether to close token ATA after swap - fixed_output_amount: MUST be set for Meteora DAMM V2 swaps - - Returns: - List of instructions for the sell operation - """ - if input_amount == 0: - raise ValueError("Amount cannot be zero") - - instructions = [] - - # Validate pool contains WSOL or USDC - if not params.is_wsol and not params.is_usdc: - raise ValueError("Pool must contain WSOL or USDC") - - # Determine if token B is output (WSOL/USDC) - is_a_in = params.token_b_mint == WSOL_TOKEN_ACCOUNT or params.token_b_mint == USDC_TOKEN_ACCOUNT - - # Meteora DAMM V2 requires fixed_output_amount - if fixed_output_amount is None: - raise ValueError("fixed_output_amount must be set for MeteoraDammV2 swap") - - minimum_amount_out = fixed_output_amount - - # Determine output mint (WSOL or USDC) - output_mint = WSOL_TOKEN_ACCOUNT if params.is_wsol else USDC_TOKEN_ACCOUNT - - # Get token programs based on direction - input_token_program = params.token_a_program if is_a_in else params.token_b_program - output_token_program = params.token_b_program if is_a_in else params.token_a_program - - # Get user token accounts - input_token_account = get_associated_token_address(payer, input_mint, input_token_program) - output_token_account = get_associated_token_address(payer, output_mint, TOKEN_PROGRAM) - - # Get event authority PDA - event_authority = get_event_authority_pda() - - # Create WSOL ATA if needed for receiving SOL - if create_output_ata and params.is_wsol: - instructions.append( - create_associated_token_account_idempotent_instruction( - payer, payer, WSOL_TOKEN_ACCOUNT, TOKEN_PROGRAM - ) - ) - - # Build instruction data - data = SWAP_DISCRIMINATOR + struct.pack(" Pubkey: - """ - Derive the bonding curve PDA for a given mint. - Seeds: ["bonding-curve", mint] - """ - seeds = [BONDING_CURVE_SEED, bytes(mint)] - (pda, _) = Pubkey.find_program_address(seeds, PUMPFUN_PROGRAM_ID) - return pda - - -def get_bonding_curve_v2_pda(mint: Pubkey) -> Pubkey: - """ - Derive the bonding curve v2 PDA for a given mint. - Seeds: ["bonding-curve-v2", mint] - """ - seeds = [BONDING_CURVE_V2_SEED, bytes(mint)] - (pda, _) = Pubkey.find_program_address(seeds, PUMPFUN_PROGRAM_ID) - return pda - - -def get_creator_vault_pda(creator: Pubkey) -> Pubkey: - """ - Derive the creator vault PDA for a given creator. - Seeds: ["creator-vault", creator] - """ - seeds = [CREATOR_VAULT_SEED, bytes(creator)] - (pda, _) = Pubkey.find_program_address(seeds, PUMPFUN_PROGRAM_ID) - return pda - - -def get_user_volume_accumulator_pda(user: Pubkey) -> Pubkey: - """ - Derive the user volume accumulator PDA for a given user. - Seeds: ["user_volume_accumulator", user] - """ - seeds = [USER_VOLUME_ACCUMULATOR_SEED, bytes(user)] - (pda, _) = Pubkey.find_program_address(seeds, PUMPFUN_PROGRAM_ID) - return pda - - -def get_creator(creator_vault_pda: Pubkey) -> Pubkey: - """ - Get the creator pubkey from the creator vault PDA. - Returns default pubkey if creator_vault_pda is default. - """ - default_pubkey = Pubkey.from_string("11111111111111111111111111111111") - if creator_vault_pda == default_pubkey: - return default_pubkey - return creator_vault_pda - - -def get_mayhem_fee_recipient_random() -> Pubkey: - """ - Get a random Mayhem fee recipient. - """ - return random.choice(MAYHEM_FEE_RECIPIENTS) - - -# ============================================ -# PumpFun Parameters Dataclass -# ============================================ - -@dataclass -class PumpFunParams: - """Parameters for PumpFun protocol trading.""" - bonding_curve_account: Optional[Pubkey] = None # If None, will derive from mint - virtual_token_reserves: int = 0 - virtual_sol_reserves: int = 0 - real_token_reserves: int = 0 - real_sol_reserves: int = 0 - token_total_supply: int = 0 - complete: bool = False - creator: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - is_mayhem_mode: bool = False - is_cashback_coin: bool = False - associated_bonding_curve: Optional[Pubkey] = None - creator_vault: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - token_program: Pubkey = TOKEN_PROGRAM - close_token_account_when_sell: bool = False - - -# ============================================ -# PumpFun Calculation Functions -# ============================================ - -def get_buy_token_amount_from_sol_amount( - virtual_token_reserves: int, - virtual_sol_reserves: int, - real_token_reserves: int, - creator: Pubkey, - sol_amount: int, -) -> int: - """ - Calculate the token amount received for a given SOL amount on PumpFun. - Uses the bonding curve formula. - """ - if sol_amount == 0: - return 0 - - default_pubkey = Pubkey.from_string("11111111111111111111111111111111") - is_non_zero_creator = creator != default_pubkey - - # Calculate using AMM formula - n = virtual_sol_reserves * virtual_token_reserves - i = virtual_sol_reserves + sol_amount - r = n // i + 1 - s = virtual_token_reserves - r - - # Apply creator fee if applicable - if is_non_zero_creator: - s = s - (s * 30) // 10000 # 0.30% creator fee - - return min(s, real_token_reserves) - - -def get_sell_sol_amount_from_token_amount( - virtual_token_reserves: int, - virtual_sol_reserves: int, - creator: Pubkey, - token_amount: int, -) -> int: - """ - Calculate the SOL amount received for a given token amount on PumpFun. - """ - if token_amount == 0: - return 0 - - default_pubkey = Pubkey.from_string("11111111111111111111111111111111") - is_non_zero_creator = creator != default_pubkey - - # Calculate using AMM formula - n = virtual_sol_reserves * virtual_token_reserves - i = virtual_token_reserves + token_amount - r = n // i + 1 - sol_amount = virtual_sol_reserves - r - - # Apply creator fee if applicable - if is_non_zero_creator: - sol_amount = sol_amount - (sol_amount * 30) // 10000 # 0.30% creator fee - - return sol_amount - - -# ============================================ -# Build Buy Instructions -# ============================================ - -def build_buy_instructions( - payer: Pubkey, - output_mint: Pubkey, - input_amount: int, - params: PumpFunParams, - slippage_bps: int = DEFAULT_SLIPPAGE, - create_output_ata: bool = True, - close_input_ata: bool = False, - fixed_output_amount: Optional[int] = None, - use_exact_sol_amount: bool = True, -) -> List[Instruction]: - """ - Build PumpFun buy instructions. - - Args: - payer: The wallet paying for the swap - output_mint: The token mint to buy - input_amount: Amount of SOL to spend - params: PumpFun protocol parameters - slippage_bps: Slippage tolerance in basis points - create_output_ata: Whether to create output token ATA if needed - close_input_ata: Whether to close WSOL ATA after swap - fixed_output_amount: If set, use this as exact output amount - use_exact_sol_amount: If True, use buy_exact_sol_in instruction - - Returns: - List of instructions for the buy operation - """ - if input_amount == 0: - raise ValueError("Amount cannot be zero") - - instructions = [] - - # Get bonding curve address - bonding_curve_addr = params.bonding_curve_account - if bonding_curve_addr is None: - bonding_curve_addr = get_bonding_curve_pda(output_mint) - - # Get creator from creator_vault - creator = get_creator(params.creator_vault) - - # Calculate token amount - if fixed_output_amount is not None: - buy_token_amount = fixed_output_amount - else: - buy_token_amount = get_buy_token_amount_from_sol_amount( - params.virtual_token_reserves, - params.virtual_sol_reserves, - params.real_token_reserves, - creator, - input_amount, - ) - - # Calculate max SOL cost with slippage - max_sol_cost = calculate_with_slippage_buy(input_amount, slippage_bps) - - # Get associated bonding curve - associated_bonding_curve = params.associated_bonding_curve - if associated_bonding_curve is None: - associated_bonding_curve = get_associated_token_address( - bonding_curve_addr, output_mint, params.token_program - ) - - # Get user token account - user_token_account = get_associated_token_address( - payer, output_mint, params.token_program - ) - - # Create ATA if needed - if create_output_ata: - instructions.append( - create_associated_token_account_idempotent_instruction( - payer, payer, output_mint, params.token_program - ) - ) - - # Get user volume accumulator - user_volume_accumulator = get_user_volume_accumulator_pda(payer) - - # Get bonding curve v2 - bonding_curve_v2 = get_bonding_curve_v2_pda(output_mint) - - # Determine fee recipient - if params.is_mayhem_mode: - fee_recipient = get_mayhem_fee_recipient_random() - else: - fee_recipient = FEE_RECIPIENT - - # Build instruction data - track_volume = bytes([1, 1]) if params.is_cashback_coin else bytes([1, 0]) - - if use_exact_sol_amount: - # buy_exact_sol_in(spendable_sol_in: u64, min_tokens_out: u64, track_volume) - min_tokens_out = calculate_with_slippage_sell(buy_token_amount, slippage_bps) - data = BUY_EXACT_SOL_IN_DISCRIMINATOR + struct.pack(" List[Instruction]: - """ - Build PumpFun sell instructions. - - Args: - payer: The wallet paying for the swap - input_mint: The token mint to sell - input_amount: Amount of tokens to sell - params: PumpFun protocol parameters - slippage_bps: Slippage tolerance in basis points - create_output_ata: Whether to create WSOL ATA for receiving SOL - close_output_ata: Whether to close WSOL ATA after swap - close_input_ata: Whether to close token ATA after swap - fixed_output_amount: If set, use this as exact output amount - - Returns: - List of instructions for the sell operation - """ - if input_amount == 0: - raise ValueError("Amount cannot be zero") - - instructions = [] - - # Get bonding curve address - bonding_curve_addr = params.bonding_curve_account - if bonding_curve_addr is None: - bonding_curve_addr = get_bonding_curve_pda(input_mint) - - # Get creator from creator_vault - creator = get_creator(params.creator_vault) - - # Calculate SOL amount - sol_amount = get_sell_sol_amount_from_token_amount( - params.virtual_token_reserves, - params.virtual_sol_reserves, - creator, - input_amount, - ) - - # Calculate min SOL output with slippage - if fixed_output_amount is not None: - min_sol_output = fixed_output_amount - else: - min_sol_output = calculate_with_slippage_sell(sol_amount, slippage_bps) - - # Get associated bonding curve - associated_bonding_curve = params.associated_bonding_curve - if associated_bonding_curve is None: - associated_bonding_curve = get_associated_token_address( - bonding_curve_addr, input_mint, params.token_program - ) - - # Get user token account - user_token_account = get_associated_token_address( - payer, input_mint, params.token_program - ) - - # Create WSOL ATA if needed for receiving SOL - if create_output_ata or close_output_ata: - instructions.extend( - create_associated_token_account_idempotent_instruction( - payer, payer, WSOL_TOKEN_ACCOUNT, TOKEN_PROGRAM - ) - ) - - # Determine fee recipient - if params.is_mayhem_mode: - fee_recipient = get_mayhem_fee_recipient_random() - else: - fee_recipient = FEE_RECIPIENT - - # Build instruction data - data = SELL_DISCRIMINATOR + struct.pack(" Instruction: - """ - Build instruction to claim cashback for PumpFun bonding curve. - Transfers native lamports from UserVolumeAccumulator to user. - """ - user_volume_accumulator = get_user_volume_accumulator_pda(payer) - - accounts = [ - AccountMeta(payer, True, True), # user (signer, writable) - AccountMeta(user_volume_accumulator, False, True), # user_volume_accumulator (writable) - AccountMeta(SYSTEM_PROGRAM, False, False), # system_program - AccountMeta(EVENT_AUTHORITY, False, False), # event_authority - AccountMeta(PUMPFUN_PROGRAM_ID, False, False), # program - ] - - return Instruction(PUMPFUN_PROGRAM_ID, CLAIM_CASHBACK_DISCRIMINATOR, accounts) - - -# ============================================ -# Exports -# ============================================ - -__all__ = [ - # Program IDs and Constants - "PUMPFUN_PROGRAM_ID", - "FEE_RECIPIENT", - "GLOBAL_ACCOUNT", - "EVENT_AUTHORITY", - "AUTHORITY", - "FEE_PROGRAM", - "GLOBAL_VOLUME_ACCUMULATOR", - "FEE_CONFIG", - "MAYHEM_FEE_RECIPIENTS", - # Discriminators - "BUY_DISCRIMINATOR", - "BUY_EXACT_SOL_IN_DISCRIMINATOR", - "SELL_DISCRIMINATOR", - "CLAIM_CASHBACK_DISCRIMINATOR", - # PDA Functions - "get_bonding_curve_pda", - "get_bonding_curve_v2_pda", - "get_creator_vault_pda", - "get_user_volume_accumulator_pda", - "get_creator", - "get_mayhem_fee_recipient_random", - # Params - "PumpFunParams", - # Calculation Functions - "get_buy_token_amount_from_sol_amount", - "get_sell_sol_amount_from_token_amount", - # Instruction Builders - "build_buy_instructions", - "build_sell_instructions", - "claim_cashback_pumpfun_instruction", -] diff --git a/sol-trade-sdk-python/src/instruction/pumpswap_builder.py b/sol-trade-sdk-python/src/instruction/pumpswap_builder.py deleted file mode 100644 index 7705df3..0000000 --- a/sol-trade-sdk-python/src/instruction/pumpswap_builder.py +++ /dev/null @@ -1,760 +0,0 @@ -""" -PumpSwap instruction builder for Solana trading SDK. -Production-grade implementation with all constants, discriminators, and PDA derivation functions. -""" - -from typing import List, Optional, Tuple -from dataclasses import dataclass -from solders.pubkey import Pubkey -from solders.instruction import Instruction, AccountMeta -import struct -import random - -from .common import ( - SYSTEM_PROGRAM, - TOKEN_PROGRAM, - TOKEN_PROGRAM_2022, - WSOL_TOKEN_ACCOUNT, - USDC_TOKEN_ACCOUNT, - DEFAULT_SLIPPAGE, - get_associated_token_address, - create_associated_token_account_idempotent_instruction, - handle_wsol, - close_wsol, - close_token_account_instruction, - calculate_with_slippage_sell, -) - -# ============================================ -# PumpSwap Program ID -# ============================================ - -PUMPSWAP_PROGRAM_ID: Pubkey = Pubkey.from_string("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA") - -# ============================================ -# PumpSwap Constants -# ============================================ - -# Fee Recipient -FEE_RECIPIENT: Pubkey = Pubkey.from_string("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV") - -# Global Account -GLOBAL_ACCOUNT: Pubkey = Pubkey.from_string("ADyA8hdefvWN2dbGGWFotbzWxrAvLW83WG6QCVXvJKqw") - -# Event Authority -EVENT_AUTHORITY: Pubkey = Pubkey.from_string("GS4CU59F31iL7aR2Q8zVS8DRrcRnXX1yjQ66TqNVQnaR") - -# Associated Token Program -ASSOCIATED_TOKEN_PROGRAM: Pubkey = Pubkey.from_string("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL") - -# Protocol Fee Recipient -PROTOCOL_FEE_RECIPIENT: Pubkey = Pubkey.from_string("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV") - -# Pump Program ID (Bonding Curve) -PUMP_PROGRAM_ID: Pubkey = Pubkey.from_string("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P") - -# Fee Program -FEE_PROGRAM: Pubkey = Pubkey.from_string("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ") - -# Global Volume Accumulator -GLOBAL_VOLUME_ACCUMULATOR: Pubkey = Pubkey.from_string("C2aFPdENg4A2HQsmrd5rTw5TaYBX5Ku887cWjbFKtZpw") - -# Fee Config -FEE_CONFIG: Pubkey = Pubkey.from_string("5PHirr8joyTMp9JMm6N7hNDVyEYdkzDqazxPD7RaTjx") - -# Default Coin Creator Vault Authority -DEFAULT_COIN_CREATOR_VAULT_AUTHORITY: Pubkey = Pubkey.from_string("8N3GDaZ2iwN65oxVatKTLPNooAVUJTbfiVJ1ahyqwjSk") - -# Mayhem Fee Recipients -MAYHEM_FEE_RECIPIENTS: List[Pubkey] = [ - Pubkey.from_string("GesfTA3X2arioaHp8bbKdjG9vJtskViWACZoYvxp4twS"), - Pubkey.from_string("4budycTjhs9fD6xw62VBducVTNgMgJJ5BgtKq7mAZwn6"), - Pubkey.from_string("8SBKzEQU4nLSzcwF4a74F2iaUDQyTfjGndn6qUWBnrpR"), - Pubkey.from_string("4UQeTP1T39KZ9Sfxzo3WR5skgsaP6NZa87BAkuazLEKH"), - Pubkey.from_string("8sNeir4QsLsJdYpc9RZacohhK1Y5FLU3nC5LXgYB4aa6"), - Pubkey.from_string("Fh9HmeLNUMVCvejxCtCL2DbYaRyBFVJ5xrWkLnMH6fdk"), - Pubkey.from_string("463MEnMeGyJekNZFQSTUABBEbLnvMTALbT6ZmsxAbAdq"), - Pubkey.from_string("6AUH3WEHucYZyC61hqpqYUWVto5qA5hjHuNQ32GNnNxA"), -] - -# Fee Basis Points -LP_FEE_BASIS_POINTS: int = 25 -PROTOCOL_FEE_BASIS_POINTS: int = 5 -COIN_CREATOR_FEE_BASIS_POINTS: int = 5 - -# ============================================ -# Instruction Discriminators -# ============================================ - -BUY_DISCRIMINATOR: bytes = bytes([102, 6, 61, 18, 1, 218, 235, 234]) -BUY_EXACT_QUOTE_IN_DISCRIMINATOR: bytes = bytes([198, 46, 21, 82, 180, 217, 232, 112]) -SELL_DISCRIMINATOR: bytes = bytes([51, 230, 133, 164, 1, 127, 131, 173]) -CLAIM_CASHBACK_DISCRIMINATOR: bytes = bytes([37, 58, 35, 126, 190, 53, 228, 197]) - -# ============================================ -# Seeds -# ============================================ - -GLOBAL_SEED = b"global" -MINT_AUTHORITY_SEED = b"mint-authority" -BONDING_CURVE_SEED = b"bonding-curve" -METADATA_SEED = b"metadata" -USER_VOLUME_ACCUMULATOR_SEED = b"user_volume_accumulator" -GLOBAL_VOLUME_ACCUMULATOR_SEED = b"global_volume_accumulator" -FEE_CONFIG_SEED = b"fee_config" -POOL_V2_SEED = b"pool-v2" -POOL_SEED = b"pool" -POOL_AUTHORITY_SEED = b"pool-authority" - -# ============================================ -# PDA Derivation Functions -# ============================================ - -def get_pool_v2_pda(base_mint: Pubkey) -> Pubkey: - """ - Derive the pool v2 PDA for a given base mint. - Seeds: ["pool-v2", base_mint] - """ - seeds = [POOL_V2_SEED, bytes(base_mint)] - (pda, _) = Pubkey.find_program_address(seeds, PUMPSWAP_PROGRAM_ID) - return pda - - -def get_pump_pool_authority_pda(mint: Pubkey) -> Pubkey: - """ - Derive the pump pool authority PDA for canonical pool. - Seeds: ["pool-authority", mint] - """ - seeds = [POOL_AUTHORITY_SEED, bytes(mint)] - (pda, _) = Pubkey.find_program_address(seeds, PUMP_PROGRAM_ID) - return pda - - -def get_canonical_pool_pda(mint: Pubkey) -> Pubkey: - """ - Derive the canonical pump pool PDA. - Seeds: ["pool", index=0, authority, mint, WSOL] - """ - index = 0 - authority = get_pump_pool_authority_pda(mint) - seeds = [ - POOL_SEED, - struct.pack(" Pubkey: - """ - Derive the user volume accumulator PDA for a given user. - Seeds: ["user_volume_accumulator", user] - """ - seeds = [USER_VOLUME_ACCUMULATOR_SEED, bytes(user)] - (pda, _) = Pubkey.find_program_address(seeds, PUMPSWAP_PROGRAM_ID) - return pda - - -def get_user_volume_accumulator_wsol_ata(user: Pubkey) -> Pubkey: - """ - Get the WSOL ATA of UserVolumeAccumulator for PumpSwap AMM. - """ - accumulator = get_user_volume_accumulator_pda(user) - return get_associated_token_address(accumulator, WSOL_TOKEN_ACCOUNT, TOKEN_PROGRAM) - - -def get_user_volume_accumulator_quote_ata( - user: Pubkey, - quote_mint: Pubkey, - quote_token_program: Pubkey, -) -> Pubkey: - """ - Get the quote-mint ATA of UserVolumeAccumulator (used for sell cashback). - """ - accumulator = get_user_volume_accumulator_pda(user) - return get_associated_token_address(accumulator, quote_mint, quote_token_program) - - -def coin_creator_vault_authority(coin_creator: Pubkey) -> Pubkey: - """ - Derive the coin creator vault authority PDA. - Seeds: ["creator_vault", coin_creator] - """ - seeds = [b"creator_vault", bytes(coin_creator)] - (pda, _) = Pubkey.find_program_address(seeds, PUMPSWAP_PROGRAM_ID) - return pda - - -def coin_creator_vault_ata(coin_creator: Pubkey, quote_mint: Pubkey) -> Pubkey: - """ - Get the coin creator vault ATA. - """ - authority = coin_creator_vault_authority(coin_creator) - return get_associated_token_address(authority, quote_mint, TOKEN_PROGRAM) - - -def fee_recipient_ata(fee_recipient: Pubkey, quote_mint: Pubkey) -> Pubkey: - """ - Get the fee recipient ATA. - """ - return get_associated_token_address(fee_recipient, quote_mint, TOKEN_PROGRAM) - - -def get_mayhem_fee_recipient_random() -> Tuple[Pubkey, AccountMeta]: - """ - Get a random Mayhem fee recipient and its AccountMeta. - """ - recipient = random.choice(MAYHEM_FEE_RECIPIENTS) - meta = AccountMeta(recipient, False, False) - return (recipient, meta) - - -# ============================================ -# PumpSwap Parameters Dataclass -# ============================================ - -@dataclass -class PumpSwapParams: - """Parameters for PumpSwap protocol trading.""" - pool: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - base_mint: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - quote_mint: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - pool_base_token_account: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - pool_quote_token_account: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - pool_base_token_reserves: int = 0 - pool_quote_token_reserves: int = 0 - coin_creator_vault_ata: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - coin_creator_vault_authority: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - base_token_program: Pubkey = TOKEN_PROGRAM - quote_token_program: Pubkey = TOKEN_PROGRAM - is_mayhem_mode: bool = False - is_cashback_coin: bool = False - - @property - def is_wsol(self) -> bool: - """Check if the pool contains WSOL.""" - return ( - (self.base_mint == WSOL_TOKEN_ACCOUNT and self.quote_mint != USDC_TOKEN_ACCOUNT) - or (self.quote_mint == WSOL_TOKEN_ACCOUNT and self.base_mint != USDC_TOKEN_ACCOUNT) - ) - - @property - def is_usdc(self) -> bool: - """Check if the pool contains USDC.""" - return ( - (self.base_mint == USDC_TOKEN_ACCOUNT and self.quote_mint != WSOL_TOKEN_ACCOUNT) - or (self.quote_mint == USDC_TOKEN_ACCOUNT and self.base_mint != WSOL_TOKEN_ACCOUNT) - ) - - -# ============================================ -# PumpSwap Calculation Functions -# ============================================ - -def buy_quote_input_internal( - quote_amount: int, - slippage_bps: int, - pool_base_reserves: int, - pool_quote_reserves: int, - creator: Pubkey, -) -> Tuple[int, int]: - """ - Calculate buy amounts for PumpSwap when quote is input. - Returns (base_amount_out, max_quote_amount_in). - """ - default_pubkey = Pubkey.from_string("11111111111111111111111111111111") - is_non_zero_creator = creator != default_pubkey - - # Apply LP fee - quote_amount_after_fee = quote_amount - (quote_amount * LP_FEE_BASIS_POINTS) // 10000 - - # Apply protocol fee - quote_amount_after_fee = quote_amount_after_fee - (quote_amount * PROTOCOL_FEE_BASIS_POINTS) // 10000 - - # Apply creator fee if applicable - if is_non_zero_creator: - quote_amount_after_fee = quote_amount_after_fee - (quote_amount * COIN_CREATOR_FEE_BASIS_POINTS) // 10000 - - # Calculate output using AMM formula - # out = (quote_in * base_reserves) / (quote_reserves + quote_in) - numerator = quote_amount_after_fee * pool_base_reserves - denominator = pool_quote_reserves + quote_amount_after_fee - base_amount_out = numerator // denominator - - # Calculate max quote with slippage - max_quote = int(quote_amount * (10000 + slippage_bps) / 10000) - - return (base_amount_out, max_quote) - - -def sell_base_input_internal( - base_amount: int, - slippage_bps: int, - pool_base_reserves: int, - pool_quote_reserves: int, - creator: Pubkey, -) -> Tuple[int, int]: - """ - Calculate sell amounts for PumpSwap when base is input. - Returns (min_quote_amount_out, base_amount_in). - """ - default_pubkey = Pubkey.from_string("11111111111111111111111111111111") - is_non_zero_creator = creator != default_pubkey - - # Apply LP fee - base_amount_after_fee = base_amount - (base_amount * LP_FEE_BASIS_POINTS) // 10000 - - # Apply protocol fee - base_amount_after_fee = base_amount_after_fee - (base_amount * PROTOCOL_FEE_BASIS_POINTS) // 10000 - - # Apply creator fee if applicable - if is_non_zero_creator: - base_amount_after_fee = base_amount_after_fee - (base_amount * COIN_CREATOR_FEE_BASIS_POINTS) // 10000 - - # Calculate output using AMM formula - # out = (base_in * quote_reserves) / (base_reserves + base_in) - numerator = base_amount_after_fee * pool_quote_reserves - denominator = pool_base_reserves + base_amount_after_fee - quote_amount_out = numerator // denominator - - # Apply slippage - min_quote = calculate_with_slippage_sell(quote_amount_out, slippage_bps) - - return (min_quote, base_amount) - - -# ============================================ -# Build Buy Instructions -# ============================================ - -def build_buy_instructions( - payer: Pubkey, - output_mint: Pubkey, - input_amount: int, - params: PumpSwapParams, - slippage_bps: int = DEFAULT_SLIPPAGE, - create_input_ata: bool = True, - create_output_ata: bool = True, - close_input_ata: bool = False, - fixed_output_amount: Optional[int] = None, - use_exact_quote_amount: bool = True, -) -> List[Instruction]: - """ - Build PumpSwap buy instructions. - - Args: - payer: The wallet paying for the swap - output_mint: The token mint to buy (base_mint) - input_amount: Amount of SOL/USDC to spend - params: PumpSwap protocol parameters - slippage_bps: Slippage tolerance in basis points - create_input_ata: Whether to create WSOL ATA if needed - create_output_ata: Whether to create output token ATA if needed - close_input_ata: Whether to close WSOL ATA after swap - fixed_output_amount: If set, use this as exact output amount - use_exact_quote_amount: If True, use buy_exact_quote_in instruction - - Returns: - List of instructions for the buy operation - """ - if input_amount == 0: - raise ValueError("Amount cannot be zero") - - instructions = [] - - # Validate pool contains WSOL or USDC - if not params.is_wsol and not params.is_usdc: - raise ValueError("Pool must contain WSOL or USDC") - - # Determine if quote is WSOL/USDC - quote_is_wsol_or_usdc = ( - params.quote_mint == WSOL_TOKEN_ACCOUNT or params.quote_mint == USDC_TOKEN_ACCOUNT - ) - - # Get creator - creator = Pubkey.from_string("11111111111111111111111111111111") - if params.coin_creator_vault_authority != DEFAULT_COIN_CREATOR_VAULT_AUTHORITY: - creator = params.coin_creator_vault_authority - - # Calculate amounts - if quote_is_wsol_or_usdc: - result = buy_quote_input_internal( - input_amount, - slippage_bps, - params.pool_base_token_reserves, - params.pool_quote_token_reserves, - creator, - ) - token_amount, sol_amount = result[0], result[1] - else: - result = sell_base_input_internal( - input_amount, - slippage_bps, - params.pool_base_token_reserves, - params.pool_quote_token_reserves, - creator, - ) - token_amount, sol_amount = result[1], result[0] - - if fixed_output_amount is not None: - token_amount = fixed_output_amount - - # Get user token accounts - user_base_token_account = get_associated_token_address( - payer, params.base_mint, params.base_token_program - ) - user_quote_token_account = get_associated_token_address( - payer, params.quote_mint, params.quote_token_program - ) - - # Determine fee recipient - if params.is_mayhem_mode: - fee_recipient, fee_recipient_meta = get_mayhem_fee_recipient_random() - else: - fee_recipient = FEE_RECIPIENT - fee_recipient_meta = AccountMeta(FEE_RECIPIENT, False, False) - - # Get fee recipient ATA - if params.is_mayhem_mode: - fee_recipient_ata_addr = fee_recipient_ata(fee_recipient, WSOL_TOKEN_ACCOUNT) - else: - fee_recipient_ata_addr = fee_recipient_ata(fee_recipient, params.quote_mint) - - # Handle WSOL if needed - if create_input_ata: - wrap_amount = input_amount if quote_is_wsol_or_usdc and use_exact_quote_amount else sol_amount - if params.is_wsol: - instructions.extend(handle_wsol(payer, wrap_amount)) - - # Create output ATA if needed - if create_output_ata: - output_mint_to_create = params.base_mint if quote_is_wsol_or_usdc else params.quote_mint - output_program = params.base_token_program if quote_is_wsol_or_usdc else params.quote_token_program - instructions.append( - create_associated_token_account_idempotent_instruction( - payer, payer, output_mint_to_create, output_program - ) - ) - - # Build accounts list - accounts = [ - AccountMeta(params.pool, False, True), # pool_id (writable) - AccountMeta(payer, True, True), # user (signer, writable) - AccountMeta(GLOBAL_ACCOUNT, False, False), # global (readonly) - AccountMeta(params.base_mint, False, False), # base_mint (readonly) - AccountMeta(params.quote_mint, False, False), # quote_mint (readonly) - AccountMeta(user_base_token_account, False, True), # user_base_token_account (writable) - AccountMeta(user_quote_token_account, False, True), # user_quote_token_account (writable) - AccountMeta(params.pool_base_token_account, False, True), # pool_base_token_account (writable) - AccountMeta(params.pool_quote_token_account, False, True), # pool_quote_token_account (writable) - fee_recipient_meta, # fee_recipient (readonly) - AccountMeta(fee_recipient_ata_addr, False, True), # fee_recipient_ata (writable) - AccountMeta(params.base_token_program, False, False), # base_token_program (readonly) - AccountMeta(params.quote_token_program, False, False), # quote_token_program (readonly) - AccountMeta(SYSTEM_PROGRAM, False, False), # system_program (readonly) - AccountMeta(ASSOCIATED_TOKEN_PROGRAM, False, False), # associated_token_program (readonly) - AccountMeta(EVENT_AUTHORITY, False, False), # event_authority (readonly) - AccountMeta(PUMPSWAP_PROGRAM_ID, False, False), # program (readonly) - AccountMeta(params.coin_creator_vault_ata, False, True), # coin_creator_vault_ata (writable) - AccountMeta(params.coin_creator_vault_authority, False, False), # coin_creator_vault_authority (readonly) - ] - - # Add volume accumulator accounts for WSOL/USDC pools - if quote_is_wsol_or_usdc: - accounts.append(AccountMeta(GLOBAL_VOLUME_ACCUMULATOR, False, True)) - uva = get_user_volume_accumulator_pda(payer) - accounts.append(AccountMeta(uva, False, True)) - - # Add fee accounts - accounts.append(AccountMeta(FEE_CONFIG, False, False)) - accounts.append(AccountMeta(FEE_PROGRAM, False, False)) - - # Add cashback WSOL ATA - if params.is_cashback_coin: - wsol_ata = get_user_volume_accumulator_wsol_ata(payer) - accounts.append(AccountMeta(wsol_ata, False, True)) - - # Add pool v2 PDA - pool_v2 = get_pool_v2_pda(params.base_mint) - accounts.append(AccountMeta(pool_v2, False, False)) - - # Build instruction data - track_volume = bytes([1, 1]) if params.is_cashback_coin else bytes([1, 0]) - - if quote_is_wsol_or_usdc: - if use_exact_quote_amount: - min_base_out = calculate_with_slippage_sell(token_amount, slippage_bps) - data = BUY_EXACT_QUOTE_IN_DISCRIMINATOR + struct.pack(" List[Instruction]: - """ - Build PumpSwap sell instructions. - - Args: - payer: The wallet paying for the swap - input_mint: The token mint to sell (base_mint) - input_amount: Amount of tokens to sell - params: PumpSwap protocol parameters - slippage_bps: Slippage tolerance in basis points - create_output_ata: Whether to create WSOL ATA for receiving SOL - close_output_ata: Whether to close WSOL ATA after swap - close_input_ata: Whether to close token ATA after swap - fixed_output_amount: If set, use this as exact output amount - - Returns: - List of instructions for the sell operation - """ - if input_amount == 0: - raise ValueError("Amount cannot be zero") - - instructions = [] - - # Validate pool contains WSOL or USDC - if not params.is_wsol and not params.is_usdc: - raise ValueError("Pool must contain WSOL or USDC") - - # Determine if quote is WSOL/USDC - quote_is_wsol_or_usdc = ( - params.quote_mint == WSOL_TOKEN_ACCOUNT or params.quote_mint == USDC_TOKEN_ACCOUNT - ) - - # Get creator - creator = Pubkey.from_string("11111111111111111111111111111111") - if params.coin_creator_vault_authority != DEFAULT_COIN_CREATOR_VAULT_AUTHORITY: - creator = params.coin_creator_vault_authority - - # Calculate amounts - if quote_is_wsol_or_usdc: - result = sell_base_input_internal( - input_amount, - slippage_bps, - params.pool_base_token_reserves, - params.pool_quote_token_reserves, - creator, - ) - token_amount, sol_amount = input_amount, result[0] - else: - result = buy_quote_input_internal( - input_amount, - slippage_bps, - params.pool_base_token_reserves, - params.pool_quote_token_reserves, - creator, - ) - token_amount, sol_amount = result[1], result[0] - - if fixed_output_amount is not None: - sol_amount = fixed_output_amount - - # Get user token accounts - user_base_token_account = get_associated_token_address( - payer, params.base_mint, params.base_token_program - ) - user_quote_token_account = get_associated_token_address( - payer, params.quote_mint, params.quote_token_program - ) - - # Determine fee recipient - if params.is_mayhem_mode: - fee_recipient, fee_recipient_meta = get_mayhem_fee_recipient_random() - else: - fee_recipient = FEE_RECIPIENT - fee_recipient_meta = AccountMeta(FEE_RECIPIENT, False, False) - - # Get fee recipient ATA - if params.is_mayhem_mode: - fee_recipient_ata_addr = fee_recipient_ata(fee_recipient, WSOL_TOKEN_ACCOUNT) - else: - fee_recipient_ata_addr = fee_recipient_ata(fee_recipient, params.quote_mint) - - # Create WSOL ATA if needed - if create_output_ata and params.is_wsol: - instructions.append( - create_associated_token_account_idempotent_instruction( - payer, payer, WSOL_TOKEN_ACCOUNT, TOKEN_PROGRAM - ) - ) - - # Build accounts list - accounts = [ - AccountMeta(params.pool, False, True), # pool_id (writable) - AccountMeta(payer, True, True), # user (signer, writable) - AccountMeta(GLOBAL_ACCOUNT, False, False), # global (readonly) - AccountMeta(params.base_mint, False, False), # base_mint (readonly) - AccountMeta(params.quote_mint, False, False), # quote_mint (readonly) - AccountMeta(user_base_token_account, False, True), # user_base_token_account (writable) - AccountMeta(user_quote_token_account, False, True), # user_quote_token_account (writable) - AccountMeta(params.pool_base_token_account, False, True), # pool_base_token_account (writable) - AccountMeta(params.pool_quote_token_account, False, True), # pool_quote_token_account (writable) - fee_recipient_meta, # fee_recipient (readonly) - AccountMeta(fee_recipient_ata_addr, False, True), # fee_recipient_ata (writable) - AccountMeta(params.base_token_program, False, False), # base_token_program (readonly) - AccountMeta(params.quote_token_program, False, False), # quote_token_program (readonly) - AccountMeta(SYSTEM_PROGRAM, False, False), # system_program (readonly) - AccountMeta(ASSOCIATED_TOKEN_PROGRAM, False, False), # associated_token_program (readonly) - AccountMeta(EVENT_AUTHORITY, False, False), # event_authority (readonly) - AccountMeta(PUMPSWAP_PROGRAM_ID, False, False), # program (readonly) - AccountMeta(params.coin_creator_vault_ata, False, True), # coin_creator_vault_ata (writable) - AccountMeta(params.coin_creator_vault_authority, False, False), # coin_creator_vault_authority (readonly) - ] - - # Add volume accumulator accounts for non-WSOL/USDC pools - if not quote_is_wsol_or_usdc: - accounts.append(AccountMeta(GLOBAL_VOLUME_ACCUMULATOR, False, True)) - uva = get_user_volume_accumulator_pda(payer) - accounts.append(AccountMeta(uva, False, True)) - - # Add fee accounts - accounts.append(AccountMeta(FEE_CONFIG, False, False)) - accounts.append(AccountMeta(FEE_PROGRAM, False, False)) - - # Add cashback accounts for sell - if params.is_cashback_coin: - quote_ata = get_user_volume_accumulator_quote_ata( - payer, params.quote_mint, params.quote_token_program - ) - accumulator = get_user_volume_accumulator_pda(payer) - accounts.append(AccountMeta(quote_ata, False, True)) - accounts.append(AccountMeta(accumulator, False, True)) - - # Add pool v2 PDA - pool_v2 = get_pool_v2_pda(params.base_mint) - accounts.append(AccountMeta(pool_v2, False, False)) - - # Build instruction data - if quote_is_wsol_or_usdc: - data = SELL_DISCRIMINATOR + struct.pack(" Instruction: - """ - Build instruction to claim cashback for PumpSwap AMM. - Transfers WSOL from UserVolumeAccumulator's WSOL ATA to user's WSOL ATA. - """ - user_volume_accumulator = get_user_volume_accumulator_pda(payer) - user_volume_accumulator_wsol_ata = get_user_volume_accumulator_wsol_ata(payer) - user_wsol_ata = get_associated_token_address(payer, quote_mint, quote_token_program) - - accounts = [ - AccountMeta(payer, True, True), # user (signer, writable) - AccountMeta(user_volume_accumulator, False, True), # user_volume_accumulator (writable) - AccountMeta(quote_mint, False, False), # quote_mint (readonly) - AccountMeta(quote_token_program, False, False), # quote_token_program (readonly) - AccountMeta(user_volume_accumulator_wsol_ata, False, True), # user_volume_accumulator_wsol_token_account (writable) - AccountMeta(user_wsol_ata, False, True), # user_wsol_token_account (writable) - AccountMeta(SYSTEM_PROGRAM, False, False), # system_program - AccountMeta(EVENT_AUTHORITY, False, False), # event_authority - AccountMeta(PUMPSWAP_PROGRAM_ID, False, False), # program - ] - - return Instruction(PUMPSWAP_PROGRAM_ID, CLAIM_CASHBACK_DISCRIMINATOR, accounts) - - -# ============================================ -# Exports -# ============================================ - -__all__ = [ - # Program IDs and Constants - "PUMPSWAP_PROGRAM_ID", - "FEE_RECIPIENT", - "GLOBAL_ACCOUNT", - "EVENT_AUTHORITY", - "ASSOCIATED_TOKEN_PROGRAM", - "PROTOCOL_FEE_RECIPIENT", - "PUMP_PROGRAM_ID", - "FEE_PROGRAM", - "GLOBAL_VOLUME_ACCUMULATOR", - "FEE_CONFIG", - "DEFAULT_COIN_CREATOR_VAULT_AUTHORITY", - "MAYHEM_FEE_RECIPIENTS", - "LP_FEE_BASIS_POINTS", - "PROTOCOL_FEE_BASIS_POINTS", - "COIN_CREATOR_FEE_BASIS_POINTS", - # Discriminators - "BUY_DISCRIMINATOR", - "BUY_EXACT_QUOTE_IN_DISCRIMINATOR", - "SELL_DISCRIMINATOR", - "CLAIM_CASHBACK_DISCRIMINATOR", - # PDA Functions - "get_pool_v2_pda", - "get_pump_pool_authority_pda", - "get_canonical_pool_pda", - "get_user_volume_accumulator_pda", - "get_user_volume_accumulator_wsol_ata", - "get_user_volume_accumulator_quote_ata", - "coin_creator_vault_authority", - "coin_creator_vault_ata", - "fee_recipient_ata", - "get_mayhem_fee_recipient_random", - # Params - "PumpSwapParams", - # Calculation Functions - "buy_quote_input_internal", - "sell_base_input_internal", - # Instruction Builders - "build_buy_instructions", - "build_sell_instructions", - "claim_cashback_pumpswap_instruction", -] diff --git a/sol-trade-sdk-python/src/instruction/raydium_amm_v4_builder.py b/sol-trade-sdk-python/src/instruction/raydium_amm_v4_builder.py deleted file mode 100644 index 20c2ef3..0000000 --- a/sol-trade-sdk-python/src/instruction/raydium_amm_v4_builder.py +++ /dev/null @@ -1,374 +0,0 @@ -""" -Raydium AMM V4 instruction builder for Solana trading SDK. -Production-grade implementation with all constants, discriminators, and PDA derivation functions. -""" - -from typing import List, Optional -from dataclasses import dataclass -from solders.pubkey import Pubkey -from solders.instruction import Instruction, AccountMeta -import struct - -from .common import ( - TOKEN_PROGRAM, - WSOL_TOKEN_ACCOUNT, - USDC_TOKEN_ACCOUNT, - DEFAULT_SLIPPAGE, - get_associated_token_address, - create_associated_token_account_idempotent_instruction, - handle_wsol, - close_wsol, - close_token_account_instruction, - calculate_with_slippage_sell, -) - -# ============================================ -# Raydium AMM V4 Program ID -# ============================================ - -RAYDIUM_AMM_V4_PROGRAM_ID: Pubkey = Pubkey.from_string("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8") - -# ============================================ -# Raydium AMM V4 Constants -# ============================================ - -# Authority -AUTHORITY: Pubkey = Pubkey.from_string("5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1") - -# Fee Rates -TRADE_FEE_NUMERATOR: int = 25 -TRADE_FEE_DENOMINATOR: int = 10000 -SWAP_FEE_NUMERATOR: int = 25 -SWAP_FEE_DENOMINATOR: int = 10000 - -# ============================================ -# Instruction Discriminators -# ============================================ - -# Note: Raydium AMM V4 uses single-byte discriminators -SWAP_BASE_IN_DISCRIMINATOR: bytes = bytes([9]) -SWAP_BASE_OUT_DISCRIMINATOR: bytes = bytes([11]) - -# ============================================ -# Seeds -# ============================================ - -POOL_SEED = b"pool" - - -# ============================================ -# Raydium AMM V4 Parameters Dataclass -# ============================================ - -@dataclass -class RaydiumAmmV4Params: - """Parameters for Raydium AMM V4 protocol trading.""" - amm: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - coin_mint: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - pc_mint: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - token_coin: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - token_pc: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - coin_reserve: int = 0 - pc_reserve: int = 0 - - @property - def is_wsol(self) -> bool: - """Check if the pool contains WSOL.""" - return self.coin_mint == WSOL_TOKEN_ACCOUNT or self.pc_mint == WSOL_TOKEN_ACCOUNT - - @property - def is_usdc(self) -> bool: - """Check if the pool contains USDC.""" - return self.coin_mint == USDC_TOKEN_ACCOUNT or self.pc_mint == USDC_TOKEN_ACCOUNT - - -# ============================================ -# Raydium AMM V4 Calculation Functions -# ============================================ - -def compute_swap_amount( - coin_reserve: int, - pc_reserve: int, - is_coin_in: bool, - amount_in: int, - slippage_bps: int, -) -> tuple: - """ - Compute swap output amount for Raydium AMM V4. - Returns (amount_out, min_amount_out). - """ - # Apply trade fee (0.25%) - amount_in_after_fee = amount_in - (amount_in * TRADE_FEE_NUMERATOR) // TRADE_FEE_DENOMINATOR - - if is_coin_in: - # Coin -> PC - # out = (amount_in * pc_reserve) / (coin_reserve + amount_in) - numerator = amount_in_after_fee * pc_reserve - denominator = coin_reserve + amount_in_after_fee - amount_out = numerator // denominator - else: - # PC -> Coin - # out = (amount_in * coin_reserve) / (pc_reserve + amount_in) - numerator = amount_in_after_fee * coin_reserve - denominator = pc_reserve + amount_in_after_fee - amount_out = numerator // denominator - - # Apply slippage - min_amount_out = calculate_with_slippage_sell(amount_out, slippage_bps) - - return (amount_out, min_amount_out) - - -# ============================================ -# Build Buy Instructions -# ============================================ - -def build_buy_instructions( - payer: Pubkey, - output_mint: Pubkey, - input_amount: int, - params: RaydiumAmmV4Params, - slippage_bps: int = DEFAULT_SLIPPAGE, - create_input_ata: bool = True, - create_output_ata: bool = True, - close_input_ata: bool = False, - fixed_output_amount: Optional[int] = None, -) -> List[Instruction]: - """ - Build Raydium AMM V4 buy instructions. - - Args: - payer: The wallet paying for the swap - output_mint: The token mint to buy - input_amount: Amount of SOL/USDC to spend - params: Raydium AMM V4 protocol parameters - slippage_bps: Slippage tolerance in basis points - create_input_ata: Whether to create WSOL ATA if needed - create_output_ata: Whether to create output token ATA if needed - close_input_ata: Whether to close WSOL ATA after swap - fixed_output_amount: If set, use this as exact output amount - - Returns: - List of instructions for the buy operation - """ - if input_amount == 0: - raise ValueError("Amount cannot be zero") - - instructions = [] - - # Validate pool contains WSOL or USDC - if not params.is_wsol and not params.is_usdc: - raise ValueError("Pool must contain WSOL or USDC") - - # Determine if coin is input (WSOL/USDC) - is_coin_in = params.coin_mint == WSOL_TOKEN_ACCOUNT or params.coin_mint == USDC_TOKEN_ACCOUNT - - # Calculate swap amount - _, min_amount_out = compute_swap_amount( - params.coin_reserve, - params.pc_reserve, - is_coin_in, - input_amount, - slippage_bps, - ) - - if fixed_output_amount is not None: - minimum_amount_out = fixed_output_amount - else: - minimum_amount_out = min_amount_out - - # Determine input mint (WSOL or USDC) - input_mint = WSOL_TOKEN_ACCOUNT if params.is_wsol else USDC_TOKEN_ACCOUNT - - # Get user token accounts - user_source_token_account = get_associated_token_address(payer, input_mint, TOKEN_PROGRAM) - user_destination_token_account = get_associated_token_address(payer, output_mint, TOKEN_PROGRAM) - - # Handle WSOL if needed - if create_input_ata and params.is_wsol: - instructions.extend(handle_wsol(payer, input_amount)) - - # Create output ATA if needed - if create_output_ata: - instructions.append( - create_associated_token_account_idempotent_instruction( - payer, payer, output_mint, TOKEN_PROGRAM - ) - ) - - # Build instruction data (1 byte discriminator + 8 bytes amount_in + 8 bytes min_out) - data = SWAP_BASE_IN_DISCRIMINATOR + struct.pack(" List[Instruction]: - """ - Build Raydium AMM V4 sell instructions. - - Args: - payer: The wallet paying for the swap - input_mint: The token mint to sell - input_amount: Amount of tokens to sell - params: Raydium AMM V4 protocol parameters - slippage_bps: Slippage tolerance in basis points - create_output_ata: Whether to create WSOL ATA for receiving SOL - close_output_ata: Whether to close WSOL ATA after swap - close_input_ata: Whether to close token ATA after swap - fixed_output_amount: If set, use this as exact output amount - - Returns: - List of instructions for the sell operation - """ - if input_amount == 0: - raise ValueError("Amount cannot be zero") - - instructions = [] - - # Validate pool contains WSOL or USDC - if not params.is_wsol and not params.is_usdc: - raise ValueError("Pool must contain WSOL or USDC") - - # Determine if pc is output (WSOL/USDC) - # For sell: is_base_in = True means we're selling PC to get Coin - # is_base_in = False means we're selling Coin to get PC - is_pc_out = params.pc_mint == WSOL_TOKEN_ACCOUNT or params.pc_mint == USDC_TOKEN_ACCOUNT - - # Calculate swap amount (is_pc_out reversed because we're selling to get WSOL/USDC) - _, min_amount_out = compute_swap_amount( - params.coin_reserve, - params.pc_reserve, - not is_pc_out, # Reversed for sell - input_amount, - slippage_bps, - ) - - if fixed_output_amount is not None: - minimum_amount_out = fixed_output_amount - else: - minimum_amount_out = min_amount_out - - # Determine output mint (WSOL or USDC) - output_mint = WSOL_TOKEN_ACCOUNT if params.is_wsol else USDC_TOKEN_ACCOUNT - - # Get user token accounts - user_source_token_account = get_associated_token_address(payer, input_mint, TOKEN_PROGRAM) - user_destination_token_account = get_associated_token_address(payer, output_mint, TOKEN_PROGRAM) - - # Create WSOL ATA if needed for receiving SOL - if create_output_ata and params.is_wsol: - instructions.append( - create_associated_token_account_idempotent_instruction( - payer, payer, WSOL_TOKEN_ACCOUNT, TOKEN_PROGRAM - ) - ) - - # Build instruction data (1 byte discriminator + 8 bytes amount_in + 8 bytes min_out) - data = SWAP_BASE_IN_DISCRIMINATOR + struct.pack(" Pubkey: - """ - Derive the pool PDA for a given amm_config and two mints. - Seeds: ["pool", amm_config, mint1, mint2] - """ - seeds = [POOL_SEED, bytes(amm_config), bytes(mint1), bytes(mint2)] - (pda, _) = Pubkey.find_program_address(seeds, RAYDIUM_CPMM_PROGRAM_ID) - return pda - - -def get_vault_pda(pool_state: Pubkey, mint: Pubkey) -> Pubkey: - """ - Derive the vault PDA for a given pool and mint. - Seeds: ["pool_vault", pool_state, mint] - """ - seeds = [POOL_VAULT_SEED, bytes(pool_state), bytes(mint)] - (pda, _) = Pubkey.find_program_address(seeds, RAYDIUM_CPMM_PROGRAM_ID) - return pda - - -def get_observation_state_pda(pool_state: Pubkey) -> Pubkey: - """ - Derive the observation state PDA for a given pool. - Seeds: ["observation", pool_state] - """ - seeds = [OBSERVATION_STATE_SEED, bytes(pool_state)] - (pda, _) = Pubkey.find_program_address(seeds, RAYDIUM_CPMM_PROGRAM_ID) - return pda - - -# ============================================ -# Raydium CPMM Parameters Dataclass -# ============================================ - -@dataclass -class RaydiumCpmmParams: - """Parameters for Raydium CPMM protocol trading.""" - pool_state: Optional[Pubkey] = None # If None, will derive from mints and amm_config - amm_config: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - base_mint: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - quote_mint: Pubkey = Pubkey.from_string("11111111111111111111111111111111") - base_reserve: int = 0 - quote_reserve: int = 0 - base_vault: Optional[Pubkey] = None - quote_vault: Optional[Pubkey] = None - base_token_program: Pubkey = TOKEN_PROGRAM - quote_token_program: Pubkey = TOKEN_PROGRAM - observation_state: Optional[Pubkey] = None - - @property - def is_wsol(self) -> bool: - """Check if the pool contains WSOL.""" - return self.base_mint == WSOL_TOKEN_ACCOUNT or self.quote_mint == WSOL_TOKEN_ACCOUNT - - @property - def is_usdc(self) -> bool: - """Check if the pool contains USDC.""" - return self.base_mint == USDC_TOKEN_ACCOUNT or self.quote_mint == USDC_TOKEN_ACCOUNT - - -# ============================================ -# Raydium CPMM Calculation Functions -# ============================================ - -def compute_swap_amount( - base_reserve: int, - quote_reserve: int, - is_base_in: bool, - amount_in: int, - slippage_bps: int, -) -> tuple: - """ - Compute swap output amount for Raydium CPMM. - Returns (amount_out, min_amount_out). - """ - # Apply trade fee (0.25%) - fee_rate = TRADE_FEE_RATE / FEE_RATE_DENOMINATOR_VALUE - amount_in_after_fee = int(amount_in * (1 - fee_rate)) - - if is_base_in: - # Base -> Quote - # out = (amount_in * quote_reserve) / (base_reserve + amount_in) - numerator = amount_in_after_fee * quote_reserve - denominator = base_reserve + amount_in_after_fee - amount_out = numerator // denominator - else: - # Quote -> Base - # out = (amount_in * base_reserve) / (quote_reserve + amount_in) - numerator = amount_in_after_fee * base_reserve - denominator = quote_reserve + amount_in_after_fee - amount_out = numerator // denominator - - # Apply slippage - min_amount_out = calculate_with_slippage_sell(amount_out, slippage_bps) - - return (amount_out, min_amount_out) - - -# ============================================ -# Build Buy Instructions -# ============================================ - -def build_buy_instructions( - payer: Pubkey, - output_mint: Pubkey, - input_amount: int, - params: RaydiumCpmmParams, - slippage_bps: int = DEFAULT_SLIPPAGE, - create_input_ata: bool = True, - create_output_ata: bool = True, - close_input_ata: bool = False, - fixed_output_amount: Optional[int] = None, -) -> List[Instruction]: - """ - Build Raydium CPMM buy instructions. - - Args: - payer: The wallet paying for the swap - output_mint: The token mint to buy - input_amount: Amount of SOL/USDC to spend - params: Raydium CPMM protocol parameters - slippage_bps: Slippage tolerance in basis points - create_input_ata: Whether to create WSOL ATA if needed - create_output_ata: Whether to create output token ATA if needed - close_input_ata: Whether to close WSOL ATA after swap - fixed_output_amount: If set, use this as exact output amount - - Returns: - List of instructions for the buy operation - """ - if input_amount == 0: - raise ValueError("Amount cannot be zero") - - instructions = [] - - # Validate pool contains WSOL or USDC - if not params.is_wsol and not params.is_usdc: - raise ValueError("Pool must contain WSOL or USDC") - - # Get pool state - pool_state = params.pool_state - if pool_state is None: - pool_state = get_pool_pda(params.amm_config, params.base_mint, params.quote_mint) - - # Determine if base is input (WSOL/USDC) - is_base_in = params.base_mint == WSOL_TOKEN_ACCOUNT or params.base_mint == USDC_TOKEN_ACCOUNT - - # Get output token program - mint_token_program = params.quote_token_program if is_base_in else params.base_token_program - - # Calculate swap amount - _, min_amount_out = compute_swap_amount( - params.base_reserve, - params.quote_reserve, - is_base_in, - input_amount, - slippage_bps, - ) - - if fixed_output_amount is not None: - minimum_amount_out = fixed_output_amount - else: - minimum_amount_out = min_amount_out - - # Determine input mint (WSOL or USDC) - input_mint = WSOL_TOKEN_ACCOUNT if params.is_wsol else USDC_TOKEN_ACCOUNT - - # Get user token accounts - input_token_account = get_associated_token_address(payer, input_mint, TOKEN_PROGRAM) - output_token_account = get_associated_token_address(payer, output_mint, mint_token_program) - - # Get vaults - input_vault = params.base_vault if is_base_in else params.quote_vault - if input_vault is None: - input_vault = get_vault_pda(pool_state, input_mint) - - output_vault = params.quote_vault if is_base_in else params.base_vault - if output_vault is None: - output_vault = get_vault_pda(pool_state, output_mint) - - # Get observation state - observation_state = params.observation_state - if observation_state is None: - observation_state = get_observation_state_pda(pool_state) - - # Handle WSOL if needed - if create_input_ata and params.is_wsol: - instructions.extend(handle_wsol(payer, input_amount)) - - # Create output ATA if needed - if create_output_ata: - instructions.append( - create_associated_token_account_idempotent_instruction( - payer, payer, output_mint, mint_token_program - ) - ) - - # Build instruction data - data = SWAP_BASE_IN_DISCRIMINATOR + struct.pack(" List[Instruction]: - """ - Build Raydium CPMM sell instructions. - - Args: - payer: The wallet paying for the swap - input_mint: The token mint to sell - input_amount: Amount of tokens to sell - params: Raydium CPMM protocol parameters - slippage_bps: Slippage tolerance in basis points - create_output_ata: Whether to create WSOL ATA for receiving SOL - close_output_ata: Whether to close WSOL ATA after swap - close_input_ata: Whether to close token ATA after swap - fixed_output_amount: If set, use this as exact output amount - - Returns: - List of instructions for the sell operation - """ - if input_amount == 0: - raise ValueError("Amount cannot be zero") - - instructions = [] - - # Validate pool contains WSOL or USDC - if not params.is_wsol and not params.is_usdc: - raise ValueError("Pool must contain WSOL or USDC") - - # Get pool state - pool_state = params.pool_state - if pool_state is None: - pool_state = get_pool_pda(params.amm_config, params.base_mint, params.quote_mint) - - # Determine if quote is output (WSOL/USDC) - is_quote_out = params.quote_mint == WSOL_TOKEN_ACCOUNT or params.quote_mint == USDC_TOKEN_ACCOUNT - - # Get input token program - mint_token_program = params.base_token_program if is_quote_out else params.quote_token_program - - # Calculate swap amount - _, min_amount_out = compute_swap_amount( - params.base_reserve, - params.quote_reserve, - is_quote_out, # Swap direction is reversed for sell - input_amount, - slippage_bps, - ) - - if fixed_output_amount is not None: - minimum_amount_out = fixed_output_amount - else: - minimum_amount_out = min_amount_out - - # Determine output mint (WSOL or USDC) - output_mint = WSOL_TOKEN_ACCOUNT if params.is_wsol else USDC_TOKEN_ACCOUNT - - # Get user token accounts - output_token_account = get_associated_token_address(payer, output_mint, TOKEN_PROGRAM) - input_token_account = get_associated_token_address(payer, input_mint, mint_token_program) - - # Get vaults - output_vault = params.quote_vault if is_quote_out else params.base_vault - if output_vault is None: - output_vault = get_vault_pda(pool_state, output_mint) - - input_vault = params.base_vault if is_quote_out else params.quote_vault - if input_vault is None: - input_vault = get_vault_pda(pool_state, input_mint) - - # Get observation state - observation_state = params.observation_state - if observation_state is None: - observation_state = get_observation_state_pda(pool_state) - - # Create WSOL ATA if needed for receiving SOL - if create_output_ata and params.is_wsol: - instructions.append( - create_associated_token_account_idempotent_instruction( - payer, payer, WSOL_TOKEN_ACCOUNT, TOKEN_PROGRAM - ) - ) - - # Build instruction data - data = SWAP_BASE_IN_DISCRIMINATOR + struct.pack("