【Solidity】合约升级2
因为使用delegatecall,所以数据是存储在proxy合约内的,但是在logic合约声明的变数顺序必须保持一次,否则就会乱掉了。
如果一开始是调用Logic1并且输入1的话,那么number变量就是2,接着我们把proxy改成了Logic2, 那么链上的数据也是保持为2不变。
在Proxy合约当中的储存Logic合约的地址不使用一般的address来存,而是使用了constant来存,这是因为比较省Gas,这个写法是参考了Openzeppelin EIP1967的写法
在proxy合约必须放fallback,因为当caller调用proxy的function,发现找不到的话就会调用fallback,然后fallback调用_delegate 实现delegatecall去到logic合约,然后把数据存入proxy合约内。
参考文档
Logic1.sol
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; contract Logic1 { uint private number;//slot(0) function setNum(uint _number)public{ number = _number + 1; } function checkNum()public view returns(uint){ return number; } }
Logic2.sol
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; contract Logic2 { uint private number;//slot(0) function setNum(uint _number)public{ number = _number + 2; } function checkNum()public view returns(uint){ return number; } }
Proxy.sol
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; library StorageSlot { function getAddressAt(bytes32 slot) internal view returns (address a) { assembly { a := sload(slot) } } function setAddressAt(bytes32 slot, address address_) internal { assembly { sstore(slot, address_) } } } contract Proxy{ bytes32 private constant _IMPL_SLOT = keccak256("eip1967.proxy.implementation"); function setImplementation(address implementation_) public { StorageSlot.setAddressAt(_IMPL_SLOT, implementation_); } function getImplementation() public view returns (address) { return StorageSlot.getAddressAt(_IMPL_SLOT); } function _delegate(address impl) internal virtual { assembly { let ptr := mload(0x40) calldatacopy(ptr, 0, calldatasize()) let result := delegatecall(gas(), impl, ptr, calldatasize(), 0, 0) let size := returndatasize() returndatacopy(ptr, 0, size) switch result case 0 { revert(ptr, size) } default { return(ptr, size) } } } fallback() external { _delegate(StorageSlot.getAddressAt(_IMPL_SLOT)); } }
Caller.sol
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; contract Caller{ event Response(bool success); event ResponseWithData(bool success, bytes data); function getCallNumber(address _addr) public returns(uint256) { (bool res,bytes memory data) = _addr.call(abi.encodeWithSignature("checkNum()")); emit ResponseWithData(res, data); uint256 number = abi.decode(data, (uint)); return number; } function setNum(address _addr, uint256 _num) public { (bool res,) = _addr.call(abi.encodeWithSignature("setNum(uint256)",_num)); emit Response(res); } }
Facebook评论