# Ethers.js入门

By [MarkTang's Blog](https://paragraph.com/@marktang-s-blog) · 2022-08-26

---

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'

---

*Originally published on [MarkTang's Blog](https://paragraph.com/@marktang-s-blog/ethers-js)*
