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")-1bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;function _getImplementation() internal view returns (address) {// 读取编号为_IMPLEMENTATION_SLOT的slot中的值,并转为address类型// 注:StorageSlot.getAddressSlot()详解参见:https://learnblockchain.cn/article/6104return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;}// 设置逻辑合约地址为newImplementationfunction _setImplementation(address newImplementation) private {// 检验新的逻辑合约地址是合约地址// 注:Address.isContract()详解参见:https://learnblockchain.cn/article/6098require(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 contractvm.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/6098Address.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 callvm.expectEmit(address(_testing));emit IERC1967.Upgraded(newImplementationAddress);_testing.upgradeToAndCall(newImplementationAddress, '', false);assertEq(_testing.getImplementation(), newImplementationAddress);// revert if new implementation address is not a contractvm.expectRevert("ERC1967: new implementation is not a contract");_testing.upgradeToAndCall(address(1024), '', false);// case 2: call with no argumentnewImplementationAddress = 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 contractvm.expectRevert("ERC1967: new implementation is not a contract");_testing.upgradeToAndCall(address(1024), data, false);// case 3: call with argumentsnewImplementationAddress = 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 contractvm.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 ImplementnewImplementationAddress = 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 contractvm.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 datanewImplementationAddress = 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 contractvm.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")-1bytes32 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逻辑合约,直接revertrevert("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 proxiableUUIDbytes32 proxiableUUID = bytes32(uint(keccak256("eip1967.proxy.implementation")) - 1);// case 2.1.1: no calladdress 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 argumentnewImplementERC1822ProxiableAddress = 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 argumentsnewImplementERC1822ProxiableAddress = 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 ImplementnewImplementERC1822ProxiableAddress = 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 datanewImplementERC1822ProxiableAddress = 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 proxiableUUIDproxiableUUID = 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 compliantproxiableUUID = 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 contractvm.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")-1bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;function _getAdmin() internal view returns (address) {// 读取编号为_ADMIN_SLOT的slot中的值,并转为address类型// 注:StorageSlot.getAddressSlot()详解参见:https://learnblockchain.cn/article/6104return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;}function _changeAdmin(address newAdmin) internal {// 按照ERC1967标准,在更换admin地址后抛出事件`AdminChanged(address,address)`emit AdminChanged(_getAdmin(), newAdmin);// 设置admin地址为newAdmin_setAdmin(newAdmin);}// 设置admin地址为newAdminfunction _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")-1bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;function _getBeacon() internal view returns (address) {// 读取编号为_BEACON_SLOT的slot中的值,并转为address类型// 注:StorageSlot.getAddressSlot()详解参见:https://learnblockchain.cn/article/6104return 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/6098Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);}}// 设置信标合约地址为newBeaconfunction _setBeacon(address newBeacon) private {// 要求newBeacon是一个合约地址// 注:Address.isContract()详解参见:https://learnblockchain.cn/article/6098require(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 calladdress 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 argumentnewBeaconAddress = 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 argumentsnewBeaconAddress = 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 ImplementnewBeaconAddress = 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 datanewBeaconAddress = 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 contractvm.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 contractnewBeaconAddress = address(new Beacon(address(1024)));vm.expectRevert("ERC1967: beacon implementation is not a contract");_testing.upgradeBeaconToAndCall(newBeaconAddress, '', false);}
}

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

在这里插入图片描述

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

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

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

相关文章

校园兼职小程序的设计

管理员账户功能包括:系统首页,个人中心,商家管理,管理员管理,用户管理,兼职管理,论坛管理,公告管理 微信端账号功能包括:系统首页,公告,兼职&…

Docker 一篇到位

目录 01. Docker使用导航 02. Build Share Run 样例 03. 理解容器 04. 安装 Docker 05. Docker 样例(常见命令使用) 下载镜像 启动容器 修改页面 保存镜像 docker commit docker save docker load 分享社区 docker login docker tag do…

数字化转型过程中企业会遇到哪些挑战?该如何应对?

你是否与我一样,也曾有过类似的疑惑: 企业数字化转型过程中会遇到哪些挑战?其中苦难,我们又该如何应对?有什么可借鉴的方法? 有了这些疑问,你肯定想知道答案。 为了解决你的心头之患&#xf…

rocketmq实现多数据源配置

rocketmq实现多数据源配置 背景:一 添加ExtRocketMQTemplateConfiguration配置类二 添加非标mq的配置参数三 非标准RocketMQTemplate 背景: 在实际项目中我们可能会遇到在springboot项目中使用多个mq数据源,那我们该如何配置呢? …

基于若依(ruoyi-vue)的周报管理系统

喂wangyinlon 填报人页面 审批人 审批不通过,填报人需要重新填写.

自动编码器简单理解及简单使用描述

1. 什么是自动编码器? 自动编码器分为编码器和解码器,其中解码器只在训练阶段用到。具体过程就是: 首先,输入训练样本,编码器对输入样本进行编码,对其进行降维,直到到达某个瓶颈层&#xff1b…

2024年江西省研究生数学建模竞赛B题投标中的竞争策略问题论文和代码分析

2024年江西省研究生数学建模竞赛B题投标中的竞争策略问题论文和代码已完成,代码为B题全部问题的代码,论文包括摘要、问题重述、问题分析、模型假设、符号说明、模型的建立和求解(问题1模型的建立和求解、问题2模型的建立和求解、问题3模型的建…

oracle体系结构详解(实例+数据文件)

提示:主要总结oracle数据库:物理结构,逻辑结构,内存结构以及oracle进程 文章目录 Oracle服务器由(实例和数据库文件组成)1、实例2、数据文件1.oracle物理体系结构2.oracle数据库逻辑结构3oracle数据库内存结…

如何通过指纹浏览器使用代理IP?

1.指纹浏览器定义 指纹浏览器是 一种浏览器技术,它根据用户设备的硬件、软件和配置等特征生成唯一标识符(称为“指纹”)。此指纹用于识别和追踪用户身份,即使用户更改其 IP 地址或清除浏览器数据(如缓存和 Cookie&…

AI生成商品图软件哪个好用

🌟 AI生成商品图软件哪个好用 —— 触站AI🚀 🎨在AI技术的浪潮中,触站AI以其专业和创新,成为广州AI设计服务的领军企业。 专注于企业AI图像领域的技术解决方案,触站AI提供包括AI绘画模型训练、AI绘图模型定…

HBM是什么?因AI而崛起,它有哪些优势?

南韩存储大厂SK海力士宣布,将与台积电公司密切合作,联手生产下一代HBM——即预计在2026年投产的第六代HBM产品HBM4,双方并于近期签署合作备忘录(MOU)。 SK海力士以往的HBM产品,包括HBM3E(第五代…

python本学期所有代码!

第一单元 ----------------------------------------------------------------------- #圆面积的计算 radius 25 area 3.1415 * radius * radius print(area) print("{:.2f}".format(area)) --------------------------------------------------------------------…

昇思25天学习打卡营第1天|yulang

今天主要了解了深度学习框架之昇思MindSpore的初学入门,没想到 ai学习入门如此简单,不愧是华为大手笔,提供的学习环境配置如此之高。这个平台有点类似百度飞桨,大大降低了AI开发门槛,使用户能够快速实现想要的模型&…

Hadoop3:Yarn的Tool接口案例

一、需求 依然以wordcount案例为基础,进行开发 我们知道,用hadoop自带的example.jar执行wordcount 命令如下 hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount -D mapreduce.job.queuename…

如何选择品牌推广公司?哪家好?收费标准及评价!

不管是什么品牌,推广对公司的成败起了很关键的作用。然而,面对市面上琳琅满目的品牌推广公司,如何选择一家既熟悉又靠谱的公司,成为许多企业主面临的难题。 作为一家手工酸奶品牌的创始人,目前全国也复制了100多家门店…

深度探索“目录名称无效“:原因、解决方案与最佳实践

目录名称无效:现象背后的秘密 在日常使用电脑或移动设备时,我们时常会遇到“目录名称无效”的错误提示,这一提示仿佛是一道无形的屏障,阻断了我们与重要数据的联系。从本质上讲,“目录名称无效”意味着系统无法识别或…

Windows下快速安装Open3D-0.18.0(python版本)详细教程

目录 一、Open3D简介 1.1主要用途 1.2应用领域 二、安装Open3D 2.1 激活环境 2.2 安装open3d 2.3测试安装是否成功 三、测试代码 3.1 代码 3.2 显示效果 一、Open3D简介 Open3D 是一个强大的开源库,专门用于处理和可视化3D数据,如点云、网格和…

Android原生与flutter模块交互

Flutter定义了三种不同类型的Channel: BasicMessageChannel:用于传递字符串和半结构化的信息,持续通信,收到消息后可以回复此次消息,如:Native将遍历到的文件信息陆续传递到Dart,在比如&#xf…

无法下载 https://mirrors./ubuntu/dists/bionic/main/binary-arm64/Packages

ubuntu系统执行sudo apt update命令的时候,遇到如下问题: 忽略:82 https://mirrors.tuna.tsinghua.edu.cn/ubuntu bionic-backports/universe arm64 Packages 错误:81 https://mirrors.tuna.tsinghua.edu.cn/ubuntu bionic-backports/main arm64 Packa…

springboot数字化医院产科系统源码

目录 一、系统概述 二、开发环境 三、功能设计 四、功能介绍 一、系统概述 数字化产科是为医院产科量身定制的信息管理系统。它管理了孕妇从怀孕开始到生产结束42天一系列医院保健服务信息。该系统由门诊系统、住院系统、数据统计模块三部分组成,与医院HIS、LI…