Solidity中call函数详解

目录

调用外部合约代码

call函数

call函数基本使用方法

 call函数返回值

call函数与回调函数

call函数与msg.data

 call函数修改外部合约的状态变量

dalegatecall

 call函数转账与回调函数细节


调用外部合约代码

在之前我们已经看到过,使用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函数如果找不到函数,默认会调用回调函数。
回调函数是特殊的函数,其没有函数名。
其形式为: function(){}

对于如下的cat合约。书写了回调函数。假设合约地址为c.那么在外部调用c.call(“abc”);会找不到此函数,默认会执行回调函数.因此在外部调用的c.call(“abc”) 会使得cat合约的状态变量变为999。而且call函数会返回true。

contract cat{
    uint public a = 5;
    function ear() public returns(uint){
        a = 256;
        return a;
    }
    
    function () {
        a = 999;
    }
}

call函数与msg.data

回调函数是非常有用的,例如我们可以在外部调用失败的时候,执行某一些操作。
对于如下的cat合约。书写了回调函数。假设合约地址为c.那么在外部调用c.call(“abc”);会找不到此函数,默认会执行回调函数.回调函数中,将msg.data的值赋值给了fail变量。通过getfail函数可查看call函数传递过来的完整数据。fail变量的值为32个字节0x6162630000000000000000000000000000000000000000000000000000000000,前3个字节是参数字母a、b、c的ASCII码。61、62、63.

contract cat{
    bytes fail;
    function (){
        fail = msg.data;
    }
    
    function getfail() returns(bytes){
        return fail;
    }
}

 call函数修改外部合约的状态变量

在下例中,cat合约与animalcall合约中都有状态变量我们首先部署cat合约,得到地址0x3456.., 接下来,将合约地址作为参数部署anumalCall合约。
调用test2方法,其调用了cat合约的eat方法,修改了cat合约中a的值为256. call函数调用外部合约,修改外部合约中的状态变量。

dalegatecall

delegatecall函数的使用方法和call函数一样,通过合约地址.delegatecall(函数标志符)的方式来调用合约。函数标志符是对于函数声明哈希之后的前4个字节的数据。
library库的远程调用正是使用了delegatecall函数。delegatecall与call不同之处在于,delegatecall不会修改外部合约中的状态变量,其好像是将外部函数的代码加载到了本地合约中执行。会修改本地合约状态变量的值。
例如下面的代码,首先部署cat合约,得到地址0x3456.., 接下来,将合约地址作为参数部署anumalCall合约。
调用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函数是危险的底层函数,不能够避免重入攻击的问题。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值