Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .solcover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
skipFiles: ['Op/mocks/'],
};
1 change: 1 addition & 0 deletions .solhintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
contracts/Op/mocks/MockCrossDomainMessenger.sol
6 changes: 3 additions & 3 deletions contracts/DomainMangager/DomainManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ abstract contract DomainManager {

/**
* @dev Initializes the contract with references to the SCI Registry.
* @param _sciRegistryAddress Address of the SCI Registry contract.
* @param _sciRegistry Address of the SCI Registry contract.
*/
constructor(address _sciRegistryAddress) {
registry = ISciRegistry(_sciRegistryAddress);
constructor(address _sciRegistry) {
registry = ISciRegistry(_sciRegistry);
}

/**
Expand Down
23 changes: 23 additions & 0 deletions contracts/Op/ICrossDomainMessanger.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.28;

/**
* @title ICrossDomainMessanger
* @dev Interface for the Superchain Cross Domain Messenger
* which facilitates sending messages between a source and a target chain.
*/
interface ICrossDomainMessanger {
/**
* @dev Sends a message to a target contract on a different chain.
* @param target The address of the target contract on the destination chain.
* @param _message The encoded message data containing function selectors and parameters.
* @param gasLimit The maximum amount of gas allocated for executing the message on the target chain.
*/
function sendMessage(address target, bytes calldata _message, uint32 gasLimit) external;

/**
* @dev Retrieves the address of the sender of the cross-domain message.
* @return The address of the entity that originated the cross-domain message.
*/
function xDomainMessageSender() external view returns (address);
}
47 changes: 47 additions & 0 deletions contracts/Op/SuperChainAccessControlDefaultAdminRules.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.28;

import {AccessControlDefaultAdminRules} from '@openzeppelin/contracts/access/extensions/AccessControlDefaultAdminRules.sol';
import {ICrossDomainMessanger} from './ICrossDomainMessanger.sol';

/**
* @title SuperChainAccessControlDefaultAdminRules
* @dev This contract extends the OpenZeppelin AccessControlDefaultAdminRules contract to include cross-chain role management.
* @custom:security-contact security@sci.domains
*/
contract SuperChainAccessControlDefaultAdminRules is AccessControlDefaultAdminRules {
ICrossDomainMessanger public immutable crossDomainMessanger;

/**
* @dev Thrown when the caller is not the cross domain messanger.
*/
error InvalidMessageSender(address account);

/**
* @dev Modifier that checks that an account on a source chain has a specific role.
* Reverts with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyCrossChainRole(bytes32 role) {
if (msg.sender != address(crossDomainMessanger)) {
revert InvalidMessageSender(msg.sender);
}

address account = crossDomainMessanger.xDomainMessageSender();
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
_;
}

/**
* @param _crossDomainMessanger Address of the cross-domain messenger contract.
* @dev See {AccessControlDefaultAdminRules-constructor}.
*/
constructor(
address _crossDomainMessanger,
uint48 initialDelay,
address initialDefaultAdmin
) AccessControlDefaultAdminRules(initialDelay, initialDefaultAdmin) {
crossDomainMessanger = ICrossDomainMessanger(_crossDomainMessanger);
}
}
49 changes: 49 additions & 0 deletions contracts/Op/mocks/MockCrossDomainMessenger.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.28;

import {ICrossDomainMessanger} from '../ICrossDomainMessanger.sol';
/// @dev Mock contract for testing cross-domain messaging functionality.
contract MockCrossDomainMessenger is ICrossDomainMessanger {
address private xDomainMessageSenderAddress;

bool public shouldSendMessage;

event MessageSent(address target, bytes message, uint32 gasLimit);

constructor(address _xDomainMessageSender, bool _shouldSendMessage) {
xDomainMessageSenderAddress = _xDomainMessageSender;
shouldSendMessage = _shouldSendMessage;
}

function sendMessage(address target, bytes calldata _message, uint32 gasLimit) external {
if (shouldSendMessage) {
(bool success, bytes memory result) = target.call{gas: uint256(gasLimit)}(_message);

if (!success) {
// Bubble up the original revert reason if present
if (result.length > 0) {
// The easiest way to bubble the reason is using assembly
assembly {
let returndata_size := mload(result)
revert(add(result, 32), returndata_size)
}
} else {
revert('Call failed without reason');
}
}
}
emit MessageSent(target, _message, gasLimit);
}

function xDomainMessageSender() external view override returns (address) {
return xDomainMessageSenderAddress;
}

function setXDomainMessageSenderAddress(address _xDomainMessageSenderAddress) external {
xDomainMessageSenderAddress = _xDomainMessageSenderAddress;
}

function setShouldSendMessage(bool _shouldSendMessage) external {
shouldSendMessage = _shouldSendMessage;
}
}
27 changes: 15 additions & 12 deletions contracts/Registrars/EnsRegistrar.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
pragma solidity 0.8.28;

import {ENS} from '@ensdomains/ens-contracts/contracts/registry/ENS.sol';
import {ISciRegistry} from '../SciRegistry/ISciRegistry.sol';
import {IVerifier} from '../Verifiers/IVerifier.sol';
import {SuperChainSourceRegistrar} from './SuperChainSourceRegistrar.sol';

/**
* @title EnsRegistrar
Expand All @@ -13,9 +13,8 @@ import {IVerifier} from '../Verifiers/IVerifier.sol';
* by verifying the domain ownership through the ENS contract.
* @custom:security-contact security@sci.domains
*/
contract EnsRegistrar {
contract EnsRegistrar is SuperChainSourceRegistrar {
ENS public immutable ensRegistry;
ISciRegistry public immutable registry;

/**
* @dev Thrown when the `account` is not the owner of the ENS `domainhash`.
Expand All @@ -36,18 +35,22 @@ contract EnsRegistrar {

/**
* @dev Initializes the contract with references to the ENS and the SCI Registry.
* @param _ensRegistryAddress Address of the ENS Registry contract.
* @param _sciRegistryAddress Address of the SCI Registry contract.
* @param _ensRegistry Address of the ENS Registry contract.
* @param _sciRegistry Address of the SCI Registry contract.
* @param _crossChainDomainMessagnger Address of the cross-chain domain messenger contract.
*/
constructor(address _ensRegistryAddress, address _sciRegistryAddress) {
ensRegistry = ENS(_ensRegistryAddress);
registry = ISciRegistry(_sciRegistryAddress);
constructor(
address _ensRegistry,
address _sciRegistry,
address _crossChainDomainMessagnger
) SuperChainSourceRegistrar(_crossChainDomainMessagnger, _sciRegistry) {
ensRegistry = ENS(_ensRegistry);
}

/**
* @dev Registers a domain in the SCI Registry contract.
* @param owner Address of the domain owner.
* @param domainHash Namehash of domain.
* @param domainHash The namehash of the domain to be registered.
*
* Requirements:
*
Expand All @@ -57,12 +60,12 @@ contract EnsRegistrar {
address owner,
bytes32 domainHash
) external onlyEnsOwner(owner, domainHash) {
registry.registerDomain(owner, domainHash);
_registerDomainCrossChain(owner, domainHash);
}

/**
* @dev Registers a domain with a verifier in the SCI Registry contract.
* @param domainHash Namehash of the domain.
* @param domainHash The namehash of the domain to be registered.
* @param verifier Address of the verifier contract.
*
* Requirements:
Expand All @@ -73,7 +76,7 @@ contract EnsRegistrar {
bytes32 domainHash,
IVerifier verifier
) external onlyEnsOwner(msg.sender, domainHash) {
registry.registerDomainWithVerifier(msg.sender, domainHash, verifier);
_registerDomainWithVerifierCrossChain(msg.sender, domainHash, verifier);
}

/**
Expand Down
10 changes: 5 additions & 5 deletions contracts/Registrars/SciRegistrar.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@ contract SciRegistrar is AccessControlDefaultAdminRules {

/**
* @dev Initializes the contract by setting up the SCI Registry reference and defining the admin rules.
* @param _sciRegistryAddress Address of the custom domain registry contract.
* @param _sciRegistry Address of the custom domain registry contract.
* @param initialDelay The {defaultAdminDelay}. See AccessControlDefaultAdminRules for more information.
*/
constructor(
address _sciRegistryAddress,
address _sciRegistry,
uint48 initialDelay
) AccessControlDefaultAdminRules(initialDelay, msg.sender) {
registry = ISciRegistry(_sciRegistryAddress);
registry = ISciRegistry(_sciRegistry);
}

/**
* @dev Registers a domain in the SCI Registry contract.
* @param owner Address expected to be the domain owner.
* @param domainHash Namehash of the domain.
* @param domainHash The namehash of the domain to be registered.
*
* Requirements:
*
Expand All @@ -52,7 +52,7 @@ contract SciRegistrar is AccessControlDefaultAdminRules {
/**
* @dev Registers a domain with a verifier in the SCI Registry contract.
* @param owner Address expected to be the domain owner.
* @param domainHash Namehash of the domain.
* @param domainHash The namehash of the domain to be registered.
* @param verifier Address of the verifier contract.
*
* Requirements:
Expand Down
65 changes: 65 additions & 0 deletions contracts/Registrars/SuperChainSourceRegistrar.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.28;

import {ISciRegistry} from '../SciRegistry/ISciRegistry.sol';
import {IVerifier} from '../Verifiers/IVerifier.sol';
import {ICrossDomainMessanger} from '../Op//ICrossDomainMessanger.sol';

/**
* @title SuperChainSourceRegistrar
* @dev This abstract contract is designed to be inherited by registrar contracts that register domains on a superchain.
* It provides functionality to register a domain on a different chain via the superchain cross-domain messaging.
*
* @custom:security-contact security@sci.domains
*/
abstract contract SuperChainSourceRegistrar {
// Cross-domain messenger contract for sending messages to the target chain.
ICrossDomainMessanger public immutable crossDomainMessanger;
// The address of the SuperChainTargetRegistrar on the target chain.
address public targetRegistrar;

// Gas limits for cross-domain messages on the target chain.
uint32 public constant REGISTER_DOMAIN_GAS_LIMIT = 200000;
uint32 public constant REGISTER_DOMAIN_WITH_VERIFIER_GAS_LIMIT = 300000;

/**
* @dev Initializes the contract by setting up the Cross domain messenger and the target registrar.
* @param _crossDomainMessanger The address of the cross-domain messenger contract.
* @param _targetRegistrar The address of the registrar contract on the target chain.
*/
constructor(address _crossDomainMessanger, address _targetRegistrar) {
crossDomainMessanger = ICrossDomainMessanger(_crossDomainMessanger);
targetRegistrar = _targetRegistrar;
}

/**
* @dev Registers a domain on the target registrar contract via cross-domain messaging.
* @param owner Address expected to be the domain owner.
* @param domainHash The namehash of the domain to be registered.
*/
function _registerDomainCrossChain(address owner, bytes32 domainHash) internal {
crossDomainMessanger.sendMessage(
targetRegistrar,
abi.encodeCall(ISciRegistry.registerDomain, (owner, domainHash)),
REGISTER_DOMAIN_GAS_LIMIT
);
}

/**
* @dev Registers a domain with a verifier on the target registrar contract via cross-domain messaging.
* @param owner Address expected to be the domain owner.
* @param domainHash The namehash of the domain to be registered.
* @param verifier The address of the verifier contract.
*/
function _registerDomainWithVerifierCrossChain(
address owner,
bytes32 domainHash,
IVerifier verifier
) internal {
crossDomainMessanger.sendMessage(
targetRegistrar,
abi.encodeCall(ISciRegistry.registerDomainWithVerifier, (owner, domainHash, verifier)),
REGISTER_DOMAIN_WITH_VERIFIER_GAS_LIMIT
);
}
}
70 changes: 70 additions & 0 deletions contracts/Registrars/SuperChainTargetRegistrar.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.28;

import {ISciRegistry} from '../SciRegistry/ISciRegistry.sol';
import {IVerifier} from '../Verifiers/IVerifier.sol';
import {SuperChainAccessControlDefaultAdminRules} from '../Op/SuperChainAccessControlDefaultAdminRules.sol';

/**
* @title SuperChainTargetRegistrar
* @dev This contract allows addresses from the source chain with REGISTER_DOMAIN_ROLE role to register a domain.
* It uses the superchain cross-domain messaging to "listen" for domain registration requests from the source chain.
*
* @custom:security-contact security@sci.domains
*/
contract SuperChainTargetRegistrar is SuperChainAccessControlDefaultAdminRules {
// Role that allows registering domains
bytes32 public constant REGISTER_DOMAIN_ROLE = keccak256('REGISTER_DOMAIN_ROLE');

ISciRegistry public immutable registry;

/**
* @dev Initializes the contract by setting up the SCI Registry reference and defining the admin rules.
* @param _sciRegistry Address of the custom domain registry contract.
* @param _crossDomainMessanger Address of the cross-domain messenger contract.
* @param initialDelay The {defaultAdminDelay}. See AccessControlDefaultAdminRules for more information.
*/
constructor(
address _sciRegistry,
address _crossDomainMessanger,
uint48 initialDelay
) SuperChainAccessControlDefaultAdminRules(_crossDomainMessanger, initialDelay, msg.sender) {
registry = ISciRegistry(_sciRegistry);
}

/**
* @dev Registers a domain in the SCI Registry contract.
* @param owner Address expected to be the domain owner.
* @param domainHash The namehash of the domain to be registered.
*
* Requirements:
*
* - The xDomainMessageSender must have the REGISTER_DOMAIN_ROLE role.
* - The caller must be the superchain cross domain messanger
*/
function registerDomain(
address owner,
bytes32 domainHash
) external onlyCrossChainRole(REGISTER_DOMAIN_ROLE) {
registry.registerDomain(owner, domainHash);
}

/**
* @dev Registers a domain with a verifier in the SCI Registry contract.
* @param owner Address expected to be the domain owner.
* @param domainHash The namehash of the domain to be registered.
* @param verifier Address of the verifier contract.
*
* Requirements:
*
* - The xDomainMessageSender must have the REGISTER_DOMAIN_ROLE role.
* - The caller must be the superchain cross domain messanger
*/
function registerDomainWithVerifier(
address owner,
bytes32 domainHash,
IVerifier verifier
) external onlyCrossChainRole(REGISTER_DOMAIN_ROLE) {
registry.registerDomainWithVerifier(owner, domainHash, verifier);
}
}
Loading