sendSignedTransaction()交易异常:间歇性无回执问题求助
Goerli测试网交易脚本间歇性卡住于
sendSignedTransaction,无法获取回执 问题描述
此前在Goerli测试网运行正常的交易复制脚本,如今出现间歇性失效情况。脚本逻辑为监听内存池交易,调用sendTx函数复制追踪目标交易,但执行到await web3.eth.sendSignedTransaction时会卡住,无法返回交易回执,该现象时好时坏。
已排查方向:
- 节点问题:使用Infura,更换其他公共RPC节点后问题未改善
- 怀疑Web3版本过时
- 怀疑钱包Nonce存在异常
附Tx.js代码:
const Web3 = require('web3'); const Tx = require('ethereumjs-tx'); process.removeAllListeners('warning'); require('dotenv').config(); const INFURA_URL_TESTNET = process.env.INFURA_URL_TESTNET; const web3 = new Web3(INFURA_URL_TESTNET); const BN = web3.utils.BN; const sendTx = async (targetAddress) => { const address = targetAddress.address; const ethAmmount = targetAddress.value; const data = targetAddress.input; const gasLimit = targetAddress.gas; const maxFeePerGas = targetAddress.maxFeePerGas; const maxPriorityFeePerGas = targetAddress.maxPriorityFeePerGas; const privKey = targetAddress.privKey; const from = targetAddress.from; const _txCount = await web3.eth.getTransactionCount(from); const createTransaction = await web3.eth.accounts.signTransaction( { to: address, from: from, data: data, gas: gasLimit, value: ethAmmount, nonce: _txCount, maxFeePerGas: maxFeePerGas, maxPriorityFeePerGas: maxPriorityFeePerGas, }, privKey, ); const createReceipt = await web3.eth.sendSignedTransaction(createTransaction.rawTransaction); console.log(`Tx successful with hash: ${createReceipt.transactionHash}`); }; module.exports = {sendTx};
排查与修复方案
1. Nonce 冲突问题(最可能原因)
当前代码中getTransactionCount(from)默认只获取已上链交易的nonce,如果钱包存在内存池中的待确认交易,这个值会比实际需要的nonce小,导致重复发送同nonce的交易,被节点拒绝后脚本无限等待回执。
修复:
调用getTransactionCount时指定pending参数,获取包含内存池待确认交易的nonce:
const _txCount = await web3.eth.getTransactionCount(from, 'pending');
同时建议添加异常捕获,当出现nonce重复错误时自动重试并递增nonce:
let txCount = await web3.eth.getTransactionCount(from, 'pending'); let success = false; while (!success) { try { const createTransaction = await web3.eth.accounts.signTransaction( { /* ... 交易参数 ... */ nonce: txCount }, privKey ); const receipt = await web3.eth.sendSignedTransaction(createTransaction.rawTransaction); console.log(`Tx successful with hash: ${receipt.transactionHash}`); success = true; } catch (err) { if (err.message.includes('nonce too low')) { txCount++; } else { throw err; } } }
2. Web3.js 版本兼容性问题
旧版Web3.js对EIP-1559交易(maxFeePerGas/maxPriorityFeePerGas)的状态处理存在bug,容易导致等待回执时卡住。
排查与修复:
- 查看当前版本:执行
npm list web3 - 升级到最新稳定版:执行
npm install web3@latest(优先选择4.x系列) - 升级后检查
signTransaction等API的参数格式,确保代码兼容新版本
3. 交易参数合理性优化
直接复用目标交易的参数可能存在风险:
gasLimit刚好够用,但复制交易可能因节点计算差异导致gas不足,交易失败后脚本卡住- 手续费参数在网络拥堵时不足以被矿工打包,交易长时间处于pending状态
优化:
- 给gasLimit增加10%-20%冗余:
const gasLimit = web3.utils.toBN(targetAddress.gas).mul(web3.utils.toBN(12)).div(web3.utils.toBN(10)); // 增加20% - 适当提高手续费(比目标交易高10%):
const maxFeePerGas = web3.utils.toBN(targetAddress.maxFeePerGas).mul(web3.utils.toBN(11)).div(web3.utils.toBN(10)); const maxPriorityFeePerGas = web3.utils.toBN(targetAddress.maxPriorityFeePerGas).mul(web3.utils.toBN(11)).div(web3.utils.toBN(10));
4. 增加超时机制避免无限等待
sendSignedTransaction默认会无限等待回执,没有超时逻辑,导致脚本卡住。
修复:
改用事件监听模式,设置超时时间:
const sendTx = async (targetAddress) => { // ... 其他代码 ... const createTransaction = await web3.eth.accounts.signTransaction(/* ... */); return new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error('交易等待回执超时')); }, 30000); // 30秒超时 web3.eth.sendSignedTransaction(createTransaction.rawTransaction) .on('receipt', (receipt) => { clearTimeout(timeout); console.log(`Tx successful with hash: ${receipt.transactionHash}`); resolve(receipt); }) .on('error', (err) => { clearTimeout(timeout); reject(err); }); }); };
内容的提问来源于stack exchange,提问作者user84407
相关产品推荐
相关产品推荐

