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.
184 lines
7.3 KiB
184 lines
7.3 KiB
// SPDX-License-Identifier: Apache-2.0
|
|
pragma solidity ^0.8.13;
|
|
|
|
// ============ Internal Imports ============
|
|
import {OwnableMulticall, Call} from "../OwnableMulticall.sol";
|
|
import {Router} from "../Router.sol";
|
|
import {IInterchainAccountRouter} from "../../interfaces/IInterchainAccountRouter.sol";
|
|
import {MinimalProxy} from "../libs/MinimalProxy.sol";
|
|
|
|
// ============ External Imports ============
|
|
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
|
|
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
|
|
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
|
|
|
/*
|
|
* @title Interchain Accounts Router that relays messages via proxy contracts on other chains.
|
|
* @dev Currently does not support Sovereign Consensus (user specified Interchain Security Modules).
|
|
*/
|
|
contract InterchainAccountRouter is Router, IInterchainAccountRouter {
|
|
address immutable implementation;
|
|
bytes32 immutable bytecodeHash;
|
|
|
|
/**
|
|
* @notice Emitted when an interchain account is created (first time message is sent from a given `origin`/`sender` pair)
|
|
* @param origin The domain of the chain where the message was sent from
|
|
* @param sender The address of the account that sent the message
|
|
* @param account The address of the proxy account that was created
|
|
*/
|
|
event InterchainAccountCreated(
|
|
uint32 indexed origin,
|
|
address sender,
|
|
address account
|
|
);
|
|
|
|
/**
|
|
* @notice Constructor deploys a relay (OwnableMulticall.sol) contract that will be cloned for each interchain account.
|
|
*/
|
|
constructor() {
|
|
implementation = address(new OwnableMulticall());
|
|
// cannot be stored immutably because it is dynamically sized
|
|
bytes memory bytecode = MinimalProxy.bytecode(implementation);
|
|
bytecodeHash = keccak256(bytecode);
|
|
}
|
|
|
|
/**
|
|
* @notice Initializes the Router contract with Hyperlane core contracts and the address of the interchain security module.
|
|
* @param _mailbox The address of the mailbox contract.
|
|
* @param _interchainGasPaymaster The address of the interchain gas paymaster contract.
|
|
* @param _interchainSecurityModule The address of the interchain security module contract.
|
|
*/
|
|
function initialize(
|
|
address _mailbox,
|
|
address _interchainGasPaymaster,
|
|
address _interchainSecurityModule
|
|
) public initializer {
|
|
// Transfer ownership of the contract to `msg.sender`
|
|
__Router_initialize(
|
|
_mailbox,
|
|
_interchainGasPaymaster,
|
|
_interchainSecurityModule
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @notice Initializes the Router contract with Hyperlane core contracts.
|
|
* @param _mailbox The address of the mailbox contract.
|
|
* @param _interchainGasPaymaster The address of the interchain gas paymaster contract.
|
|
*/
|
|
function initialize(address _mailbox, address _interchainGasPaymaster)
|
|
public
|
|
initializer
|
|
{
|
|
// Transfer ownership of the contract to `msg.sender`
|
|
__Router_initialize(_mailbox, _interchainGasPaymaster);
|
|
}
|
|
|
|
/**
|
|
* @notice Dispatches a sequence of calls to be relayed by the sender's interchain account on the destination domain.
|
|
* @param _destinationDomain The domain of the chain where the message will be sent to.
|
|
* @param calls The sequence of calls to be relayed.
|
|
*/
|
|
function dispatch(uint32 _destinationDomain, Call[] calldata calls)
|
|
external
|
|
returns (bytes32)
|
|
{
|
|
return _dispatch(_destinationDomain, abi.encode(msg.sender, calls));
|
|
}
|
|
|
|
/**
|
|
* @notice Dispatches a single call to be relayed by the sender's interchain account on the destination domain.
|
|
* @param _destinationDomain The domain of the chain where the message will be sent to.
|
|
* @param target The address of the contract to be called.
|
|
* @param data The ABI-encoded data to be called on target contract.
|
|
* @return The message ID of the dispatched message.
|
|
*/
|
|
function dispatch(
|
|
uint32 _destinationDomain,
|
|
address target,
|
|
bytes calldata data
|
|
) external returns (bytes32) {
|
|
Call[] memory calls = new Call[](1);
|
|
calls[0] = Call({to: target, data: data});
|
|
return _dispatch(_destinationDomain, abi.encode(msg.sender, calls));
|
|
}
|
|
|
|
/**
|
|
* @notice Returns the address of the interchain account deployed on the current chain for a given `origin`/`sender` pair.
|
|
* @param _origin The origin domain of the interchain account.
|
|
* @param _sender The parent account address on the origin domain.
|
|
* @return The address of the interchain account.
|
|
*/
|
|
function getInterchainAccount(uint32 _origin, address _sender)
|
|
public
|
|
view
|
|
returns (address)
|
|
{
|
|
return _getInterchainAccount(_salt(_origin, _sender));
|
|
}
|
|
|
|
/**
|
|
* @notice Returns and deploys (if not already) the interchain account for a given `origin`/`sender` pair.
|
|
* @param _origin The origin domain of the interchain account.
|
|
* @param _sender The parent account address on the origin domain.
|
|
* @return The address of the interchain account.
|
|
*/
|
|
function getDeployedInterchainAccount(uint32 _origin, address _sender)
|
|
public
|
|
returns (OwnableMulticall)
|
|
{
|
|
bytes32 salt = _salt(_origin, _sender);
|
|
address interchainAccount = _getInterchainAccount(salt);
|
|
if (!Address.isContract(interchainAccount)) {
|
|
bytes memory bytecode = MinimalProxy.bytecode(implementation);
|
|
interchainAccount = Create2.deploy(0, salt, bytecode);
|
|
OwnableMulticall(interchainAccount).initialize();
|
|
emit InterchainAccountCreated(_origin, _sender, interchainAccount);
|
|
}
|
|
return OwnableMulticall(interchainAccount);
|
|
}
|
|
|
|
/**
|
|
* @notice Returns the salt used to deploy the interchain account for a given `origin`/`sender` pair.
|
|
* @param _origin The origin domain of the interchain account.
|
|
* @param _sender The parent account address on the origin domain.
|
|
* @return The CREATE2 salt used for deploying the interchain account.
|
|
*/
|
|
function _salt(uint32 _origin, address _sender)
|
|
internal
|
|
pure
|
|
returns (bytes32)
|
|
{
|
|
return bytes32(abi.encodePacked(_origin, _sender));
|
|
}
|
|
|
|
/**
|
|
* @notice Returns the address of the interchain account deployed on the current chain for a given salt.
|
|
* @param salt The salt used to deploy the interchain account.
|
|
* @return The address of the interchain account.
|
|
*/
|
|
function _getInterchainAccount(bytes32 salt)
|
|
internal
|
|
view
|
|
returns (address)
|
|
{
|
|
return Create2.computeAddress(salt, bytecodeHash);
|
|
}
|
|
|
|
/**
|
|
* @notice Handles dispatched messages by relaying calls to the interchain account.
|
|
* @param _origin The origin domain of the interchain account.
|
|
* @param _message The ABI-encoded message containing the sender and the sequence of calls to be relayed.
|
|
*/
|
|
function _handle(
|
|
uint32 _origin,
|
|
bytes32, // router sender
|
|
bytes calldata _message
|
|
) internal override {
|
|
(address sender, Call[] memory calls) = abi.decode(
|
|
_message,
|
|
(address, Call[])
|
|
);
|
|
getDeployedInterchainAccount(_origin, sender).proxyCalls(calls);
|
|
}
|
|
}
|
|
|