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

news2024/12/26 23:14:48

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.coloradmin.cn/o/1887463.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

11--ElasticStack7-ELK+Kafka

前言:日志分析管理平台对于平时的规模化运维占的权重非常大,这一章涉及的程序较多,会将每个程序的基础使用和模块分开梳理,基础概念会分布在每小节开头,最后串联成一个完整的工作环境。 1、ELK架构 ELK 是一个非常流…

基于机器学习的永磁同步电机矢量控制策略-高分资源-下载可用!

基于机器学习的永磁同步电机矢量控制策略 优势 训练了RL-Agent,能够提高电机在非线性负载下的性能。 部分程序 仿真结果 转矩估计及dq轴电流。 代码有偿,50,需要的可以联系。

【STM32HAL库学习】通信方式:USART、IIC、SPI

通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统 通信接口区别 名称引脚双工时钟电平设备USARTTX、RX全双工异步单端点对点I2CSCL、SDA半双工同步单端多设备SPISCLK、MOSI、MISO、CS全双工同步单端多设备CANCAN_H、CAN_L半双工异步差分多设…

【antd + vue】表格行合并,同时使用插槽

一、需求说明 表格中,如果一个学校有多个考试科目,则分行展示,其余列,则合并为一行展示,如图所示 二、需求分析 1、表格行合并 相当于有4行,其中1、2行是同一个学校包含不同考试科目及对应人次的数据&am…

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

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

Java知识点整理 18 — Lambda表达式

一. 简介 Lambda 表达式是函数式编程思想的体现,强调做什么,而不是以什么方式去做。 面向对象编程思想强调的是对象,必须通过对象的形式来做一些事情。比如多线程执行任务,需要创建对象,对象需要实现指定接口&#x…

【吴恩达机器学习-week2】可选实验:使用 Scikit-Learn 进行线性回归

支持我的工作 🎉 📃亲爱的朋友们,感谢你们一直以来对我的关注和支持! 💪🏻 为了提供更优质的内容和更有趣的创作,我付出了大量的时间和精力。如果你觉得我的内容对你有帮助或带来了欢乐&#xf…

看完这篇文章你就知道什么是未来软件开发的方向了!即生成式AI在软件开发领域的革新=CodeFlying

从最早的UGC(用户生成内容)到PGC(专业生成内容)再到AIGC(人工智能生成内容)体现了web1.0→web2.0→web3.0的发展历程。 毫无疑问UGC已经成为了当前拥有群体数量最大的内容生产方式。 同时随着人工智能技术…

SAP 表字段调整,表维护生成器调整

表维护生成器->已生成的对象->更改->专家模式

Linux下的wifi开发

了解什么是wifi 可参考: 什么是Wi-Fi?Wi-Fi和WLAN的区别是什么? - 华为 (huawei.com) WLAN的基本元素 工作站STA(Station):支持802.11标准的终端设备。例如带无线网卡的电脑、支持WLAN的手机等。 接入点AP&…

OpenSSH RCE (CVE-2024-6387) | 附poc | 小试

Ⅰ 漏洞描述 OpenSSH 远程代码执行漏洞(CVE-2024-6387)&#xff0c;该漏洞是由于OpenSSH服务器 (sshd) 中的信号处理程序竞争问题&#xff0c;未经身份验证的攻击者可以利用此漏洞在Linux系统上以root身份执行任意代码。 Ⅱ 影响范围 8.5p1 < OpenSSH < 9.8p1 但OpenSS…

数学建模--层次分析法~~深入解读

目录 1.基本概念 &#xff08;1&#xff09;研究案例 &#xff08;2&#xff09;模型框架 &#xff08;3&#xff09;阐述说明 &#xff08;4&#xff09;注意事项 2.模型的建立和求解 &#xff08;1&#xff09;数量级的统一 &#xff08;2&#xff09;归一化处理 &am…

用Vue3和Rough.js绘制一个粗糙的3D条形图

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 使用 Rough.js 和 D3.js 绘制粗糙手写风格条形图 应用场景 该代码适用于需要在 Web 应用程序中创建具有粗糙手写风格的条形图的情况。它可以用于数据可视化、信息图表或任何需要以独特和有吸引力的方式呈现数…

Java StringBuffer类和StringBuilder类

在使用 StringBuffer 类时&#xff0c;每次都会对 StringBuffer 对象本身进行操作&#xff0c;而不是生成新的对象&#xff0c;所以如果需要对字符串进行修改推荐使用 StringBuffer。 StringBuilder 类在 Java 5 中被提出&#xff0c;它和 StringBuffer 之间的最大不同在于 St…

【PYG】Cora数据集分类任务计算损失,cross_entropy为什么不能直接替换成mse_loss

cross_entropy计算误差方式&#xff0c;输入向量z为[1,2,3]&#xff0c;预测y为[1]&#xff0c;选择数为2&#xff0c;计算出一大坨e的式子为3.405&#xff0c;再用-23.405计算得到1.405MSE计算误差方式&#xff0c;输入z为[1,2,3]&#xff0c;预测向量应该是[1,0,0]&#xff0…

IAR工程目录移动报错(改变文件目录结构)

刚开始用IAR&#xff0c;记录一下。 工作中使用华大单片机&#xff0c;例程的文件目录结构太复杂了想精简一点。 1.如果原本的C文件相对工程文件&#xff08;.eww文件&#xff09;路径变化了&#xff0c;需要先打开工程&#xff0c;再将所有的.c文件右键Add添加进工程&#xf…

【Godot4.2】Godot中的贝塞尔曲线

概述 通过指定平面上的多个点&#xff0c;然后顺次连接&#xff0c;我们可以得到折线段&#xff0c;如果闭合图形&#xff0c;就可以获得多边形。通过向量旋转我们可以获得圆等特殊图形。 但是对于任意曲线&#xff0c;我们无法使用简单的方式来获取其顶点&#xff0c;好在计…

X-ObjectMount: 对象存储访问接入的新选择

XEOS 自 2017 年发布面世以来&#xff0c;历经 7 年的研发迭代&#xff0c;上个月正式发布了 XSKY SDS 6.4 版本&#xff0c;包含了最新的多站点统一命名空间能力&#xff0c;也标志了 XEOS 在对象存储领域的全方面优势和领先市场地位。 在 XSKY 过去对象存储服务历程里&#…

mysql 命令 —— 查看表信息(show table status)

查询表信息&#xff0c;如整个表的数据量大小、表的索引占用空间大小等 1、查询某个库下面的所有表信息&#xff1a; SHOW TABLE STATUS FROM your_database_name;2、查询指定的表信息&#xff1a; SHOW TABLE STATUS LIKE your_table_name;如&#xff1a;Data_length 显示表…

openGauss真的比PostgreSQL差了10年?

前不久写了MogDB针对PostgreSQL的兼容性文章&#xff0c;我在文中提到针对PostgreSQL而言&#xff0c;MogDB兼容性还是不错的&#xff0c;其中也给出了其中一个能源客户之前POC的迁移报告数据。 But很快我发现总有人回留言喷我&#xff0c;而且我发现每次喷的这帮人是根本不看文…