Example: Cross-Chain Messaging Demo

Sending a simple string across chains.

In the Telepathy Messager Demo, we demonstrate how to concatenate together a string message that includes:

  • arbitrary text

  • the sender's balance

  • the sender's ENS name (if applicable)


The full source code for the contracts used in this demo can be found at the Messenger Demo Github in contracts/src/CrossChainMailbox.sol. This contains the source contract CrossChainMailer and destination contract CrossChainMailbox.

CrossChainMailer (Source Contract)

This contract needs a reference to the Telepathy Router, so we pass in that information to the constructor:

constructor(address _telepathyRouter) {
    telepathyRouter = ITelepathyRouter(_telepathyRouter);

The entrypoint for this contract is the sendMail function, which a user (EOA) calls to send a message across chains.

function sendMail(
    uint32 _destinationChainId, 
    address _destinationMailbox, 
    bytes memory _message
) external payable
    if (msg.value < fee) {
        revert InsufficientFee(msg.value, fee);
    string memory data = StringHelper.formatMessage(
        _message, msg.sender.balance, ENSHelper.getName(msg.sender)
    telepathyRouter.send(_destinationChainId, _destinationMailbox, bytes(data));

The user calling this function specifies which destination chain they would like to send their message to (with the parameter _destinationChainId) as well as the _destinationMailbox contract address (a CrossChainMailbox deployed on the destination chain).

The body of sendMail uses the StringHelper library to piece together the information of message + balance + ENS name in one formatted string. Then we use send to send that actual message to our CrossChainMailbox through Telepathy.

CrossChainMailbox (Destination Contract)

Destination contracts should inherit from the TelepathyHandler contract:

contract CrossChainMailbox is TelepathyHandler

This gives convenient functionality for ensuring that ONLY the Telepathy Router can call this function, and gives a function to override to receive messages. To set up this contract a TelepathyHandler, pass in the Router's address in the constructor:

constructor(address _telepathyRouter) TelepathyHandler(_telepathyRouter) {}

Then override the handleTelepathyImpl function with custom logic:

function handleTelepathyImpl(uint32 _sourceChainId, address _sourceAddress, bytes memory _message)
    emit MessageReceived(_sourceChainId, _sourceAddress, string(_message));

In this case, we store the message and emit an event.

Last updated