今天遇到一个问题,记录这个坑
pragma solidity ^0.4.26;
contract Proxy {
address internal masterCopy;
constructor(address _masterCopy)
public
{
require(_masterCopy != address(0), "Invalid master copy address provided");
masterCopy = _masterCopy;
}
/// @dev Fallback function forwards all transactions and returns all received return data.
function ()
external
payable
{
// solium-disable-next-line security/no-inline-assembly
assembly {
let masterCopy := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
// 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
mstore(0, masterCopy)
return(0, 0x20)
}
calldatacopy(0, 0, calldatasize())
let success := delegatecall(gas, masterCopy, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
if eq(success, 0) { revert(0, returndatasize()) }
return(0, returndatasize())
}
}
}
contract Erc20 {
address public sender;
event DoSomething(address);
function dosomthing() public returns(bool){
sender = msg.sender;
emit DoSomething(msg.sender);
return true;
}
}
contract Hacker {
event Ok(address,bytes,uint256);
event Failed(bool);
function setErc20Addr(address addr) public returns(bool) {
erc20 = Erc20(addr);
}
Erc20 public erc20 ;
function exec(address addr, bytes data, uint256 amount) public payable returns(bool){
bool success = erc20.dosomthing();
if(success) {
emit Ok(addr, data, amount);
return true;
} else {
emit Failed(false);
return false;
}
}
}
调用setErc20Addr
之后,直接调用Hacker
则成功,但是通过调用proxy
的fallback
函数总是失败。
remix中报错如下
到底是什么原因呢?
原因是delegatecall
的机制: https://solidity-cn.readthedocs.io/zh/develop/introduction-to-smart-contracts.html?highlight=delegatecall#index-13
也就是说, bool success = erc20.dosomthing();
这一句,erc20
是Hacker
的成员,当在Proxy
的fallback
调用起Hacker
的exec
时,仅仅把exec
函数代码拿来用来,并没有把Hacker
的erc20
一起复制过去,所以,erc20
是空的!
修改之后:
contract Hacker {
event Ok(address,bytes,uint256);
event Failed(bool);
function exec(address addr, bytes data, uint256 amount) public payable returns(bool){
Erc20 erc20 = Erc20(addr);
bool success = erc20.dosomthing();
if(success) {
emit Ok(addr, data, amount);
return true;
} else {
emit Failed(false);
return false;
}
}
}
也就是说,erc20
合约地址由Proxy
的fallback
调用时传进来,再初始化erc20
。现在就可以正常调用了。