ethers.js的基础用法

4,267 阅读4分钟

P.S.

执行下面的代码前,需要在浏览器中安装MetaMask插件,并将账户连接到BSC测试网。可以通过ChainList快捷地添加网络到钱包插件。

生成Web3Provider实例,以MetaMask为例

使用ethers需要准备一个Web3Provider,该实例可以通过ethers.providers.Web3Provider方法构造,需要传入一个标准的Web3 provider作为参数。MetaMask提供的Web3 provider为window.ethereum

import { ethers } from 'ethers';

// A Web3Provider wraps a standard Web3 provider, which is
// what MetaMask injects as window.ethereum into each page
const provider = new ethers.providers.Web3Provider(window.ethereum);

连接钱包账户

// MetaMask requires requesting permission to connect users accounts
provider.send('eth_requestAccounts', []).then((value) => {
  console.log('>>> accounts: ', value);  // 打印你的当前连接上的地址
});

连接MetaMask钱包账户的过程中,会弹窗请求用户确认。

获取Signer

MetaMask插件支持用户对消耗代币的交易进行签名,或者消耗代币来改变链上的数据。如果要进行上述操作,需要获取Signer。

const signer = provider.getSigner();

使用Provider查询当前块号

// Look up the current block number
provider.getBlockNumber().then((value) => {
  console.log('>>> block number: ', value);
});

使用Provider查询指定账户地址的原生代币余额

import { formatUnits } from '@ethersproject/units';

// Get the balance of an account
provider.getBalance(address1).then((value) => {
  console.log(
    '>>> native token balance: ',
    formatUnits(value.toString(), 18),
  );
});

获取到的余额以wei为单位,因此需要formatUnits方法转换为可读的数值。

发送0.01个原生代币到指定地址

// Send 0.01 native token to an address.
const sendNativeToken = (toAddress: string) => {
  signer
    .sendTransaction({
    to: toAddress,
    value: ethers.utils.parseEther('0.01'),
  })
    .then(
    (resp) => {
      console.log('>>> transaction response: ', resp);
    },
    (error) => {
      console.log('>>> send tx error: ', error);
    },
  );
};

<Button
  onClick={() => {
    sendNativeToken('指定地址');
  }}
>
  Send Native Token
</Button>

生成合约实例,以BSC测试网上的ERC-20合约为例

获取ABI

以下使用简写的ABI,也可以从JSON文件读取。

支持可读性更好的简写ABI是ethers.js相对于web3.js的优势之一。

const ABI = [
  // 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)',

  // 向某个账户地址指定可用代币额度
  'function approve(address spender, unit amount) nonpayable returns (bool)',

  // An event triggered whenever anyone transfers to someone else
  // 该合约的代币发生转账时会触发的事件
  'event Transfer(address indexed from, address indexed to, uint amount)',
];

生成合约实例

使用BSC测试网上的LINK代币的合约地址。LINK测试代币可以从ChainLInk的水龙头获取。

// The Contract object of LINK token
const tokenContract = new ethers.Contract(
  '0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06',  // BSC测试网上的LINK代币的合约地址
  ABI,
  provider,
);

执行读取链上数据的合约方法

获取ERC-20代币的名称

// Get the ERC-20 token name
tokenContract.name().then((value: any) => {
  console.log('>>> token name: ', value);
});

获取ERC-20代币的代号

// Get the ERC-20 token symbol (for tickers and UIs)
tokenContract.symbol().then((value: any) => {
  console.log('>>> token symbol: ', value);
});

查看指定账户地址中的指定ERC-20代币的余额

tokenContract.balanceOf(address1).then((value: any) => {
  console.log(
    '>>> ERC-20 balance: ',
    formatUnits(value.toString(), 18),
  );
});

和获取原生代币余额时一样,获取到的余额以wei(1e18)为单位,因此需要formatUnits方法转换为可读的数值。

执行写入数据到链上的合约方法

此时的合约实例仅连接到了provider,而provider只能提供读取链上数据的方法。如果我们想要向链上写入数据,我们需要让合约实例连接到signer。这是provider和signer的主要区别。

将合约实例连接到Signer

// The token Contract is currently connected to the Provider,
// which is read-only. You need to connect to a Signer, so
// that you can pay to send state-changing transactions.
const tokenContractWithSigner = tokenContract.connect(signer);

发送指定数量的ERC-20代币到指定账户地址

import { parseUnits } from '@ethersproject/units';

const sendERC20Token = (toAddress: string, amount: string) => {
  const tx: Promise<any> = tokenContractWithSigner.transfer(
    toAddress,
    parseUnits(amount, 18),
  );
  
  tx.then(
    (resp) => {
      console.log('>>> transaction response: ', resp);
    },
    (error) => {
      if (error?.code === 4001) {
        message.error(error?.message);
      }
      console.log('>>> send tx error: ', error);
    },
  );
};

<Button
  onClick={() => {
    sendERC20Token('指定地址', '指定数量');
  }}
>
  Send ERC-20 Token
</Button>

发送的的代币数量需要以wei(1e18)为单位,因此上面代码通过parseUnits将可读的数量转换为以wei为单位的数量

监听链上事件

监听ERC-20合约的Transfer事件

// Receive an event when ANY transfer occurs on the token contract
tokenContract.on('Transfer', (from, to, amount, event) => {
  console.log(`${from} sent ${formatUnits(amount.toString(), 18)} to ${to}`);
});

向事件监听指定筛选条件

以下代码监听ERC-20合约中每一次向我的地址转账的事件

// A filter for when a specific address receives tokens
const filter = tokenContract.filters.Transfer(null, '我的地址');

// Receive an event when that filter occurs
tokenContract.on(filter, (from, to, amount, event) => {
  // The to will always be "address"
  console.log(`我从 ${from} 收到 ${formatUnits(amount.toString(), 18)} 个代币`);
});