refactor: Major SDK architecture refactoring and API consolidation

- Consolidate separate buy/sell modules into unified trading interface
- Remove protocol-specific buy/sell files (bonk, pumpfun, pumpswap)
- Add new trading constants and utility functions
- Simplify API with unified buy/sell methods supporting multiple protocols
- Enhance documentation with comprehensive examples and usage guides
- Add balance checking and token account management utilities
- Improve code organization and maintainability
This commit is contained in:
ysq
2025-07-10 18:14:21 +08:00
parent 57c2848a57
commit b891b2bc27
41 changed files with 1297 additions and 2744 deletions
+5 -3
View File
@@ -1,15 +1,17 @@
use anyhow::{anyhow, Result};
use solana_sdk::{instruction::Instruction, pubkey::Pubkey, signer::Signer};
use solana_sdk::{instruction::Instruction, signer::Signer};
use spl_associated_token_account::instruction::create_associated_token_account_idempotent;
use crate::{
constants::bonk::{
accounts, trade::DEFAULT_SLIPPAGE, BUY_EXECT_IN_DISCRIMINATOR, SELL_EXECT_IN_DISCRIMINATOR,
accounts, BUY_EXECT_IN_DISCRIMINATOR, SELL_EXECT_IN_DISCRIMINATOR,
},
constants::trade::trade::DEFAULT_SLIPPAGE,
trading::bonk::{
common::{get_amount_out, get_pool_pda, get_token_balance, get_vault_pda},
common::{get_amount_out, get_pool_pda, get_vault_pda},
pool::Pool,
},
trading::common::utils::get_token_balance,
trading::core::{
params::{BuyParams, BonkParams, SellParams},
traits::InstructionBuilder,
+24 -27
View File
@@ -1,34 +1,28 @@
use anyhow::{anyhow, Result};
use solana_sdk::{
instruction::Instruction, native_token::sol_to_lamports,
};
use solana_sdk::{instruction::Instruction, native_token::sol_to_lamports};
use spl_associated_token_account::{
get_associated_token_address, instruction::create_associated_token_account,
};
use spl_token::instruction::close_account;
use crate::{
constants, trading::pumpfun::common::{
get_bonding_curve_pda, get_global_pda, get_metadata_pda, get_mint_authority_pda
}
constants,
trading::pumpfun::common::{
get_bonding_curve_pda, get_global_pda, get_metadata_pda, get_mint_authority_pda,
},
};
use solana_sdk::{
instruction::AccountMeta,
pubkey::Pubkey,
signature::Keypair,
signer::Signer,
};
use solana_sdk::{instruction::AccountMeta, pubkey::Pubkey, signature::Keypair, signer::Signer};
use crate::{
constants::pumpfun::{global_constants::FEE_RECIPIENT, trade::DEFAULT_SLIPPAGE},
trading::pumpfun::common::{
calculate_with_slippage_buy, get_buy_token_amount_from_sol_amount, get_creator_vault_pda,
},
constants::pumpfun::global_constants::FEE_RECIPIENT,
constants::trade::trade::DEFAULT_SLIPPAGE,
trading::common::utils::calculate_with_slippage_buy,
trading::core::{
params::{BuyParams, PumpFunParams, SellParams},
traits::InstructionBuilder,
},
trading::pumpfun::common::{get_buy_token_amount_from_sol_amount, get_creator_vault_pda},
};
/// PumpFun协议的指令构建器
@@ -56,9 +50,7 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
let max_sol_cost = calculate_with_slippage_buy(
params.amount_sol,
params
.slippage_basis_points
.unwrap_or(DEFAULT_SLIPPAGE),
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
);
let creator_vault_pda = bonding_curve.get_creator_vault_pda();
@@ -161,22 +153,24 @@ pub struct Create {
impl Create {
pub fn data(&self) -> Vec<u8> {
let mut data = Vec::with_capacity(8 + 4 + self._name.len() + 4 + self._symbol.len() + 4 + self._uri.len() + 32);
let mut data = Vec::with_capacity(
8 + 4 + self._name.len() + 4 + self._symbol.len() + 4 + self._uri.len() + 32,
);
// 追加 discriminator
data.extend_from_slice(&[24, 30, 200, 40, 5, 28, 7, 119]); // discriminator
// 添加 name 字符串长度和内容
data.extend_from_slice(&(self._name.len() as u32).to_le_bytes()); // 添加 name 长度
data.extend_from_slice(self._name.as_bytes()); // 添加 name 内容
data.extend_from_slice(&(self._name.len() as u32).to_le_bytes()); // 添加 name 长度
data.extend_from_slice(self._name.as_bytes()); // 添加 name 内容
// 添加 symbol 字符串长度和内容
data.extend_from_slice(&(self._symbol.len() as u32).to_le_bytes()); // 添加 symbol 长度
data.extend_from_slice(self._symbol.as_bytes()); // 添加 symbol 内容
data.extend_from_slice(&(self._symbol.len() as u32).to_le_bytes()); // 添加 symbol 长度
data.extend_from_slice(self._symbol.as_bytes()); // 添加 symbol 内容
// 添加 uri 字符串长度和内容
data.extend_from_slice(&(self._uri.len() as u32).to_le_bytes()); // 添加 uri 长度
data.extend_from_slice(self._uri.as_bytes()); // 添加 uri 内容
data.extend_from_slice(&(self._uri.len() as u32).to_le_bytes()); // 添加 uri 长度
data.extend_from_slice(self._uri.as_bytes()); // 添加 uri 内容
data.extend_from_slice(&self._creator.to_bytes());
@@ -233,7 +227,10 @@ pub fn create(payer: &Keypair, mint: &Keypair, args: Create) -> Instruction {
AccountMeta::new(payer.pubkey(), true),
AccountMeta::new_readonly(constants::pumpfun::accounts::SYSTEM_PROGRAM, false),
AccountMeta::new_readonly(constants::pumpfun::accounts::TOKEN_PROGRAM, false),
AccountMeta::new_readonly(constants::pumpfun::accounts::ASSOCIATED_TOKEN_PROGRAM, false),
AccountMeta::new_readonly(
constants::pumpfun::accounts::ASSOCIATED_TOKEN_PROGRAM,
false,
),
AccountMeta::new_readonly(constants::pumpfun::accounts::RENT, false),
AccountMeta::new_readonly(constants::pumpfun::accounts::EVENT_AUTHORITY, false),
AccountMeta::new_readonly(constants::pumpfun::accounts::PUMPFUN, false),
+63 -124
View File
@@ -3,18 +3,19 @@ use solana_sdk::{instruction::Instruction, pubkey::Pubkey, signer::Signer};
use spl_associated_token_account::instruction::create_associated_token_account_idempotent;
use crate::{
constants::pumpswap::{
accounts, trade::DEFAULT_SLIPPAGE, BUY_DISCRIMINATOR, SELL_DISCRIMINATOR,
},
trading::pumpswap::common::{
calculate_with_slippage_buy, calculate_with_slippage_sell, coin_creator_vault_ata,
coin_creator_vault_authority, find_pool, get_buy_token_amount, get_sell_sol_amount,
get_token_balance,
constants::pumpswap::{accounts, BUY_DISCRIMINATOR, SELL_DISCRIMINATOR},
constants::trade::trade::DEFAULT_SLIPPAGE,
trading::common::utils::{
calculate_with_slippage_buy, calculate_with_slippage_sell, get_token_balance,
},
trading::core::{
params::{BuyParams, PumpSwapParams, SellParams},
traits::InstructionBuilder,
},
trading::pumpswap::common::{
coin_creator_vault_ata, coin_creator_vault_authority, find_pool, get_buy_token_amount,
get_sell_sol_amount,
},
};
/// PumpSwap协议的指令构建器
@@ -35,27 +36,11 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
}
// 根据是否提供了账户信息来构建指令
match (
&protocol_params.pool,
&protocol_params.pool_base_token_account,
&protocol_params.pool_quote_token_account,
&protocol_params.user_base_token_account,
&protocol_params.user_quote_token_account,
) {
(
Some(pool),
Some(pool_base_token_account),
Some(pool_quote_token_account),
Some(user_base_token_account),
Some(user_quote_token_account),
) => {
match (&protocol_params.pool,) {
(Some(pool),) => {
self.build_buy_instructions_with_accounts(
params,
*pool,
*pool_base_token_account,
*pool_quote_token_account,
*user_base_token_account,
*user_quote_token_account,
protocol_params.auto_handle_wsol,
)
.await
@@ -73,29 +58,10 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
.ok_or_else(|| anyhow!("Invalid protocol params for PumpSwap"))?;
// 根据是否提供了账户信息来构建指令
match (
&protocol_params.pool,
&protocol_params.pool_base_token_account,
&protocol_params.pool_quote_token_account,
&protocol_params.user_base_token_account,
&protocol_params.user_quote_token_account,
) {
(
Some(pool),
Some(pool_base_token_account),
Some(pool_quote_token_account),
Some(user_base_token_account),
Some(user_quote_token_account),
) => {
self.build_sell_instructions_with_accounts(
params,
*pool,
*pool_base_token_account,
*pool_quote_token_account,
*user_base_token_account,
*user_quote_token_account,
)
.await
match (&protocol_params.pool,) {
(Some(pool),) => {
self.build_sell_instructions_with_accounts(params, *pool)
.await
}
_ => self.build_sell_instructions_auto_discover(params).await,
}
@@ -115,41 +81,8 @@ impl PumpSwapInstructionBuilder {
// 查找池
let pool = find_pool(rpc.as_ref(), &params.mint).await?;
// 创建用户代币账户
let user_base_token_account = spl_associated_token_account::get_associated_token_address(
&params.payer.pubkey(),
&params.mint,
);
let user_quote_token_account = spl_associated_token_account::get_associated_token_address(
&params.payer.pubkey(),
&accounts::WSOL_TOKEN_ACCOUNT,
);
// 获取池的代币账户
let pool_base_token_account =
spl_associated_token_account::get_associated_token_address_with_program_id(
&pool,
&params.mint,
&accounts::TOKEN_PROGRAM,
);
let pool_quote_token_account =
spl_associated_token_account::get_associated_token_address_with_program_id(
&pool,
&accounts::WSOL_TOKEN_ACCOUNT,
&accounts::TOKEN_PROGRAM,
);
self.build_buy_instructions_with_accounts(
params,
pool,
pool_base_token_account,
pool_quote_token_account,
user_base_token_account,
user_quote_token_account,
true,
)
.await
self.build_buy_instructions_with_accounts(params, pool, true)
.await
}
/// 自动发现池和账户信息并构建卖出指令
@@ -165,6 +98,30 @@ impl PumpSwapInstructionBuilder {
// 查找池
let pool = find_pool(rpc.as_ref(), &params.mint).await?;
self.build_sell_instructions_with_accounts(params, pool)
.await
}
/// 使用提供的账户信息构建买入指令
async fn build_buy_instructions_with_accounts(
&self,
params: &BuyParams,
pool: Pubkey,
auto_handle_wsol: bool,
) -> Result<Vec<Instruction>> {
if params.rpc.is_none() {
return Err(anyhow!("RPC is not set"));
}
let rpc = params.rpc.as_ref().unwrap().clone();
// 计算预期的代币数量
let token_amount = get_buy_token_amount(rpc.as_ref(), &pool, params.amount_sol).await?;
// 计算滑点后的最大SOL数量
let max_sol_amount = calculate_with_slippage_buy(
params.amount_sol,
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
);
// 创建用户代币账户
let user_base_token_account = spl_associated_token_account::get_associated_token_address(
&params.payer.pubkey(),
@@ -190,41 +147,6 @@ impl PumpSwapInstructionBuilder {
&accounts::TOKEN_PROGRAM,
);
self.build_sell_instructions_with_accounts(
params,
pool,
pool_base_token_account,
pool_quote_token_account,
user_base_token_account,
user_quote_token_account,
)
.await
}
/// 使用提供的账户信息构建买入指令
async fn build_buy_instructions_with_accounts(
&self,
params: &BuyParams,
pool: Pubkey,
pool_base_token_account: Pubkey,
pool_quote_token_account: Pubkey,
user_base_token_account: Pubkey,
user_quote_token_account: Pubkey,
auto_handle_wsol: bool,
) -> Result<Vec<Instruction>> {
if params.rpc.is_none() {
return Err(anyhow!("RPC is not set"));
}
let rpc = params.rpc.as_ref().unwrap().clone();
// 计算预期的代币数量
let token_amount = get_buy_token_amount(rpc.as_ref(), &pool, params.amount_sol).await?;
// 计算滑点后的最大SOL数量
let max_sol_amount = calculate_with_slippage_buy(
params.amount_sol,
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
);
let mut instructions = vec![];
if auto_handle_wsol {
@@ -328,10 +250,6 @@ impl PumpSwapInstructionBuilder {
&self,
params: &SellParams,
pool: Pubkey,
pool_base_token_account: Pubkey,
pool_quote_token_account: Pubkey,
user_base_token_account: Pubkey,
user_quote_token_account: Pubkey,
) -> Result<Vec<Instruction>> {
if params.rpc.is_none() {
return Err(anyhow!("RPC is not set"));
@@ -341,8 +259,8 @@ impl PumpSwapInstructionBuilder {
// 获取代币余额
let mut amount = params.amount_token;
if params.amount_token.is_none() {
let (balance_u64, _) =
get_token_balance(rpc.as_ref(), params.payer.as_ref(), &params.mint).await?;
let balance_u64 =
get_token_balance(rpc.as_ref(), &params.payer.pubkey(), &params.mint).await?;
amount = Some(balance_u64);
}
let amount = amount.unwrap_or(0);
@@ -363,6 +281,27 @@ impl PumpSwapInstructionBuilder {
let coin_creator_vault_ata = coin_creator_vault_ata(params.creator);
let coin_creator_vault_authority = coin_creator_vault_authority(params.creator);
let user_base_token_account = spl_associated_token_account::get_associated_token_address(
&params.payer.pubkey(),
&params.mint,
);
let user_quote_token_account = spl_associated_token_account::get_associated_token_address(
&params.payer.pubkey(),
&accounts::WSOL_TOKEN_ACCOUNT,
);
let pool_base_token_account =
spl_associated_token_account::get_associated_token_address_with_program_id(
&pool,
&params.mint,
&accounts::TOKEN_PROGRAM,
);
let pool_quote_token_account =
spl_associated_token_account::get_associated_token_address_with_program_id(
&pool,
&accounts::WSOL_TOKEN_ACCOUNT,
&accounts::TOKEN_PROGRAM,
);
let mut instructions = vec![];
// 插入wsol