Receive Messages

The on-chain API for receiving cross-chain messages

Receive messages from other chains by implementing the handle() method in your cross-chain application.

Interface

The handle method passes a cross-chain message to the application.

  • _origin is a Nomad domain. List of domains here

  • _nonce is unique for each message within a (origin domain, destination domain) tuple. Many apps won't need to use this field, but it can be used to ensure uniqueness of a message.

  • _sender is the address that sent the message on the origin domain

  • _message contains the contents of the cross-chain message

/*
 * @notice Receive a message from a sender on the origin domain 
 * @param _origin Domain of the origin chain
 * @param _nonce Unique nonce for the (origin, destination) tuple. 
 * @param _sender Address of sender on origin chain as bytes32
 * @param _message Raw bytes content of message
 */
 function handle(
    uint32 _origin,
    uint32 _nonce,
    bytes32 _sender,
    bytes memory _message
) external;

Hello World

The example app we know and love - but make it cross-chain 🤝

 import {TypeCasts} from "@nomad-xyz/contracts-core/libs/TypeCasts.sol";
 
 contract HelloWorld {
    // emitted when a Hello message is received   
    event Hello(uint32 origin, address sender, string memory message);
    
   /*
    * @notice Receive a Hello message from any sender :) 
    * @param _origin Domain of the origin chain
    * @param _sender Address of sender on origin chain as bytes32
    * @param _message Raw bytes content of message
    */
    function handle(
       uint32 _origin,
       uint32 _nonce,
       bytes32 _sender,
       bytes memory _message
    ) onlyReplica {
       address _sendr = TypeCasts.bytes32ToAddress(_sender);
       // emit the message so off-chain friends can see it
       emit Hello(_origin, _sendr, _message);
    }
}

Example Usage

Ready to get a little more advanced? Let's play PingPong across chains 🏓

contract PingPong {
    // registry of opponents on each chain
    mapping(uint32 => bytes32) public opponents;
    
    // emitted when a Ping volley is received
    event Ping(uint32 domain, bytes32 opponent, uint256 volleyNumber);
    // emitted when a Pong volley is received
    event Pong(uint32 domain, bytes32 opponent, uint256 volleyNumber);

    /**
     * @notice Start a PingPong match with the destination chain
     * by serving the ball.
     * @param _destination The domain to initiate the match with
     */
    function handle(
       uint32 _origin,
       uint32 _nonce,
       bytes32 _sender,
       bytes memory _message
    ) onlyReplica {
        // validate the message comes from a known opponent
        require(opponents[_origin] == _sender, "not a registered opponent!");
        // decode the message contents using abi.decode
        bool _ping;
        uint256 _volley;
        (_ping, _volley) = abi.decode(_message, (bool, uint256));
        // emit an event for your off-chain spectators
        if (ping) {
            emit Ping(_origin, _sender, _volley);
        } else {
            emit Pong(_origin, _sender, _volley);
        }
        // send a PingPong message back to your opponent!
        bytes memory _message = abi.encode(!_ping, _volley + 1);
        // dispatch your message across chains
        home.dispatch(_origin, _sender, _message);
    }
}

Advanced Examples

FAQs

Why ensure messages come from onlyReplica?

This ensures that the message is a real cross-chain message coming from the Nomad system, not an impostor!

Last updated