【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合约内。

参考文档

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/ERC1967/ERC1967Upgrade.sol

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);
    }
}

Loading

Facebook评论