关于 jupiter-swap-integration
面向 Jupiter Swap 集成开发的技能指南,涵盖报价、交易构建与发送,以及滑点、费用和错误处理。适用于构建兑换界面、交易 Bot 或后端服务。
主要功能
- 01Jupiter API 报价流程
- 02构建并发送 V0 交易
- 03按代币类型设置滑点
- 04优先费策略
- 05重试与限流处理
- 06兑换界面与 Bot 模板
适用场景
- 01开发 SOL 与其他代币的兑换功能
- 02为交易 Bot 配置滑点和重试
- 03处理网络拥堵时的优先费
- 04构建带状态提示的兑换界面
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ sanctifiedops/ solana-skills --skill jupiter-swap-integration安装后在 Claude Code 中输入/jupiter-swap-integration确认已加载。
详细介绍
Jupiter 兑换集成
角色定位:你是一名 Jupiter 集成专家,负责构建兑换界面和交易机器人。你的目标是实现可靠、高效的兑换,并妥善处理错误、提供良好的用户体验。
初步评估
- 你要构建什么:前端兑换 UI、交易机器人还是后端服务?
- 预期交易量:普通用户兑换还是高频交易?
- 代币类型:主流代币(SOL/USDC)还是长尾迷因币?
- 滑点容忍度要求:严格(0.5%)还是宽松(5% 以上)?
- 是否需要高级功能:限价单、DCA,还是只需现货兑换?
- 你的 RPC 配置是什么:公共端点还是专用服务提供商?
- 错误处理要求:重试逻辑、回退策略?
核心原则
- Jupiter 聚合路由,但不执行交易:它会在各个 DEX 中寻找最佳路径;交易需要由你提交。
- 滑点是保护措施,不是建议:根据波动性设置;过于严格会导致交易失败,过于宽松则会造成成交价格不佳。
- 优先费必不可少:没有优先费,网络拥堵时兑换会失败。预算为 0.0001-0.001 SOL。
- 报价 ≠ 保证成交:价格会变动;始终使用
onlyDirectRoutes: false,以获得更好的成交结果。 - 存在速率限制:免费套餐为每分钟 60 次请求;付费套餐的上限更高。尽可能缓存报价。
- 需要使用版本化交易:Jupiter 返回 V0 交易;确保你的钱包/SDK 能够处理这些交易。
工作流
1. API 设置与身份验证
// Base URLs
const JUPITER_API = 'https://quote-api.jup.ag/v6';
const JUPITER_PRICE_API = 'https://price.jup.ag/v6';
// No API key required for basic usage
// For high volume, contact Jupiter for dedicated endpoints
// Rate limits (free tier):
// - Quote API: 60 requests/minute
// - Price API: 600 requests/minute
2. 获取报价
interface QuoteParams {
inputMint: string; // Token to sell
outputMint: string; // Token to buy
amount: string; // Amount in smallest units (lamports/base units)
slippageBps: number; // Slippage in basis points (100 = 1%)
onlyDirectRoutes?: boolean; // false = better routes, true = simpler
asLegacyTransaction?: boolean; // false = versioned tx (recommended)
}
async function getQuote(params: QuoteParams) {
const url = new URL(`${JUPITER_API}/quote`);
url.searchParams.set('inputMint', params.inputMint);
url.searchParams.set('outputMint', params.outputMint);
url.searchParams.set('amount', params.amount);
url.searchParams.set('slippageBps', params.slippageBps.toString());
const response = await fetch(url.toString());
if (!response.ok) {
throw new Error(`Quote failed: ${response.status}`);
}
return response.json();
}
// Example: Swap 1 SOL to USDC
const quote = await getQuote({
inputMint: 'So11111111111111111111111111111111111111112', // SOL
outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
amount: '1000000000', // 1 SOL in lamports
slippageBps: 50, // 0.5%
});
// Quote response includes:
// - inAmount: input amount
// - outAmount: expected output (before slippage)
// - otherAmountThreshold: minimum output (after slippage)
// - routePlan: array of swap steps
// - priceImpactPct: price impact percentage
3. 构建兑换交易
interface SwapParams {
quoteResponse: any;
userPublicKey: string;
wrapAndUnwrapSol?: boolean; // true = auto wrap/unwrap SOL
computeUnitPriceMicroLamports?: number; // priority fee
dynamicComputeUnitLimit?: boolean; // auto-size compute
}
async function getSwapTransaction(params: SwapParams) {
const response = await fetch(`${JUPITER_API}/swap`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
quoteResponse: params.quoteResponse,
userPublicKey: params.userPublicKey,
wrapAndUnwrapSol: params.wrapAndUnwrapSol ?? true,
computeUnitPriceMicroLamports: params.computeUnitPriceMicroLamports ?? 1000,
dynamicComputeUnitLimit: params.dynamicComputeUnitLimit ?? true,
}),
});
const { swapTransaction } = await response.json();
return swapTransaction; // Base64 encoded versioned transaction
}
4. 签名并发送交易
import { VersionedTransaction, Connection } from '@solana/web3.js';
async function executeSwap(
swapTransaction: string,
wallet: any, // Wallet adapter
connection: Connection
) {
// Decode the transaction
const txBuffer = Buffer.from(swapTransaction, 'base64');
const tx = VersionedTransaction.deserialize(txBuffer);
// Sign with wallet
const signedTx = await wallet.signTransaction(tx);
// Send with retry logic
const signature = await connection.sendTransaction(signedTx, {
skipPreflight: false,
maxRetries: 3,
});
// Confirm transaction
const confirmation = await connection.confirmTransaction(signature, 'confirmed');
if (confirmation.value.err) {
throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`);
}
return signature;
}
5. 按代币类型制定滑点策略
| 代币类型 | 建议滑点 | 原因 |
|---|---|---|
| SOL/USDC | 0.1-0.3% | 流动性深厚且稳定 |
| 主流代币(JUP、BONK) | 0.5-1% | 流动性良好 |
| 中市值迷因币 | 1-3% | 流动性不稳定 |
| 新代币/低市值代币 | 3-10% | 流动性稀薄、波动较大 |
| Pump.fun 代币 | 5-15% | 波动极大 |
6. 优先费策略
// Estimate priority fee based on network conditions
async function estimatePriorityFee(connection: Connection): Promise<number> {
const recentFees = await connection.getRecentPrioritizationFees();
if (recentFees.length === 0) return 1000; // default 1000 micro-lamports
// Use 75th percentile for reliability
const sorted = recentFees.map(f => f.prioritizationFee).sort((a, b) => a - b);
const p75Index = Math.floor(sorted.length * 0.75);
return Math.max(sorted[p75Index], 1000);
}
// Priority fee tiers:
// - 1,000 micro-lamports: Normal conditions
// - 10,000 micro-lamports: Moderate congestion
// - 100,000 micro-lamports: High congestion
// - 1,000,000+ micro-lamports: Extreme (minting events, etc.)
7. 错误处理与重试
async function swapWithRetry(
params: SwapParams,
maxRetries: number = 3
): Promise<string> {
let lastError: Error | null = null;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
// Get fresh quote (prices change)
const quote = await getQuote(params.quoteParams);
// Check if price moved too much
if (quote.priceImpactPct > 5) {
throw new Error(`Price impact too high: ${quote.priceImpactPct}%`);
}
// Get swap transaction
const swapTx = await getSwapTransaction({
quoteResponse: quote,
userPublicKey: params.userPublicKey,
computeUnitPriceMicroLamports: params.priorityFee * attempt, // Increase fee on retry
});
// Execute
return await executeSwap(swapTx, params.wallet, params.connection);
} catch (error: any) {
lastError = error;
// Don't retry on certain errors
if (error.message.includes('insufficient funds')) throw error;
if (error.message.includes('slippage')) throw error;
// Wait before retry (exponential backoff)
await new Promise(r => setTimeout(r, 1000 * attempt));
}
}
throw lastError || new Error('Swap failed after retries');
}
模板 / 操作指南
前端兑换组件结构
// State management
interface SwapState {
inputToken: Token | null;
outputToken: Token | null;
inputAmount: string;
quote: QuoteResponse | null;
loading: boolean;
error: string | null;
txStatus: 'idle' | 'signing' | 'confirming' | 'success' | 'failed';
}
// Component flow:
// 1. User selects tokens → fetch quote
// 2. User enters amount → debounced quote refresh
// 3. Display: output amount, price impact, route
// 4. User clicks swap → sign → send → confirm
// 5. Show success/failure with tx link
// Quote refresh interval: 10-15 seconds (balance freshness vs rate limits)
机器人兑换配置
interface BotSwapConfig {
// Execution
maxSlippageBps: number;
minPriorityFeeMicroLamports: number;
maxPriorityFeeMicroLamports: number;
maxRetries: number;
// Safety
maxTradeSize: number; // In USD
maxPriceImpact: number; // Percentage
cooldownMs: number; // Between trades
// Monitoring
logAllQuotes: boolean;
alertOnFailure: boolean;
}
const defaultBotConfig: BotSwapConfig = {
maxSlippageBps: 100,
minPriorityFeeMicroLamports: 1000,
maxPriorityFeeMicroLamports: 100000,
maxRetries: 3,
maxTradeSize: 1000,
maxPriceImpact: 3,
cooldownMs: 1000,
logAllQuotes: true,
alertOnFailure: true,
};
价格影响阈值
function assessPriceImpact(impactPct: number): {
level: 'low' | 'medium' | 'high' | 'extreme';
warning: string | null;
} {
if (impactPct < 0.5) return { level: 'low', warning: null };
if (impactPct < 2) return { level: 'medium', warning: 'Moderate price impact' };
if (impactPct < 5) return { level: 'high', warning: 'High price impact - consider smaller trade' };
return { level: 'extreme', warning: 'Extreme price impact - trade size too large for liquidity' };
}
常见故障模式与调试
“交易模拟失败”
- 原因:报价过期,价格变动超出滑点范围
- 检测:错误消息包含“slippage”或“amount out”
- 解决方法:提高滑点,或在执行前刷新报价
“交易已过期”
- 原因:确认前区块哈希已过期(超过 60 秒)
- 检测:错误消息包含“blockhash not found”
- 解决方法:提高优先费;使用
skipPreflight: false
“SOL 不足以支付费用”
- 原因:用户没有足够的 SOL 支付租金和优先费
- 检测:兑换前检查余额
- 解决方法:至少预留 0.01 SOL;向用户发出警告
“被 Jupiter 限流”
- 原因:每分钟请求数超过 60 次
- 检测:状态码为 429
- 解决方法:实现请求排队;缓存报价;使用付费套餐
“未找到路径”
- 原因:代币之间不存在流动性路径
- 检测:报价响应中的路径列表为空
- 解决方法:检查代币是否有流动性;尝试使用中间代币(SOL/USDC)
“交易过大”
- 原因:路径复杂,包含多个跳转
- 检测:错误提示包含交易大小相关信息
- 解决方法:使用
onlyDirectRoutes: true或增加计算预算
质量标准 / 验证
集成完成的标准:
- 能够可靠地获取报价,并妥善处理错误
- 根据代币类型正确设置滑点
- 优先费会根据网络状况进行调整
- 失败的交易会采用退避策略重试
- 用户能清楚看到每一步的状态(加载、签名、确认)
- 大额交易会显示价格影响警告
- 能够妥善处理速率限制
- 交易成功后会提供交易链接
输出格式
提供:
- 实现代码:包含适当类型和错误处理的 TypeScript
- 配置值:滑点、优先费用、超时时间
- 错误处理策略:哪些错误需要重试,哪些错误需要直接反馈
- 用户体验考量:加载状态、确认、警告
- 测试清单:部署前需要验证的场景
示例
简单示例:基本兑换函数
输入:“创建一个将 SOL 兑换为任意代币的函数”
输出:
import { Connection, PublicKey, VersionedTransaction } from '@solana/web3.js';
const JUPITER_API = 'https://quote-api.jup.ag/v6';
const SOL_MINT = 'So11111111111111111111111111111111111111112';
interface SwapSolParams {
outputMint: string;
amountLamports: number;
slippageBps: number;
userPublicKey: PublicKey;
signTransaction: (tx: VersionedTransaction) => Promise<VersionedTransaction>;
connection: Connection;
}
export async function swapSolToToken(params: SwapSolParams): Promise<string> {
// 1. Get quote
const quoteUrl = `${JUPITER_API}/quote?inputMint=${SOL_MINT}&outputMint=${params.outputMint}&amount=${params.amountLamports}&slippageBps=${params.slippageBps}`;
const quoteResponse = await fetch(quoteUrl);
if (!quoteResponse.ok) throw new Error('Failed to get quote');
const quote = await quoteResponse.json();
// 2. Get swap transaction
const swapResponse = await fetch(`${JUPITER_API}/swap`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
quoteResponse: quote,
userPublicKey: params.userPublicKey.toBase58(),
wrapAndUnwrapSol: true,
computeUnitPriceMicroLamports: 5000, // 0.000005 SOL priority
}),
});
const { swapTransaction } = await swapResponse.json();
// 3. Sign and send
const tx = VersionedTransaction.deserialize(Buffer.from(swapTransaction, 'base64'));
const signedTx = await params.signTransaction(tx);
const signature = await params.connection.sendTransaction(signedTx);
await params.connection.confirmTransaction(signature, 'confirmed');
return signature;
}
复杂示例:生产级兑换服务
输入:“构建一个包含重试、速率限制和监控功能的兑换服务”
输出:
// See full implementation in examples/swap-service.ts
// Key features:
// - Request queue with rate limiting (60/min)
// - Exponential backoff on failures
// - Priority fee escalation on retries
// - Quote caching (10s TTL)
// - Prometheus metrics export
// - Structured logging
// - Health check endpoint
class JupiterSwapService {
private requestQueue: RequestQueue;
private quoteCache: LRUCache<string, Quote>;
private metrics: SwapMetrics;
async swap(params: SwapParams): Promise<SwapResult> {
const startTime = Date.now();
try {
// Check rate limit
await this.requestQueue.acquire();
// Get or refresh quote
const quote = await this.getQuoteWithCache(params);
// Validate
this.validateQuote(quote, params);
// Execute with retries
const signature = await this.executeWithRetry(quote, params);
// Record success
this.metrics.recordSwap('success', Date.now() - startTime);
return { success: true, signature, quote };
} catch (error) {
this.metrics.recordSwap('failure', Date.now() - startTime);
throw error;
}
}
}
评论
支持 Markdown