跳转至主要内容

如何使用 Ethers.js 以更高的 gas 价格重新发送交易

更新于
2025年11月26日

阅读时间 8 分钟

重要通知

本指南中提到了 Kovan 测试网,该测试网目前已不再积极维护。虽然与该链相关的具体步骤可能不再适用,但整体流程对其他链可能仍然有效。我们建议您在实施过程中探索当前的替代方案。如果您希望查看本指南的更新版本,请告知我们

概述

有时,由于网络拥堵,或者存在大量待处理交易(这些交易的 gas 价格高于您在交易中设定的价格),您在以太坊上提交的交易可能因 gas 不足而无法成功。如果您有一笔优先级较高但 gas 不足的交易,最终可能需要等待数小时甚至数天才能完成结算。在这种情况下,您需要重新发送一笔 gas 更高且 nonce 相同的交易,以便让该交易尽早被矿工挖入区块。

在本指南中,我们将详细介绍在以太坊上发送交易以及使用 Ethers.js 在某个以太坊测试网络上重新发送同一笔交易的相关术语和信息。

以太坊中的交易是什么?

每当我们在以太坊网络上添加、更新或修改某些内容时,都会发起一笔交易。简而言之,交易是现实世界与以太坊区块链网络交互的方式。在以太坊网络上进行的每笔交易,都需要支付一笔称为“gas”的手续费。gas的单位可以是wei(ETH的最小组合单位)或gwei。

以太坊中的交易类型

以太坊网络上有三种常见的交易类型:

  1. 一笔普通交易,即ETH/以太币从一个账户转入另一个账户。
  2. 创建合约的交易是指在以太坊区块链网络上部署智能合约的交易类型。
  3. 调用合约的交易,该交易会携带一些数据发送,用于查询或与先前部署的智能合约进行交互。

典型以太坊交易的参数

  • from:发件人地址,一个20字节的地址,代表发起交易的账户。
  • 收件人地址:一个20字节的地址,代表收件人的账户或合约账户。
  • value:这是交易中从一个账户发送到另一个账户的 ETH 数量。
  • data:该字段包含合约部署交易的字节码。对于合约函数的执行,该字段包含函数签名和编码后的参数。在资金转账交易中,该字段既非必需,也通常留空。
  • gasLimit:这是交易可使用的最大 gas 数量(单位为 wei)。
  • gasPrice:这是发件人愿意为该交易支付的金额(单位为wei)。
  • chainId:这是您的以太坊节点的网络 ID(主网:1,Rinkeby:4,Kovan:42,等等)
  • nonce:这是某个地址发送的交易数量。该地址每次发送一笔交易时,nonce 就会增加 1。

以太坊中的交易流程

  • 创建一个事务对象,并为其提供所有必要的参数。
  • 使用发件人的私钥对交易进行签名。
  • 使用以太坊节点将交易发送至以太坊区块链网络。

签署交易

对交易进行签名,是指使用发件人的私钥对交易对象生成一个签名。如果您想进一步了解以太坊中的签名和验证机制,以下是两篇非常值得一读的文章:Yos 撰写的《以太坊签名的生成与验证》以及Angello Pozo 撰写的《以太坊:签名与验证》

现在,既然我们对以太坊中的交易有了更深入的了解,那就让我们亲自发送一笔交易吧。

先决条件

  • 您的系统上已安装 Node.js
  • 一个文本编辑器
  • 终端(又称命令行)

安装必要的工具。

我们将使用 NodeJS 来管理我们的库包并处理 JavaScript 文件。要查看您的系统上是否已安装 NodeJS,请打开终端并运行以下命令:

$ node -v

如果尚未安装,您可以从官方网站下载 NodeJS 的 LTS 版本。

要发送一笔交易,我们将使用一段简短的 JavaScript 代码,并使用 ethers.js 库(请确保版本为 5.7)。你可以通过命令行/终端使用 npm 安装它:

$ npm install --save ethers@5.7

此步骤中最常见的问题是 `node-gyp` 出现内部故障。您可以按照此处的 node-gyp 安装说明进行操作。

另一个常见问题是缓存过期。只需在终端中输入以下命令,即可清除 npm 缓存:

$ npm cache clean

您可以查阅我们的ethers.js 指南,以进一步了解 ethers.js 库。

创建钱包并获取一些测试以太币

现在,让我们为自己创建一个钱包。要做到这一点,首先需要创建一个名为 index.js 的文件,或者创建一个任意名称的 JavaScript 文件。你可以将以下内容复制并粘贴到你的代码编辑器中:

var ethers = require('ethers');  
var privateKey = "0x0111111111111111111122222222222222222223333333333333333333344445";
var 钱包 = new ethers.钱包(privateKey);
控制台.log("地址:" + 钱包.地址);

让我们看看这里发生了什么:导入我们之前安装的 ethers.js 库(第 1 行),  生成一个专属于我们钱包的私钥,你需要自己生成一个,数字可以任意,但请确保位数与上文一致(第 2 行),使用该私钥通过 ethers 创建钱包实例(第 3 行),在控制台输出我们的钱包地址并附上“Address”字样(第 4 行)。

现在,请使用以下命令运行该文件:

$ node index.js

如果一切顺利,终端窗口中显示的输出应如下所示:

地址:0x3f5D3070Fc7924479B5e367a83EC2b284E9c4e04

由于每笔交易都需要支付 gas 作为手续费,而且我们需要一些 gas 来提交交易,所以让我们在钱包里存入一些测试用 ETH。为此,我们将使用一个 Kovan 节点,请访问这个Kovan 水龙头获取一些测试用 ETH,粘贴运行上述代码后生成的钱包地址,然后点击“send me KETH!”。 

注:这将是测试用的以太币(ETH),不具备市场价值。

设置您的 Quicknode 以太坊端点

就今天的目的而言,我们几乎可以使用任何以太坊客户端,例如 Geth 或 OpenEthereum(原名 Parity)。若要自行启动一个以太坊节点,我们首先需要选择一个客户端并进行配置;同步以太坊节点并对其进行维护是一项具有挑战性的任务;同步一个以太坊全节点可能需要数天时间。

由于仅发送一笔交易而言,这操作稍显复杂,因此我们将使用 Quicknode 提供的免费接口来简化这一过程。创建好免费的以太坊接口后,请复制您的 HTTP 提供商接口地址: 

本指南中,我们使用的是 Kovan 测试网节点。

Quicknode Kovan 端点的屏幕截图

稍后你会用到这个,所以请复制并保存下来。

发送和重新发送交易

现在,我们有一个钱包,里面有一些测试用的 ETH。接下来,让我们发送一笔交易,将部分 ETH 转入另一个账户/钱包。为此,让我们在 index.js 文件中添加一些有用的代码。你可以将以下内容复制并粘贴到你的代码编辑器中:

var ethers = require('ethers');  
var url = 'ADD_YOUR_ETHEREUM_NODE_URL';
var customHttpProvider = new ethers.providers.JsonRpcProvider(url);
var privateKey = "0x0111111111111111111122222222222222222223333333333333333333344445";
var wallet = new ethers.Wallet(privateKey);
console.log("Address: " + wallet.address);
tx = {
to: "0x6E0d01A76C3Cf4288372a29124A26D4353EE51BE",
value: ethers.utils.parseEther("0.05"),
chainId: 42,
nonce: 3
}
customHttpProvider.estimateGas(tx).then(function(estimate) {
tx.gasLimit = estimate;
tx.gasPrice = ethers.utils.parseUnits("0.14085197", "gwei");
wallet.signTransaction(tx).then((signedTX)=>{
customHttpProvider.sendTransaction(signedTX).then(console.log);
});
});

那么,请将 `ADD_YOUR_ETHEREUM_NODE_URL` 替换为上文所述的 HTTP 提供商。 

对上述代码的简要说明——我们正在导入之前安装的 ethers 库(第 1 行),设置以太坊节点 URL(第 2 行),实例化一个 ethers JsonRpcProvider 实例(第 3 行),创建一个专属于我们钱包的私钥(你需要自己创建一个,数字可以是任意数值,但请确保位数一致)(第 4 行), 使用该私钥通过 ethers 创建钱包实例(第 5 行),在控制台输出钱包地址并附带“Address”字样(第 6 行),创建交易对象并在 'to' 字段中添加收款人地址(你可以使用 `var wallet = Wallet.createRandom();` 生成随机地址`生成随机地址,添加'value'字段指定要发送的ETH金额,'chainId'字段此处设为42(因我们使用Kovan网络),nonce字段设为3(第7-12行),通过节点估算gasLimit并设置gas价格(第13-15行), 使用钱包的私钥对交易对象进行签名并发送,同时在控制台打印该交易(第16-18行)。

我们来运行一下这个:

$ node index.js

我们将得到类似于以下内容的输出:

Node.js index.js 的输出

复制哈希字段中的字符串(例如“0xe49c8122656c612bf5f1e10b251f56671acf01d831b6876c37c5a52053035642”),将其粘贴到Kovan Etherscan的搜索框中,以查看交易状态。 你会发现该交易尚未被添加到区块链上的新区块中,状态显示为“待处理”。这是因为我们设置的 gas 值过低。

现在,让我们尝试发送一笔具有相同nonce的交易,但这次将gas值调高;将代码第15行中的gasPrice值从'0.14085197'改为'2.14085197',然后重新运行代码:

$ node index.js 

从新输出的结果中复制哈希字符串,并将其粘贴到Kovan Etherscan的搜索框中,以查看交易状态。这次,由于我们在发送交易时适当提高了 gasPrice 值,因此该交易将被加入区块,并显示为成功状态。

注意:需要特别注意的是,两笔交易中的“nonce”值必须相同。如前所述,nonce 指向一个以太坊地址拥有的交易数量。若要重新提交一笔 gas 费用更高的交易,必须使用与原始交易相同的 nonce。如果使用了更高的 nonce,那将是一笔新的交易,从而导致消耗的 ETH 数量超过您的预期。

结论

因此,正如上文示例所示,交易要获得确认,其 gas 费用必须具有竞争力。区块链矿工会根据可用区块空间以及所有交易为占用该区块空间所愿意支付的费用来设定 gas 费用;当某笔交易的 gas 费用低于阈值时,该交易将无法获得确认。与其他交易相比,gas 费用更高的交易会更快获得确认。

订阅我们的通讯,获取更多关于以太坊的文章和指南。如果您有任何反馈,欢迎通过Twitter 联系我们;如有疑问,您也可以随时通过Discord 上的社区服务器与我们交流。那里聚集了一些最棒的开发者,谢谢 :)

分享本指南