Example: ERC-20 Contract

元类(Meta-Classes)的概念有些令人困惑,因此我们将介绍一个简短的例子。

元类是在运行时(run-time)定义的类。合约是由应用程序二进制接口(ABI)指定的,它描述了它所拥有的方法和事件。 这些描述是合约(Contract)对象处于run-time时传入的,它创建一个新的Class,在run-time时添加了ABI中定义的所有方法。

部署合约

大多数情况下,您需要与之交互的任何合约都已经部署到区块链上,但对于本例需要先部署合约。

new ethers.ContractFactory( abi , bytecode , signer )

创建一个新的ContractFactory,它可以将合约部署到区块链。

const bytecode = "0x608060405234801561001057600080fd5b506040516103bc3803806103bc83398101604081905261002f9161007c565b60405181815233906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a333600090815260208190526040902055610094565b60006020828403121561008d578081fd5b5051919050565b610319806100a36000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c8063313ce5671461005157806370a082311461006557806395d89b411461009c578063a9059cbb146100c5575b600080fd5b604051601281526020015b60405180910390f35b61008e610073366004610201565b6001600160a01b031660009081526020819052604090205490565b60405190815260200161005c565b604080518082018252600781526626bcaa37b5b2b760c91b6020820152905161005c919061024b565b6100d86100d3366004610222565b6100e8565b604051901515815260200161005c565b3360009081526020819052604081205482111561014b5760405162461bcd60e51b815260206004820152601a60248201527f696e73756666696369656e7420746f6b656e2062616c616e6365000000000000604482015260640160405180910390fd5b336000908152602081905260408120805484929061016a9084906102b6565b90915550506001600160a01b0383166000908152602081905260408120805484929061019790849061029e565b90915550506040518281526001600160a01b0384169033907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a350600192915050565b80356001600160a01b03811681146101fc57600080fd5b919050565b600060208284031215610212578081fd5b61021b826101e5565b9392505050565b60008060408385031215610234578081fd5b61023d836101e5565b946020939093013593505050565b6000602080835283518082850152825b818110156102775785810183015185820160400152820161025b565b818111156102885783604083870101525b50601f01601f1916929092016040019392505050565b600082198211156102b1576102b16102cd565b500190565b6000828210156102c8576102c86102cd565b500390565b634e487b7160e01b600052601160045260246000fdfea2646970667358221220d80384ce584e101c5b92e4ee9b7871262285070dbcd2d71f99601f0f4fcecd2364736f6c63430008040033"; // 一个人类可读(Human-Readable)的ABI;我们只需要指定相关的部分内容,在这里我们用到了构造函数 const abi = [ "constructor(uint totalSupply)" ]; const factory = new ethers.ContractFactory(abi, bytecode, signer) // 部署,设置token的total supply为100,分配给部署人(deployer) const contract = await factory.deploy(parseUnits("100")); //链上现在虽然我们还没有这个合约,但是这个合约地址我们是预先经过计算后得到的 contract.address // '0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC' // 在交易被挖出来之前(即合约被部署)等待(Wait) // - 部署后,交易会返回receipt await contract.deployTransaction.wait(); // { // blockHash: '0xe9b244958d066490c46a826a9733c1a43210316777185353b3ecbc2ec362ea87', // blockNumber: 22, // byzantium: true, // confirmations: 1, // contractAddress: '0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC', // cumulativeGasUsed: { BigNumber: "250842" }, // effectiveGasPrice: { BigNumber: "1553830469" }, // events: [ // { // address: '0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC', // blockHash: '0xe9b244958d066490c46a826a9733c1a43210316777185353b3ecbc2ec362ea87', // blockNumber: 22, // data: '0x0000000000000000000000000000000000000000000000056bc75e2d63100000', // getBlock: [Function], // getTransaction: [Function], // getTransactionReceipt: [Function], // logIndex: 0, // removeListener: [Function], // topics: [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // '0x0000000000000000000000000000000000000000000000000000000000000000', // '0x000000000000000000000000894ed91b666facce5a4d2ff8261924b4754a5759' // ], // transactionHash: '0x03164a2299c8334178d5a2c731149910d5c21db0309bfbc8aae89a34c8069ad4', // transactionIndex: 0 // } // ], // from: '0x894ed91B666FacCe5a4D2FF8261924b4754A5759', // gasUsed: { BigNumber: "250842" }, // logs: [ // { // address: '0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC', // blockHash: '0xe9b244958d066490c46a826a9733c1a43210316777185353b3ecbc2ec362ea87', // blockNumber: 22, // data: '0x0000000000000000000000000000000000000000000000056bc75e2d63100000', // logIndex: 0, // topics: [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // '0x0000000000000000000000000000000000000000000000000000000000000000', // '0x000000000000000000000000894ed91b666facce5a4d2ff8261924b4754a5759' // ], // transactionHash: '0x03164a2299c8334178d5a2c731149910d5c21db0309bfbc8aae89a34c8069ad4', // transactionIndex: 0 // } // ], // logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000080000000000000000000000000000000000000000020000000000000000000800000000000000000002000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000002000000000000000000000000000000000000000000000000000020000000800008000000000000000000000000000000200000000000000000000000', // status: 1, // to: null, // transactionHash: '0x03164a2299c8334178d5a2c731149910d5c21db0309bfbc8aae89a34c8069ad4', // transactionIndex: 0, // type: 2 // }

连接到合约

ERC20Contract inherits 合约(Contract)

new ethers.Contract( address , abi , providerOrSigner )

创建一个Contract的新实例,是通过在区块链上指定这个合约的地址、以及它的abi(用于填充类的方法)和providerOrSigner来连接到的。

如果给定了Provider,那么合约只有只读访问权限,而Signer则提供了对状态操作方法的访问权限。

// 一个人类可读(Human-Readable)的ABI;用来与合约交互,必须要包含我们要具体使用的函数内容 const abi = [ // Read-Only Functions "function balanceOf(address owner) view returns (uint256)", "function decimals() view returns (uint8)", "function symbol() view returns (string)", // Authenticated Functions "function transfer(address to, uint amount) returns (bool)", // Events "event Transfer(address indexed from, address indexed to, uint amount)" ]; // 可以是 address 也可以是 一个 ENS name const address = "0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC"; // 只读; 通过 Provider连接,允许以下操作: // - 访问常量函数 constant function // - 查询过滤器 // - 对于 non-constant methods 填充未签名的交易 // - 对于non-constant估算Gas(作为匿名发送者) // - 对于non-constant methods静态调用 (作为匿名发送者) const erc20 = new ethers.Contract(address, abi, provider); // 可读写; 通过Signer连接,允许以下操作: // - 任何只读的操作 (作为Signer而非匿名者) // - 为non-constant functions发送交易 const erc20_rw = new ethers.Contract(address, abi, signer);

属性(inheritted from 合约(Contract))

erc20.address string< 地址(Address) >

合约被创建的 地址address (或 ENS name)

erc20.resolvedAddress string< 地址(Address) >

这是一个解析到合约对象的地址的promise。 如果是地址(Address)提供给构造函数,结果就是这个地址(Address); 如果提供的是ENS name,结果就是解析后地址。

erc20.deployTransaction TransactionResponse

如果Contract对象是ContractFactory部署的得到的,在这里的返回的结果就是用于部署合约的交易。

erc20.interface Interface

这是作为Interface的ABI。

erc20.provider Provider

如果一个provider被提供给构造函数,这条语句的结果就是provider; 如果提供了的Provider是一个signer,结果就是signer。

erc20.signer Signer

如果是一个signer提供给构造函数,结果就是signer。

Methods(inheritted from 合约(Contract))

erc20.attach( addressOrName ) 合约(Contract)

返回一个附在新地址的合约的新实例。 如果网络上有多个类似或相同的合约副本,并且您希望与它们中的每一个进行交互,那么这是非常有用的。

erc20.connect( providerOrSigner ) 合约(Contract)

返回合约的新实例,但需要连接provider或者Signer。

通过传入一个Provider,这将返回一个低级的合约,它只有只读访问(即常量调用)。

通过传入一个Signer。这将返回一个代表该signer的合约

erc20.deployed( ) Promise< Contract >
Contract.isIndexed( value ) boolean

事件(Events)(inheritted from 合约(Contract))

关于使用事件的例子请参考Meta-Class Filters

erc20.queryFilter( event [ , fromBlockOrBlockHash [ , toBlock ] ) Promise< Array< Event > >

返回匹配event参数的事件。

erc20.listenerCount( [ event ] ) number

返回订阅该event的监听器数量。如果没有提供event,则返回所有事件的总数。

erc20.listeners( event ) Array< Listener >

返回订阅该event的监听器列表。

erc20.off( event , listener ) this

返回订阅该event的监听器列表。

erc20.on( event , listener ) this

监听event事件,当事件发生时,会调用listener函数。

erc20.once( event , listener ) this

监听event事件,当事件发生时,仅调用一次listener函数。

erc20.removeAllListeners( [ event ] ) this

取消所有订阅event事件的监听器。如果未提供event事件,则取消订阅所有事件的监听。

Meta-Class Methods(added at Runtime)

因为合约是元类,这里面可用的方法取决于传入合约的ABI。

erc20.decimals( [ overrides ] ) Promise< number >

返回此ERC-20 token所使用的小数位数。在前端界面,当从用户获取输入时,可以使用parseUnits转化后传入合约; 从合约获得token后可以通过[formatUnits](utils-formatunits]转化后再显示给用户。

await erc20.decimals(); // 18
erc20.balanceOf( owner [ , overrides ] ) Promise< 大数(BigNumber) >

返回持有这个ERC-20 token的持有者(owner)的余额。

await erc20.balanceOf(signer.getAddress()) // { BigNumber: "100000000000000000000" }
erc20.symbol( [ overrides ] ) Promise< string >

返回token的symbol。

await erc20.symbol(); // 'MyToken'
erc20_rw.transfer( target , amount [ , overrides ] ) Promise< TransactionResponse >

从当前的signer将数量为amount的tokens转给接收者target。 在交易处于写入操作时,返回值(布尔类型)是得不到的。 如果需要这个值,则需要其他方法(如事件)。链上合约调用transfer函数可以得到这个结果。

// 在这之前先转化格式 formatUnits(await erc20_rw.balanceOf(signer.getAddress())); // '100.0' // 转 1.23 tokens 到 ENS name 为 "ricmoo.eth" 的地址 tx = await erc20_rw.transfer("ricmoo.eth", parseUnits("1.23")); // { // accessList: [], // chainId: 31337, // confirmations: 0, // data: '0xa9059cbb0000000000000000000000005555763613a12d8f3e73be831dff8598089d3dca0000000000000000000000000000000000000000000000001111d67bb1bb0000', // from: '0x894ed91B666FacCe5a4D2FF8261924b4754A5759', // gasLimit: { BigNumber: "51558" }, // gasPrice: null, // hash: '0xceb49c9db1d35d18db9b3c56d285e691f006d62529dec397eec004337206e600', // maxFeePerGas: { BigNumber: "1607660938" }, // maxPriorityFeePerGas: { BigNumber: "1500000000" }, // nonce: 2, // r: '0x33284b92f1197507d7bd48aee7ccf4f7386527e72b6bf3fe23245026cf0a7eac', // s: '0x64eb0ca43292a98695f702ed0d59ec1d97618905c3366ac1d4ff50841f4cd0fb', // to: '0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC', // type: 2, // v: 1, // value: { BigNumber: "0" }, // wait: [Function] // } // 等待交易所在的区块被挖出打包 await tx.wait(); // { // blockHash: '0x95ac5d8669e6c73c1784296c0ad09c3ccf3e06055c78361056d971c6498171f1', // blockNumber: 23, // byzantium: true, // confirmations: 1, // contractAddress: null, // cumulativeGasUsed: { BigNumber: "51558" }, // effectiveGasPrice: { BigNumber: "1547214185" }, // events: [ // { // address: '0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC', // args: [ // '0x894ed91B666FacCe5a4D2FF8261924b4754A5759', // '0x5555763613a12D8F3e73be831DFf8598089d3dCa', // { BigNumber: "1230000000000000000" }, // amount: { BigNumber: "1230000000000000000" }, // from: '0x894ed91B666FacCe5a4D2FF8261924b4754A5759', // to: '0x5555763613a12D8F3e73be831DFf8598089d3dCa' // ], // blockHash: '0x95ac5d8669e6c73c1784296c0ad09c3ccf3e06055c78361056d971c6498171f1', // blockNumber: 23, // data: '0x0000000000000000000000000000000000000000000000001111d67bb1bb0000', // decode: [Function], // event: 'Transfer', // eventSignature: 'Transfer(address,address,uint256)', // getBlock: [Function], // getTransaction: [Function], // getTransactionReceipt: [Function], // logIndex: 0, // removeListener: [Function], // topics: [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // '0x000000000000000000000000894ed91b666facce5a4d2ff8261924b4754a5759', // '0x0000000000000000000000005555763613a12d8f3e73be831dff8598089d3dca' // ], // transactionHash: '0xceb49c9db1d35d18db9b3c56d285e691f006d62529dec397eec004337206e600', // transactionIndex: 0 // } // ], // from: '0x894ed91B666FacCe5a4D2FF8261924b4754A5759', // gasUsed: { BigNumber: "51558" }, // logs: [ // { // address: '0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC', // blockHash: '0x95ac5d8669e6c73c1784296c0ad09c3ccf3e06055c78361056d971c6498171f1', // blockNumber: 23, // data: '0x0000000000000000000000000000000000000000000000001111d67bb1bb0000', // logIndex: 0, // topics: [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // '0x000000000000000000000000894ed91b666facce5a4d2ff8261924b4754a5759', // '0x0000000000000000000000005555763613a12d8f3e73be831dff8598089d3dca' // ], // transactionHash: '0xceb49c9db1d35d18db9b3c56d285e691f006d62529dec397eec004337206e600', // transactionIndex: 0 // } // ], // logsBloom: '0x00000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000080000000000000000000000000000000000000000000000000000000000000000000000000000000002000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000002000000000000000000000000000000000000000000000000000000000000800008000001000000000200000000000000200000000000000000000000', // status: 1, // to: '0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC', // transactionHash: '0xceb49c9db1d35d18db9b3c56d285e691f006d62529dec397eec004337206e600', // transactionIndex: 0, // type: 2 // } // 成功后转化格式 formatUnits(await erc20_rw.balanceOf(signer.getAddress())); // '98.77' formatUnits(await erc20_rw.balanceOf("ricmoo.eth")); // '1.23'
erc20.callStatic.transfer( target , amount [ , overrides ] ) Promise< boolean >

执行一次从当前signer向target地址转移amount数量的token的演练,而不实际签名或发送交易。

这可以用于检查真实的转账前,交易是否能成功。

// signer有足够的tokens发送的话,结果会返回true await erc20_rw.callStatic.transfer("ricmoo.eth", parseUnits("1.23")); // true // 一个随机的地址不会有足够的tokens用于发送,在这个例子里会抛出错误 erc20_random = erc20_rw.connect(randomWallet); await erc20_random.callStatic.transfer("ricmoo.eth", parseUnits("1.23")); // [Error: call revert exception; VM Exception while processing transaction: reverted with reason string "insufficient token balance" [ See: https://links.ethers.org/v5-errors-CALL_EXCEPTION ]] { // address: '0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC', // args: [ // 'ricmoo.eth', // { BigNumber: "1230000000000000000" } // ], // code: 'CALL_EXCEPTION', // data: '0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001a696e73756666696369656e7420746f6b656e2062616c616e6365000000000000', // errorArgs: [ // 'insufficient token balance' // ], // errorName: 'Error', // errorSignature: 'Error(string)', // method: 'transfer(address,uint256)', // reason: 'insufficient token balance', // transaction: { // data: '0xa9059cbb0000000000000000000000005555763613a12d8f3e73be831dff8598089d3dca0000000000000000000000000000000000000000000000001111d67bb1bb0000', // from: '0x3744A6A188e46e3f2A10f918126d13F73E3C58D9', // to: '0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC' // } // }
erc20.estimateGas.transfer( target , amount [ , overrides ] ) Promise< 大数(BigNumber) >

返回估计的“将amount数量的tokens发送target地址”所需的多少gas值。

await erc20_rw.estimateGas.transfer("ricmoo.eth", parseUnits("1.23")); // { BigNumber: "34458" }
erc20.populateTransaction.transfer( target , amount [ , overrides ] ) Promise< UnsignedTx >

返回一个未签名交易(UnsignedTransaction),它可以被签名并提交给网络,达成“将amount数量的tokens发送target地址”的目的。

await erc20_rw.populateTransaction.transfer("ricmoo.eth", parseUnits("1.23")); // { // data: '0xa9059cbb0000000000000000000000005555763613a12d8f3e73be831dff8598089d3dca0000000000000000000000000000000000000000000000001111d67bb1bb0000', // from: '0x894ed91B666FacCe5a4D2FF8261924b4754A5759', // to: '0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC' // }
关于估算和静态调用的注意事项

当你执行一个静态调用时,以太坊会尽可能地考虑当前状态。 在许多情况下,这可能会提供假阳性和假阴性。 The eventually consistent model of the blockchain also means there are certain consistency modes that cannot be known until an actual transaction is attempted.

Meta-Class Filters(added at Runtime)

因为合约是元类,这里面可用的方法取决于传入合约的ABI。

erc20.filters.Transfer( [ fromAddress [ , toAddress ] ] ) Filter

返回一个新的Filter用于查询或者去subscribe/unsubscribe to events

如果fromAddress 是空的或者没有填写,将匹配任何发送人的地址。 如果 toAddress是空的或者没有填写,将匹配任何接受人的地址。

query filter *from* events
filterFrom = erc20.filters.Transfer(signer.address); // { // address: '0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC', // topics: [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // '0x000000000000000000000000894ed91b666facce5a4d2ff8261924b4754a5759' // ] // } // 在最新的10个区块内查询我发送的交易 logsFrom = await erc20.queryFilter(filterFrom, -10, "latest"); // [ // { // address: '0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC', // args: [ // '0x894ed91B666FacCe5a4D2FF8261924b4754A5759', // '0x5555763613a12D8F3e73be831DFf8598089d3dCa', // { BigNumber: "1230000000000000000" }, // amount: { BigNumber: "1230000000000000000" }, // from: '0x894ed91B666FacCe5a4D2FF8261924b4754A5759', // to: '0x5555763613a12D8F3e73be831DFf8598089d3dCa' // ], // blockHash: '0x95ac5d8669e6c73c1784296c0ad09c3ccf3e06055c78361056d971c6498171f1', // blockNumber: 23, // data: '0x0000000000000000000000000000000000000000000000001111d67bb1bb0000', // decode: [Function], // event: 'Transfer', // eventSignature: 'Transfer(address,address,uint256)', // getBlock: [Function], // getTransaction: [Function], // getTransactionReceipt: [Function], // logIndex: 0, // removeListener: [Function], // removed: false, // topics: [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // '0x000000000000000000000000894ed91b666facce5a4d2ff8261924b4754a5759', // '0x0000000000000000000000005555763613a12d8f3e73be831dff8598089d3dca' // ], // transactionHash: '0xceb49c9db1d35d18db9b3c56d285e691f006d62529dec397eec004337206e600', // transactionIndex: 0 // } // ] // 请注意,args提供了事件的详细信息,每个参数都是可用的,这是因为我们的ABI包含了所有参数的名称 logsFrom[0].args // [ // '0x894ed91B666FacCe5a4D2FF8261924b4754A5759', // '0x5555763613a12D8F3e73be831DFf8598089d3dCa', // { BigNumber: "1230000000000000000" }, // amount: { BigNumber: "1230000000000000000" }, // from: '0x894ed91B666FacCe5a4D2FF8261924b4754A5759', // to: '0x5555763613a12D8F3e73be831DFf8598089d3dCa' // ]
query filter with *to* events
filterTo = erc20.filters.Transfer(null, signer.address); // { // address: '0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC', // topics: [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // null, // '0x000000000000000000000000894ed91b666facce5a4d2ff8261924b4754a5759' // ] // } // 在最新的10个区块内查询发送给我的交易 // 注意: 这是因为合约在部署时,构造函数写了将totalSupply tokens转给我 logsTo = await erc20.queryFilter(filterTo, -10, "latest"); // [ // { // address: '0xa22aB6748282B3125dC26dAFb79e38B7eb24EAcC', // args: [ // '0x0000000000000000000000000000000000000000', // '0x894ed91B666FacCe5a4D2FF8261924b4754A5759', // { BigNumber: "100000000000000000000" }, // amount: { BigNumber: "100000000000000000000" }, // from: '0x0000000000000000000000000000000000000000', // to: '0x894ed91B666FacCe5a4D2FF8261924b4754A5759' // ], // blockHash: '0xe9b244958d066490c46a826a9733c1a43210316777185353b3ecbc2ec362ea87', // blockNumber: 22, // data: '0x0000000000000000000000000000000000000000000000056bc75e2d63100000', // decode: [Function], // event: 'Transfer', // eventSignature: 'Transfer(address,address,uint256)', // getBlock: [Function], // getTransaction: [Function], // getTransactionReceipt: [Function], // logIndex: 0, // removeListener: [Function], // removed: false, // topics: [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // '0x0000000000000000000000000000000000000000000000000000000000000000', // '0x000000000000000000000000894ed91b666facce5a4d2ff8261924b4754a5759' // ], // transactionHash: '0x03164a2299c8334178d5a2c731149910d5c21db0309bfbc8aae89a34c8069ad4', // transactionIndex: 0 // } // ] // 请注意,args提供了事件的详细信息,每个参数都是可用的,这是因为我们的ABI包含了所有参数的名称 logsTo[0].args // [ // '0x0000000000000000000000000000000000000000', // '0x894ed91B666FacCe5a4D2FF8261924b4754A5759', // { BigNumber: "100000000000000000000" }, // amount: { BigNumber: "100000000000000000000" }, // from: '0x0000000000000000000000000000000000000000', // to: '0x894ed91B666FacCe5a4D2FF8261924b4754A5759' // ]
listen for events
// 监听signer的发送的event事件: erc20.on(filterFrom, (from, to, amount, event) => { // The `from` will always be the signer address }); // 监听signer的接受转账的event事件: erc20.on(filterTo, (from, to, amount, event) => { // The `to` will always be the signer address }); // 监听所有的转账交易event事件: erc20.on("Transfer", (from, to, amount, event) => { // ... });