Jupiter API能否单笔交易完成代币兑换并直接转至商户账户?
问题概述
尝试通过Jupiter API实现单笔交易完成代币兑换,并将兑换后的代币直接发送至商户账户,按文档设置destinationTokenAccount为商户钱包主地址后交易失败。失败交易签名:37GAcwzCfEbydZknATZXRMjDEzMPKV5b3TF3VtM2JUD5y6cKoKWipQD5qfpB2MLtmkDsLgVhzyWrU5kdHRi9HRqh
核心问题排查
1. destinationTokenAccount参数错误
Jupiter API的destinationTokenAccount要求传入输出代币对应的关联代币账户地址,而非商户的主钱包地址。你当前传入的ANJt85VAVGhknPAhKBaS2qWVZUWW59rkQSbAg4sW4dFA是商户主地址,主地址本身不是USDC的代币账户,直接传递会触发InvalidAccountData错误,这是交易失败的主要原因。
2. 交易签名逻辑错误
在sendTransaction函数中,你已完成交易签名得到signedTransaction,但后续模拟和发送时使用的是未签名的原始transaction,会导致交易因缺少有效签名而失败。
3. 交易块哈希覆盖问题
Jupiter返回的swapTransaction已包含有效块哈希,手动覆盖transaction.recentBlockhash会破坏交易的有效性。
修正方案
步骤1:获取商户的输出代币关联账户地址
使用@solana/spl-token库计算商户主地址对应的USDC关联账户地址:
import { getAssociatedTokenAddress } from '@solana/spl-token'; // 计算商户的USDC关联账户地址 const merchantUsdcAccount = await getAssociatedTokenAddress( new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // USDC mint地址 new PublicKey("ANJt85VAVGhknPAhKBaS2qWVZUWW59rkQSbAg4sW4dFA"), // 商户主地址 false );
将该地址作为destinationTokenAccount传入Jupiter的swap接口。
步骤2:修复交易签名与发送逻辑
修改sendTransaction函数,使用签名后的交易进行模拟和发送:
const sendTransaction = async (swapTransaction: string, walletAdapter: WalletAdapter) => { const versionedMessage = VersionedMessage.deserialize(Buffer.from(swapTransaction, 'base64')); const transaction = new VersionedTransaction(versionedMessage); // Jupiter返回的交易已包含有效块哈希,无需手动覆盖 transaction.feePayer = walletAdapter.publicKey; // 签名交易 const signedTransaction = await walletAdapter.signTransaction(transaction); // 模拟签名后的交易 const simulation = await connection.simulateTransaction(signedTransaction, { commitment: "finalized" }); if (simulation.value.err) { throw new Error(`Simulation failed: ${simulation.value.err.toString()}`); } // 发送签名后的交易 try { const signature = await connection.sendRawTransaction(signedTransaction.serialize(), { maxRetries: 5, skipPreflight: false, // 开启预检查提前发现问题 preflightCommitment: "finalized", }); // 确认交易 const confirmation = await connection.confirmTransaction({ signature, blockhash: transaction.message.recentBlockhash, lastValidBlockHeight: transaction.message.lastValidBlockHeight, }, "finalized"); if (confirmation.value.err) { throw new Error(`Transaction not confirmed: ${confirmation.value.err.toString()}`); } console.log("Confirmed: ", signature); } catch (error) { console.error("Failed: ", error); throw error; } };
步骤3:调整swap接口请求参数
将skipUserAccountsRpcCalls设为false,让Jupiter自动处理账户检查与创建逻辑:
const requestBody = { userPublicKey: walletAddress.toBase58(), destinationTokenAccount: merchantUsdcAccount.toBase58(), // 使用计算出的关联账户地址 useSharedAccounts: true, quoteResponse: swapInfo.quoteResponse, skipUserAccountsRpcCalls: false, };
功能可行性确认
Jupiter API完全支持单笔交易完成兑换并直接将代币发送至第三方账户的场景,只要正确传递destinationTokenAccount为目标代币的关联账户地址,即可实现该功能。
内容的提问来源于stack exchange,提问作者creative pioneer

