The home for Hyperlane core contracts, sdk packages, and other infrastructure
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
hyperlane-monorepo/solidity/contracts/isms/routing/DomainRoutingIsm.sol

133 lines
4.3 KiB

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
// ============ External Imports ============
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
// ============ Internal Imports ============
import {AbstractRoutingIsm} from "./AbstractRoutingIsm.sol";
import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol";
import {Message} from "../../libs/Message.sol";
import {TypeCasts} from "../../libs/TypeCasts.sol";
import {EnumerableMapExtended} from "../../libs/EnumerableMapExtended.sol";
/**
* @title DomainRoutingIsm
*/
contract DomainRoutingIsm is AbstractRoutingIsm, OwnableUpgradeable {
using EnumerableMapExtended for EnumerableMapExtended.UintToBytes32Map;
using Message for bytes;
using TypeCasts for bytes32;
using TypeCasts for address;
using Address for address;
using Strings for uint32;
// ============ Mutable Storage ============
EnumerableMapExtended.UintToBytes32Map internal _modules;
// ============ External Functions ============
/**
* @param _owner The owner of the contract.
*/
function initialize(address _owner) public initializer {
__Ownable_init();
_transferOwnership(_owner);
}
/**
* @notice Sets the ISMs to be used for the specified origin domains
* @param _owner The owner of the contract.
* @param _domains The origin domains
* @param __modules The ISMs to use to verify messages
*/
function initialize(
address _owner,
uint32[] calldata _domains,
IInterchainSecurityModule[] calldata __modules
) public initializer {
__Ownable_init();
require(_domains.length == __modules.length, "length mismatch");
uint256 _length = _domains.length;
for (uint256 i = 0; i < _length; ++i) {
_set(_domains[i], address(__modules[i]));
}
_transferOwnership(_owner);
}
/**
* @notice Sets the ISM to be used for the specified origin domain
* @param _domain The origin domain
* @param _module The ISM to use to verify messages
*/
function set(
uint32 _domain,
IInterchainSecurityModule _module
) external onlyOwner {
_set(_domain, address(_module));
}
/**
* @notice Removes the specified origin domain
* @param _domain The origin domain
*/
function remove(uint32 _domain) external onlyOwner {
_remove(_domain);
}
function domains() external view returns (uint256[] memory) {
return _modules.keys();
}
function module(
uint32 origin
) public view virtual returns (IInterchainSecurityModule) {
(bool contained, bytes32 _module) = _modules.tryGet(origin);
if (contained) {
return IInterchainSecurityModule(_module.bytes32ToAddress());
}
revert(_originNotFoundError(origin));
}
// ============ Public Functions ============
/**
* @notice Returns the ISM responsible for verifying _message
* @dev Can change based on the content of _message
* @param _message Formatted Hyperlane message (see Message.sol).
* @return module The ISM to use to verify _message
*/
function route(
bytes calldata _message
) public view override returns (IInterchainSecurityModule) {
return module(_message.origin());
}
// ============ Internal Functions ============
/**
* @notice Removes the specified origin domain's ISM
* @param _domain The origin domain
*/
function _remove(uint32 _domain) internal {
require(_modules.remove(_domain), _originNotFoundError(_domain));
}
function _originNotFoundError(
uint32 _origin
) internal pure returns (string memory) {
return string.concat("No ISM found for origin: ", _origin.toString());
}
/**
* @notice Sets the ISM to be used for the specified origin domain
* @param _domain The origin domain
* @param _module The ISM to use to verify messages
*/
function _set(uint32 _domain, address _module) internal {
require(_module.isContract(), "ISM must be a contract");
_modules.set(_domain, _module.addressToBytes32());
}
}