Solana支付网关开发问题:Jupiter API签名后SOL-USDC兑换未执行
SOL转USDC兑换无报错但未执行的排查与修复
针对你用Jupiter API实现SOL到USDC兑换时,签名后无报错但代币未变化的问题,以下是核心排查点和修复方案:
1. 关闭预检查跳过,暴露隐性错误
你当前设置了skipPreflight: true,这会绕过Solana节点的交易预验证——即使交易存在签名不匹配、余额不足、指令错误等问题,节点也会接收交易但不会执行,且不会返回明确报错。
修复:
将sendRawTransaction的参数改为:
const signature = await connection.sendRawTransaction(transactionBinary, { maxRetries: 2, skipPreflight: false, // 关闭跳过预检查 preflightCommitment: "confirmed" });
预检查会直接返回交易的潜在问题(如签名错误、余额不足),帮你快速定位原因。
2. 验证签名钱包与用户地址一致性
Jupiter生成的交易是绑定你传入的userPublicKey的,必须使用该地址对应的私钥签名。如果mainWallet不是userPublicKey对应的Keypair,交易会被节点拒绝执行,但因跳过预检查未暴露。
验证:
在签名前添加检查:
if (!mainWallet.publicKey.equals(userPublicKey)) { throw new Error("签名钱包与用户地址不匹配"); }
3. 排查小额兑换的精度与平台限制
你测试的兑换金额是0.00001 SOL(10,000 lamports),可能存在两个问题:
- USDC是6位小数精度,兑换后的USDC金额可能过小(如按1SOL=100USDC计算,仅0.001 USDC=1000 units),部分钱包会默认隐藏极小余额;
- Jupiter可能对最小兑换金额有门槛,过小金额无法触发兑换。
验证:
- 兑换后直接查询用户的USDC关联账户余额:
// 交易确认后查询USDC余额 const usdcAta = getAssociatedTokenAddressSync( new PublicKey(outputMint), userPublicKey ); const usdcAccount = await connection.getAccountInfo(usdcAta); const usdcBalance = usdcAccount ? Number(Buffer.from(usdcAccount.data.slice(64, 72)).readBigUInt64LE()) / 1e6 : 0; console.log("用户USDC余额:", usdcBalance);
- 尝试增大兑换金额(如0.001 SOL)测试是否能正常兑换。
4. 完善交易结果校验
即使confirmTransaction返回无错误,也需通过交易日志进一步确认兑换指令是否执行成功:
添加日志校验:
const transactionDetails = await connection.getTransaction(signature, { commitment: "finalized", maxSupportedTransactionVersion: 0 }); console.log("交易指令日志:", transactionDetails?.transaction.message.instructions);
查看日志中是否包含Jupiter的兑换指令,以及是否有执行成功的标记。
修复后的完整代码示例
const { Connection, Keypair, PublicKey, clusterApiUrl, VersionedTransaction, } = require("@solana/web3.js"); const { getAssociatedTokenAddressSync, } = require("@solana/spl-token"); const fetch = require("node-fetch"); const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed"); const swapTokens = async (req, res) => { try { // 配置参数 const inputMint = "So11111111111111111111111111111111111111112"; // SOL const outputMint = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"; // USDC const userPublicKey = new PublicKey("YOUR_WALLET_PUBLIC_KEY"); const mainWallet = Keypair.fromSecretKey(Uint8Array.from(YOUR_SECRET_KEY)); // 验证钱包一致性 if (!mainWallet.publicKey.equals(userPublicKey)) { throw new Error("签名钱包与用户地址不匹配"); } // 准备兑换金额(建议先测试0.001 SOL) const amount = 0.001; const solAmount = Math.floor(amount * 1e9); // 获取兑换报价 const quoteResponse = await ( await fetch( `https://api.jup.ag/swap/v1/quote?inputMint=${inputMint}&outputMint=${outputMint}&amount=${solAmount}&slippageBps=50&restrictIntermediateTokens=true&swapMode=ExactIn` ) ).json(); if (quoteResponse.error) { throw new Error(`报价获取失败: ${quoteResponse.error}`); } console.log("报价响应:", JSON.stringify(quoteResponse, null, 2)); // 获取兑换交易 const swapResponse = await ( await fetch("https://api.jup.ag/swap/v1/swap", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ quoteResponse, userPublicKey: userPublicKey.toBase58(), }), }) ).json(); if (swapResponse.error) { throw new Error(`交易生成失败: ${swapResponse.error}`); } console.log("兑换交易响应:", swapResponse); // 反序列化并签名交易 const transaction = VersionedTransaction.deserialize(Buffer.from(swapResponse.swapTransaction, "base64")); transaction.sign([mainWallet]); // 发送交易(关闭预检查) const signature = await connection.sendRawTransaction(transaction.serialize(), { maxRetries: 2, skipPreflight: false, preflightCommitment: "confirmed" }); console.log("交易签名:", signature); // 确认交易 const confirmation = await connection.confirmTransaction({ signature }, "finalized"); console.log("交易确认结果:", confirmation); if (confirmation.value.err) { throw new Error(`交易执行失败: ${JSON.stringify(confirmation.value.err)}`); } // 查询余额变化 const solBalance = await connection.getBalance(userPublicKey); const usdcAta = getAssociatedTokenAddressSync(new PublicKey(outputMint), userPublicKey); const usdcAccount = await connection.getAccountInfo(usdcAta); const usdcBalance = usdcAccount ? Number(Buffer.from(usdcAccount.data.slice(64, 72)).readBigUInt64LE()) / 1e6 : 0; console.log(`兑换后SOL余额: ${solBalance / 1e9} SOL`); console.log(`兑换后USDC余额: ${usdcBalance} USDC`); res.status(200).json({ message: "兑换执行成功", signature, solBalance: solBalance / 1e9, usdcBalance }); } catch (error) { console.error("兑换错误:", error); res.status(500).json({ message: "兑换失败: " + error.message }); } };
内容的提问来源于stack exchange,提问作者Hamza Zulqarnain
相关产品推荐
相关产品推荐

