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,一经查实,立即删除!

相关文章

详解大模型是如何理解并使用 tools ?

前文 大家肯定对使用大模型的函数回调或者说 Tools 已经耳熟能详了,那么他们具体内部是如何运作的呢,本文就此事会详细给大家介绍具体的细节。 tools 首先是大家最熟悉的环节,定义两个 tool 的具体实现,其实就是两个函数&#…

校园兼职小程序的设计

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

Docker 一篇到位

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

730天的创作之旅:我的技术写作之路

机缘 两年前的今天,我作为一名测试工程师,带着对技术的热爱和对分享的渴望,开启了我的创作者之旅。最初,我希望通过我的文章,将我在实战项目中的经验、日常学习过程中的点滴记录下来,并通过这些文字与更多…

EtherCAT主站IGH-- 5 -- IGH之debug.h/c文件解析

EtherCAT主站IGH-- 5 -- IGH之debug.h/c文件解析 0 预览一 该文件功能`debug.c` 文件功能函数预览二 函数功能介绍`debug.c` 中主要函数的作用1. `ec_debug_init`2. `ec_debug_clear`3. `ec_debug_register`4. `ec_debug_unregister`5. `ec_debug_send`6. `ec_dbgdev_open`7. `…

【408考点之数据结构】二叉树的概念与实现

二叉树的概念与实现 一、二叉树的概念 二叉树是一种特殊的树结构,其中每个节点最多有两个子节点,分别称为左子节点和右子节点。二叉树广泛应用于许多计算机科学领域,如表达式解析、排序、搜索算法等。 二、二叉树的性质 性质1&#xff1a…

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

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

高级队列实现代理穿透及迁移

一、参数调优 1.1、登录穿透专用数据库 su - oracle 1.2、通过sqlplus登录 sqlplus create pfile =/home/oracle/pfile11.ora from spfile; alter system set processes=8000 scope=spfile; alter system set sessions=10400 scope=spfile; alter system set result_ca…

rocketmq实现多数据源配置

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

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

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

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

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

Python基础总结之什么是迭代?迭代的概念介绍

Python基础总结之什么是迭代?迭代的概念介绍 在Python中,迭代(Iteration)是一种基本的编程概念,用于逐个访问集合(如列表、元组、字典、集合等)中的每个元素。迭代是Python编程中不可或缺的一部…

高考后如何进入IT领域:详细学习路线图与实战经验分享

高考终于告一段落,是不是感觉整个人都轻松了许多?先恭喜你,顺利迈过了这一重要的阶段!但别忘了,高考的结束只是一个新的开始,特别是对于那些有志于进入IT领域的同学们。这个暑假,是你们开启IT世…

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绘图模型定…

实现抽象方法、实现接口 题目

题目 JAVA25 实现抽象方法分析:代码: JAVA26 实现接口分析:代码: JAVA25 实现抽象方法 描述 已知抽象类Base中定义了calculate方法,该方法的计算过程依赖于sum()和avg(),而后两个方法均为抽象方法。要求定义…

python处理txt文件, 如果第一列和第二列的值在连续的行中重复,则只保留一行

处理txt文件, 如果第一列和第二列的值在连续的行中重复,则只保留一个实例,使用Python的内置函数来读取文件,并逐行检查和处理数据。 一个txt文件,里面的数据是893.554382324,-119.955825806,0.0299997832626,-0.133618548512,28.1155740884,112.876833236,46.7922,19.62582…

Python应对反爬虫的策略

Python应对反爬虫的策略 概述User-Agent 伪造应对302重定向IP限制与代理使用Cookies和Session管理动态内容加载数据加密与混淆请求频率限制爬虫检测算法法律与道德考量结语 概述 在数字化时代,网络数据采集已成为获取信息的重要手段之一。然而,随着技术…