Ethers极简入门: 3. 读取合约信息
我最近在重新学ethers.js
,巩固一下细节,也写一个WTF Ethers极简入门
,供小白们使用。
WTF Academy社群: 官网 wtf.academy | WTF Solidity教程 | discord | 微信群申请
所有代码和教程开源在github: github.com/WTFAcademy/WTF-Ethers
这一讲,我们将介绍Contract
合约类,并利用它来读取链上的合约信息。
Contract
类
在ethers
中,Contract
类是部署在以太坊网络上的合约(EVM
字节码)的抽象。通过它,开发者可以非常容易的对合约进行读取call
和交易transaction
,并可以获得交易的结果和事件。以太坊强大的地方正是合约,所以对于合约的操作要熟练掌握。
创建Contract
变量
只读和可读写Contract
Contract
对象分为两类,只读和可读写。只读Contract
只能读取链上合约信息,执行call
操作,即调用合约中view
和pure
的函数,而不能执行交易transaction
。创建这两种Contract
变量的方法有所不同:
- 只读
Contract
:参数分别是合约地址,合约abi
和provider
变量(只读)。
const contract = new ethers.Contract(`address`, `abi`, `provider`);
- 可读写
Contract
:参数分别是合约地址,合约abi
和signer
变量。Signer
签名者是ethers
中的另一个类,用于签名交易,之后我们会讲到。
const contract = new ethers.Contract(`address`, `abi`, `signer`);
注意 ethers
中的call
指的是只读操作,与solidity
中的call
不同。
读取合约信息
1. 创建Provider
我们使用Infura节点的API Key创建Provider
(见第2讲:Provider):
import { ethers } from "ethers";
// 利用Infura的rpc节点连接以太坊网络
// 准备Infura API Key, 教程:https://github.com/AmazingAng/WTFSolidity/blob/main/Topics/Tools/TOOL02_Infura/readme.md
const INFURA_ID = ''
// 连接以太坊主网
const provider = new ethers.JsonRpcProvider(`https://mainnet.infura.io/v3/${INFURA_ID}`)
2. 创建只读Contract实例
创建只读Contract实例需要填入3
个参数,分别是合约地址,合约abi
和provider
变量。合约地址可以在网上查到,provider
变量上一步我们已经创建了,那么abi
怎么填?
ABI
(Application Binary Interface) 是与以太坊智能合约交互的标准,更多内容见WTF Solidity教程第27讲: ABI编码。ethers
支持两种abi
填法:
- 方法1. 直接输入合约
abi
。你可以从remix
的编译页面中复制,在本地编译合约时生成的artifact
文件夹的json
文件中得到,或者从etherscan
开源合约的代码页面得到。我们用这个方法创建WETH
的合约实例:
// 第1种输入abi的方式: 复制abi全文
// WETH的abi可以在这里复制:https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code
const abiWETH = '[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view",...太长后面省略...';
const addressWETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' // WETH Contract
const contractWETH = new ethers.Contract(addressWETH, abiWETH, provider)
- 方法2. 由于
abi
可读性太差,ethers
创新的引入了Human-Readable Abi
(人类可读abi)。开发者可以通过function signature
和event signature
来写abi
。我们用这个方法创建稳定币DAI
的合约实例:
// 第2种输入abi的方式:输入程序需要用到的函数,逗号分隔,ethers会自动帮你转换成相应的abi
// 人类可读abi,以ERC20合约为例
const abiERC20 = [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function totalSupply() view returns (uint256)",
"function balanceOf(address) view returns (uint)",
];
const addressDAI = '0x6B175474E89094C44Da98b954EedeAC495271d0F' // DAI Contract
const contractDAI = new ethers.Contract(addressDAI, abiERC20, provider)
3. 读取WETH
和DAI
的链上信息
我们可以利用只读Contract
实例调用合约的view
和pure
函数,获取链上信息:
const main = async () => {
// 1. 读取WETH合约的链上信息(WETH abi)
const nameWETH = await contractWETH.name()
const symbolWETH = await contractWETH.symbol()
const totalSupplyWETH = await contractWETH.totalSupply()
console.log("\n1. 读取WETH合约信息")
console.log(`合约地址: ${addressWETH}`)
console.log(`名称: ${nameWETH}`)
console.log(`代号: ${symbolWETH}`)
console.log(`总供给: ${ethers.formatEther(totalSupplyWETH)}`)
const balanceWETH = await contractWETH.balanceOf('vitalik.eth')
console.log(`Vitalik持仓: ${ethers.formatEther(balanceWETH)}\n`)
// 2. 读取DAI合约的链上信息(IERC20接口合约)
const nameDAI = await contractDAI.name()
const symbolDAI = await contractDAI.symbol()
const totalSupplDAI = await contractDAI.totalSupply()
console.log("\n2. 读取DAI合约信息")
console.log(`合约地址: ${addressDAI}`)
console.log(`名称: ${nameDAI}`)
console.log(`代号: ${symbolDAI}`)
console.log(`总供给: ${ethers.formatEther(totalSupplDAI)}`)
const balanceDAI = await contractDAI.balanceOf('vitalik.eth')
console.log(`Vitalik持仓: ${ethers.formatEther(balanceDAI)}\n`)
}
main()
可以看到,用两种方法创建的合约实例都能成功与链上交互。Vitalik的钱包里有0.05 WETH
及555508 DAI
,见下图。
说明
我们可以通过以太坊浏览器 验证Vitalik钱包里的WETH
余额, 是否与通过Contract
读取的一致。 通过ENS 查到Vitalik钱包地址是0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
,然后通过合约方法balanceOf
得到余额正好是0.05 WETH
, 结论是一致!
总结
这一讲,我们介绍了ethers
中的Contract
合约类,并创建了WETH
和DAI
的只读Contract
实例,成功读取了Vitalik这两个币的持仓。