在钱包应用中,让用户清晰了解实时交易状态并掌握控制权至关重要。这不仅能增强用户的确定性感知,还能显著提升整体体验。因此,交易管理成为不可或缺的功能。本文将介绍钱包应用如何管理已发出的交易,包括取消与加速操作,以及如何通过监听 Mempool 数据实时追踪链上滞留交易。
Gas 费用过低的常见问题
多数钱包应用允许用户自定义交易 Gas 费用,当执行速度非首要考虑时,用户可设置较低费用以节约成本。然而,过低的 Gas 费用会导致交易滞留(Pending),例如在 Etherscan 的合约交易列表中会显示为等待状态。
Gas Price 越低,交易滞留时间越长——只有当区块链网络的 Gas Price 降至该交易设定值时,交易才可能上链。极端情况下,一笔低 Gas 交易可能卡滞数天。
可视化工具 TxStreet 可实时展示比特币和以太坊网络的区块状态与交易打包过程,并标识交易来源 DApp。图中左侧以太坊网络数据显示,当前Pending交易数量超过 75,000 笔。
交易滞留常困扰以太坊新用户:若首笔交易未确认,后续交易将同样卡滞,导致用户误认为所有交易均发送失败。如何优化此类场景下的用户体验?
滞留交易管理策略
既然用户可能主动选择低 Gas 费用,解决方案之一是明确告知预计上链时间并动态更新。Etherscan 提供 gasestimate API,可预估给定 Gas Price 的交易确认所需时间。
例如,当前建议 Gas Price 为 24 Gwei时:
- 30 Gwei 预估耗时:45 秒
- 25 Gwei 预估耗时:95 秒
- 20 Gwei 预估耗时:1 小时
该结果符合预期:若设定 Gas Price 接近当前值,难保后续区块费用波动;而 20 Gwei 的预估时长源于无法预测费用下降时间,此时 Etherscan 显示“>1 小时”。钱包应用可沿用相同逻辑实现时间预估。
此外,当用户存在滞留交易并发起新交易时,需避免新旧交易使用相同 Nonce 导致覆盖。回顾 Token 转账交易实现代码:
final transferTx = Transaction.callContract(
contract: contract,
function: transferFunction,
parameters: [EthereumAddress.fromHex(toAddress), amount],
maxFeePerGas: await web3Client.getGasPrice(),
maxPriorityFeePerGas: await getMaxPriorityFee(),
);
final tx = await signTransaction(
privateKey: privateKey,
transaction: transferTx,
);代码未显式指定 Nonce,由 web3dart 库自动处理。追踪其内部实现可见 _fillMissingData() 函数会补全未设置的 Nonce 值:
Future<_SigningInput> _fillMissingData({
required Credentials credentials,
required Transaction transaction,
int? chainId,
bool loadChainIdFromNetwork = false,
Web3Client? client,
}) async {
// ...
final nonce = transaction.nonce ??
await client!
.getTransactionCount(sender, atBlock: const BlockNum.pending());
// ...
}该代码调用 RPC 节点的 eth_getTransactionCount 方法,并传入 atBlock = pending 参数。此参数指定查询数据的时间点,也可用于查询历史区块的交易计数。
pending 参数的含义是将未确认交易纳入地址交易计数——因为 RPC 节点可在其 Mempool 中追踪地址的滞留交易。至此我们理解 web3dart 的细粒度行为:当用户连续发送多笔交易且前序交易滞留时,库能正确计算下一笔交易的 Nonce,避免覆盖旧交易。
交易取消与加速实操
若用户不希望发送新交易,而是希望加速确认或取消前序交易,可利用以太坊“相同 Nonce 仅一笔交易上链”的特性覆盖原交易。
取消交易
常见方法是向自己发送 0 ETH 转账交易,且 Gas 费用至少比原交易高 10%(否则触发 Replacement Transaction Underpriced 错误)。选择转账交易因其 Gas 消耗量最低(21,000),能最大限度节省成本。基于 Day 13 代码的实例如下:
class TransactionWithHash {
final String hash;
final Transaction transaction;
TransactionWithHash({
required this.hash,
required this.transaction,
});
}
Future sendCancelTransaction({
required EthPrivateKey privateKey,
required int nonce,
required EtherAmount lastGasPrice,
}) async {
try {
// 20% up
final newGasPrice =
lastGasPrice.getInWei * BigInt.from(6) ~/ BigInt.from(5);
final cancelTx = Transaction(
from: privateKey.address,
to: privateKey.address,
maxFeePerGas: EtherAmount.inWei(newGasPrice),
maxPriorityFeePerGas: EtherAmount.inWei(newGasPrice),
maxGas: 21000,
value: EtherAmount.zero(),
nonce: nonce,
);
final tx = await signTransaction(
privateKey: privateKey,
transaction: cancelTx,
);
print('tx: $tx , nonce: $nonce');
final txHash = await sendRawTransaction(tx);
print('txHash: $txHash');
return TransactionWithHash(hash: txHash, transaction: cancelTx);
} catch (e) {
rethrow;
}
}加速交易
操作逻辑类似:保留原交易所有参数,仅提高 Gas Price 重新发送。代码此处从略。
为演示取消功能,在发送代币交易时故意设置低 Gas 费用:
// in sendTokenTransaction()
final nonce = await web3Client.getTransactionCount(
EthereumAddress.fromHex(privateKey.address.hex),
atBlock: const BlockNum.pending(),
);
var maxFeePerGas = await web3Client.getGasPrice();
maxFeePerGas = EtherAmount.inWei(maxFeePerGas.getInWei - BigInt.from(1));
var maxPriorityFeePerGas = EtherAmount.zero();
final transferTx = Transaction.callContract(
contract: contract,
function: transferFunction,
parameters: [EthereumAddress.fromHex(toAddress), amount],
maxFeePerGas: maxFeePerGas,
maxPriorityFeePerGas: maxPriorityFeePerGas,
nonce: nonce,
);界面添加取消按钮后,点击“Send Tx”生成低 Gas 交易(滞留不上链),再点击“Send Cancel Tx”发送高 Gas 取消交易(成功上链覆盖原交易)。
Mempool 机制深度解析
若用户通过其他平台(如浏览器插件)以同一钱包地址发送低 Gas 交易,如何在应用中呈现这些交易以便管理?这需引入 Mempool 监听机制。
Mempool(内存池)暂存所有已广播但未上链的交易。调用 RPC 节点的 eth_sendRawTransaction 方法即请求节点将交易广播至全网,各节点将这些交易存储于内存中形成 Mempool。
开启挖矿功能的节点(矿工)负责决定下一批打包交易(通常按 Gas Price 降序排列),未上链交易即来源于 Mempool。
由于 Mempool 公开透明,任何人均可通过 API 获取其中交易。但这也导致 MEV(矿工可提取价值)攻击风险:攻击者发现特定交易后,可通过抢先交易(Front run)或尾随交易(Back run)套利。由此衍生出私人交易(Private Transactions)需求——不经 Mempool 直接发送交易至矿工。相关技术可参考 Flashbots 及 Alchemy 的私人交易解说。
Mempool 数据获取方案
市场提供多种获取 Mempool 数据的服务,例如 Blocknative 和 Quicknode,均支持订阅未确认交易功能。以 Blocknative 为例:
- 访问 Blocknative Explorer
- 输入欲监控的地址(如 USDT 合约地址)
- 系统开始监听该合约所有未确认交易并实时显示
- 可创建复杂过滤器:按合约函数、来源地址等条件筛选
Blocknative 同时提供 Mempool 监听 API,更适合后端监控全网未确认交易并过滤出目标交易。完整实现虽超出本文范围,但开发者可尝试对接其 API 监控自有地址交易。
常见问题
什么是 Gas Price?
Gas Price 决定交易执行优先级。矿工优先打包高 Gas Price 交易,低 Gas Price 交易可能延迟确认或始终无法上链。
为何取消交易需支付 Gas 费用?
取消交易本质是覆盖原交易的新交易,需消耗网络资源。转账 0 ETH 至自身地址是成本最低的覆盖方式。
Mempool 数据是否完全可靠?
Mempool 数据源自节点内存,不同节点可能存在数据差异。且交易在未确认前可能被节点丢弃,建议结合多源数据校验。
私人交易如何避免 MEV 风险?
私人交易通过专属通道直接发送给矿工,不经过公开 Mempool,有效防止交易策略被窥探与恶意套利。
如何选择交易加速服务?
需评估服务的节点覆盖率、推送延迟与可靠性。主流方案包括 Blocknative、Quicknode 及 Flashbots Protect。
非 EVM 链是否存在类似机制?
其他区块链(如 Solana、BNB Chain)均有类似交易池设计,但具体实现与工具链存在差异。
结语
通过交易管理与 Mempool 监听,钱包应用可显著提升用户体验。本文介绍了交易取消、加速的实现原理,以及监控未确认交易的技术方案。合理运用这些技术,能使链上交互更加流畅可靠。
👉 探索更多链上策略