Ethers.js入门

1,121 阅读9分钟

Ethers.js入门

术语解释

Provider: Provider 是一个为连接到以太坊网络提供抽象的类,提供对blockchain及其状态的只读访问

Signer: Signer是一个类,通常以某种方式直接或间接地接触到一个私钥,它可以签署消息和交易,以授权网络向你的账户收取以太坊来执行操作

Contract: Contract是一个抽象概念,它代表了与以太坊网络上特定合约的连接,因此,应用程序可以像普通的JavaScript对象一样使用它

连接方式及操作

Connecting to Ethereum: MetaMask

在Ethereum上进行实验和开始开发的最快速和最简单的方法是使用MetaMask,它是一个浏览器扩展

// Web3Provider包装了一个标准的Web3 provider,MetaMask为每个页面注入了window.ethereum
const provider = new ethers.providers.Web3Provider(window.ethereum);
​
// MetaMask Plugin 还允许签名transactions 来发送ether和支付以改变链上的状态,所以需要 signer
const signer = provider.getSigner();

Connecting to Ethereum: RPC

JSON-RPC API是另一种常用的与Ethereum交互的方式,在所有主要的以太坊节点实现(Geth, Parity)以及许多第三方网络服务(Infura)中都有

// 如果你不指定一个 url地址,会默认为: http://localhost:8545
const provider = new ethers.providers.JsonRpcProvider();
​
// 也允许签名transactions
const signer = provider.getSigner();

Querying the Blockchain

一旦你有了Provider,你就有了与Blockchain的只读连接,可以用它来查询当前状态,获取历史日志,查询部署的代码等等

// Look up the current block number
await provider.getBlockNumber(); // 137223// Get the balance of an account (by address or ENS name, if supported by network)
balance = await provider.getBalance("ethers.eth"); 
// { BigNumber: "2337132817842795605" }
​
ethers.utils.formatEther(balance);
// '2.337132817842795605'
​
ethers.utils.parseEther("1.0");
// { BigNumber: "1000000000000000000" }

Note: ENS(Ethereum Name Service)之于以太坊就像是DNS对于因特网,ENS允许用户使用个性化的以太坊域名为自己注册,比如用“vitalik.eth”来替代密码地址‘0x32b724f073ec346edd64b0cc67757e4f6fe42950’。ENS一直在迭代开发中,ENS不仅仅用于以太坊地址,未来ENS将覆盖以太坊的各个方面

Writing to the Blockchain

// Send 1 ether to an ens name.
const tx = signer.sendTransaction({
    to"marktang.firefly.eth",
    value: ethers.utils.parseEther("1.0")
});

Contracts

合约是在Ethereum blockchain上运行的代码的一种抽象

Contract类为了与链上合约进行通信,需要知道有哪些方法可以调用,这需要传入ABI文件

这个类是一个meta-class,在运行时构造的,传入ABI给构造函数时,它用ABI来决定要添加哪些方法

ABI由 Solidity或Vyper编译成,但你可以在代码中使用人类可读的ABI,如:

// You can also use an ENS name for the contract address
const daiAddress = "dai.tokens.ethers.eth";
​
// The ERC-20 Contract ABI, which is a common contract interface
// for tokens (this is the Human-Readable ABI format)
const daiAbi = [
  // Some details about the token
  "function name() view returns (string)",
  "function symbol() view returns (string)",
​
  // Get the account balance
  "function balanceOf(address) view returns (uint)",
​
  // Send some of your tokens to someone else
  "function transfer(address to, uint amount)",
​
  // An event triggered whenever anyone transfers to someone else
  "event Transfer(address indexed from, address indexed to, uint amount)"
];
​
// The Contract object
const daiContract = new ethers.Contract(daiAddress, daiAbi, provider);

Read-Only Methods

// Get the ERC-20 token name
await daiContract.name()
// 'Dai Stablecoin'// Get the ERC-20 token symbol (for tickers and UIs)
await daiContract.symbol()
// 'DAI'// Get the balance of an address
balance = await daiContract.balanceOf("ricmoo.firefly.eth")
// { BigNumber: "18190624174838529547383" }// Format the DAI for displaying to the user
ethers.utils.formatUnits(balance, 18)
// '18190.624174838529547383'

State Changing Methods

// DAI 合约目前连接到Provider,但是Provider是 read-only的
// 需要连接到一个Signer,这样就可以付费发送一些改变状态的交易
const daiWithSigner = contract.connect(signer);
​
// Each DAI has 18 decimal places
const dai = ethers.utils.parseUnits("1.0"18);
​
// Send 1 DAI to "ricmoo.firefly.eth"
tx = daiWithSigner.transfer("ricmoo.firefly.eth", dai);

Listening to Events

// Receive an event when ANY transfer occurs
daiContract.on("Transfer", (from, to, amount, event) => {
    console.log(`${ from } sent ${ formatEther(amount) } to ${ to}`);
    // 事件对象包含逐字记录数据、EventFragment和获取块、交易和接收的函数以及事件函数
});
​
// @Todo: I don't know if Can I get all transactions here.
// A filter for when a specific address receives tokens
myAddress = "0x8ba1f109551bD432803012645Ac136ddd64DBA72";
filter = daiContract.filters.Transfer(null, myAddress)
// {
//  address: 'dai.tokens.ethers.eth',
//  topics: [
//    '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
//    null,
//    '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72'
//  ]
// }// Receive an event when that filter occurs
daiContract.on(filter, (from, to, amount, event) => {
    // The to will always be "address"
    console.log(`I got ${ formatEther(amount) } from ${ from }.`);
});

Query Historic Events

// Get the address of the Signer
myAddress = await signer.getAddress()
// '0x8ba1f109551bD432803012645Ac136ddd64DBA72'
​
// Filter for all token transfers from me
filterFrom = daiContract.filters.Transfer(myAddress, null);
// {
//  address: 'dai.tokens.ethers.eth',
//  topics: [
//    '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
//    '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72'
//  ]
// }
​
// Filter for all token transfers to me
filterTo = daiContract.filters.Transfer(null, myAddress);
// {
//  address: 'dai.tokens.ethers.eth',
//  topics: [
//    '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
//    null,
//    '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72'
//  ]
// }
​
// List all transfers sent from me a specific block range
await daiContract.queryFilter(filterFrom, 98434709843480)
// [
//  {
//    address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
//    args: [
//      '0x8ba1f109551bD432803012645Ac136ddd64DBA72',
//      '0x8B3765eDA5207fB21690874B722ae276B96260E0',
//      { BigNumber: "4750000000000000000" },
//      amount: { BigNumber: "4750000000000000000" },
//      from: '0x8ba1f109551bD432803012645Ac136ddd64DBA72',
//      to: '0x8B3765eDA5207fB21690874B722ae276B96260E0'
//    ],
//    blockHash: '0x8462eb2fbcef5aa4861266f59ad5f47b9aa6525d767d713920fdbdfb6b0c0b78',
//    blockNumber: 9843476,
//    data: '0x00000000000000000000000000000000000000000000000041eb63d55b1b0000',
//    decode: [Function],
//    event: 'Transfer',
//    eventSignature: 'Transfer(address,address,uint256)',
//    getBlock: [Function],
//    getTransaction: [Function],
//    getTransactionReceipt: [Function],
//    logIndex: 69,
//    removeListener: [Function],
//    removed: false,
//    topics: [
//      '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
//      '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72',
//      '0x0000000000000000000000008b3765eda5207fb21690874b722ae276b96260e0'
//    ],
//    transactionHash: '0x1be23554545030e1ce47391a41098a46ff426382ed740db62d63d7676ff6fcf1',
//    transactionIndex: 81
//  },
//  {
//    address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
//    args: [
//      '0x8ba1f109551bD432803012645Ac136ddd64DBA72',
//      '0x00De4B13153673BCAE2616b67bf822500d325Fc3',
//      { BigNumber: "250000000000000000" },
//      amount: { BigNumber: "250000000000000000" },
//      from: '0x8ba1f109551bD432803012645Ac136ddd64DBA72',
//      to: '0x00De4B13153673BCAE2616b67bf822500d325Fc3'
//    ],
//    blockHash: '0x8462eb2fbcef5aa4861266f59ad5f47b9aa6525d767d713920fdbdfb6b0c0b78',
//    blockNumber: 9843476,
//    data: '0x00000000000000000000000000000000000000000000000003782dace9d90000',
//    decode: [Function],
//    event: 'Transfer',
//    eventSignature: 'Transfer(address,address,uint256)',
//    getBlock: [Function],
//    getTransaction: [Function],
//    getTransactionReceipt: [Function],
//    logIndex: 70,
//    removeListener: [Function],
//    removed: false,
//    topics: [
//      '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
//      '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72',
//      '0x00000000000000000000000000de4b13153673bcae2616b67bf822500d325fc3'
//    ],
//    transactionHash: '0x1be23554545030e1ce47391a41098a46ff426382ed740db62d63d7676ff6fcf1',
//    transactionIndex: 81
//  }
// ]
​
// List all transfers sent in the last 10,000 blocks
await daiContract.queryFilter(filterFrom, -10000)
​
// List all transfers ever sent to me
await daiContract.queryFilter(filterTo)

Signing Messages

// To sign a simple string, which are used for
// logging into a service, such as CryptoKitties,
// pass the string in.
signature = await signer.signMessage("Hello World");
// '0x5a77beb84677b221d7110b08605a2658dd6c1e88a2ba9293436e587dbf6479d4798d0ca34ba2113bdb51ad97cd831975ccaccaf5f6fbd5d566fe662f11e2ca411b'//
// A common case is also signing a hash, which is 32
// bytes. It is important to note, that to sign binary
// data it MUST be an Array (or TypedArray)
//// This string is 66 characters long
message = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
​
// This array representation is 32 bytes long
messageBytes = ethers.utils.arrayify(message);
// Uint8Array [ 221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, 161, 22, 40, 245, 90, 77, 245, 35, 179, 239 ]// To sign a hash, you most often want to sign the bytes
signature = await signer.signMessage(messageBytes)
// '0xe099cce5e80dec1d8464d00c4156855d1c14cc4f83473deee7ab8e60be770f4b5ea04e90e7b7102ac5b493f7822b0ad10dc26bfda0761530a58e5f461f90b2fd1b'


\