昨天研究了下重入攻击,有查到call
这个函数,感觉它很万能--几乎能干所有的事情!既能转帐以太,又能调用合约,也就无怪乎有重入风险。现在solidity早就建议用transfer
来转帐以太,调用合约用接口(interface)。其实用call来调用合约也是很方便的,同时还有个类似的函数delegatecall
。
调用另一个合约中的函数,主要是两个方法:call 和 delegatecall。但推荐用接口调用!
call 会切换到被调合约中去执行方法,会切换上下文。msg.sender是主调合约地址。
delegatecall 则是将被调合约中的方法调到本合约中执行,也就是不会切换上下文。msg.sender是发起者本人。
传入的参数(address _addr)是合约地址。下边有个案例,可以试着去体会细节。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SetNum {
uint256 public n = 20;
address public sender;
function setN(uint256 _n) public {
n = _n;
sender = msg.sender;
}
}
contract CallSet {
uint256 public n = 15;
address public sender;
function CallTest(address _addr, uint256 _n) public {
_addr.call(abi.encodeWithSignature("setN(uint256)", _n)); //改变SetNum中的n值
sender = msg.sender; //是调用者本人,同时会改变SetNum的sender地址
}
function delegatecallTest(address _addr, uint256 _n) public {
_addr.delegatecall(abi.encodeWithSignature("setN(uint256)", _n)); //改变自身的n值
sender = msg.sender; //是调用者本人,不会改变SetNum的sender地址
}
}
call和delegatecal这两个方法做了很大改进,我估计也是为了防止重入的风险。以前是要算出函数的ID值,现在是计算整个的签名值。刚刚还不知错误在哪,到google上又是一顿好找,终于解决!solidity的文件写得干巴巴的,不好理解。找了个案例集,很不错,分享下。