本文会采用几个案例,概述一下 Ethers.js 的常见用法
下面所有案例使用的 Ethers.js 版本均为6.12.0
官方参考文档:https://docs.ethers.org/v6
概述
Ethers.js 主要由下面几个部件组成
Provider
Provider是与区块链的只读连接,允许查询区块链状态,例如帐户、区块或交易详细信息,查询事件日志或使用调用评估只读代码。
Signer
Signer包装与帐户交互的所有操作。每个帐户会有一个私钥,可用于对各种操作进行签名。
私钥可能位于内存中(使用Wallet)或通过某些 IPC 层进行保护,例如 MetaMask,它代理从网站到浏览器插件的交互,从而使私钥远离网站,并且仅在请求用户许可并收到授权后才允许交互。
Transaction
要对区块链进行任何状态更改,需要进行交易,这需要支付费用,其中费用涵盖执行交易(例如读取磁盘和执行数学)和存储更新信息的相关成本。
如果交易恢复,仍然需要支付费用,因为验证者仍然必须花费资源来尝试运行交易以确定它已恢复,并且记录其失败的详细信息。
交易包括从一个用户向另一个用户发送以太币、部署合约或针对合约执行状态更改操作。
Contract
Contract是一个已部署到区块链的程序,其中包含一些代码并分配了可以读取和写入的存储空间。
当它连接到一个Provider或者当连接到一个时可以调用状态改变操作Signer。
Receipt
一旦交易被提交到区块链,它就会被放置在内存池(mempool)中,直到验证者决定将其包含在内。
交易只有在被纳入区块链后才会发生更改,此时会收到收据,其中包含有关交易的详细信息,例如它包含在哪个区块中、实际支付的费用、使用的 Gas 以及所有事件它发出了什么以及它是否成功或恢复。
余额查询
本节我们来实现一个查询以太坊余额的脚本,为了实现这个功能,我们需要连接到以太坊节点。当然,我们没必要为了查询余额而真的去部署一个以太坊节点,市面上有许多工具可以用。这里我们使用 INFURA
来获取以太坊节点:https://app.infura.io/
注册好之后可以获取免费的APIKey,找到自己的 Endpoints,就可以直接访问以太坊了
获取到节点之后,就可以通过 Provider
来查询任意地址的以太坊余额
const { ethers } = require("ethers"); const INFURA_API_KEY = "..."; const ADDRESS = "..."; const provider = new ethers.JsonRpcProvider( `https://mainnet.infura.io/v3/${INFURA_API_KEY}` ); const getBalance = async () => { const balance = await provider.getBalance(ADDRESS); console.log( `ETH Balance of ${ADDRESS} --> ${ethers.formatEther(balance)} ETH\n` ); }; getBalance();
读取合约
本节我们以 DAI
(一个稳定币) 为例,先在 etherscan 上找到 DAI 合约,可以看到它可以列出了 DAI 的所有读取合约信息的函数,并能在上面直接交互
地址:https://etherscan.io/token/0x6b175474e89094c44da98b954eedeac495271d0f#readContract
我们要做的就是使用 Ethers.js 来实现同样的效果
DAI 是一个 ERC20 合约,意味着它实现了实现了一系列函数,比如常见的 totalSupply、balanceOf、name等等
Ethers.js 库可以接收一个 ABI 数组,来得知如何与合约交互
创建完 contract
实例之后,就可以通过它来调用定义在 ERC20_ABI
中的函数了
const { ethers } = require("ethers"); const INFURA_API_KEY = "..."; const DAI_TOKEN_ADDRESS = "0x6B175474E89094C44Da98b954EedeAC495271d0F"; const provider = new ethers.JsonRpcProvider( `https://mainnet.infura.io/v3/${INFURA_API_KEY}` ); const ERC20_ABI = [ "function name() view returns (string)", "function symbol() view returns (string)", "function totalSupply() view returns (uint256)", "function balanceOf(address) view returns (uint256)", ]; const contract = new ethers.Contract(DAI_TOKEN_ADDRESS, ERC20_ABI, provider) const main = async () => { const name = await contract.name(); const symbol = await contract.symbol(); const totalSupply = await contract.totalSupply(); console.log(`Token Name: ${name}`); console.log(`Token Symbol: ${symbol}`); console.log(`Total Supply: ${totalSupply}`); const balance = await contract.balanceOf("0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11") console.log(`Balance: ${ethers.formatUnits(balance)}`); }; main();
运行结果:
Token Name: Dai Stablecoin
Token Symbol: DAI
Total Supply: 3213933035046622754824652548
Balance: 7288394.450956033361944629
发送交易
本节我们来实现通过 Ethers.js 将一个钱包中的代币发送给另一个钱包
这里使用的是 sepolia 测试网
主要思路是先定义好发送者与接受者的地址,然后获取发送者的私钥,通过这个私钥创建一个 wallet 对象,然后调用 sendTransaction 方法来发送代币
实现逻辑也很简单,具体代码如下
const { ethers } = require("ethers"); const INFURA_API_KEY = "..."; const provider = new ethers.JsonRpcProvider( `https://sepolia.infura.io/v3/${INFURA_API_KEY}` ); const sender = "0x3Ee7EbFb5823c8C8af05BC371873D371085447D1"; const receiver = "0x8cC4f3fBa4b89da6e700D5602e7622CDc0444B62"; const senderPrivateKey = "..."; const wallet = new ethers.Wallet(senderPrivateKey, provider); const main = async () => { const senderBalanceBefore = await provider.getBalance(sender); const receiverBalanceBefore = await provider.getBalance(receiver); console.log( `Sender balance before --> ${ethers.formatEther(senderBalanceBefore)} ETH` ); console.log( `Receiver balance before --> ${ethers.formatEther(receiverBalanceBefore)} ETH` ); // Send 0.001 ETH to receiver const tx = await wallet.sendTransaction({ to: receiver, value: ethers.parseEther("0.001"), }); // Wait for the transaction to be mined await tx.wait(); console.log(tx); const senderBalanceAfter = await provider.getBalance(sender); const receiverBalanceAfter = await provider.getBalance(receiver); console.log( `Sender balance After --> ${ethers.formatEther(senderBalanceAfter)} ETH` ); console.log( `Receiver balance After --> ${ethers.formatEther(receiverBalanceAfter)} ETH` ); }; main();
运行结果
Sender balance before --> 0.029886093476039388 ETH
Receiver balance before --> 0.1 ETH
TransactionResponse {
provider: JsonRpcProvider {},
blockNumber: null,
blockHash: null,
index: undefined,
hash: '0x15b13a89590c9e56fe93b91d610255487f2b2a22a17bf917a87f9d30aad043ad',
type: 2,
to: '0x8cC4f3fBa4b89da6e700D5602e7622CDc0444B62',
from: '0x3Ee7EbFb5823c8C8af05BC371873D371085447D1',
nonce: 40,
gasLimit: 21000n,
gasPrice: undefined,
maxPriorityFeePerGas: 546176991n,
maxFeePerGas: 564848631n,
maxFeePerBlobGas: null,
data: '0x',
value: 1000000000000000n,
chainId: 11155111n,
signature: Signature { r: "0xf2667f4712bab6df16ff902127c5410c74165886de2941843bca53a28440e5b4", s: "0x09bafe8c0e7f7ab7fa573205c606f490a14654c44c86f8047bcae1d88ee2661a", yParity: 0, networkV: null },
accessList: [],
blobVersionedHashes: null
}
Sender balance After --> 0.028874431147816388 ETH
Receiver balance After --> 0.101 ETH
合约转账
本节我们通过合约本身的 transfer 函数来实现转账
还是使用 sepolia 测试网,这次用的是 Link 代币,它也是一个 ERC20 代币,可以通过这个链接来领取一些测试代币:https://faucets.chain.link/sepolia
在 etherscan 中找到这个代币对应的合约地址:https://sepolia.etherscan.io/token/0xdbc1856cbd9553b8f2be31f6e6d5695dc823b47c
因为是 ERC20 代币,所以转账等常用功能的 ABI 是固定的,可以和上一节一样直接定义在数组中使用
具体转账代码如下
const { ethers } = require("ethers"); const INFURA_API_KEY = "..."; const provider = new ethers.JsonRpcProvider( `https://sepolia.infura.io/v3/${INFURA_API_KEY}` ); const sender = "0x3Ee7EbFb5823c8C8af05BC371873D371085447D1"; const receiver = "0x8cC4f3fBa4b89da6e700D5602e7622CDc0444B62"; const senderPrivateKey = "..."; const wallet = new ethers.Wallet(senderPrivateKey, provider); const ERC20_ABI = [ "function balanceOf(address) view returns (uint256)", "function transfer(address to, uint256 amount) returns (bool)", ]; const ChainLinkTokenAddress = "0x779877A7B0D9E8603169DdbD7836e478b4624789"; const contract = new ethers.Contract( ChainLinkTokenAddress, ERC20_ABI, provider ); const main = async () => { const senderBalanceBefore = await contract.balanceOf(sender); const receiverBalanceBefore = await contract.balanceOf(receiver); console.log( `Sender balance --> ${ethers.formatEther(senderBalanceBefore)} Link` ); console.log( `Receiver balance --> ${ethers.formatEther(receiverBalanceBefore)} Link` ); const contractWithWallect = contract.connect(wallet); const txResponse = await contractWithWallect.transfer( receiver, ethers.parseEther("1") ); const receipt = await txResponse.wait(); console.log(receipt); const senderBalanceAfter = await contract.balanceOf(sender); const receiverBalanceAfter = await contract.balanceOf(receiver); console.log( `Sender balance --> ${ethers.formatEther(senderBalanceAfter)} Link` ); console.log( `Receiver balance --> ${ethers.formatEther(receiverBalanceAfter)} Link` ); }; main()
运行结果
Sender balance --> 40.0 Link
Receiver balance --> 0.0 Link
ContractTransactionReceipt {
provider: JsonRpcProvider {},
to: '0x779877A7B0D9E8603169DdbD7836e478b4624789',
from: '0x3Ee7EbFb5823c8C8af05BC371873D371085447D1',
contractAddress: null,
hash: '0x8d6ecd27959f86d87b06ae30184f2c97d78064af5d20b49dd4c338990d176b31',
index: 63,
blockHash: '0xce05ec859ecc91dd060756ba1680a28f2940e57a7a12aca631ff8741fe2bea46',
blockNumber: 5775997,
logsBloom: '0x00000004000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000010000000000000000000000000000400000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000002004000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000002000000000',
gasUsed: 51646n,
blobGasUsed: null,
cumulativeGasUsed: 12901369n,
gasPrice: 663138827n,
blobGasPrice: null,
type: 2,
status: 1,
root: undefined
}
Sender balance --> 39.0 Link
Receiver balance --> 1.0 Link
事件查询
Ethers.js 也可以查询特定的事件,比如 ERC20 标准下的代币,每次转账的时候都会触发 Transfer 事件,这些事件都会在区块链中记录下来,我们就可以通过 Ethers.js 来查询对应的事件
const { ethers } = require("ethers"); const INFURA_API_KEY = "..."; const DAI_TOKEN_ADDRESS = "0x6B175474E89094C44Da98b954EedeAC495271d0F"; const provider = new ethers.JsonRpcProvider( `https://mainnet.infura.io/v3/${INFURA_API_KEY}` ); const ERC20_ABI = [ "function name() view returns (string)", "function symbol() view returns (string)", "function totalSupply() view returns (uint256)", "function balanceOf(address) view returns (uint256)", "event Transfer(address indexed from, address indexed to, uint256 value)", ]; const contract = new ethers.Contract(DAI_TOKEN_ADDRESS, ERC20_ABI, provider); const main = async () => { const block = await provider.getBlockNumber(); const transferEvents = await contract.queryFilter( "Transfer", block - 1, block ); console.log(transferEvents); }; main();
作者:加密鲸拓
版权:此文章版权归 加密鲸拓 所有,如有转载,请注明出处!