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)} 个代币`);
});