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.
187 lines
7.1 KiB
187 lines
7.1 KiB
// SPDX-License-Identifier: Apache-2.0
|
|
pragma solidity ^0.8.13;
|
|
|
|
// ============ Internal Imports ============
|
|
import {OwnableMulticall} from "../OwnableMulticall.sol";
|
|
import {Router} from "../Router.sol";
|
|
import {IInterchainAccountRouter} from "../../interfaces/IInterchainAccountRouter.sol";
|
|
import {InterchainCallMessage} from "./InterchainCallMessage.sol";
|
|
import {MinimalProxy} from "../libs/MinimalProxy.sol";
|
|
import {CallLib} from "../libs/Call.sol";
|
|
import {TypeCasts} from "../libs/TypeCasts.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 {
|
|
using TypeCasts for address;
|
|
using TypeCasts for bytes32;
|
|
|
|
address internal immutable implementation;
|
|
bytes32 internal 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,
|
|
bytes32 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.
|
|
* @param _owner The address with owner privileges.
|
|
*/
|
|
function initialize(
|
|
address _mailbox,
|
|
address _interchainGasPaymaster,
|
|
address _interchainSecurityModule,
|
|
address _owner
|
|
) external initializer {
|
|
__HyperlaneConnectionClient_initialize(
|
|
_mailbox,
|
|
_interchainGasPaymaster,
|
|
_interchainSecurityModule,
|
|
_owner
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @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.
|
|
* @dev Recommend using CallLib.build to format the interchain calls.
|
|
*/
|
|
function dispatch(uint32 _destinationDomain, CallLib.Call[] calldata calls)
|
|
external
|
|
returns (bytes32)
|
|
{
|
|
return
|
|
_dispatch(
|
|
_destinationDomain,
|
|
InterchainCallMessage.format(
|
|
calls,
|
|
msg.sender.addressToBytes32()
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @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, bytes32 _sender)
|
|
public
|
|
view
|
|
returns (address payable)
|
|
{
|
|
return _getInterchainAccount(_salt(_origin, _sender));
|
|
}
|
|
|
|
function getInterchainAccount(uint32 _origin, address _sender)
|
|
external
|
|
view
|
|
returns (address payable)
|
|
{
|
|
return getInterchainAccount(_origin, _sender.addressToBytes32());
|
|
}
|
|
|
|
/**
|
|
* @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, bytes32 _sender)
|
|
public
|
|
returns (OwnableMulticall)
|
|
{
|
|
bytes32 salt = _salt(_origin, _sender);
|
|
address payable interchainAccount = _getInterchainAccount(salt);
|
|
if (!Address.isContract(interchainAccount)) {
|
|
bytes memory bytecode = MinimalProxy.bytecode(implementation);
|
|
interchainAccount = payable(Create2.deploy(0, salt, bytecode));
|
|
emit InterchainAccountCreated(_origin, _sender, interchainAccount);
|
|
// transfers ownership to this contract
|
|
OwnableMulticall(interchainAccount).initialize();
|
|
}
|
|
return OwnableMulticall(interchainAccount);
|
|
}
|
|
|
|
function getDeployedInterchainAccount(uint32 _origin, address _sender)
|
|
public
|
|
returns (OwnableMulticall)
|
|
{
|
|
return
|
|
getDeployedInterchainAccount(_origin, _sender.addressToBytes32());
|
|
}
|
|
|
|
/**
|
|
* @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, bytes32 _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 payable)
|
|
{
|
|
return payable(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 {
|
|
OwnableMulticall interchainAccount = getDeployedInterchainAccount(
|
|
_origin,
|
|
InterchainCallMessage.sender(_message)
|
|
);
|
|
interchainAccount.proxyCalls(InterchainCallMessage.calls(_message));
|
|
}
|
|
}
|
|
|