logoAcademy

Receiving a Message

Learn to receive messages with Teleporter.

To receive a message we need to enable out cross-Subnet dApps to being called by the Teleporter contract.

The Teleporter does not know out 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 Teleporter 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 Teleporter contract
  • originSenderAddress: The address of the user or contract that called the sendCrossChainMessage function of the Teleporter contract on the origin Subnet
  • message: The message encoded in bytes

An example for a contract being able to receive Teleporter 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 Teleporter 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 Teleporter contract is supplied to the constructor. The contract implements the ITelepoterReceiver interface and therefore we also implement the receiveTeleporterMessage function.

Updated:

On this page

No Headings
Edit on Github