UpgradeableBeacon库是信标代理模式中的信标合约的实现,与一个或多个BeaconProxy
库实例配合使用。所有到BeaconProxy
的调用都会被委托到本库指向的逻辑合约上。本库的owner具有更换逻辑合约地址的权限,从而实现信标代理合约的升级功能。
[openzeppelin]:v4.8.3,[forge-std]:v1.5.6
UpgradeableBeacon库是信标代理模式中的信标合约的实现,与一个或多个BeaconProxy
库实例配合使用。所有到BeaconProxy
的调用都会被委托到本库指向的逻辑合约上。本库的owner具有更换逻辑合约地址的权限,从而实现信标代理合约的升级功能。
注:BeaconProxy库详解参见:https://learnblockchain.cn/article/8810
UpgradeableBeacon合约可直接部署。
全部foundry测试合约:
测试使用的物料合约:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
interface IImplementation {
event ChangeStorageUint(uint);
}
contract Implementation is IImplementation {
// storage
uint public i;
function __Implementation_init(uint i_) external {
i = i_;
emit ChangeStorageUint(i_);
}
}
contract ImplementationNew is Implementation {
// add a function
function addI(uint i_) external {
i += i_;
emit ChangeStorageUint(i_);
}
}
设置了逻辑合约地址,并且只有UpgradeableBeacon的部署者才可升级该信标合约。
// 逻辑合约地址
address private _implementation;
// 当信标合约指向的逻辑合约地址变化时被抛出。该事件符合ERC1967标准
// 注:ERC1967标准详解参见:https://learnblockchain.cn/article/8581
event Upgraded(address indexed implementation);
constructor(address implementation_) {
// 设置逻辑合约地址为implementation_
_setImplementation(implementation_);
}
// 为信标合约设置逻辑合约地址为newImplementation
function _setImplementation(address newImplementation) private {
// 要求newImplementation地址为合约地址,否则revert
require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract");
// 将逻辑合约地址设置为newImplementation
_implementation = newImplementation;
}
foundry代码验证:
contract UpgradeableBeaconTest is Test {
Implementation private _implementation = new Implementation();
UpgradeableBeacon private _testing = new UpgradeableBeacon(address(_implementation));
function test_Constructor() external {
assertEq(_testing.owner(), address(this));
// revert if the implementation address is an EOA
vm.expectRevert("UpgradeableBeacon: implementation is not a contract");
new UpgradeableBeacon(address(1024));
}
}
implementation()
:返回当前信标合约的逻辑合约地址;upgradeTo(address newImplementation)
:owner升级更新该信标合约的逻辑合约地址为newImplementation。 function implementation() public view virtual override returns (address) {
// 返回_implementation
return _implementation;
}
function upgradeTo(address newImplementation) public virtual onlyOwner {
// 为该信标合约设置逻辑合约地址为newImplementation
_setImplementation(newImplementation);
// 抛出事件
emit Upgraded(newImplementation);
}
foundry代码验证:
contract UpgradeableBeaconTest is Test, IERC1967, IImplementation {
Implementation private _implementation = new Implementation();
UpgradeableBeacon private _testing = new UpgradeableBeacon(address(_implementation));
ImplementationNew private _implementationNew = new ImplementationNew();
function test_UpgradeToAndImplementation() external {
// test for implementation()
assertEq(_testing.implementation(), address(_implementation));
// deploy beacon proxies
BeaconProxy beaconProxy1 = new BeaconProxy(
address(_testing),
abi.encodeCall(
Implementation.__Implementation_init,
(1024)
)
);
BeaconProxy beaconProxy2 = new BeaconProxy(
address(_testing),
abi.encodeCall(
Implementation.__Implementation_init,
(2048)
)
);
// check beacon proxies
assertEq(Implementation(address(beaconProxy1)).i(), 1024);
assertEq(Implementation(address(beaconProxy2)).i(), 2048);
// no function addI()
vm.expectRevert();
ImplementationNew(address(beaconProxy1)).addI(1);
vm.expectRevert();
ImplementationNew(address(beaconProxy2)).addI(1);
// test for upgradeTo()
vm.expectEmit();
emit IERC1967.Upgraded(address(_implementationNew));
_testing.upgradeTo(address(_implementationNew));
assertEq(_testing.implementation(), address(_implementationNew));
// check the upgrade for beacon proxy
uint i = 4096;
// function addI() is available for beaconProxy1
vm.expectEmit(address(beaconProxy1));
emit IImplementation.ChangeStorageUint(i);
ImplementationNew(address(beaconProxy1)).addI(i);
assertEq(ImplementationNew(address(beaconProxy1)).i(), 1024 + i);
// function addI() is available for beaconProxy2
vm.expectEmit(address(beaconProxy2));
emit IImplementation.ChangeStorageUint(i);
ImplementationNew(address(beaconProxy2)).addI(i);
assertEq(ImplementationNew(address(beaconProxy2)).i(), 2048 + i);
// revert if the caller is not owner
vm.prank(address(1024));
vm.expectRevert("Ownable: caller is not the owner");
_testing.upgradeTo(address(_implementationNew));
// revert if the implementation address is an EOA
vm.expectRevert("UpgradeableBeacon: implementation is not a contract");
_testing.upgradeTo(address(1024));
}
}
ps: 本人热爱图灵,热爱中本聪,热爱V神。 以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。 同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下! 如果需要转发,麻烦注明作者。十分感谢!
公众号名称:后现代泼痞浪漫主义奠基人
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!