Michael.W基于Foundry精读Openzeppelin第61期——ERC1967Upgrade.sol

Michael.W基于Foundry精读Openzeppelin第61期——ERC1967Upgrade.sol

      • 0. 版本
        • 0.1 ERC1967Upgrade.sol
      • 1. 目标合约
      • 2. 代码精读
        • 2.1 _getImplementation() internal && _upgradeTo(address newImplementation) internal
        • 2.2 _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal
        • 2.3 _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal
        • 2.4 _getAdmin() internal && _changeAdmin(address newAdmin) internal
        • 2.5 _getBeacon() internal && _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal

0. 版本

[openzeppelin]:v4.8.3,[forge-std]:v1.5.6

0.1 ERC1967Upgrade.sol

Github: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.3/contracts/proxy/ERC1967/ERC1967Upgrade.sol

ERC1967Upgrade库实现了基于ERC1967标准(代理合约的slot分布)的slots读写函数,并在对应slot更新时emit出标准中相应的event。对于各种可升级合约和代理合约的实现而言,本库的作用举足轻重。

ERC1967详情参见:https://eips.ethereum.org/EIPS/eip-1967

1. 目标合约

继承ERC1967Upgrade合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/src/proxy/ERC1967/MockERC1967Upgrade.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Upgrade.sol";

contract MockERC1967Upgrade is ERC1967Upgrade {
    bytes32 private constant _ROLLBACK_SLOT = bytes32(uint(keccak256("eip1967.proxy.rollback")) - 1);

    function setRollbackSlot(bool value) external {
        StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value = value;
    }

    function getImplementation() external view returns (address){
        return _getImplementation();
    }

    function upgradeTo(address newImplementation) external {
        _upgradeTo(newImplementation);
    }

    function upgradeToAndCall(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) external {
        _upgradeToAndCall(newImplementation, data, forceCall);
    }

    function upgradeToAndCallUUPS(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) external {
        _upgradeToAndCallUUPS(newImplementation, data, forceCall);
    }

    function getAdmin() external view returns (address) {
        return _getAdmin();
    }


    function changeAdmin(address newAdmin) external {
        _changeAdmin(newAdmin);
    }

    function getBeacon() external view returns (address) {
        return _getBeacon();
    }

    function upgradeBeaconToAndCall(
        address newBeacon,
        bytes memory data,
        bool forceCall
    ) external {
        _upgradeBeaconToAndCall(newBeacon, data, forceCall);
    }
}

全部foundry测试合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/test/proxy/ERC1967/ERC1967Upgrade/ERC1967Upgrade.t.sol

测试使用的物料合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/test/proxy/ERC1967/ERC1967Upgrade/Implement.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "openzeppelin-contracts/contracts/interfaces/draft-IERC1822.sol";

interface IImplement {
    event InitialCallWithoutArgs();
    event InitialCallWithArgs(uint, address, string);
    event Receive();
    event Fallback(bytes);
}

contract Implement is IImplement {
    function initialCallWithoutArgs() external {
        emit InitialCallWithoutArgs();
    }

    function initialCallWithArgs(uint arg1, address arg2, string memory arg3) external {
        emit InitialCallWithArgs(arg1, arg2, arg3);
    }

    receive() external payable {
        emit Receive();
    }

    fallback() external {
        emit Fallback(msg.data);
    }
}

contract ImplementERC1822Proxiable is Implement, IERC1822Proxiable {
    bytes32 public proxiableUUID;

    constructor(bytes32 newProxiableUUID){
        proxiableUUID = newProxiableUUID;
    }
}

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/test/proxy/ERC1967/ERC1967Upgrade/Beacon.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "openzeppelin-contracts/contracts/proxy/beacon/IBeacon.sol";

contract Beacon is IBeacon {
    address public implementation;

    constructor(address newImplementation){
        implementation = newImplementation;
    }
}

2. 代码精读

2.1 _getImplementation() internal && _upgradeTo(address newImplementation) internal
  • _getImplementation() internal:返回当前存储的逻辑合约地址;
  • _upgradeTo(address newImplementation) internal:将代理合约背后的逻辑合约地址升级更换为newImplementation。
    // 用于存储逻辑合约地址的slot号
    // 计算逻辑:keccak256("eip1967.proxy.implementation")-1
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    function _getImplementation() internal view returns (address) {
        // 读取编号为_IMPLEMENTATION_SLOT的slot中的值,并转为address类型
        // 注:StorageSlot.getAddressSlot()详解参见:https://learnblockchain.cn/article/6104
        return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
    }

    // 设置逻辑合约地址为newImplementation
    function _setImplementation(address newImplementation) private {
        // 检验新的逻辑合约地址是合约地址
        // 注:Address.isContract()详解参见:https://learnblockchain.cn/article/6098
        require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
        // 将新的逻辑合约地址写入编号为_IMPLEMENTATION_SLOT的slot中
        StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
    }

    function _upgradeTo(address newImplementation) internal {
        // 设置新的逻辑合约地址
        _setImplementation(newImplementation);
        // 按照ERC1967标准,在升级逻辑合约地址后抛出事件`Upgraded(address indexed)`
        emit Upgraded(newImplementation);
    }

foundry代码验证:

contract ERC1967UpgradeTest is Test, IERC1967 {
    MockERC1967Upgrade private _testing = new MockERC1967Upgrade();
    Implement private _implement = new Implement();

    function test_GetImplementationAndUpgradeTo() external {
        assertEq(_testing.getImplementation(), address(0));
        address newImplementationAddress = address(_implement);
        vm.expectEmit(address(_testing));
        emit IERC1967.Upgraded(newImplementationAddress);

        _testing.upgradeTo(newImplementationAddress);
        assertEq(_testing.getImplementation(), newImplementationAddress);

        // revert if new implementation address is not a contract
        vm.expectRevert("ERC1967: new implementation is not a contract");
        _testing.upgradeTo(address(1024));
    }
}
2.2 _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal

将代理合约背后的逻辑合约地址升级更换为newImplementation,再执行一个额外的到新逻辑合约的delegatecall。

注:这个delegatecall可以理解为是去调用一个适配合约升级的类似constructor的函数。

    function _upgradeToAndCall(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        // 将代理合约背后的逻辑合约地址升级更换为newImplementation
        _upgradeTo(newImplementation);
        if (data.length > 0 || forceCall) {
            // 如果data不为空或forceCall为true,将delegatecall到新逻辑合约(以data作为calldata)
            // 注:Address.functionDelegateCall()详解参见:https://learnblockchain.cn/article/6098
            Address.functionDelegateCall(newImplementation, data);
        }
    }

foundry代码验证:

contract ERC1967UpgradeTest is Test, IERC1967, IImplement {
    MockERC1967Upgrade private _testing = new MockERC1967Upgrade();
    Implement private _implement = new Implement();

    function test_UpgradeToAndCall() external {
        assertEq(_testing.getImplementation(), address(0));
        address newImplementationAddress = address(_implement);
        // case 1: no call
        vm.expectEmit(address(_testing));
        emit IERC1967.Upgraded(newImplementationAddress);

        _testing.upgradeToAndCall(newImplementationAddress, '', false);
        assertEq(_testing.getImplementation(), newImplementationAddress);

        // revert if new implementation address is not a contract
        vm.expectRevert("ERC1967: new implementation is not a contract");
        _testing.upgradeToAndCall(address(1024), '', false);

        // case 2: call with no argument
        newImplementationAddress = address(new Implement());
        vm.expectEmit(address(_testing));
        emit IERC1967.Upgraded(newImplementationAddress);
        emit IImplement.InitialCallWithoutArgs();

        bytes memory data = abi.encodeCall(_implement.initialCallWithoutArgs, ());
        _testing.upgradeToAndCall(newImplementationAddress, data, false);
        assertEq(_testing.getImplementation(), newImplementationAddress);

        // revert if new implementation address is not a contract
        vm.expectRevert("ERC1967: new implementation is not a contract");
        _testing.upgradeToAndCall(address(1024), data, false);

        // case 3: call with arguments
        newImplementationAddress = address(new Implement());
        uint arg1 = 1024;
        address arg2 = address(1024);
        string memory arg3 = "1024";
        vm.expectEmit(address(_testing));
        emit IERC1967.Upgraded(newImplementationAddress);
        emit IImplement.InitialCallWithArgs(arg1, arg2, arg3);

        data = abi.encodeCall(
            _implement.initialCallWithArgs,
            (arg1, arg2, arg3)
        );
        _testing.upgradeToAndCall(newImplementationAddress, data, false);
        assertEq(_testing.getImplementation(), newImplementationAddress);

        // revert if new implementation address is not a contract
        vm.expectRevert("ERC1967: new implementation is not a contract");
        _testing.upgradeToAndCall(address(1024), data, false);

        // case 4: with forceCall and no data
        // NOTE: force call to the receive function of Implement
        newImplementationAddress = address(new Implement());
        vm.expectEmit(address(_testing));
        emit IERC1967.Upgraded(newImplementationAddress);
        emit IImplement.Receive();

        _testing.upgradeToAndCall(newImplementationAddress, '', true);
        assertEq(_testing.getImplementation(), newImplementationAddress);

        // revert if new implementation address is not a contract
        vm.expectRevert("ERC1967: new implementation is not a contract");
        _testing.upgradeToAndCall(address(1024), '', true);

        // case 5: with forceCall and data
        // NOTE: it will enter the fallback function of Implement with non-selector data
        newImplementationAddress = address(new Implement());
        data = 'unknown';
        vm.expectEmit(address(_testing));
        emit IERC1967.Upgraded(newImplementationAddress);
        emit IImplement.Fallback(data);

        _testing.upgradeToAndCall(newImplementationAddress, data, true);
        assertEq(_testing.getImplementation(), newImplementationAddress);

        // revert if new implementation address is not a contract
        vm.expectRevert("ERC1967: new implementation is not a contract");
        _testing.upgradeToAndCall(address(1024), data, true);
    }
}
2.3 _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal

将代理合约背后的逻辑合约地址升级更换为newImplementation并对其进行UUPS安全检查,再执行一个额外的到新逻辑合约的delegatecall。

注:这个delegatecall可以理解为是去调用一个适配合约升级的类似constructor的函数。

    // 用于存储rollback测试标志的slot号。
    // 如果当前的升级调用_upgradeToAndCallUUPS()处于rollback测试中,应先将该slot中设置为非0值。反之该slot中设置为0值。
    // 计算逻辑:keccak256("eip1967.proxy.rollback")-1
    bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;

    function _upgradeToAndCallUUPS(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        // 注:将UUPS proxy背后的逻辑合约从旧升到新时一般需要进行一个rollback测试,来确保逻辑合约的兼容性。
        // 该测试是将代理背后的新逻辑合约升级回旧逻辑合约,如果可以回滚成功就说明新逻辑合约是有效的UUPS逻辑合约。
        if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
            // 如果当前_upgradeToAndCallUUPS()的调用是处于UUPS的rollback测试中,
            // 那么将逻辑合约地址直接设置为newImplementation
            _setImplementation(newImplementation);
        } else {
            // 如果当前_upgradeToAndCallUUPS()的调用不是处于UUPS的rollback测试中,
            // 那么将调用newImplementation合约的proxiableUUID方法,并检查返回值。
            // 注:这是在检查新逻辑合约的有效兼容性
            try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                // 如果成功获得newImplementation合约的proxiableUUID方法的返回值,要求该返回值为_IMPLEMENTATION_SLOT
                // 否则认为newImplementation合约不兼容
                require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
            } catch {
                // 如果newImplementation合约中没有proxiableUUID方法,说明新逻辑合约不是UUPS逻辑合约,直接revert
                revert("ERC1967Upgrade: new implementation is not UUPS");
            }
            // 将proxy合约背后的逻辑合约地址升级更换为newImplementation,再执行一个额外的到新逻辑合约的delegatecall
            _upgradeToAndCall(newImplementation, data, forceCall);
        }
    }

foundry代码验证:

contract ERC1967UpgradeTest is Test, IERC1967, IImplement {
    MockERC1967Upgrade private _testing = new MockERC1967Upgrade();
    Implement private _implement = new Implement();

    function test_UpgradeToAndCallUUPS() external {
        assertEq(_testing.getImplementation(), address(0));
        address newImplementationAddress = address(_implement);

        // case 1: in rollback test
        // NOTE: only change implementation address no matter what the data and forceCall arguments are
        _testing.setRollbackSlot(true);
        _testing.upgradeToAndCallUUPS(newImplementationAddress, '', false);
        assertEq(_testing.getImplementation(), newImplementationAddress);

        newImplementationAddress = address(new Implement());
        _testing.upgradeToAndCallUUPS(newImplementationAddress, '1024', false);
        assertEq(_testing.getImplementation(), newImplementationAddress);

        newImplementationAddress = address(new Implement());
        _testing.upgradeToAndCallUUPS(newImplementationAddress, '', true);
        assertEq(_testing.getImplementation(), newImplementationAddress);

        newImplementationAddress = address(new Implement());
        _testing.upgradeToAndCallUUPS(newImplementationAddress, '1024', true);
        assertEq(_testing.getImplementation(), newImplementationAddress);

        // case 2: out of rollback test
        _testing.setRollbackSlot(false);

        // case 2.1: with supported proxiableUUID
        bytes32 proxiableUUID = bytes32(uint(keccak256("eip1967.proxy.implementation")) - 1);

        // case 2.1.1: no call
        address payable newImplementERC1822ProxiableAddress = payable(address(new ImplementERC1822Proxiable(proxiableUUID)));
        vm.expectEmit(address(_testing));
        emit IERC1967.Upgraded(newImplementERC1822ProxiableAddress);

        _testing.upgradeToAndCallUUPS(newImplementERC1822ProxiableAddress, '', false);
        assertEq(_testing.getImplementation(), newImplementERC1822ProxiableAddress);

        // case 2.1.2: call with no argument
        newImplementERC1822ProxiableAddress = payable(address(new ImplementERC1822Proxiable(proxiableUUID)));
        vm.expectEmit(address(_testing));
        emit IERC1967.Upgraded(newImplementERC1822ProxiableAddress);
        emit IImplement.InitialCallWithoutArgs();

        bytes memory data = abi.encodeCall(
            ImplementERC1822Proxiable(newImplementERC1822ProxiableAddress).initialCallWithoutArgs,
            ()
        );
        _testing.upgradeToAndCallUUPS(newImplementERC1822ProxiableAddress, data, false);
        assertEq(_testing.getImplementation(), newImplementERC1822ProxiableAddress);

        // case 2.1.3: call with arguments
        newImplementERC1822ProxiableAddress = payable(address(new ImplementERC1822Proxiable(proxiableUUID)));
        uint arg1 = 1024;
        address arg2 = address(1024);
        string memory arg3 = "1024";
        vm.expectEmit(address(_testing));
        emit IERC1967.Upgraded(newImplementERC1822ProxiableAddress);
        emit IImplement.InitialCallWithArgs(arg1, arg2, arg3);

        data = abi.encodeCall(
            ImplementERC1822Proxiable(newImplementERC1822ProxiableAddress).initialCallWithArgs,
            (arg1, arg2, arg3)
        );
        _testing.upgradeToAndCallUUPS(newImplementERC1822ProxiableAddress, data, false);
        assertEq(_testing.getImplementation(), newImplementERC1822ProxiableAddress);

        // case 2.1.4: with forceCall and no data
        // NOTE: force call to the receive function of Implement
        newImplementERC1822ProxiableAddress = payable(address(new ImplementERC1822Proxiable(proxiableUUID)));
        vm.expectEmit(address(_testing));
        emit IERC1967.Upgraded(newImplementERC1822ProxiableAddress);
        emit IImplement.Receive();

        _testing.upgradeToAndCallUUPS(newImplementERC1822ProxiableAddress, '', true);
        assertEq(_testing.getImplementation(), newImplementERC1822ProxiableAddress);

        // case 2.1.5: with forceCall and data
        // NOTE: it will enter the fallback function of Implement with non-selector data
        newImplementERC1822ProxiableAddress = payable(address(new ImplementERC1822Proxiable(proxiableUUID)));
        data = 'unknown';
        vm.expectEmit(address(_testing));
        emit IERC1967.Upgraded(newImplementERC1822ProxiableAddress);
        emit IImplement.Fallback(data);

        _testing.upgradeToAndCallUUPS(newImplementERC1822ProxiableAddress, data, true);
        assertEq(_testing.getImplementation(), newImplementERC1822ProxiableAddress);

        // case 2.2: revert with unsupported proxiableUUID
        proxiableUUID = bytes32(uint(keccak256("eip1967.proxy.implementation")) - 2);
        newImplementERC1822ProxiableAddress = payable(address(new ImplementERC1822Proxiable(proxiableUUID)));

        vm.expectRevert("ERC1967Upgrade: unsupported proxiableUUID");
        _testing.upgradeToAndCallUUPS(newImplementERC1822ProxiableAddress, '', false);

        // case 2.3: revert if the new implementation was a non-ERC1822 compliant
        proxiableUUID = bytes32(uint(keccak256("eip1967.proxy.implementation")) - 1);
        newImplementERC1822ProxiableAddress = payable(address(new Implement()));
        vm.expectRevert("ERC1967Upgrade: new implementation is not UUPS");
        _testing.upgradeToAndCallUUPS(newImplementERC1822ProxiableAddress, '', false);

        // case 2.4: revert without msg if the new implementation address is not a contract
        vm.expectRevert();
        _testing.upgradeToAndCallUUPS(address(1024), '', false);
    }
}
2.4 _getAdmin() internal && _changeAdmin(address newAdmin) internal
  • _getAdmin() internal:返回当前admin地址;
  • _changeAdmin(address newAdmin) internal:更换admin地址为newAdmin。
    // 用于存储admin地址的slot号
    // 计算逻辑:keccak256("eip1967.proxy.admin")-1
    bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    function _getAdmin() internal view returns (address) {
        // 读取编号为_ADMIN_SLOT的slot中的值,并转为address类型
        // 注:StorageSlot.getAddressSlot()详解参见:https://learnblockchain.cn/article/6104
        return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
    }

    function _changeAdmin(address newAdmin) internal {
        // 按照ERC1967标准,在更换admin地址后抛出事件`AdminChanged(address,address)`
        emit AdminChanged(_getAdmin(), newAdmin);
        // 设置admin地址为newAdmin
        _setAdmin(newAdmin);
    }
    
    // 设置admin地址为newAdmin
    function _setAdmin(address newAdmin) private {
        // 要求newAdmin不为0地址
        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
        // 将newAdmin写入编号为_ADMIN_SLOT的slot中
        StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
    }

foundry代码验证:

contract ERC1967UpgradeTest is Test, IERC1967 {
    MockERC1967Upgrade private _testing = new MockERC1967Upgrade();

    function test_GetAdminAndChangeAdmin() external {
        address currentAdmin = address(0);
        assertEq(_testing.getAdmin(), currentAdmin);
        address newAdminAddress = address(1024);
        vm.expectEmit(address(_testing));
        emit IERC1967.AdminChanged(currentAdmin, newAdminAddress);

        _testing.changeAdmin(newAdminAddress);
        assertEq(_testing.getAdmin(), newAdminAddress);
    }
}
2.5 _getBeacon() internal && _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal
  • _getBeacon() internal:返回当前存储的信标合约地址;
  • _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal:将代理合约背后的信标合约地址升级更换为newBeacon,再执行一个额外的到新信标合约背后的逻辑合约的delegatecall。注:这个delegatecall可以理解为是去调用一个适配合约升级的类似constructor的函数。
    // 用于存储信标合约地址的slot号
    // 计算逻辑:keccak256("eip1967.proxy.beacon")-1
    bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    function _getBeacon() internal view returns (address) {
        // 读取编号为_BEACON_SLOT的slot中的值,并转为address类型
        // 注:StorageSlot.getAddressSlot()详解参见:https://learnblockchain.cn/article/6104
        return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
    }

    function _upgradeBeaconToAndCall(
        address newBeacon,
        bytes memory data,
        bool forceCall
    ) internal {
        // 设置信标合约地址为newBeacon
        _setBeacon(newBeacon);
        // 按照ERC1967标准,在升级信标合约地址后抛出事件`BeaconUpgraded(address indexed)`
        emit BeaconUpgraded(newBeacon);
        if (data.length > 0 || forceCall) {
            // 如果data不为空或forceCall为true,将delegatecall到新信标合约背后的逻辑合约(以data作为calldata)
            // 注:Address.functionDelegateCall()详解参见:https://learnblockchain.cn/article/6098
            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
        }
    }
    
    // 设置信标合约地址为newBeacon
    function _setBeacon(address newBeacon) private {
        // 要求newBeacon是一个合约地址
        // 注:Address.isContract()详解参见:https://learnblockchain.cn/article/6098
        require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
        // 要求newBeacon合约中存储的逻辑合约地址是一个合约地址
        require(
            Address.isContract(IBeacon(newBeacon).implementation()),
            "ERC1967: beacon implementation is not a contract"
        );
        // 将newBeacon写入编号为_BEACON_SLOT的slot中
        StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
    }

foundry代码验证:

contract ERC1967UpgradeTest is Test, IERC1967, IImplement {
    MockERC1967Upgrade private _testing = new MockERC1967Upgrade();
    Implement private _implement = new Implement();

    function test_GetBeaconAndUpgradeBeaconToAndCall() external {
        assertEq(_testing.getBeacon(), address(0));

        // case 1: no call
        address newBeaconAddress = address(new Beacon(address(_implement)));
        vm.expectEmit(address(_testing));
        emit IERC1967.BeaconUpgraded(newBeaconAddress);

        _testing.upgradeBeaconToAndCall(newBeaconAddress, '', false);
        assertEq(_testing.getBeacon(), newBeaconAddress);

        // case 2: call with no argument
        newBeaconAddress = address(new Beacon(address(_implement)));
        vm.expectEmit(address(_testing));
        emit IERC1967.BeaconUpgraded(newBeaconAddress);
        emit IImplement.InitialCallWithoutArgs();

        bytes memory data = abi.encodeCall(
            _implement.initialCallWithoutArgs,
            ()
        );
        _testing.upgradeBeaconToAndCall(newBeaconAddress, data, false);
        assertEq(_testing.getBeacon(), newBeaconAddress);

        // case 3: call with arguments
        newBeaconAddress = address(new Beacon(address(_implement)));
        uint arg1 = 1024;
        address arg2 = address(1024);
        string memory arg3 = "1024";
        vm.expectEmit(address(_testing));
        emit IERC1967.BeaconUpgraded(newBeaconAddress);
        emit IImplement.InitialCallWithArgs(arg1, arg2, arg3);

        data = abi.encodeCall(
            _implement.initialCallWithArgs,
            (arg1, arg2, arg3)
        );
        _testing.upgradeBeaconToAndCall(newBeaconAddress, data, false);
        assertEq(_testing.getBeacon(), newBeaconAddress);

        // case 4: with forceCall and no data
        // NOTE: force call to the receive function of Implement
        newBeaconAddress = address(new Beacon(address(_implement)));
        vm.expectEmit(address(_testing));
        emit IERC1967.BeaconUpgraded(newBeaconAddress);
        emit IImplement.Receive();

        _testing.upgradeBeaconToAndCall(newBeaconAddress, '', true);
        assertEq(_testing.getBeacon(), newBeaconAddress);

        // case 5: with forceCall and data
        // NOTE: it will enter the fallback function of Implement with non-selector data
        newBeaconAddress = address(new Beacon(address(_implement)));
        data = 'unknown';
        vm.expectEmit(address(_testing));
        emit IERC1967.BeaconUpgraded(newBeaconAddress);
        emit IImplement.Fallback(data);

        _testing.upgradeBeaconToAndCall(newBeaconAddress, data, true);
        assertEq(_testing.getBeacon(), newBeaconAddress);

        // revert if new beacon address is not a contract
        vm.expectRevert("ERC1967: new beacon is not a contract");
        _testing.upgradeBeaconToAndCall(address(1024), '', false);

        // revert if the implementation address in the new beacon is not a contract
        newBeaconAddress = address(new Beacon(address(1024)));
        vm.expectRevert("ERC1967: beacon implementation is not a contract");
        _testing.upgradeBeaconToAndCall(newBeaconAddress, '', false);
    }
}

ps:
本人热爱图灵,热爱中本聪,热爱V神。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!

在这里插入图片描述

公众号名称:后现代泼痞浪漫主义奠基人

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/766089.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

常见反爬及应对

一,特殊混淆的还原 1.1 还原 AAEncode 与 JJEncode AAEncode是一种JavaScript代码混淆算法,利用它,可以将代码转换成 颜文字 表示的JavaScript代码。 去掉代码最后的 (‘‘),这是函数的自调用,去除后就是函数的声明…

【计算机网络仿真】b站湖科大教书匠思科Packet Tracer——实验13 静态路由配置错误导致的路由环路问题

一、实验目的 1.验证静态路由配置错误导致的路由环路问题; 二、实验要求 1.使用Cisco Packet Tracer仿真平台; 2.观看B站湖科大教书匠仿真实验视频,完成对应实验。 三、实验内容 1.构建网络拓扑; 2.验证路由环路。 四、实验…

探囊取物之多形式登录页面(基于BootStrap4)

基于BootStrap4的登录页面,支持手机验证码登录、账号密码登录、二维码登录、其它统一登录 低配置云服务器,首次加载速度较慢,请耐心等候;演练页面可点击查看源码 预览页面:http://www.daelui.com/#/tigerlair/saas/pr…

【AI提升】如何使用大模型:本机离线和FastAPI服务调用

大模型本身提供的功能,类似于windows中的一个exe小工具,我们可以本机离线调用然后完成具体的功能,但是别的机器需要访问这个exe是不可行的。常见的做法就是用web容器封装起来,提供一个http接口,然后接口在后端调用这个…

electron教程(二)控制应用程序的事件生命周期

1.will-finish-launching 当应用程序完成基础的启动的时候被触发,在 Windows 和 Linux 中, will-finish-launching 事件与 ready 事件是相同的; 在 macOS 中,这个事件相当于 NSApplication 中的 applicationWillFinishLaunching 提示。 app.on(will-fi…

Andrej Karpathy提出未来计算机2.0构想: 完全由神经网络驱动!网友炸锅了

昨天凌晨,知名人工智能专家、OpenAI的联合创始人Andrej Karpathy提出了一个革命性的未来计算机的构想:完全由神经网络驱动的计算机,不再依赖传统的软件代码。 嗯,这是什么意思?全部原生LLM硬件设备的意思吗&#xff1f…

机械设备制造企业MES系统解决方案介绍

机械设备制造行业涵盖了各类工业设备、工程机械、农业机械等多个领域,对生产精度、质量控制和效率提出了较高要求。为了提升生产效率、保证产品质量并满足客户需求,越来越多的机械设备制造企业引入了MES系统。本文将详细介绍MES系统在机械设备制造行业的…

魔镜魔镜,我要变得更漂亮!按需搭配一键叠穿,效果拿下新SOTA!中山大学字节智创数字人团队提出虚拟试穿新框架

魔镜魔镜,我要变得更漂亮!按需搭配一键叠穿,效果拿下新SOTA!中山大学&字节智创数字人团队提出虚拟试穿新框架。 多件衣服按指定穿法一键虚拟试穿! 中山大学&字节智创数字人团队提出了一个名为MMTryon的虚拟试穿框架,可以通过输入多个服装图像及指定穿法的文本指…

COB封装的LED显示屏是什么?

COB(Chip on Board)封装的LED显示屏,是一种采用先进倒装COB封装技术的显示屏,其中LED芯片是直接被安装并封装在PCB电路板上,而不是先对单个封装再焊接至电路板,与SMD(Surface Mount Device&…

怎么快速给他人分享图片?扫描二维码看图的简单做法

现在通过二维码来查看图片是一种很常见的方法,通过二维码来查看图片不仅能够减少对手机存储空间的占用,而且获取图片变得更加方便快捷,只需要扫码就能够查看图片,有利于图片的展现。很多的场景中都有图片二维码的应用,…

2024软件设计师经验贴(一考就过)

2024软件设计师经验贴(一考就过) 第一阶段、基础积累:把书读厚 这一阶段可以跟着视频、书籍或文章进行基础知识的学习。 推荐的视频系列: 「软件设计师」 上午题 #1 计算机系统_哔哩哔哩_bilibili 40–14.3设计模式 推荐的文…

下载和使用SLUN数据集

1. 下载数据集 网址在https://opendatalab.com/OpenDataLab/lsun/tree/main/raw/scenes 下载bedroom_val_lmdb.zip 然后解压后会又两个文件,一个data.mdb,另一个lock.mdb。 2. 使用torchvison使用LSUN数据集 我把解压后的bedroom_val_lmdb放在/home/…

3.js - 深度测试、深度写入、深度函数

md,艹,这玩意得理解,只看代码不管事 效果图 代码 // ts-nocheck// 引入three.js import * as THREE from three// 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls// 导入lil.gui import { GUI } …

山东访老友 | 同成建材数字化转型两年前后的巨大变化

山东省济宁市同成建材有限公司(简称“同成建材”)成立于2013年,目前共建有混凝土生产线2条、砂浆生产线1条,是济宁市最早建成的预拌混凝土搅拌站之一。 2020年,同成建材产量增长,市场环境变化,…

Redis的使用(二)redis的命令总结

1.概述 这一小节,我们主要来研究一下redis的五大类型的基本使用,数据类型如下: redis我们接下来看一看这八种类型的基本使用。我们可以在redis的官网查询这些命令:Commands | Docs,同时我们也可以用help 数据类型查看命令的帮助文档。 2. 常…

【漏洞复现】D-Link NAS 未授权RCE漏洞(CVE-2024-3273)

0x01 产品简介 D-Link 网络存储 (NAS)是中国友讯(D-link)公司的一款统一服务路由器。 0x02 漏洞概述 D-Link NAS nas_sharing.cgi接口存在命令执行漏洞,该漏洞存在于“/cgi-bin/nas_sharing.cgi”脚本中,影响其 HTTP GET 请求处…

STM32F1+HAL库+FreeTOTS学习3——任务创建(动态和静态两种)

STM32F1HAL库FreeTOTS学习3——任务创建(动态和静态两种) 任务创建API函数任务创建流程代码实现1. 动态任务创建和删除2. 静态任务创建和删除 上期我们学习了STM32移植FreeRTOS搭建基准工程,现在我们来学习任务创建 任务创建API函数 前面我们…

大数据可视化实验(八):大数据可视化综合实训

目录 一、实验目的... 1 二、实验环境... 1 三、实验内容... 1 1)Python纵向柱状图实训... 1 2)Python水平柱状图实训... 3 3)Python多数据并列柱状图实训.. 3 4)Python折线图实训... 4 5)Python直方图实训...…

Redis---保证主从节点一致性问题 +与数据库数据保持一致性问题

保证主从节点一致性问题 Redis的同步方式默认是异步的,这种异步的同步方式导致了主从之间的数据存在一定的延迟,因此Redis默认是弱一致性的。 解决: 1.使用Redisson这样的工具,它提供了分布式锁的实现,确保在分布式环…

搭贝这个低代码开发平台靠谱吗?

在应用开发领域,低代码开发平台因其拖拽式的操作给用户带来了极大的便利和灵活性。根据相关调查数据,2022年国内低代码开发平台已超过100家。搭贝在众多低代码平台中也享有一定的知名度。那么,搭贝究竟怎么样,是否值得信赖&#x…