Building Protocols with the Metalayer

The Metalayer requires that protocol developers implement two contracts - one on the source chain, and another on the destination chain. This pattern is the same regardless of if the developer is intending to perform a read or a write.

Both contracts interact with the MetalayerRouter contract on each chain — and on both sides, the developer should allowlist the specific MetalayerRouter contract on the local chain as part of their protocol’s configuration.

Sending the Message on the Source Chain

On the souce chain, the sending contract calls the dispatch function on the MetalayerRouter contract. This function takes in the destination domain, recipient address, reads, write call data, and a boolean indicating whether or not to wait for finality on the source chainbefore relaying the message.

/**
    * @notice Dispatches a message to the destination domain & recipient with the given reads and write.
    * @param _destinationDomain Domain of destination chain
    * @param _recipientAddress Address of recipient on destination chain as bytes32
    * @param _reads Read operations
    * @param _writeCallData The raw bytes to be called on the recipient address.
    * @param _useFinalized Whether or not to wait for finality before relaying the message.
    */
function dispatch(
    uint32 _destinationDomain,
    address _recipientAddress,
    ReadOperation[] memory _reads, // can be empty
    bytes memory _writeCallData,
    bool _useFinalized
) external payable;


function quoteGasPayment(
    uint32 _destinationDomain,
    uint256 _gasLimit
) public view virtual override returns (uint256);

Gas Payment

The quoteGasPayment function is used to calculate the amount of gas that will be paid for the message. This function takes in the destination domain and the gas limit, and returns the amount of gas that is needed to relay the message. This gas payment covers the cost of relaying the message as well as the gas cost of the message delivery on the destination chain. It also works for relaying messsages to chains with a different gas token than the token on your local chain. The mayment should be included in the value that is sent to the dispatch function.

Receiving the Message on the Destination Chain

On the destination chain, the receiving contract must implement the IMetalayerRecipient interface. The MetalayerRouter on the destination chain will call the handle function on the recipient contract in order to deliver the message.

/**
 * @notice Struct defining a cross-chain read operation
 * @param sourceChainId Domain of the chain to read from
 * @param sourceContract Contract address to read from as address
 * @param callDataLength Length of the call data
 * @param callData The encoded function call data
 */
struct ReadOperation {
    uint32 sourceChainId;
    address sourceContract;
    bytes callData;
}

interface IMetalayerRecipient {
    // Here, _readResults will be the results of every read in the message, in order. This will be input by the relayer.
    function handle(
        uint32 _chainId,
        address _sender,
        bytes calldata _message, // The body of the Metalayer message, or writeCallData.
        ReadOperation[] calldata _reads,
        bytes[] calldata _readResults
    ) external payable;
}