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

使用Ethers、MetaMask在BSC转账USDT时随机出现Internal JSON RPC错误

Dapp USDT转账异常:MetaMask显示失败但链上实际成功

问题详情

开发的Dapp实现钱包A向钱包B转账USDT功能,流程为唤起MetaMask授权,用户确认后调用sendTransaction方法。但偶尔出现Internal JSON RPC错误:MetaMask提示交易失败,然而链上(BSC)交易已成功,USDT完成转账。

  • 异常随机出现,约每10-20次交易触发1次
  • 本次异常交易nonce为349,使用Ankr付费RPC节点

相关代码

let nonce = 0;

try {
    nonce = await tmpProvider.getTransactionCount(await signer.getAddress());
    console.log('Nonce:', nonce);
} catch (error) {
    console.error('Error fetching nonce:', error);
}

const burnTx = await signer.populateTransaction({
    to: usdtContractAddress,
    value: 0,
    data: usdtContract.interface.encodeFunctionData("transfer", [recipientAddress, amount]),
    nonce: nonce
})

const estimatedGas = await signer.estimateGas(burnTx);
const gasLimit = estimatedGas + 1000000n;
burnTx.gasLimit = gasLimit;

const tx = await signer.sendTransaction(burnTx);

异常表现

  • MetaMask界面显示交易失败
  • 链上浏览器显示交易成功
  • 报错信息为「Internal JSON RPC错误」

可能原因

  1. RPC节点同步延迟:Ankr节点在交易上链后未及时同步状态,MetaMask查询交易状态时返回错误,但实际链上已确认。
  2. MetaMask状态不同步:MetaMask本地缓存的交易状态与链上数据不一致,导致误判交易失败。
  3. Nonce获取逻辑漏洞:仅获取已确认交易的nonce,理论上可能因未确认交易导致冲突,但本次异常交易已成功,此可能性较低。

解决方案

1. 优化Nonce获取

获取包含未确认交易的nonce,避免潜在冲突:

// 增加'pending'参数,获取包含未确认交易的nonce
nonce = await tmpProvider.getTransactionCount(await signer.getAddress(), 'pending');

2. 主动轮询交易确认

不依赖MetaMask返回,主动等待交易上链确认:

const tx = await signer.sendTransaction(burnTx);
// 等待1个区块确认,确保交易已上链
const confirmedTx = await tx.wait(1);
console.log('交易已确认,区块高度:', confirmedTx.blockNumber);

3. 多RPC节点 fallback

配置多个可靠RPC节点(如BSC官方节点+Ankr),单个节点异常时自动切换:

// 示例:简单多节点切换逻辑
const providers = [
    new ethers.providers.JsonRpcProvider('BSC官方节点URL'),
    new ethers.providers.JsonRpcProvider('Ankr节点URL')
];

async function getActiveProvider() {
    for (const provider of providers) {
        try {
            await provider.getBlockNumber();
            return provider;
        } catch (e) {
            continue;
        }
    }
    throw new Error('所有RPC节点不可用');
}

// 使用时替换tmpProvider
const tmpProvider = await getActiveProvider();

4. 错误后校验链上状态

捕获错误后,主动查询链上对应nonce的交易状态:

try {
    const tx = await signer.sendTransaction(burnTx);
    await tx.wait(1);
} catch (error) {
    const userAddress = await signer.getAddress();
    // 查询该地址最近10笔交易,检查对应nonce的交易状态
    const transactions = await tmpProvider.getHistory(userAddress, 10);
    const targetTx = transactions.find(tx => tx.nonce === nonce);
    
    if (targetTx && targetTx.status === 1) {
        // 交易实际已成功,执行成功逻辑
        console.log('交易已成功,TxHash:', targetTx.hash);
    } else {
        // 交易确实失败,处理错误
        console.error('交易失败:', error);
    }
}

内容的提问来源于stack exchange,提问作者Alvin Low

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 06:17:08