合约依赖与调用
依赖引入:合约可以通过import引入依赖的外部合约,抽象合约,Interface或者库。通常,我们使用npm管理合约的外部依赖,管理合约的依赖也有其他办法(例如git submodule)这会在工作流章节中详细叙述。
调用:合约可以调用其他合约,只需知道地址和ABI,我们就可以在合约内部调用其他合约,需要注意的是,调用合约也是事务性操作,因此,你不需要通过手动管理异步操作的方式来等待返回结果。在合约内部调用其他合约需要消耗额外的Gas费用。调用合约可能由于ABI错误或者不支持某个函数方法而导致失败,但Gas费用并不会返还,我们需要确保在调用其他第三方合约前理解对方合约的接口(包括参数类型,顺序,返回结构)
如果你试图调试本地合约调用某个生产环境的线上合约,可以使用fork的方式将某个高度的区块链下载到本地运行,这会在工作流章节中详细叙述。
ABI:也叫应用程序二进制接口(Application Binary Interface)ABI是我们理解如何操作一个合约的具体方法的描述,通常在Interface文件中被定义(如果合约命名为Membership.sol,那么它的Interface文件通常叫做IMembership.sol)
注意:通过这种方式定义可以让任意合约通过引用interface的方式来调用你的合约,但如果你不在Interface中文件定义它,编译器也能帮助你编译出ABI。
我们可以依赖完整的ABI来调用合约(对外部调用者来说,ABI通常被编译成一个JSON文件),也可以使用它其中的一部分来调用,只要它满足真实合约所声明的函数(包括参数,参数类型,返回值,返回值类型都一致)后者通常被成为human-readable ABI,例如:
calldatas[0]=abi.encodeWithSignature(
‘execTransfer(uint256,address,address[],uint256[])’,
memberId,
memberWallet,
payroll.tokens.addresses,
payroll.tokens.amounts
);
合约事件:由于合约的函数调用是事务性的,并且无法为外部调用者(指代DApp或钱包用户)提供返回值,合约引入了事件的概念。
事件通过向日志系统中写入特定数据的方式来实现函数修改的记录。我们可以通过监听和查询的方式列出一个合约注册的所有事件,实现对函数异步结果的查询和前端UI状态变更。合约事件以某个单一合约为key来进行索引,同时,在声明事件时,我们可以指定不多于三个index key来确保DApp前端对这些索引key的查询效率,例如:
event ModuleProposalCreated(
address indexed module,
bytes32 indexed id,
address indexed sender,
uint256 timestamp
);
如果你期望的查询是非常复杂的,包括一系列相关联的合约事件,更好的方法是采用Relay提供的graph/webhook来进行查询。
创建合约:我们可以通过合约创建其他合约,这意味着,合约可以成为其他合约的工厂合约或者代理合约。我们也可以通过外部调用者(钱包账户)向0x00地址发送合约创建操作来新建网络上的合约,这是我们进行测试和依赖工作流创建合约的方法。
创建合约需要消耗大量Gas费用,通常,我们会使用特定工具在创建合约前预估并计算费用