Skip to main content

MailboxClient Library

Inheriting from MailboxClient is a simple way to ensure your contract knows where to send or receive interchain messages to or from.

This mix-in contract maintains a pointers to the three contracts Hyperlane developers may need to interact with:

  1. Mailbox (required)
  2. IPostDispatchHook (optional)
  3. IInterchainSecurityModule (optional)

MailboxClient exposes functions that allow subclasses to easily send messages to the Mailbox via the mailbox storage variable, and permission message delivery via the onlyMailbox modifier.

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;

// ============ Internal Imports ============
import {IMailbox} from "../interfaces/IMailbox.sol";
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol";
import {IInterchainSecurityModule} from "../interfaces/IInterchainSecurityModule.sol";
import {Message} from "../libs/Message.sol";

// ============ External Imports ============
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

abstract contract MailboxClient is OwnableUpgradeable {
using Message for bytes;

IMailbox public immutable mailbox;

uint32 public immutable localDomain;

IPostDispatchHook public hook;

IInterchainSecurityModule public interchainSecurityModule;

uint256[48] private __GAP; // gap for upgrade safety

// ============ Modifiers ============
modifier onlyContract(address _contract) {
require(
Address.isContract(_contract),
"MailboxClient: invalid mailbox"
);
_;
}

modifier onlyContractOrNull(address _contract) {
require(
Address.isContract(_contract) || _contract == address(0),
"MailboxClient: invalid contract setting"
);
_;
}

/**
* @notice Only accept messages from an Hyperlane Mailbox contract
*/
modifier onlyMailbox() {
require(
msg.sender == address(mailbox),
"MailboxClient: sender not mailbox"
);
_;
}

constructor(address _mailbox) onlyContract(_mailbox) {
mailbox = IMailbox(_mailbox);
localDomain = mailbox.localDomain();
_transferOwnership(msg.sender);
}

/**
* @notice Sets the address of the application's custom hook.
* @param _hook The address of the hook contract.
*/
function setHook(address _hook) public onlyContractOrNull(_hook) onlyOwner {
hook = IPostDispatchHook(_hook);
}

/**
* @notice Sets the address of the application's custom interchain security module.
* @param _module The address of the interchain security module contract.
*/
function setInterchainSecurityModule(
address _module
) public onlyContractOrNull(_module) onlyOwner {
interchainSecurityModule = IInterchainSecurityModule(_module);
}

// ======== Initializer =========
function _MailboxClient_initialize(
address _hook,
address _interchainSecurityModule,
address _owner
) internal onlyInitializing {
__Ownable_init();
setHook(_hook);
setInterchainSecurityModule(_interchainSecurityModule);
_transferOwnership(_owner);
}

function _isLatestDispatched(bytes32 id) internal view returns (bool) {
return mailbox.latestDispatchedId() == id;
}

function _isDelivered(bytes32 id) internal view returns (bool) {
return mailbox.delivered(id);
}

function _metadata(
uint32 /*_destinationDomain*/
) internal view virtual returns (bytes memory) {
return "";
}

function _dispatch(
uint32 _destinationDomain,
bytes32 _recipient,
bytes memory _messageBody
) internal virtual returns (bytes32) {
return
_dispatch(_destinationDomain, _recipient, msg.value, _messageBody);
}

function _dispatch(
uint32 _destinationDomain,
bytes32 _recipient,
uint256 _value,
bytes memory _messageBody
) internal virtual returns (bytes32) {
return
mailbox.dispatch{value: _value}(
_destinationDomain,
_recipient,
_messageBody,
_metadata(_destinationDomain),
hook
);
}

function _dispatch(
uint32 _destinationDomain,
bytes32 _recipient,
uint256 _value,
bytes memory _messageBody,
bytes memory _hookMetadata,
IPostDispatchHook _hook
) internal virtual returns (bytes32) {
return
mailbox.dispatch{value: _value}(
_destinationDomain,
_recipient,
_messageBody,
_hookMetadata,
_hook
);
}

function _quoteDispatch(
uint32 _destinationDomain,
bytes32 _recipient,
bytes memory _messageBody
) internal view virtual returns (uint256) {
return
mailbox.quoteDispatch(
_destinationDomain,
_recipient,
_messageBody,
_metadata(_destinationDomain),
hook
);
}

function _quoteDispatch(
uint32 _destinationDomain,
bytes32 _recipient,
bytes memory _messageBody,
bytes calldata _hookMetadata,
IPostDispatchHook _hook
) internal view virtual returns (uint256) {
return
mailbox.quoteDispatch(
_destinationDomain,
_recipient,
_messageBody,
_hookMetadata,
_hook
);
}
}