Ethers.js入门

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, 9843470, 9843480)
// [
//   {
//     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'
Subscribe to MarkTang's Blog
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.