Interface

接口(Interface)类是以太坊网络上的与合约交互所需的编码和解码的一种抽象。

许多标准都是随着Solidity语言而发展的,其他语言也采用这种方式去与现有部署的合约的保持兼容。

EVM本身并不理解ABI是什么。它只是一组商定的格式,用于编码合约所需的各种类型的数据,以便它们可以相互交互。

创建实例

new ethers.utils.Interface( abi )

从表示abi的JSON字符串或对象创建一个新的接口。

abi可以是JSON字符串或Solidity 编译器(或其他兼容语言)使用JSON.parse生产的对象。

abi 也可以是Human-Readable Abi, 它是Ethers创建的一种格式,以简化将abi手动输入到源文件中的操作, 这样就可以在同一个源文件中简单地引用Contract abi。

创建接口实例
// 接口用法示例如下 const iface = new Interface([ // 构造函数 "constructor(string symbol, string name)", // 会改变状态的方法 "function transferFrom(address from, address to, uint amount)", // 会改变状态的方法,是 payable 的(可接收以太币) "function mint(uint amount) payable", // 常量方法 (如 "view" 或 "pure") "function balanceOf(address owner) view returns (uint)", // 事件 "event Transfer(address indexed from, address indexed to, uint256 amount)", // 自定义的 Solidity Error "error AccountLocked(address owner, uint256 balance)", // 带有结构体类型的例子 "function addUser(tuple(string name, address addr) user) returns (uint id)", "function addUsers(tuple(string name, address addr)[] user) returns (uint[] id)", "function getUser(uint id) view returns (tuple(string name, address addr) user)" ]);

属性

interface.fragments ⇒ Array< Fragment >

接口中所有的Fragments。

interface.errors ⇒ Array< ErrorFragment >

接口中所有的Error Fragments。

interface.events ⇒ Array< EventFragment >

接口中所有的Event Fragments。

interface.functions ⇒ Array< FunctionFragment >

接口中所有的Function Fragments。

interface.deploy ⇒ ConstructorFragment

接口中所有的Constructor Fragments。

格式化

interface.format( [ format ] ) ⇒ string | Array< string >

返回格式化的接口。如果格式类型是json,则返回一个字符串,否则返回一个由human-readable(人类可读)字符串组成的数组。

const FormatTypes = ethers.utils.FormatTypes; iface.format(FormatTypes.json) // '[{"type":"constructor","payable":false,"inputs":[{"type":"string","name":"symbol"},{"type":"string","name":"name"}]},{"type":"function","name":"transferFrom","constant":false,"payable":false,"inputs":[{"type":"address","name":"from"},{"type":"address","name":"to"},{"type":"uint256","name":"amount"}],"outputs":[]},{"type":"function","name":"mint","constant":false,"stateMutability":"payable","payable":true,"inputs":[{"type":"uint256","name":"amount"}],"outputs":[]},{"type":"function","name":"balanceOf","constant":true,"stateMutability":"view","payable":false,"inputs":[{"type":"address","name":"owner"}],"outputs":[{"type":"uint256"}]},{"type":"event","anonymous":false,"name":"Transfer","inputs":[{"type":"address","name":"from","indexed":true},{"type":"address","name":"to","indexed":true},{"type":"uint256","name":"amount"}]},{"type":"error","name":"AccountLocked","inputs":[{"type":"address","name":"owner"},{"type":"uint256","name":"balance"}]},{"type":"function","name":"addUser","constant":false,"payable":false,"inputs":[{"type":"tuple","name":"user","components":[{"type":"string","name":"name"},{"type":"address","name":"addr"}]}],"outputs":[{"type":"uint256","name":"id"}]},{"type":"function","name":"addUsers","constant":false,"payable":false,"inputs":[{"type":"tuple[]","name":"user","components":[{"type":"string","name":"name"},{"type":"address","name":"addr"}]}],"outputs":[{"type":"uint256[]","name":"id"}]},{"type":"function","name":"getUser","constant":true,"stateMutability":"view","payable":false,"inputs":[{"type":"uint256","name":"id"}],"outputs":[{"type":"tuple","name":"user","components":[{"type":"string","name":"name"},{"type":"address","name":"addr"}]}]}]' iface.format(FormatTypes.full) // [ // 'constructor(string symbol, string name)', // 'function transferFrom(address from, address to, uint256 amount)', // 'function mint(uint256 amount) payable', // 'function balanceOf(address owner) view returns (uint256)', // 'event Transfer(address indexed from, address indexed to, uint256 amount)', // 'error AccountLocked(address owner, uint256 balance)', // 'function addUser(tuple(string name, address addr) user) returns (uint256 id)', // 'function addUsers(tuple(string name, address addr)[] user) returns (uint256[] id)', // 'function getUser(uint256 id) view returns (tuple(string name, address addr) user)' // ] iface.format(FormatTypes.minimal) // [ // 'constructor(string,string)', // 'function transferFrom(address,address,uint256)', // 'function mint(uint256) payable', // 'function balanceOf(address) view returns (uint256)', // 'event Transfer(address indexed,address indexed,uint256)', // 'error AccountLocked(address,uint256)', // 'function addUser(tuple(string,address)) returns (uint256)', // 'function addUsers(tuple(string,address)[]) returns (uint256[])', // 'function getUser(uint256) view returns (tuple(string,address))' // ]

Fragment Access

interface.getFunction( fragment ) ⇒ FunctionFragment

返回fragment的FunctionFragment(参考Specifying Fragments)。

// 通过方法(method)的签名,这是经过标准化后的,因此空格和多余的属性会被舍去 iface.getFunction("transferFrom(address, address, uint256)"); // 通过name的方式;这只在方法名称是唯一确定的情况下才有效 iface.getFunction("transferFrom"); // 通过函数选择器 iface.getFunction("0x23b872dd"); // 如果方法不存在将抛出异常 iface.getFunction("doesNotExist()"); // [Error: no matching function] { // argument: 'signature', // code: 'INVALID_ARGUMENT', // reason: 'no matching function', // value: 'doesNotExist()' // }
interface.getError( fragment ) ⇒ ErrorFragment

返回fragment的ErrorFragment(参考Specifying Fragments)。

// 通过错误(error)的签名,这是经过标准化后的,因此空格和多余的属性会被舍去 iface.getError("AccountLocked(address, uint256)"); // 通过name的方式;这只在方法名称是唯一确定的情况下才有效 iface.getError("AccountLocked"); // 通过error的选择器 iface.getError("0xf7c3865a"); // 如果方法不存在将抛出异常 iface.getError("DoesNotExist()"); // [Error: no matching error] { // argument: 'signature', // code: 'INVALID_ARGUMENT', // reason: 'no matching error', // value: 'DoesNotExist()' // }
interface.getEvent( fragment ) ⇒ EventFragment

返回fragment的EventFragment(参考Specifying Fragments)。

// 通过事件(event)的签名,这是经过标准化后的,因此空格和多余的属性会被舍去 iface.getEvent("Transfer(address, address, uint256)"); // 通过name的方式;这只在方法名称是唯一确定的情况下才有效 iface.getEvent("Transfer"); // 通过事件topic哈希 iface.getEvent("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"); // 如果方法不存在将抛出异常 iface.getEvent("DoesNotExist()"); // [Error: no matching event] { // argument: 'signature', // code: 'INVALID_ARGUMENT', // reason: 'no matching event', // value: 'DoesNotExist()' // }

签名(Signature) 和 主题(Topic) 的哈希

interface.getSighash( fragment ) ⇒ string< DataHexString< 4 > >

返回fragment的签名哈希(sighash)或函数选择器(Function Selector)(参考Specifying Fragments)。

iface.getSighash("balanceOf"); // '0x70a08231' iface.getSighash("balanceOf(address)"); // '0x70a08231' const fragment = iface.getFunction("balanceOf") iface.getSighash(fragment); // '0x70a08231'
interface.getEventTopic( fragment ) ⇒ string< DataHexString< 32 > >

返回fragment的主题哈希(topic hash)(参考Specifying Fragments)。

iface.getEventTopic("Transfer"); // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' iface.getEventTopic("Transfer(address, address, uint)"); // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' const fragment = iface.getEvent("Transfer") iface.getEventTopic(fragment); // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'

编码数据

interface.encodeDeploy( [ values ] ) ⇒ string< DataHexString >

返回已编码的部署数据,这些数据可以连接到合约的部署字节码,以便将值(values)传递到合约构造函数中。

// 这些数据表示的是在部署期间应该追加到字节码形式的构造函数的参数 iface.encodeDeploy([ "SYM", "Some Name" ]) // '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000353594d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009536f6d65204e616d650000000000000000000000000000000000000000000000'
interface.encodeErrorResult( fragment [ , values ] ) ⇒ string< DataHexString >

返回编码后的错误结果,这通常是对给定值的fragment(参考Specifying Fragments))的reverted调用的响应结果。

大多数开发人员将不需要此方法,但对于那些模拟自己的区块链的开发人员很有用。

// 编码结果数据(像在revert期间被eth_call返回) iface.encodeErrorResult("AccountLocked", [ "0x8ba1f109551bD432803012645Ac136ddd64DBA72", parseEther("1.0") ]); // '0xf7c3865a0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba720000000000000000000000000000000000000000000000000de0b6b3a7640000'
interface.encodeFilterTopics( fragment , values ) ⇒ Array< topic | Array< topic > >

返回已编码的主题过滤器,可以在fragment(参考Specifying Fragments)将values值传递给getLogs函数。

每个主题是一个32字节(64nibble)的DataHexString。

// 匹配的Transfer事件的过滤器 iface.encodeFilterTopics("Transfer", []) // [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' // ] // 匹配的sender的过滤器 iface.encodeFilterTopics("Transfer", [ "0x8ba1f109551bD432803012645Ac136ddd64DBA72" ]) // [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72' // ] // 匹配receiver的过滤器 iface.encodeFilterTopics("Transfer", [ null, "0x8ba1f109551bD432803012645Ac136ddd64DBA72" ]) // [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // null, // '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72' // ]
interface.encodeFunctionData( fragment [ , values ] ) ⇒ string< DataHexString >

返回编码后的数据,可以在fragment(参考Specifying Fragments)指定values值,这个数据用作交易的data中。

// 编码数据,用于交易或调用的tx.data iface.encodeFunctionData("transferFrom", [ "0x8ba1f109551bD432803012645Ac136ddd64DBA72", "0xaB7C8803962c0f2F5BBBe3FA8bf41cd82AA1923C", parseEther("1.0") ]) // '0x23b872dd0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c0000000000000000000000000000000000000000000000000de0b6b3a7640000' // 使用 positional 数组编码结构体数据 user = [ "Richard Moore", "0x8ba1f109551bD432803012645Ac136ddd64DBA72" ]; iface.encodeFunctionData("addUser", [ user ]); // '0x43967833000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72000000000000000000000000000000000000000000000000000000000000000d52696368617264204d6f6f726500000000000000000000000000000000000000' // 使用对象的方式编码结构体数据,只有参数被命名才可用 user = { name: "Richard Moore", addr: "0x8ba1f109551bD432803012645Ac136ddd64DBA72" }; iface.encodeFunctionData("addUser", [ user ]); // '0x43967833000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72000000000000000000000000000000000000000000000000000000000000000d52696368617264204d6f6f726500000000000000000000000000000000000000'
interface.encodeFunctionResult( fragment [ , values ] ) ⇒ string< DataHexString >

Most developers will not need this method, but may be useful for authors of a mock blockchain. 返回编码后的结果,这通常是对给定值的fragment(参考Specifying Fragments))的调用的响应结果。

大多数开发人员将不需要此方法,但对于那些模拟自己的区块链的开发人员很有用。

// 编码 result 数据 (像被eth_call后的返回结果数据) iface.encodeFunctionResult("balanceOf", [ "0x8ba1f109551bD432803012645Ac136ddd64DBA72" ]) // '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72'

解码数据

interface.decodeErrorResult( fragment , data ) ⇒ Result

在对给定数据的fragment(参考Specifying Fragments))revert期间,返回此调用的结果中已解码的值。

大多数开发人员不需要这个数据,因为如果数据表示revert,decodeFunctionResult将自动解码errors。

// 解码Result数据(例如eth_call) errorData = "0xf7c3865a0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba720000000000000000000000000000000000000000000000000de0b6b3a7640000"; iface.decodeErrorResult("AccountLocked", errorData) // [ // '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // { BigNumber: "1000000000000000000" }, // balance: { BigNumber: "1000000000000000000" }, // owner: '0x8ba1f109551bD432803012645Ac136ddd64DBA72' // ]
interface.decodeEventLog( fragment , data [ , topics ] ) ⇒ Result

从事件日志中为带有可选主题(topics)的给定数据fragment(参考Specifying Fragments))返回已解码的事件值。

如果没有指定主题(topics),将在结果中插入占位符。

大多数开发人员会发现parsing methods更便于解码事件数据,因为它们会自动检测匹配的事件。

// 解码日志和主题数据 (在整个收据中) const data = "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"; const topics = [ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72", "0x000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c" ]; iface.decodeEventLog("Transfer", data, topics); // [ // '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // '0xaB7C8803962c0f2F5BBBe3FA8bf41cd82AA1923C', // { BigNumber: "1000000000000000000" }, // amount: { BigNumber: "1000000000000000000" }, // from: '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // to: '0xaB7C8803962c0f2F5BBBe3FA8bf41cd82AA1923C' // ]
interface.decodeFunctionData( fragment , data ) ⇒ Result

对于给定数据fragment(参考Specifying Fragments)),从交易数据中返回已解码的值。

大多数开发人员不需要这个方法,但是对于调试或检查交易可能很有用。

大多数开发人员还会发现parsing methods对于解码交易数据更方便,因为它们会自动检测匹配的函数。

// 解码函数数据(tx.data的值) const txData = "0x23b872dd0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c0000000000000000000000000000000000000000000000000de0b6b3a7640000"; iface.decodeFunctionData("transferFrom", txData); // [ // '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // '0xaB7C8803962c0f2F5BBBe3FA8bf41cd82AA1923C', // { BigNumber: "1000000000000000000" }, // amount: { BigNumber: "1000000000000000000" }, // from: '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // to: '0xaB7C8803962c0f2F5BBBe3FA8bf41cd82AA1923C' // ]
interface.decodeFunctionResult( fragment , data ) ⇒ Result

返回给定数据的fragment(参考Specifying Fragments))调用结果的解码值。

// 解码result数据(例如,来自eth_call) resultData = "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"; iface.decodeFunctionResult("balanceOf", resultData) // [ // { BigNumber: "1000000000000000000" } // ] // 解码由revert触发的result数据 // 抛出一个CALL_EXCEPTION,具有额外的信息 errorData = "0xf7c3865a0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba720000000000000000000000000000000000000000000000000de0b6b3a7640000"; iface.decodeFunctionResult("balanceOf", errorData) // [Error: call revert exception [ See: https://links.ethers.org/v5-errors-CALL_EXCEPTION ]] { // code: 'CALL_EXCEPTION', // data: '0xf7c3865a0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba720000000000000000000000000000000000000000000000000de0b6b3a7640000', // errorArgs: [ // '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // { BigNumber: "1000000000000000000" }, // balance: { BigNumber: "1000000000000000000" }, // owner: '0x8ba1f109551bD432803012645Ac136ddd64DBA72' // ], // errorName: 'AccountLocked', // errorSignature: 'AccountLocked(address,uint256)', // method: 'balanceOf(address)', // reason: null // } // 解码结构体数据将返回一个Result对象,该对象将按位置包含所有值,如果ABI包含names,则值还将通过names使用。 resultData = "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72000000000000000000000000000000000000000000000000000000000000000d52696368617264204d6f6f726500000000000000000000000000000000000000"; result = iface.decodeFunctionResult("getUser", resultData); // [ // [ // 'Richard Moore', // '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // addr: '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // name: 'Richard Moore' // ], // user: [ // 'Richard Moore', // '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // addr: '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // name: 'Richard Moore' // ] // ] // 位置访问:下标为0的输出参数,结构体的第一个属性 result[0][0]; // 'Richard Moore' // 通过name访问:(仅当因为参数已命名才可用) result.user.name // 'Richard Moore'

解析(Parsing)

对于大多数开发人员来说,这些函数通常是最有用的。 它们将自动在ABI中搜索匹配的事件(Event)或函数(Function),并将组件解码为完整的描述信息。

interface.parseError( data ) ⇒ ErrorDescription

在数据中搜索与错误选择器匹配的错误,并解析出详细信息。

const data = "0xf7c3865a0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba720000000000000000000000000000000000000000000000000de0b6b3a7640000"; iface.parseError(data); // ErrorDescription { // args: [ // '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // { BigNumber: "1000000000000000000" }, // balance: { BigNumber: "1000000000000000000" }, // owner: '0x8ba1f109551bD432803012645Ac136ddd64DBA72' // ], // errorFragment: [Function: ErrorFragment], // name: 'AccountLocked', // sighash: '0xf7c3865a', // signature: 'AccountLocked(address,uint256)' // }
interface.parseLog( log ) ⇒ LogDescription

搜索与日志主题哈希匹配的事件,并解析日志表示的值。

const data = "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"; const topics = [ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72", "0x000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c" ]; iface.parseLog({ data, topics }); // LogDescription { // args: [ // '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // '0xaB7C8803962c0f2F5BBBe3FA8bf41cd82AA1923C', // { BigNumber: "1000000000000000000" }, // amount: { BigNumber: "1000000000000000000" }, // from: '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // to: '0xaB7C8803962c0f2F5BBBe3FA8bf41cd82AA1923C' // ], // eventFragment: [Function: EventFragment], // name: 'Transfer', // signature: 'Transfer(address,address,uint256)', // topic: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' // }
interface.parseTransaction( transaction ) ⇒ TransactionDescription

搜索与交易数据签名哈希匹配的函数,并解析出交易的属性。

const data = "0x23b872dd0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c0000000000000000000000000000000000000000000000000de0b6b3a7640000"; const value = parseEther("1.0"); iface.parseTransaction({ data, value }); // TransactionDescription { // args: [ // '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // '0xaB7C8803962c0f2F5BBBe3FA8bf41cd82AA1923C', // { BigNumber: "1000000000000000000" }, // amount: { BigNumber: "1000000000000000000" }, // from: '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // to: '0xaB7C8803962c0f2F5BBBe3FA8bf41cd82AA1923C' // ], // functionFragment: [Function: FunctionFragment], // name: 'transferFrom', // sighash: '0x23b872dd', // signature: 'transferFrom(address,address,uint256)', // value: { BigNumber: "1000000000000000000" } // }

类型(Types)

Result inherits Array<any>

一个Result是一个数组,因此每个值都可以作为位置参数访问。

此外,如果值已命名,则可以通过名称访问与其位置值相同的对象。

但是对象中的name为length是不可用的,因为它是Array的一部分, 所以这个键的任何命名值都被重命名为_length。

如果存在名称冲突,则只有第一个键可用。

ErrorDescription

errorDescription.args ⇒ Result

error的输入参数的值。

errorDescription.errorFragment ⇒ ErrorFragment

与数据中选择器匹配的ErrorFragment。

errorDescription.name ⇒ string

error的name。(例如AccountLocked)

errorDescription.signature ⇒ string

error的签名。(例如AccountLocked(address,uint256))

errorDescription.sighash ⇒ string

error的选择器(selector)。

LogDescription

logDescription.args ⇒ Result

事件的输入参数的值。

logDescription.eventFragment ⇒ EventFragment

与日志中的主题匹配的EventFragment。

logDescription.name ⇒ string

事件的名称。(例如Transfer)

logDescription.signature ⇒ string

事件的签名。(如Transfer(address,address,uint256))

logDescription.topic ⇒ string

主题的哈希。(topic hash)

TransactionDescription

transactionDescription.args ⇒ Result

从交易数据的输入参数中解码出来的值。

transactionDescription.functionFragment ⇒ FunctionFragment

交易数据中的与签名哈希匹配的FunctionFragment。

transactionDescription.name ⇒ string

函数的名称。(如transfer)

transactionDescription.sighash ⇒ string

匹配交易数据的签名哈希(或函数选择器)。

transactionDescription.signature ⇒ string

函数的签名。(如transfer(address,uint256))

transactionDescription.value ⇒ 大数(BigNumber)

交易的value值(以太币).

Specifying Fragments

当指定一个fragment到接口中的函数时,可以使用以下任意一个: