logoAcademy

Receiving a Message

Learn to receive messages with Avalanche Interchain Messaging.

To receive a message we need to enable our cross-Subnet dApps to being called by the Interchain Messaging contract.

The Interchain Messaging does not know our contract and what functions it has. Therefore, our dApp on the destination Subnet has to implement the ITeleporterReceiver interface. It is very straight forward and only requires a single method for receiving the message that then can be called by the Interchain Messaging contract:

pragma solidity 0.8.18;
 
/**
 * @dev Interface that cross-chain applications must implement to receive messages from Teleporter.
 */
interface ITeleporterReceiver {
    /**
     * @dev Called by TeleporterMessenger on the receiving chain.
     *
     * @param originChainID is provided by the TeleporterMessenger contract.
     * @param originSenderAddress is provided by the TeleporterMessenger contract.
     * @param message is the TeleporterMessage payload set by the sender.
     */
    function receiveTeleporterMessage(
        bytes32 originChainID,
        address originSenderAddress,
        bytes calldata message
    ) external;
}

The function receiveTeleporterMessage has three parameters:

  • originChainID: The chainID where the message originates from, meaning where the user or contract called the sendCrossChainMessage function of the Interchain Messaging contract
  • originSenderAddress: The address of the user or contract that called the sendCrossChainMessage function of the Interchain Messaging contract on the origin Subnet
  • message: The message encoded in bytes

An example for a contract being able to receive Interchain Messaging messages and storing these in a mapping could look like this:

pragma solidity 0.8.18;
 
import "https://github.com/ava-labs/teleporter/blob/main/contracts/src/Teleporter/ITeleporterMessenger.sol";
import "https://github.com/ava-labs/teleporter/blob/main/contracts/src/Teleporter/ITeleporterReceiver.sol";
 
contract MessageReceiver is ITeleporterReceiver {
    // Messages sent to this contract.
    struct Message {
        address sender;
        string message;
    }
  
  	mapping(bytes32 => Message) private _messages;
 
    ITeleporterMessenger public immutable teleporterMessenger;
 
    // Errors
    error Unauthorized();
 
    constructor(address teleporterMessengerAddress) {
        teleporterMessenger = ITeleporterMessenger(teleporterMessengerAddress);
    }
 
    /**
     * @dev See {ITeleporterReceiver-receiveTeleporterMessage}.
     *
     * Receives a message from another chain.
     */
    function receiveTeleporterMessage(
        bytes32 originChainID,
        address originSenderAddress,
        bytes calldata message
    ) external {
      	// Only the Interchain Messaging receiver can deliver a message.
        if (msg.sender != address(teleporterMessenger)) {
            revert Unauthorized();
        }
      
        string memory messageString = abi.decode(message, (string));
        _messages[originChainID] = Message(originSenderAddress, messageString);
    }
 
}

This contract stores the last Message and it's sender of each chain it has received. When it is instantiated, the address of the Interchain Messaging contract is supplied to the constructor. The contract implements the ITelepoterReceiver interface and therefore we also implement the receiveTeleporterMessage function.

On this page

No Headings