-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #78 from aboutcircles/v1.0.1-patch2-upgradable-gro…
…up-policies (groups): upgradeableRenounceableProxy for group policies
- Loading branch information
Showing
14 changed files
with
298 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-only | ||
pragma solidity >=0.8.24; | ||
|
||
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; | ||
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; | ||
|
||
interface IUpgradeableRenounceableProxy { | ||
function implementation() external view returns (address); | ||
function upgradeToAndCall(address _newImplementation, bytes memory _data) external; | ||
function renounceUpgradeability() external; | ||
} | ||
|
||
contract UpgradeableRenounceableProxy is ERC1967Proxy { | ||
// Errors | ||
|
||
error BlockReceive(); | ||
|
||
// Constants | ||
|
||
/// @dev Initial proxy admin. | ||
address internal immutable ADMIN_INIT; | ||
|
||
// Constructor | ||
|
||
constructor(address _implementation, bytes memory _data) ERC1967Proxy(_implementation, _data) { | ||
// set the admin to the deployer | ||
ERC1967Utils.changeAdmin(msg.sender); | ||
// set the admin as immutable | ||
ADMIN_INIT = msg.sender; | ||
} | ||
|
||
/// @dev Handles proxy function calls: attempts to dispatch to a specific | ||
/// function or delegates all calls to the implementation contract. | ||
function _fallback() internal virtual override { | ||
// staticcall implementation() returns the address | ||
if (msg.sig == IUpgradeableRenounceableProxy.implementation.selector) { | ||
bytes32 slot = ERC1967Utils.IMPLEMENTATION_SLOT; | ||
assembly { | ||
let implementation := sload(slot) | ||
mstore(0, shr(12, shl(12, implementation))) | ||
return(0, 0x20) | ||
} | ||
} | ||
// dispatch if caller is admin, otherwise delegate to the implementation | ||
if (msg.sender == ADMIN_INIT && msg.sender == ERC1967Utils.getAdmin()) { | ||
_dispatchAdmin(); | ||
} else { | ||
super._fallback(); | ||
} | ||
} | ||
|
||
/// @dev Upgrades to new implementation, renounces the ability to upgrade or moves to regular flow based on admin request. | ||
function _dispatchAdmin() private { | ||
if (msg.sig == IUpgradeableRenounceableProxy.upgradeToAndCall.selector) { | ||
// upgrades to new implementation | ||
(address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes)); | ||
ERC1967Utils.upgradeToAndCall(newImplementation, data); | ||
} else if (msg.sig == IUpgradeableRenounceableProxy.renounceUpgradeability.selector) { | ||
// renounces the ability to upgrade the contract, by setting the admin to 0x01. | ||
ERC1967Utils.changeAdmin(address(0x01)); | ||
} else { | ||
_delegate(_implementation()); | ||
} | ||
} | ||
|
||
// Fallback function | ||
|
||
receive() external payable { | ||
revert BlockReceive(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
test/groups/upgradeableProxy/adminOperationsUpgradeableRenounceableProxy.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-only | ||
pragma solidity >=0.8.13; | ||
|
||
import {Test} from "forge-std/Test.sol"; | ||
import {StdCheats} from "forge-std/StdCheats.sol"; | ||
import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol"; | ||
import "forge-std/console.sol"; | ||
import "../../../src/groups/UpgradeableRenounceableProxy.sol"; | ||
import "../../../src/errors/Errors.sol"; | ||
import "../groupSetup.sol"; | ||
|
||
contract adminOperationsUpgradeableRenounceableProxy is Test, GroupSetup { | ||
// Constants | ||
|
||
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; | ||
|
||
// State variables | ||
|
||
address public group; | ||
IUpgradeableRenounceableProxy public proxy; | ||
|
||
// Constructor | ||
|
||
constructor() GroupSetup() {} | ||
|
||
// Setup | ||
|
||
function setUp() public { | ||
// first 35 addresses are registered as human | ||
// in mock deployment, with 14 days of mint | ||
groupSetup(); | ||
|
||
// 36: Kevin | ||
group = addresses[36]; | ||
|
||
// create a proxy deployment with the mint policy as implementation | ||
vm.startPrank(group); | ||
proxy = IUpgradeableRenounceableProxy(address(new UpgradeableRenounceableProxy(mintPolicy, ""))); | ||
hub.registerGroup(address(proxy), "ProxyPolicyGroup", "PPG", bytes32(0)); | ||
vm.stopPrank(); | ||
|
||
for (uint256 i = 0; i < 5; i++) { | ||
vm.prank(group); | ||
hub.trust(addresses[i], INDEFINITE_FUTURE); | ||
} | ||
} | ||
|
||
// Tests | ||
|
||
function testGetImplementation() public { | ||
address implementation = proxy.implementation(); | ||
assertEq(implementation, mintPolicy); | ||
} | ||
|
||
/* todo: - test getting admin from proxy | ||
* - test admin cannot be changed | ||
* - test noone else can call upgradeToAndCall | ||
* - test upgradeToAndCall with call data | ||
* - test renouncing admin (DONE) | ||
* - test accessibility of interface functions from non-Admin callers | ||
*/ | ||
|
||
function testUpgradeToAndCall() public { | ||
address originalImplementation = proxy.implementation(); | ||
assertEq(originalImplementation, mintPolicy); | ||
|
||
// deploy a new copy of base mint policy | ||
address newMintPolicy = address(new MintPolicy()); | ||
|
||
// upgrade the proxy to the new implementation | ||
vm.prank(group); | ||
proxy.upgradeToAndCall(newMintPolicy, ""); | ||
|
||
// check that the implementation has changed | ||
address newImplementation = proxy.implementation(); | ||
assertEq(newImplementation, newMintPolicy); | ||
|
||
// test minting to group with new policy | ||
_testGroupMintOwnCollateral(addresses[0], group, 1 * CRC); | ||
} | ||
|
||
function testRenounceAdmin() public { | ||
// current admin | ||
address admin = address(uint160(uint256(vm.load(address(proxy), ADMIN_SLOT)))); | ||
assertEq(admin, group); | ||
|
||
// renounce admin | ||
vm.prank(group); | ||
proxy.renounceUpgradeability(); | ||
|
||
// renounced admin | ||
admin = address(uint160(uint256(vm.load(address(proxy), ADMIN_SLOT)))); | ||
assertEq(admin, address(0x1)); | ||
|
||
// expect revert when trying to upgrade to implementation 0xdead | ||
vm.startPrank(group); | ||
vm.expectRevert(); | ||
proxy.upgradeToAndCall(address(0xdead), ""); | ||
vm.stopPrank(); | ||
} | ||
|
||
// Internal functions | ||
|
||
// todo: this is a duplicate; test helpers can be better structured | ||
function _testGroupMintOwnCollateral(address _minter, address _group, uint256 _amount) internal { | ||
uint256 tokenIdGroup = uint256(uint160(_group)); | ||
|
||
address[] memory collateral = new address[](1); | ||
uint256[] memory amounts = new uint256[](1); | ||
collateral[0] = _minter; | ||
amounts[0] = _amount; | ||
|
||
// check balance of group before mint | ||
uint256 balanceBefore = hub.balanceOf(_minter, tokenIdGroup); | ||
|
||
vm.prank(_minter); | ||
hub.groupMint(_group, collateral, amounts, ""); | ||
|
||
// check balance of group after mint | ||
uint256 balanceAfter = hub.balanceOf(_minter, tokenIdGroup); | ||
assertEq(balanceAfter, balanceBefore + _amount); | ||
} | ||
} |
79 changes: 79 additions & 0 deletions
79
test/groups/upgradeableProxy/usePolicyUpgradeableRenouncableProxy.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-only | ||
pragma solidity >=0.8.13; | ||
|
||
import {Test} from "forge-std/Test.sol"; | ||
import {StdCheats} from "forge-std/StdCheats.sol"; | ||
import "forge-std/console.sol"; | ||
import "../../../src/groups/UpgradeableRenounceableProxy.sol"; | ||
import "../../../src/errors/Errors.sol"; | ||
import "../groupSetup.sol"; | ||
|
||
contract usePolicyUpgradeableRenounceableProxyTest is Test, GroupSetup { | ||
// State variables | ||
address public group; | ||
|
||
// Constructor | ||
|
||
constructor() GroupSetup() {} | ||
|
||
// Setup | ||
|
||
function setUp() public { | ||
// first 35 addresses are registered as human | ||
// in mock deployment, with 14 days of mint | ||
groupSetup(); | ||
|
||
// 36: Kevin | ||
group = addresses[36]; | ||
} | ||
|
||
// Tests | ||
|
||
function testRegisterGroupWithProxyPolicy() public { | ||
_createGroupWithProxyPolicy(); | ||
} | ||
|
||
function testMintGroupWithProxyPolicy() public { | ||
UpgradeableRenounceableProxy proxy = _createGroupWithProxyPolicy(); | ||
|
||
address alice = addresses[0]; | ||
|
||
// group must trust alice | ||
vm.prank(group); | ||
hub.trust(alice, INDEFINITE_FUTURE); | ||
|
||
// test minting to group | ||
_testGroupMintOwnCollateral(alice, group, 1 * CRC); | ||
} | ||
|
||
// Internal functions | ||
|
||
function _createGroupWithProxyPolicy() internal returns (UpgradeableRenounceableProxy) { | ||
// create a proxy deployment with the mint policy as implementation | ||
vm.startPrank(group); | ||
UpgradeableRenounceableProxy proxy = new UpgradeableRenounceableProxy(mintPolicy, ""); | ||
hub.registerGroup(address(proxy), "ProxyPolicyGroup", "PPG", bytes32(0)); | ||
vm.stopPrank(); | ||
|
||
return proxy; | ||
} | ||
|
||
function _testGroupMintOwnCollateral(address _minter, address _group, uint256 _amount) internal { | ||
uint256 tokenIdGroup = uint256(uint160(_group)); | ||
|
||
address[] memory collateral = new address[](1); | ||
uint256[] memory amounts = new uint256[](1); | ||
collateral[0] = _minter; | ||
amounts[0] = _amount; | ||
|
||
// check balance of group before mint | ||
uint256 balanceBefore = hub.balanceOf(_minter, tokenIdGroup); | ||
|
||
vm.prank(_minter); | ||
hub.groupMint(_group, collateral, amounts, ""); | ||
|
||
// check balance of group after mint | ||
uint256 balanceAfter = hub.balanceOf(_minter, tokenIdGroup); | ||
assertEq(balanceAfter, balanceBefore + _amount); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.