You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 14:17:32