Skip to content

update Zenith admin structure #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions script/Zenith.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,34 @@ pragma solidity ^0.8.24;
import {Script} from "forge-std/Script.sol";
import {Zenith} from "../src/Zenith.sol";

contract DeployZenith is Script {
contract ZenithScript is Script {
// deploy:
// forge script DeployZenith --sig "run()" --rpc-url $RPC_URL --etherscan-api-key $ETHERSCAN_API_KEY --private-key $PRIVATE_KEY --broadcast --verify
function run() public {
vm.broadcast();
new Zenith(block.chainid + 1, msg.sender);
// forge script ZenithScript --sig "deploy(uint256,address,address)" --rpc-url $RPC_URL --etherscan-api-key $ETHERSCAN_API_KEY --private-key $PRIVATE_KEY --broadcast --verify $ROLLUP_CHAIN_ID $WITHDRAWAL_ADMIN_ADDRESS $SEQUENCER_ADMIN_ADDRESS
function deploy(uint256 defaultRollupChainId, address withdrawalAdmin, address sequencerAdmin)
public
returns (Zenith z)
{
vm.startBroadcast();
z = new Zenith(defaultRollupChainId, withdrawalAdmin, sequencerAdmin);
// send some ETH to newly deployed Zenith to populate some rollup state
payable(address(z)).transfer(0.00123 ether);
}

// NOTE: script must be run using SequencerAdmin key
// set sequencer:
// forge script ZenithScript --sig "setSequencerRole(address,address)" --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast $ZENITH_ADDRESS $SEQUENCER_ADDRESS
function setSequencerRole(address payable z, address sequencer) public {
vm.startBroadcast();
Zenith zenith = Zenith(z);
zenith.grantRole(zenith.SEQUENCER_ROLE(), sequencer);
}

// NOTE: script must be run using SequencerAdmin key
// revoke sequencer:
// forge script ZenithScript --sig "revokeSequencerRole(address,address)" --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast $ZENITH_ADDRESS $SEQUENCER_ADDRESS
function revokeSequencerRole(address payable z, address sequencer) public {
vm.startBroadcast();
Zenith zenith = Zenith(z);
zenith.revokeRole(zenith.SEQUENCER_ROLE(), sequencer);
}
}
17 changes: 6 additions & 11 deletions src/Passage.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

// import IERC20 from OpenZeppelin
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {AccessControlDefaultAdminRules} from
"openzeppelin-contracts/contracts/access/extensions/AccessControlDefaultAdminRules.sol";
import {ZenithAdmin} from "./ZenithAdmin.sol";

/// @notice A contract deployed to Host chain that allows tokens to enter the rollup,
/// and enables Builders to fulfill requests to exchange tokens on the Rollup for tokens on the Host.
contract Passage is AccessControlDefaultAdminRules {
contract Passage is ZenithAdmin {
/// @notice The chainId of rollup that Ether will be sent to by default when entering the rollup via fallback() or receive().
uint256 immutable defaultRollupChainId;

Expand Down Expand Up @@ -57,14 +55,11 @@ contract Passage is AccessControlDefaultAdminRules {
uint256 amount;
}

/// @notice Initializes the Admin role.
/// @dev See `AccessControlDefaultAdminRules` for information on contract administration.
/// - Admin role can grant and revoke Sequencer roles.
/// - Admin role can be transferred via two-step process with a 1 day timelock.
/// @param _defaultRollupChainId - the chainId of the rollup that Ether will be sent to by default
/// when entering the rollup via fallback() or receive() fns.
/// @param admin - the address that will be the initial admin.
constructor(uint256 _defaultRollupChainId, address admin) AccessControlDefaultAdminRules(1 days, admin) {
constructor(uint256 _defaultRollupChainId, address withdrawalAdmin, address sequencerAdmin)
ZenithAdmin(withdrawalAdmin, sequencerAdmin)
{
defaultRollupChainId = _defaultRollupChainId;
}

Expand Down Expand Up @@ -139,7 +134,7 @@ contract Passage is AccessControlDefaultAdminRules {
/// @notice Allows the admin to withdraw tokens from the contract.
/// @dev Only the admin can call this function.
/// @param withdrawals - The withdrawals to process. See Withdrawal struct docs for details.
function withdraw(Withdrawal[] calldata withdrawals) external onlyRole(DEFAULT_ADMIN_ROLE) {
function withdraw(Withdrawal[] calldata withdrawals) external onlyRole(WITHDRAWAL_ADMIN_ROLE) {
for (uint256 i = 0; i < withdrawals.length; i++) {
// transfer ether
if (withdrawals[i].ethAmount > 0) {
Expand Down
13 changes: 3 additions & 10 deletions src/Zenith.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

// import openzeppelin Role contracts
import {Passage} from "./Passage.sol";

contract Zenith is Passage {
Expand All @@ -19,9 +18,6 @@ contract Zenith is Passage {
address rewardAddress;
}

/// @notice Role that allows a key to sign commitments to rollup blocks.
bytes32 public constant SEQUENCER_ROLE = bytes32("SEQUENCER_ROLE");

/// @notice The sequence number of the next block that can be submitted for a given rollup chainId.
/// rollupChainId => nextSequence number
mapping(uint256 => uint256) public nextSequence;
Expand Down Expand Up @@ -67,12 +63,9 @@ contract Zenith is Passage {
/// @notice Emit the entire block data for easy visibility
event BlockData(bytes blockData);

/// @notice Initializes the Admin role.
/// @dev See `AccessControlDefaultAdminRules` for information on contract administration.
/// - Admin role can grant and revoke Sequencer roles.
/// - Admin role can be transferred via two-step process with a 1 day timelock.
/// @param admin - the address that will be the initial admin.
constructor(uint256 defaultRollupChainId, address admin) Passage(defaultRollupChainId, admin) {}
constructor(uint256 _defaultRollupChainId, address withdrawalAdmin, address sequencerAdmin)
Passage(_defaultRollupChainId, withdrawalAdmin, sequencerAdmin)
{}

/// @notice Submit a rollup block with block data submitted via calldata.
/// @dev Blocks are submitted by Builders, with an attestation to the block data signed by a Sequencer.
Expand Down
65 changes: 65 additions & 0 deletions src/ZenithAdmin.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {AccessControl} from "openzeppelin-contracts/contracts/access/AccessControl.sol";

/// @notice A contract deployed to Host chain that allows tokens to enter the rollup,
/// and enables Builders to fulfill requests to exchange tokens on the Rollup for tokens on the Host.
contract ZenithAdmin is AccessControl {
/// @notice Role that allows a key to sign commitments to rollup blocks.
bytes32 public constant SEQUENCER_ROLE = bytes32("SEQUENCER_ROLE");

/// @notice Admin Role that can grant and revoke Sequencer roles.
bytes32 public constant SEQUENCER_ADMIN_ROLE = bytes32("SEQUENCER_ADMIN_ROLE");

/// @notice Role that can withdraw funds from Passage.
bytes32 public constant WITHDRAWAL_ADMIN_ROLE = bytes32("WITHDRAWAL_ADMIN_ROLE");

/// @notice Thrown when a role attempts to renounce itself.
error RenounceDisabled();

/// @notice Thrown when attempting to set a default admin.
error NoDefaultAdmin();

/// @notice Thrown when attempting to transfer a non-admin role (e.g. Sequencer can't transfer its own role).
error OnlyAdminRolesCanTransfer();

constructor(address withdrawalAdmin, address sequencerAdmin) {
// there is no admin for WITHDRAWAL_ADMIN_ROLE nor SEQUENCER_ADMIN_ROLE, so nobody can grantRole or revokeRole for those roles
// the only way to change the WITHDRAWAL_ADMIN_ROLE or SEQUENCER_ADMIN_ROLE is to call transferRole
_grantRole(WITHDRAWAL_ADMIN_ROLE, withdrawalAdmin);
_grantRole(SEQUENCER_ADMIN_ROLE, sequencerAdmin);
// SEQUENCER_ADMIN_ROLE can grantRole(SEQUENCER_ROLE) and revokeRole(SEQUENCER_ROLE)
_setRoleAdmin(SEQUENCER_ROLE, SEQUENCER_ADMIN_ROLE);
}

/// @notice Cannot renounce a role. Admins can revoke their administrated roles. Admins cannot renounce their own role.
function renounceRole(bytes32, address) public pure override {
revert RenounceDisabled();
}

/// @notice Transfer Admin role to a new account.
/// @dev Only callable by the current Admin role holder.
function transferAdminRole(bytes32 role, address newAdmin) public onlyRole(role) {
if (!isAdminRole(role)) revert OnlyAdminRolesCanTransfer();
if (newAdmin == address(0)) revert RenounceDisabled();
_grantRole(role, newAdmin);
_revokeRole(role, msg.sender);
}

// CANNOT set default admin role (which would be admin for WITHDRAWAL_ADMIN_ROLE and SEQUENCER_ADMIN_ROLE)
function _grantRole(bytes32 role, address account) internal override returns (bool) {
if (role == DEFAULT_ADMIN_ROLE) revert NoDefaultAdmin();
return super._grantRole(role, account);
}

// CANNOT set admin for WITHDRAWAL_ADMIN_ROLE and SEQUENCER_ADMIN_ROLE
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal override {
if (isAdminRole(role)) revert NoDefaultAdmin();
super._setRoleAdmin(role, adminRole);
}

function isAdminRole(bytes32 role) internal pure returns (bool) {
return role == WITHDRAWAL_ADMIN_ROLE || role == SEQUENCER_ADMIN_ROLE;
}
}
6 changes: 4 additions & 2 deletions test/Helpers.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ contract HelpersTest is Test {

function setUp() public {
vm.createSelectFork("https://rpc.holesky.ethpandaops.io");
target = new Zenith(block.chainid + 1, 0x0a53e650c6f015eF70a15Da7B18fa95F051465aB);
target = new Zenith(
block.chainid + 1, 0x11Aa4EBFbf7a481617c719a2Df028c9DA1a219aa, 0x29403F107781ea45Bf93710abf8df13F67f2008f
);
}

function test_signature() public {
function check_signature() public {
bytes32 hash = 0xdcd0af9a45fa82dcdd1e4f9ef703d8cd459b6950c0638154c67117e86facf9c1;
uint8 v = 28;
bytes32 r = 0xb89764d107f812dbbebb925711b320d336ff8d03f08570f051123df86334f3f5;
Expand Down
2 changes: 1 addition & 1 deletion test/Zenith.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ contract ZenithTest is Test {
);

function setUp() public {
target = new Zenith(block.chainid + 1, address(this));
target = new Zenith(block.chainid + 1, address(this), address(this));
target.grantRole(target.SEQUENCER_ROLE(), vm.addr(sequencerKey));

// set default block values
Expand Down