2025-10-06 23:40:27 +08:00
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
|
|
|
|
|
|
# PumpFun 真实买入延迟测试
|
|
|
|
|
|
# 测试从调用buy到提交交易的完整流程
|
|
|
|
|
|
|
|
|
|
|
|
set -e
|
|
|
|
|
|
|
|
|
|
|
|
echo "================================"
|
|
|
|
|
|
echo " PumpFun 真实买入延迟测试"
|
|
|
|
|
|
echo "================================"
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
|
|
# 检查 pkg-config
|
|
|
|
|
|
if ! command -v pkg-config &> /dev/null; then
|
|
|
|
|
|
echo "❌ 错误: 缺少 pkg-config"
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
echo "请先安装 pkg-config:"
|
|
|
|
|
|
echo " brew install pkg-config"
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# 使用临时生成的私钥
|
|
|
|
|
|
if [ -z "$PAYER_KEYPAIR" ]; then
|
|
|
|
|
|
echo "📝 未设置PAYER_KEYPAIR,将在测试代码中生成临时密钥对"
|
|
|
|
|
|
PAYER_KEYPAIR="GENERATE_NEW" # 标记让测试代码生成新密钥对
|
|
|
|
|
|
else
|
|
|
|
|
|
echo "📝 使用用户提供的PAYER_KEYPAIR"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# 使用真实的 PumpFun 代币
|
|
|
|
|
|
TEST_MINT=${TEST_MINT:-"Dna9Y9VwbFTfFzB4kN1hAbsMfPuwGHmrfD6LUQL2pump"}
|
|
|
|
|
|
echo "🪙 测试代币: $TEST_MINT"
|
|
|
|
|
|
|
|
|
|
|
|
RPC_URL=${RPC_URL:-"https://api.mainnet-beta.solana.com"}
|
|
|
|
|
|
echo "📡 RPC地址: $RPC_URL"
|
|
|
|
|
|
|
|
|
|
|
|
# SWQOS配置 - 4个并发发送节点
|
|
|
|
|
|
SWQOS_JITO=${SWQOS_JITO:-"https://mainnet.block-engine.jito.wtf/api/v1/transactions"}
|
|
|
|
|
|
SWQOS_BLOXROUTE=${SWQOS_BLOXROUTE:-"https://ny.solana.dex.blxrbdn.com"}
|
|
|
|
|
|
SWQOS_NEXTBLOCK=${SWQOS_NEXTBLOCK:-"https://api.nextblock.io/v1/solana"}
|
|
|
|
|
|
SWQOS_FLASHBLOCK=${SWQOS_FLASHBLOCK:-"https://api.flashblock.io/v1/solana"}
|
|
|
|
|
|
echo "🚀 SWQOS节点数: 4 (Jito, Bloxroute, NextBlock, FlashBlock)"
|
|
|
|
|
|
|
|
|
|
|
|
# 买入金额 (lamports, 默认0.001 SOL)
|
|
|
|
|
|
BUY_AMOUNT=${BUY_AMOUNT:-1000000}
|
|
|
|
|
|
echo "💰 买入金额: $BUY_AMOUNT lamports (0.001 SOL)"
|
|
|
|
|
|
|
|
|
|
|
|
# 滑点
|
|
|
|
|
|
SLIPPAGE=${SLIPPAGE:-1000}
|
|
|
|
|
|
echo "📊 滑点: $SLIPPAGE basis points (10%)"
|
|
|
|
|
|
|
|
|
|
|
|
# 设置日志级别
|
|
|
|
|
|
export RUST_LOG=${RUST_LOG:-"info,sol_trade_sdk=debug"}
|
|
|
|
|
|
echo "📊 日志级别: $RUST_LOG"
|
|
|
|
|
|
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
echo "⚠️ 注意: 此测试使用临时生成的密钥对(无余额)"
|
|
|
|
|
|
echo " 测试目的: 验证交易构建和提交流程的延迟"
|
|
|
|
|
|
echo " 交易预期失败(余额不足),但会测量完整的性能数据"
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
|
|
# 导出环境变量
|
|
|
|
|
|
export PAYER_KEYPAIR
|
|
|
|
|
|
export RPC_URL
|
|
|
|
|
|
export TEST_MINT
|
|
|
|
|
|
export BUY_AMOUNT
|
|
|
|
|
|
export SLIPPAGE
|
|
|
|
|
|
export SWQOS_JITO
|
|
|
|
|
|
export SWQOS_BLOXROUTE
|
|
|
|
|
|
export SWQOS_NEXTBLOCK
|
|
|
|
|
|
export SWQOS_FLASHBLOCK
|
|
|
|
|
|
|
|
|
|
|
|
# 清理可能存在的旧目录
|
|
|
|
|
|
rm -rf examples/pumpfun_buy_test
|
|
|
|
|
|
|
|
|
|
|
|
# 创建测试程序目录
|
|
|
|
|
|
mkdir -p examples/pumpfun_buy_test/src
|
|
|
|
|
|
|
|
|
|
|
|
# 创建测试程序
|
|
|
|
|
|
cat > examples/pumpfun_buy_test/src/main.rs << 'EOF'
|
|
|
|
|
|
use sol_trade_sdk::{
|
|
|
|
|
|
common::{TradeConfig, AnyResult},
|
2026-04-11 18:43:18 +08:00
|
|
|
|
constants::TOKEN_PROGRAM,
|
2025-10-06 23:40:27 +08:00
|
|
|
|
swqos::{SwqosConfig, SwqosRegion},
|
|
|
|
|
|
trading::{core::params::PumpFunParams, factory::DexType},
|
|
|
|
|
|
SolanaTrade, TradeTokenType, TradeBuyParams,
|
|
|
|
|
|
};
|
|
|
|
|
|
use solana_commitment_config::CommitmentConfig;
|
|
|
|
|
|
use solana_sdk::{signature::{Keypair, Signer}, pubkey::Pubkey};
|
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
use std::env;
|
|
|
|
|
|
|
|
|
|
|
|
#[tokio::main]
|
|
|
|
|
|
async fn main() -> AnyResult<()> {
|
|
|
|
|
|
env_logger::init();
|
|
|
|
|
|
|
|
|
|
|
|
println!("\n🚀 初始化 PumpFun 交易客户端...\n");
|
|
|
|
|
|
|
|
|
|
|
|
// 生成临时测试密钥对
|
|
|
|
|
|
let payer_key = env::var("PAYER_KEYPAIR").unwrap_or_else(|_| "GENERATE_NEW".to_string());
|
|
|
|
|
|
let payer = if payer_key == "GENERATE_NEW" {
|
|
|
|
|
|
println!("📝 生成临时测试密钥对...");
|
|
|
|
|
|
Keypair::new()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Keypair::from_base58_string(&payer_key)
|
|
|
|
|
|
};
|
|
|
|
|
|
println!("📝 钱包地址: {}", payer.pubkey());
|
|
|
|
|
|
|
|
|
|
|
|
let rpc_url = env::var("RPC_URL").unwrap_or_else(|_| "https://api.mainnet-beta.solana.com".to_string());
|
|
|
|
|
|
let commitment = CommitmentConfig::confirmed();
|
|
|
|
|
|
|
|
|
|
|
|
// 配置4个SWQOS节点并发发送
|
|
|
|
|
|
let swqos_configs: Vec<SwqosConfig> = vec![
|
|
|
|
|
|
SwqosConfig::Jito(
|
|
|
|
|
|
String::new(), // uuid
|
|
|
|
|
|
SwqosRegion::Default,
|
|
|
|
|
|
Some(env::var("SWQOS_JITO").unwrap_or_else(|_| "https://mainnet.block-engine.jito.wtf/api/v1/transactions".to_string()))
|
|
|
|
|
|
),
|
|
|
|
|
|
SwqosConfig::Bloxroute(
|
|
|
|
|
|
String::new(), // api_token
|
|
|
|
|
|
SwqosRegion::Default,
|
|
|
|
|
|
Some(env::var("SWQOS_BLOXROUTE").unwrap_or_else(|_| "https://ny.solana.dex.blxrbdn.com".to_string()))
|
|
|
|
|
|
),
|
|
|
|
|
|
SwqosConfig::NextBlock(
|
|
|
|
|
|
String::new(), // api_token
|
|
|
|
|
|
SwqosRegion::Default,
|
|
|
|
|
|
Some(env::var("SWQOS_NEXTBLOCK").unwrap_or_else(|_| "https://api.nextblock.io/v1/solana".to_string()))
|
|
|
|
|
|
),
|
|
|
|
|
|
SwqosConfig::FlashBlock(
|
|
|
|
|
|
String::new(), // api_token
|
|
|
|
|
|
SwqosRegion::Default,
|
|
|
|
|
|
Some(env::var("SWQOS_FLASHBLOCK").unwrap_or_else(|_| "https://api.flashblock.io/v1/solana".to_string()))
|
|
|
|
|
|
),
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
println!("🚀 SWQOS配置: {} 个并发节点", swqos_configs.len());
|
|
|
|
|
|
|
|
|
|
|
|
let trade_config = TradeConfig::new(rpc_url, swqos_configs, commitment);
|
|
|
|
|
|
let client = SolanaTrade::new(Arc::new(payer), trade_config).await;
|
|
|
|
|
|
|
|
|
|
|
|
// 设置 PumpFun 的 gas 策略
|
|
|
|
|
|
sol_trade_sdk::common::GasFeeStrategy::set_global_fee_strategy(200000, 1000000, 0.005, 0.01);
|
|
|
|
|
|
|
|
|
|
|
|
println!("✅ 客户端初始化完成\n");
|
|
|
|
|
|
|
|
|
|
|
|
let mint_str = env::var("TEST_MINT").expect("TEST_MINT not set");
|
|
|
|
|
|
let mint = mint_str.parse().expect("Invalid mint address");
|
|
|
|
|
|
let buy_amount = env::var("BUY_AMOUNT")
|
|
|
|
|
|
.unwrap_or_else(|_| "1000000".to_string())
|
|
|
|
|
|
.parse::<u64>()
|
|
|
|
|
|
.expect("Invalid buy amount");
|
|
|
|
|
|
let slippage = env::var("SLIPPAGE")
|
|
|
|
|
|
.unwrap_or_else(|_| "1000".to_string())
|
|
|
|
|
|
.parse::<u64>()
|
|
|
|
|
|
.expect("Invalid slippage");
|
|
|
|
|
|
|
|
|
|
|
|
println!("🔍 获取最新区块哈希...");
|
|
|
|
|
|
let recent_blockhash = client.rpc.get_latest_blockhash().await?;
|
|
|
|
|
|
println!("✅ 区块哈希: {}\n", recent_blockhash);
|
|
|
|
|
|
|
|
|
|
|
|
println!("================================");
|
|
|
|
|
|
println!(" PumpFun 买入延迟测试");
|
|
|
|
|
|
println!("================================");
|
|
|
|
|
|
println!("🪙 代币: {}", mint);
|
|
|
|
|
|
println!("💰 金额: {} lamports", buy_amount);
|
|
|
|
|
|
println!("📊 滑点: {} basis points", slippage);
|
|
|
|
|
|
println!("================================\n");
|
|
|
|
|
|
|
2026-04-11 18:43:18 +08:00
|
|
|
|
// PumpFun买入参数 (占位;实际路径应使用 RPC 或事件填充)
|
2025-10-06 23:40:27 +08:00
|
|
|
|
let params = PumpFunParams::from_trade(
|
|
|
|
|
|
Pubkey::default(), // bonding_curve
|
|
|
|
|
|
Pubkey::default(), // associated_bonding_curve
|
|
|
|
|
|
mint, // mint
|
|
|
|
|
|
Pubkey::default(), // creator
|
|
|
|
|
|
Pubkey::default(), // creator_vault
|
|
|
|
|
|
0, // virtual_token_reserves
|
|
|
|
|
|
0, // virtual_sol_reserves
|
|
|
|
|
|
0, // real_token_reserves
|
|
|
|
|
|
0, // real_sol_reserves
|
|
|
|
|
|
None, // close_token_account_when_sell
|
2026-04-11 18:43:18 +08:00
|
|
|
|
Pubkey::default(), // fee_recipient
|
|
|
|
|
|
TOKEN_PROGRAM,
|
|
|
|
|
|
false, // is_cashback_coin
|
|
|
|
|
|
None, // mayhem_mode(None=按 fee_recipient 推断)
|
2025-10-06 23:40:27 +08:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
let buy_params = TradeBuyParams {
|
|
|
|
|
|
dex_type: DexType::PumpFun,
|
|
|
|
|
|
input_token_type: TradeTokenType::SOL,
|
|
|
|
|
|
mint,
|
|
|
|
|
|
input_token_amount: buy_amount,
|
|
|
|
|
|
slippage_basis_points: Some(slippage),
|
|
|
|
|
|
recent_blockhash: Some(recent_blockhash),
|
|
|
|
|
|
extension_params: Box::new(params),
|
2025-10-07 20:56:02 +08:00
|
|
|
|
address_lookup_table_account: None,
|
2025-10-06 23:40:27 +08:00
|
|
|
|
wait_transaction_confirmed: false, // 不等待确认,测试最快提交速度
|
|
|
|
|
|
create_input_token_ata: true,
|
|
|
|
|
|
close_input_token_ata: true,
|
|
|
|
|
|
create_mint_ata: true,
|
|
|
|
|
|
open_seed_optimize: false,
|
|
|
|
|
|
durable_nonce: None,
|
|
|
|
|
|
fixed_output_token_amount: None,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
println!("⏱️ 开始执行买入流程...");
|
|
|
|
|
|
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
|
|
|
|
|
|
|
|
|
|
|
|
match client.buy(buy_params).await {
|
|
|
|
|
|
Ok((success, signature)) => {
|
|
|
|
|
|
println!("\n================================");
|
|
|
|
|
|
println!(" ✅ 买入流程完成");
|
|
|
|
|
|
println!("================================");
|
|
|
|
|
|
println!("✅ 提交成功: {}", success);
|
|
|
|
|
|
println!("📝 签名: {}", signature);
|
|
|
|
|
|
println!("================================\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
Err(e) => {
|
|
|
|
|
|
println!("\n================================");
|
|
|
|
|
|
println!(" ⚠️ 买入流程完成(交易失败)");
|
|
|
|
|
|
println!("================================");
|
|
|
|
|
|
println!("ℹ️ 错误: {:?}", e);
|
|
|
|
|
|
println!("\n💡 说明: 交易失败是预期的(测试账户无余额)");
|
|
|
|
|
|
println!(" 耗时统计见上方SDK日志输出");
|
|
|
|
|
|
println!("================================\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 显示性能统计
|
|
|
|
|
|
println!("================================");
|
|
|
|
|
|
println!(" 性能优化模块状态");
|
|
|
|
|
|
println!("================================\n");
|
|
|
|
|
|
|
|
|
|
|
|
use sol_trade_sdk::swqos::serialization::get_serializer_stats;
|
|
|
|
|
|
let (available, capacity) = get_serializer_stats();
|
|
|
|
|
|
println!("📦 序列化器缓冲池:");
|
|
|
|
|
|
println!(" 容量: {}", capacity);
|
|
|
|
|
|
println!(" 可用: {}", available);
|
|
|
|
|
|
println!(" 使用: {}", capacity - available);
|
|
|
|
|
|
|
|
|
|
|
|
use sol_trade_sdk::trading::core::transaction_pool::get_pool_stats;
|
|
|
|
|
|
let (pool_available, pool_capacity) = get_pool_stats();
|
|
|
|
|
|
println!("\n🔧 交易构建器池:");
|
|
|
|
|
|
println!(" 容量: {}", pool_capacity);
|
|
|
|
|
|
println!(" 可用: {}", pool_available);
|
|
|
|
|
|
println!(" 使用: {}", pool_capacity - pool_available);
|
|
|
|
|
|
|
|
|
|
|
|
println!("\n================================");
|
|
|
|
|
|
println!("✅ 延迟测试完成!");
|
|
|
|
|
|
println!("================================\n");
|
|
|
|
|
|
|
|
|
|
|
|
println!("💡 提示: 查看上面的日志了解各环节详细耗时");
|
|
|
|
|
|
println!(" 日志中包含每个步骤的 step 和 total 时间\n");
|
|
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
EOF
|
|
|
|
|
|
|
|
|
|
|
|
# 创建 Cargo.toml
|
|
|
|
|
|
cat > examples/pumpfun_buy_test/Cargo.toml << 'EOF'
|
|
|
|
|
|
[package]
|
|
|
|
|
|
name = "pumpfun_buy_test"
|
|
|
|
|
|
version = "0.1.0"
|
|
|
|
|
|
edition = "2021"
|
|
|
|
|
|
|
|
|
|
|
|
[[bin]]
|
|
|
|
|
|
name = "pumpfun_buy_test"
|
|
|
|
|
|
path = "src/main.rs"
|
|
|
|
|
|
|
|
|
|
|
|
[dependencies]
|
|
|
|
|
|
sol-trade-sdk = { path = "../.." }
|
|
|
|
|
|
solana-sdk = "3.0.0"
|
|
|
|
|
|
solana-commitment-config = "3.0.0"
|
|
|
|
|
|
tokio = { version = "1", features = ["full"] }
|
|
|
|
|
|
anyhow = "1.0"
|
|
|
|
|
|
env_logger = "0.11"
|
|
|
|
|
|
EOF
|
|
|
|
|
|
|
|
|
|
|
|
# 添加到workspace
|
|
|
|
|
|
if ! grep -q "examples/pumpfun_buy_test" Cargo.toml; then
|
|
|
|
|
|
sed -i.bak '/members = \[/a\
|
|
|
|
|
|
"examples/pumpfun_buy_test",
|
|
|
|
|
|
' Cargo.toml
|
|
|
|
|
|
rm -f Cargo.toml.bak
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
echo "================================"
|
|
|
|
|
|
echo " 开始编译和运行测试..."
|
|
|
|
|
|
echo "================================"
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
|
|
# 编译并运行
|
|
|
|
|
|
cargo run --release -p pumpfun_buy_test
|
|
|
|
|
|
|
|
|
|
|
|
# 从workspace中移除
|
|
|
|
|
|
sed -i.bak '/examples\/pumpfun_buy_test/d' Cargo.toml
|
|
|
|
|
|
rm -f Cargo.toml.bak
|
|
|
|
|
|
|
|
|
|
|
|
# 清理
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
echo "清理测试文件..."
|
|
|
|
|
|
rm -rf examples/pumpfun_buy_test
|
|
|
|
|
|
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
echo "================================"
|
|
|
|
|
|
echo " 测试完成"
|
|
|
|
|
|
echo "================================"
|