4

用 ethers.js 发送高 gas 的替换交易

 2 years ago
source link: https://learnblockchain.cn/article/3131
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

有时,在以太坊上提交的交易没有足够的 gas ,因为网络拥堵或有太多的待处理交易提供了比你的交易报价更高的 gas 价格。如果你有一个高优先级的交易,但由于 gas 不足,可能最终不得不等待数小时甚至数天才能打包结算。在这种情况下,你需要用更高的 gas 价格和相同的 nonce 重新发送交易,以使它尽早被打包。

本文中,我们将在以太坊测试网上用 ethers.js 重发交易,并介绍相关的术语和知识。

什么是以太坊交易?

当我们想在以太坊网络上添加、更新或修改一些内容时,就需要发送一笔交易。基本上,交易是现实世界与以太坊网络互动的方式。每一笔在以太坊网络上交易,都需要支付一笔叫做 gas 的费用。gas 的单位是 wei(ETH的最小单位)或 gwei。

以太坊的交易类型

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

  1. 普通交易:如把以太币(ETH/ether)从一个账户转移到另一个账户。
  2. 创建合约交易:在以太坊区块链网络上部署智能合约的交易。
  3. 调用合约交易:该交易用于(发送一些数据)与先前部署的智能合约交互。

一个典型的以太坊交易的参数:

  • from: 交易发起地址,20字节的地址,代表发起交易的账户。
  • to:交易目标地址(接收者),20个字节的地址,代表交易向谁发起(或向谁转账)。
  • value: 交易中从一个账户发送至另一个账户的 ETH 数量。
  • data: 对于创建合约,这个字段包含编译为字节码的智能合约代码。对于调用合约,这个字段包含函数签名(函数选择器)和参数 - 标识如何调用智能合约函数,对于普通的资金交易,则留空或是留言信息。
  • gasLimit: 交易中可以作为最大 gas 使用额度。
  • gasPrice:这是发送方愿意为单位 gas 交易支付的金额,以 wei 计价。
  • chainId:交易发送到哪一个以太坊网络,网络ID(主网mainnet:1,rinkeby:4,kovan:42等,可参考以太坊主要网络 chainId 及 NetworkID
  • nonce:它是一个账号发出过的交易计数。每当一个地址发送一笔交易,nonce就会增加1。

以太坊的交易流程

  • 构造一个交易对象,并向其提供所有必要的参数。
  • 用发送者的私钥对交易进行签名。
  • 用以太坊节点将交易发送到以太坊网络中。

对交易进行签名

签署交易是指使用发送者的私钥在交易对象上生成一个签名。如果你想了解更多关于以太坊签名和验证的信息,这里有一些很好的读物:Yos 写 以太坊签名和验证签名和Angello Pozo的 以太坊签名和验证

现在,我们对以太坊的交易有了更好的了解,下面我们自己发一笔交易:

依赖条件:

  • 安装好 NodeJS
  • 一个文本编辑器
  • 终端,又称命令行

安装必要的工具

我们使用 NodeJS 来管理库和处理 javascript 文件。先看看是否安装了 NodeJS ,打开终端并运行:

$ node -v

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

为了发送交易,我们将使用一段简短的 javascript 代码,以及将使用 ethers.js 库,你可以在命令行终端用 npm 安装它:

$ npm install --save ethers

在这一步最常见的问题是node-gyp的内部故障,可以参考node-gyp安装说明

另一个常见的问题是缓存过期,只需要除 npm 的缓存:

$ npm cache clean

查看ethers.js指南以了解更多关于 ethers.js 库的信息。

创建钱包账号并获得一些测试 ETH

现在,让我们为自己创建一个钱包账号。首先,创建一个 index.js 文件或你要的其他命名的 javascript 文件。然后将下面复制/粘贴到你的代码编辑器中:

var ethers = require('ethers');  
var privateKey = "0x0111111111111111111122222222222222222223333333333333333333344445";
var wallet = new ethers.Wallet(privateKey);
console.log("Address: " + wallet.address);

稍微解释一下代码:导入之前安装的 ethers.js 库(第1行),为钱包创建一个特定的私钥,它可以是任何数字,并确保它包含与上面(第2行)相同的位数,使用私钥在以太坊创建一个钱包实例(第3行),在控制台打印文字"Address:"及钱包地址(第4行)。

用以下方法运行该文件:

node index.js

如果一切顺利,它应该看起来像这样:

由于每笔交易都需要 gas 费,我们需要一些 gas 在交易中发送,我们可以在钱包中获得一些测试 ETH。为此,我们将使用一个 Kovan 节点,去 Kovan 水龙头获得一些测试 ETH,复制粘贴你运行上述代码得到的钱包地址,然后点击“send me KETH!” 。

注意:这是测试 ETH ,没有市场价值。

启动以太坊节点

对于本文来说,我们可以使用任何以太坊客户端,如 Geth 或 OpenEthereum(之前名为 Parity)。要独自启动一个以太坊节点,首先需要选择一个客户端并对其进行配置;同步一个以太坊节点并维护它是一项具有挑战的任务;同步一个以太坊全节点可能需要几天时间。

这对于仅仅发送一个交易来说有点太过复杂,我们选择 QuikNode 提供的免费节点URL。成功创建免费以太坊节点后,复制提供者地址( HTTP Provider)地址:

本文中使用的是 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);
    });
});

然后,用上面一节中的 HTTP 提供者替换ADD_YOUR_ETHEREUM_NODE_URL

简单解释一下上面的代码:(第1行)导入之前安装的 ethers 库,(第2行)设置以太坊节点 URL,(第3行)实例化一个 ethers JsonRpcProvider 实例,(第4行)创建一个特定的钱包的私钥,可以是任何数字,并确保它包含相同位数。(第5行)用私钥创建一个以太坊的钱包实例,(第6行)在控制台中打印钱包地址,(第7-12行)创建一个交易对象并在 to 字段中添加接收者的地址,你也可以用var wallet = Wallet.createRandom();生成一个随机地址,在value字段设置要发送的 ETH 的价值,在chainId字段,因为我们使用的是 Kovan ,设置为42,nonce字段设置为3,(第13-15行)根据节点估计 gasLimit 并设置 gas 价格,(第16-18行)用钱包的私钥为交易签名并发送,同时在控制台打印该交易。

运行一下:

node index.js

我们会得到一个类似这样的输出:

复制hash字段的字符串(类似0xe49c8122656c612bf5f1e10b251f56671acf01d831b6876c37c5a52053035642)并粘贴到Kovan 浏览器的搜索框中,以检查交易的状态。你会看到,该交易尚未被添加到区块链上的新区块中,是等待(pending)状态。这是因为我们设置的 gas 值明显偏低。

现在,我们尝试用相同的 nonce 发送相同的交易,但这次是增加 gas 值;将代码中第15行的gasPrice值从0.14085197改为2.14085197并重新运行代码:

node index.js

复制新输出的ash字段的字符串,并将其粘贴到Kovan 浏览器的搜索框中,再次检查交易的状态。这一次,我们的交易成功被添加到区块中,并且状态为成功,因为我们在发送交易时增加了gasPrice值。

注意:nonce值在两次交易中是相同的。如前所述,nonce 是一个以太坊地址的交易数量。为了重新提交一个有更多 gas 的交易,你必须使用与原始交易相同的nonce。如果你使用增加的 nonce,这将是一个新的交易,那么你的发送 ETH 会比你的想象的更多(因为发送了两次)。

因此,正如我们在上面的例子中看到的,交易的 gas 价格是用于竞争交易优先打包权。区块链上的矿工通常会根据可用的区块空间占有率来设置 gas ,当有一笔交易的 gas 低于阈值 gas 值时,它不会被验证。与其他交易相比,gas 值较高的交易会很快得到验证。


本翻译由 CellETF 赞助支持。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK