调用外部合约的代码
在之前我们已经看到过,使用interface、library的方式调用外部合约的代码。
接下来,我们将为大家补充第三种形式:
在下面的代码中,部署cat合约之后,例如地址为
0x345678..
在部署animal合约时,传递此cat合约地址。从而能够存储合约的引用。调用test方法即可调用到外部合约的方法。
| |
call函数
不管是interface、library还是上面看到的形式,要调用外部代码,都是底层调用了call或者是delecall函数。
call函数基本使用方法
call函数的使用方法,首先需要外部合约的地址。如下例中的animalCall合约,在部署合约时,传递了外部合约cat的地址
0x345678..
,存储在address c当中。
通过合约地址.call(函数标志符)的方式来调用合约。函数标志符是对于函数声明哈希之后的前4个字节的数据。
如下例中,c.call(bytes4(keccak256(“eat()”)))将调用cat合约中的eat方法
| |
call函数返回值
call函数的返回值为true或者false。只有当能够找到此方法并执行成功后,会返回true,而如果不能够找到此函数或执行失则会返回false。因此调用test1方法会返回true,调用test2方法会返回false,因为找不到函数。
call 函数与回调函数
call函数如果找不到函数,默认会调用回调函数。
回调函数是特殊的函数,其没有函数名。
其形式为:
| |
对于如下的cat合约。书写了回调函数。假设合约地址为c.那么在外部调用c.call(“abc”);会找不到此函数,默认会执行回调函数.因此在外部调用的c.call(“abc”) 会使得cat合约的状态变量变为999。而且call函数会返回true。
| |
call函数与msg.data
回调函数是非常有用的,例如我们可以在外部调用失败的时候,执行某一些操作。
对于如下的cat合约。书写了回调函数。假设合约地址为c.那么在外部调用c.call(“abc”);会找不到此函数,默认会执行回调函数.回调函数中,将msg.data的值赋值给了fail变量。通过getfail函数可查看call函数传递过来的完整数据。fail变量的值为32个字节
0x6162630000000000000000000000000000000000000000000000000000000000
,前3个字节是参数字母a、b、c的ASCII码。61、62、63.
| |
call函数修改外部合约的状态变量
在下例中,cat合约与animalcall合约中都有状态变量我们首先部署cat合约,得到地址
0x3456..
, 接下来,将合约地址作为参数部署anumalCall合约。
调用test2方法,其调用了cat合约的eat方法,修改了cat合约中a的值为256. call函数调用外部合约,修改外部合约中的状态变量。
| |
delegatecall
delegatecall函数的使用方法和call函数一样,通过合约地址.delegatecall(函数标志符)的方式来调用合约。函数标志符是对于函数声明哈希之后的前4个字节的数据。
library库的远程调用正是使用了delegatecall函数。delegatecall与call不同之处在于,delegatecall不会修改外部合约中的状态变量,其好像是将外部函数的代码加载到了本地合约中执行。会修改本地合约状态变量的值。
例如下面的代码,首先部署cat合约,得到地址
0x3456..
调用test2方法,其调用了cat合约的eat方法,但是却是修改了animalcall合约中的状态变量a。因此当查询后发现,cat合约中的a并没有变化,animalCall合约变量a变为了了256。
| |
call函数转账与回调函数细节
call函数可以进行转账,并且是transfer与send的底层函数。call函数转账的使用方法是
地址.call.value(转账金额)()
要注意的是,执行转账的时候,如果转账的地址为合约,并且转账合约中有回调函数。那么将默认会执行回调函数。
但是以太坊为了避免重入***,对于transfer与send函数进行了限制。当使用transfer与send函数,回调函数中执行的操作最多不能够超过2300gas。这也就意味着不能够执行转账、赋值等操作,而只能够执行事件触发等操作。
例如下面的代码: 首先部署Receiver合约,得到地址
0x3456..
,再传递Receiver的地址部署Sender合约。当调用sendMoney方法的时候,为合约地址
0x3456..
转账的操作会触发回调函数,将状态变量balance的数量增加。但是由于修改状态变量的操作超过了最大2300gas的限制,所以下面的操作不会成功。
| |
call函数能够让上面的操作成功。call函数能够指定gas的限制,超过2300gas限制的约束。
如下例所示:
首先部署Receiver合约,得到地址
0x3456..
,再传递Receiver的地址部署Sender合约。当调用sendMoney方法转移100wei的时候,为合约地址
0x3456..
转账的操作会触发回调函数,将状态变量balance的数量增加。由于call函数指定的最大gas限制为20317,所以触发回调函数可以将balance的金额修改为100.但是要注意,正因为此,call函数是危险的底层函数,不能够避免重入***的问题。
| |