Skip to content
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

feat: adds SuperOwnable #256

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
131 changes: 131 additions & 0 deletions contracts/src/SuperOwnable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.25;

import {Ownable} from "@solady/auth/Ownable.sol";
import {Predeploys} from "@contracts-bedrock/libraries/Predeploys.sol";

import {ISemver} from "@contracts-bedrock/universal/interfaces/ISemver.sol";
import {ICrossL2Inbox} from "@contracts-bedrock/L2/interfaces/ICrossL2Inbox.sol";

abstract contract SuperOwnable is Ownable, ISemver {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

error IdOriginNotSuperOwnable();
error DataNotCrosschainOwnershipTransfer();
error DataNotCrosschainSuperAdminChainIdUpdate();
error OwnershipNotInSync();
error NoOwnershipChange();
error ChainNotSuperAdmin();

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

event InitiateCrosschainOwnershipTransfer(address indexed previousOwner, address indexed newOwner);
event CrosschainOwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event SuperAdminChainIdUpdated(uint64 indexed superAdminChainId);
event InitiateCrosschainSuperAdminChainIdUpdate(uint64 indexed superAdminChainId, uint64 indexed newSuperAdminChainId);

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STATE VARIABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

uint64 superAdminChainId;

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

modifier onlySuperAdmin() {
if (block.chainid != superAdminChainId) revert ChainNotSuperAdmin();
_;
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EXTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @notice Semantic version.
/// @custom:semver 1.0.0-beta.1
function version() external view virtual returns (string memory) {
return "1.0.0-beta.1";
}

function _setSuperAdminChainId(uint64 newSuperAdminChainId) internal virtual {
superAdminChainId = newSuperAdminChainId;
emit SuperAdminChainIdUpdated(superAdminChainId);
}

function updateCrosschainSuperAdminChainId(ICrossL2Inbox.Identifier calldata _identifier, bytes calldata _data) external virtual {
if (_identifier.origin != address(this)) revert IdOriginNotSuperOwnable();
ICrossL2Inbox(Predeploys.CROSS_L2_INBOX).validateMessage(_identifier, keccak256(_data));

// Decode `InitiateCrosschainSuperAdminChainIdUpdate` event
bytes32 selector = abi.decode(_data[:32], (bytes32));
if (selector != InitiateCrosschainSuperAdminChainIdUpdate.selector) revert DataNotCrosschainSuperAdminChainIdUpdate();

(uint64 newSuperAdminChainId) = abi.decode(_data[32:], (uint64));
_setSuperAdminChainId(newSuperAdminChainId);
}

function updateSuperAdminChainId(uint64 newSuperAdminChainId) external virtual onlyOwner {
if (block.chainid != superAdminChainId) revert ChainNotSuperAdmin();
_setSuperAdminChainId(newSuperAdminChainId);
emit InitiateCrosschainSuperAdminChainIdUpdate(superAdminChainId, newSuperAdminChainId);
}

/**
* @notice Updates the owner of the contract.
* @param _identifier The identifier of the cross-chain message.
* @param _data The data of the cross-chain message.
*/
function updateCrosschainOwner(ICrossL2Inbox.Identifier calldata _identifier, bytes calldata _data) external virtual {
if (_identifier.origin != address(this)) revert IdOriginNotSuperOwnable();
ICrossL2Inbox(Predeploys.CROSS_L2_INBOX).validateMessage(_identifier, keccak256(_data));

// Decode `CrosschainOwnershipTransfer` event
bytes32 selector = abi.decode(_data[:32], (bytes32));
if (selector != InitiateCrosschainOwnershipTransfer.selector) revert DataNotCrosschainOwnershipTransfer();
(address previousOwner, address newOwner) = abi.decode(_data[32:], (address, address));
if (previousOwner != owner()) revert OwnershipNotInSync();
if (newOwner == owner()) revert NoOwnershipChange();

_setOwner(newOwner);

emit CrosschainOwnershipTransferred(previousOwner, newOwner);
}

function transferOwnership(address newOwner) public payable virtual override onlyOwner onlySuperAdmin {
super.transferOwnership(newOwner);
}

function renounceOwnership() public payable virtual override onlyOwner onlySuperAdmin {
super.renounceOwnership();
}

function completeOwnershipHandover(address pendingOwner) public payable virtual override onlyOwner onlySuperAdmin {
super.completeOwnershipHandover(pendingOwner);
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* emits an extra event to notify the cross-chain contract of the ownership change.
* Internal function without access restriction.
*/
function _setOwner(address newOwner) internal override {
emit InitiateCrosschainOwnershipTransfer(owner(), newOwner);
super._setOwner(newOwner);
}

function _initializeSuperOwner(uint64 newSuperAdminchainId, address newOwner) internal virtual {
_setOwner(newOwner);
_setSuperAdminChainId(newSuperAdminchainId);
}
}