Cross-Chain Messaging

Learn how to send cross-chain messages with Telepathy.

With Telepathy, you can send messages from any supported source chain that uses Ethereum consensus to any destination chain that can run the Telepathy light client (currently implemented in EVM).

To use Telepathy to send a message, you need a sending contract on the source chain and a destination contract on the destination chain. We'll go through the process of writing both while integrating with the Telepathy Router.

The data contained in a cross-chain message can be anything that fits your needs:

  • simple informational strings

  • complex data structures

  • function calls

This arbitrary message passing is the basis for any kind of cross-chain application you may want to develop.

Sending Messages

1) To send a message to another chain from your sending contract, you will need to add the following ITelepathyRouter interface:

interface ITelepathyRouter {
    function send(uint32 destinationChainId, address destinationAddress, bytes calldata data)
        external
        returns (bytes32);
}

2) Store the address of the Router inside your contract. See the full contract address list to determine which address to use for a given chain. For example, on Mainnet the address is 0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a so we store this in our sending contract as

address TELEPATHY_ROUTER = 0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a;
ITelepathyRouter router = ITelepathyRouter(TELEPATHY_ROUTER);

3) Send a message using your destination contract address and chainId. To find the chainId number that corresponds to a destination chain, refer to this website. For example, the chainId for Gnosis chain is100:

contract TelepathySenderExample {
    address TELEPATHY_ROUTER = 0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a;
    ITelepathyRouter router = ITelepathyRouter(TELEPATHY_ROUTER);

    function sendMessageWithTelepathy() external {
        uint16 destinationChainId = 100;
        // A contract I deployed on Gnosis Chain
        address destinationAddress = 0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990;
        router.send(
            destinationChainId,
            destinationAddress,
            bytes("Hello world!")
        );
    }
}

Receiving Messages

1) Store the address of the Telepathy Router inside your destination contract. Once again, refer to the contract address list.

address TELEPATHY_ROUTER = 0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a;
ITelepathyRouter router = ITelepathyRouter(TELEPATHY_ROUTER);

2) Implement the handleTelepathy function from the ITelepathyHandler interface on your destination contract:

contract TelepathyRecipientExample is ITelepathyHandler {
    address TELEPATHY_ROUTER = 0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a;
    ITelepathyRouter router = ITelepathyRouter(TELEPATHY_ROUTER);
    // The contract I deployed on Ethereum
    address sourceChainContract = 0x71C7656EC7ab88b098defB751B7401B5f6d8976F;
    
    event SentMessage(uint32 srcChain, address srcSender, bytes message);
 
    function handleTelepathy(
        uint32 _sourceChainId, address _senderAddress, bytes memory _data
    ) external {
        require(msg.sender == address(router));
        require(_senderAddress == sourceChainContract);

        emit SentMessage(_sourceChainId, _senderAddress, _data);

        return ITelepathyHandler.handleTelepathy.selector;
    }
}

You must require that msg.sender is the Telepathy Router, or else any contract can call handleTelepathy.

You must require that the _senderAddress is the address of the sending contract on the source chain. Otherwise any contract on the source chain can send messages to your contract on the destination chain.

You must return ITelepathyHandler.handleTelepathy.selectorto have our contracts correctly record your message execution status.

If you would like to not worry about the above checks, use our abstract contract TelepathyHandler. You can refer to an example of using the abstract contract here.

Relaying Messages

The Succinct team currently runs a relayer that relays all messages sent on any of the supported source chains with 200k gas for message execution. If your project or team is looking for SLAs around relaying or looking to change these gas parameters, please fill out this form to discuss options with the Succinct team.

FAQ

Do these need to be separate contracts?

The sending and destination contract can be the same bytecode deployed to both chains, as long the logic for both sending and receiving is present. Alternatively, these contracts can be totally distinct if the separation of code is cleaner for your use-case.

How can I see the status of a message?

Use the Telepathy Explorer for message tracking.

How long does it take for a message to be relayed?

Finality on Ethereum takes ~12 minutes, and there are additional time delays for security purposes. We recommend waiting ~20 minutes before troubleshooting a message.

Last updated