【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评论