# Introduction

Ethereum, everywhere. A decentralized and secure zkSNARK interoperability protocol.

<figure><img src="/files/W91PcXwIwgbevYXbmk7Z" alt=""><figcaption></figcaption></figure>

Telepathy is a protocol built by [Succinct](https://succinct.xyz) that enables interoperability without compromise. Using Telepathy, developers can send messages from Ethereum to any other chain with the security of Ethereum's light client protocol. For the first time, developers no longer need to trust centralized actors or permissioned multisigs when passing information from Ethereum to other chains.

**Overview**

Telepathy's core innovation is a zkSNARK circuit that verifies Ethereum validator signatures, allowing for a gas-efficient light client to run as a smart contract on any EVM chain. This core primitive allows developers to use Telepathy in a few different ways:

* Send messages from Ethereum to any EVM chain: [Cross-Chain Messaging](/build-with-telepathy/interchain-messaging)
* Request arbitrary Ethereum data from any EVM chain: [Ethereum Data Oracle](/build-with-telepathy/interchain-data-oracle)
* Access Ethereum consensus data in the execution layer: [Ethereum Consensus Oracle](/build-with-telepathy/intrachain-consensus-oracle)

**How Telepathy Works**

Check out the [Telepathy Protocol](/telepathy-protocol/overview) section for a detailed overview of how the Ethereum light client protocol works and the inner workings of Telepathy.


# Why Telepathy?

Don't trust, verify zkSNARKs.

### **The same security as Ethereum's light client protocol**

Unlike other bridging solutions that exist today, Telepathy is not secured by a [multisig](https://cointelegraph.com/news/multisigs-mean-funds-in-bridges-are-one-small-slipup-from-being-hacked) or an [optimistic fraud system](https://blog.connext.network/optimistic-bridges-fb800dc7b0e0). Instead, Telepathy uses [zkSNARKs](https://vitalik.ca/general/2021/01/26/snarks.html) to efficiently verify Ethereum validator signatures on-chain to validate information about Ethereum state without the need for centralized actors. Because we use a zkSNARK proof to verify the Ethereum light client protocol, Telepathy's security is **the same** as Ethereum's [light client protocol](https://github.com/ethereum/consensus-specs/tree/dev/specs/altair/light-client) itself.

### **Decentralized and Permissionless**

All parts of our system, including our on-chain light client and messaging layer, are designed with the intention of making the protocol decentralized and censorship-resistant. In particular, all the functions used to update the `LightClient` contract are `external` and have no access controls--meaning anyone can generate and send light client updates. Additionally, the entrypoint in the `TelepathyRouter` contract that controls execution of messages (`executeMessageFromLog`) is `external` with no access controls since the smart contract uses the Ethereum light client to verify merkle proofs that show messages were sent on the source chain.

**Developers can use Telepathy and feel safe in knowing that they do not have to trust any centralized actors in operating the protocol, making Telepathy one of the most decentralized, secure and permissionless interoperability protocols on Mainnet today.**

### Our Values

At [Succinct](https://succinct.xyz/), we firmly believe that decentralized, permissionless systems that do not require trust in centralized entities are the foundation of the blockchain substrate. Security is defined by the weakest link and the current state of interchain messaging leaves a lot to be desired. If we are building the foundations of a new global financial system, it is unacceptable to rely on a small subset of centralized actors for such a mission-critical component. The status quo of permissioned multisigs is broken and unsustainable for the long-term future of the crypto industry as a whole.

The launch of our zkSNARK Ethereum light client powering Telepathy is only the first-step in a long journey. At Succinct, our mission is to build an interoperability hyperstructure: an unstoppable, permissionless, credibly neutral and no-compromise interoperability protocol, controlled by no one company or entity.

The blockchain space is one of the few opportunities teams have to build a protocol that lasts beyond the confines of a company or even beyond any one lifetime. We want to build an interoperability protocol that lives for as long as Ethereum itself.


# Getting Started

Develop applications with Telepathy and learn about the protocol.

## Building with Telepathy

The core Telepathy innovation, a zkSNARK on-chain Ethereum light client, powers a few different methods of using the protocol.

### Cross-Chain Messaging

Send arbitrary messages from a supported source chain (Ethereum Mainnet, Goerli testnet, Gnosis) and receive them on a destination chain by integrating your contract with the Telepathy [Router](/telepathy-protocol/contracts#router). Telepathy cross-chain messaging is useful for making applications such as cross-chain governance and token-bridging.

{% content-ref url="/pages/G6FBBinHEHmMczOvcxIV" %}
[Cross-Chain Messaging](/build-with-telepathy/interchain-messaging)
{% endcontent-ref %}

### Ethereum Data Oracle

Need to fetch some Ethereum data from another chain? Use the Telepathy [Oracle](https://github.com/succinctlabs/telepathy-oracle) to send a cross-chain query request, and handle this data as a callback in your smart contract on an different chain. This feature is in beta, so please fill out [this form](https://airtable.com/shrKT6HrHi2oDcmrf) for prioritized support.

{% content-ref url="/pages/y6uKhzL7quDypwoZhQ0J" %}
[Ethereum Data Oracle](/build-with-telepathy/interchain-data-oracle)
{% endcontent-ref %}

### Ethereum Consensus Oracle

Get information about Mainnet consensus data, such as validator balances and slashings, within your smart contract by using the Telepathy [Light Client](/telepathy-protocol/contracts#light-client) and the Ethereum consensus oracle. This feature is in beta, so please fill out [this form](https://airtable.com/shrKT6HrHi2oDcmrf) for prioritized support.

{% content-ref url="/pages/FTsfJzPvFSsig0QAoOYs" %}
[Ethereum Consensus Oracle](/build-with-telepathy/intrachain-consensus-oracle)
{% endcontent-ref %}

## Protocol & Security

Gain a deeper understanding of the protocol and the underlying security implications. Gain a high-level overview of the system, and learn more about each of the individual component that make Telepathy work.

{% content-ref url="/pages/ecAktaCWSJSEU7XMIQ1z" %}
[Overview](/telepathy-protocol/overview)
{% endcontent-ref %}

## Ecosystem & Support

Have a question? We're here to help! Get in touch with the team at [Succinct](https://succinct.xyz/) and other developers on the official [Discord](https://discord.gg/succinctlabs). Fill out the [early partner form](https://airtable.com/shrKT6HrHi2oDcmrf) if you have a particular application in mind that uses Telepathy and you want prioritized support from the Succinct team.


# 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).&#x20;

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](/telepathy-protocol/contracts#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](https://github.com/succinctlabs/telepathy-contracts/blob/fe9566b7837487dfb6d39e708a2c0bfb7c52ceaf/src/amb/interfaces/ITelepathy.sol#L19) interface:

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

2\) Store the address of the [Router](/telepathy-protocol/contracts#router) inside your contract. See the full [contract address list](/resources/contract-addresses) 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

```solidity
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](https://chainlist.org/). For example, the chainId for Gnosis chain is`100`:

<pre class="language-solidity"><code class="lang-solidity">contract TelepathySenderExample {
    address TELEPATHY_ROUTER = 0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a;
    ITelepathyRouter router = ITelepathyRouter(TELEPATHY_ROUTER);

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

## Receiving Messages

1\) Store the address of the Telepathy Router inside your *destination* contract. Once again, refer to the [contract address list](/resources/contract-addresses).

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

2\) Implement the `handleTelepathy` function from the [ITelepathyHandler](https://github.com/succinctlabs/telepathy-contracts/blob/fe9566b7837487dfb6d39e708a2c0bfb7c52ceaf/src/amb/interfaces/ITelepathy.sol#L66) interface on your *destination* contract:

```solidity
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;
    }
}
```

{% hint style="danger" %}
You must require that `msg.sender` is the Telepathy Router, or else any contract can call `handleTelepathy`.
{% endhint %}

{% hint style="danger" %}
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.
{% endhint %}

{% hint style="danger" %}
You must return `ITelepathyHandler.handleTelepathy.selector`to have our contracts correctly record your message execution status.
{% endhint %}

If you would like to not worry about the above checks, use our abstract contract [TelepathyHandler](https://github.com/succinctlabs/telepathy-contracts/blob/fe9566b7837487dfb6d39e708a2c0bfb7c52ceaf/src/amb/interfaces/TelepathyHandler.sol#L5). You can refer to an example of using the abstract contract [here](https://github.com/succinctlabs/telepathy-contracts/blob/main/examples/example-counter/ExampleCounter.sol#L22).

### 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](https://airtable.com/shrKT6HrHi2oDcmrf) 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](/resources/telepathy-explorer) for message tracking.

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

[Finality](https://hackmd.io/@prysmaticlabs/finality) on Ethereum takes \~12 minutes, and there are additional time delays for security purposes. We recommend waiting \~20 minutes before troubleshooting a message.


# Example: Cross-Chain Counter

Write a counter that will increment from a source chain to a destination chain.

Below we implement an example of incrementing a counter cross-chain--commonly used as the cross-chain "Hello World".

### Contracts and Patterns

The contracts are simple: a `SourceCounter` dispatches messages to a destination chain, specifying an `amount` to increment by. Then, `TargetCounter` increments by `amount` on the destination chain.

```solidity
pragma solidity 0.8.16;

import {ITelepathyRouter} from "src/amb/interfaces/ITelepathy.sol";
import {TelepathyHandler} from "src/amb/interfaces/TelepathyHandler.sol";

contract SourceCounter {
    ITelepathyRouter router;
    uint32 destinationChainId;

    constructor(address _router, uint32 _destinationChainId) {
        router = ITelepathyRouter(_router);
        destinationChainId = _destinationChainId;
    }

    // Increment counter on target chain by given amount
    function increment(uint256 _amount, address _destinationCounter) external virtual {
        bytes memory msgData = abi.encode(_amount);
        router.send(destinationChainId, _destinationCounter, msgData);
    }
}

contract TargetCounter is TelepathyHandler {
    uint256 public counter = 0;
    
    event Incremented(address incrementer, uint256 value);

    constructor(address _router) TelepathyHandler(_router) {}

    // Handle messages being sent and decoding
    function handleTelepathyImpl(
        uint32 _sourceChainId, address _sourceAddress, bytes memory _msgData
    ) internal override {
        (uint256 amount) = abi.decode(_msgData, (uint256));
        counter += amount;
        emit Incremented(_sourceAddress, counter);
    }
}
```

The `ITelepathyRouter` interface is used to send messages from source chain to destination chain. The `TelepathyHandler` interface is used to handle messages sent to a destination chain contract from the Telepathy [Router](/telepathy-protocol/contracts#router).&#x20;

If we look closely at `SourceContract`, we see that incrementing is dispatched by a call to `router.send(...)`. We specify information about the destination chain, the sender, and the information we want to send using this call. The information we want to send across chains must be encoded bytes using `abi.encode(...)`.&#x20;

To handle messages sent to `TargetCounter`, it inherits from the `TelepathyHandler` contract and overrides the `handleTelepathyImpl(...)` method. This function is called by the `TelepathyRouter` after a message is relayed to it. The paramaters to `handleTelepathyImpl` contain the following information:

* `_sourceChainId` is the chain the message was sent on
* `_sourceAddress` is the message sender on the source chain. **Usually** receiving contracts should restrict who the `_sourceAddress` should be.&#x20;
* `_msgData` is the bytes of the message sent on the source chain

### Unit Testing

To read more about unit testing cross-chain contracts with Telepathy, please go to the [Unit Testing](/build-with-telepathy/interchain-messaging/unit-testing) section.


# Example: Cross-Chain Messaging Demo

Sending a simple string across chains.

In the [Telepathy Messager Demo](https://demo.telepathy.xyz/), we demonstrate how to concatenate together a string message that includes:

* arbitrary text
* the sender's balance
* the sender's [ENS](https://docs.ens.domains/) name (if applicable)

<figure><img src="/files/EWDI1xQQx0PtjX7OakEc" alt=""><figcaption><p>Play with the demo <a href="https://demo.telepathy.xyz">here</a>.</p></figcaption></figure>

## Contracts

The full source code for the contracts used in this demo can be found at the [Messenger Demo Github](https://github.com/succinctlabs/messenger-demo) in [contracts/src/CrossChainMailbox.sol](https://github.com/succinctlabs/messenger-demo/blob/61cd7d9a71b47e24bd9fbd2d22f7257d752a87ec/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](/telepathy-protocol/overview), so we pass in that information to the constructor:

```solidity
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.

```solidity
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).&#x20;

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](https://github.com/succinctlabs/telepathy-contracts/blob/fe9566b7837487dfb6d39e708a2c0bfb7c52ceaf/src/amb/interfaces/TelepathyHandler.sol#L5) contract:

```solidity
contract CrossChainMailbox is TelepathyHandler
```

This gives convenient functionality for ensuring that ONLY the Telepathy [Router](/telepathy-protocol/contracts#router) can call this function, and gives a function to override to receive messages. To set up this contract a [TelepathyHandler](https://github.com/succinctlabs/telepathy-contracts/blob/fe9566b7837487dfb6d39e708a2c0bfb7c52ceaf/src/amb/interfaces/TelepathyHandler.sol#L5), pass in the Router's address in the constructor:

```solidity
constructor(address _telepathyRouter) TelepathyHandler(_telepathyRouter) {}
```

Then override the [handleTelepathyImpl](https://github.com/succinctlabs/telepathy-contracts/blob/fe9566b7837487dfb6d39e708a2c0bfb7c52ceaf/src/amb/interfaces/TelepathyHandler.sol#L26) function with custom logic:

```solidity
function handleTelepathyImpl(uint32 _sourceChainId, address _sourceAddress, bytes memory _message)
    internal
    override
{
    messages.push(string(_message));
    emit MessageReceived(_sourceChainId, _sourceAddress, string(_message));
}
```

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


# Unit Testing

Recommended Telepathy testing patterns.

### Testing

To test cross-chain contracts, we leverage mocking patterns. [`MockTelepathy`](https://github.com/succinctlabs/telepathy-contracts/blob/main/src/amb/mocks/MockTelepathy.sol) simulates the behavior of `TelepathyRouter` without the overhead of generating and relaying proofs. An example is below.

```solidity
pragma solidity 0.8.16;

import "forge-std/console.sol";
import "ds-test/test.sol";
import "forge-std/Vm.sol";
import "forge-std/Test.sol";

import "src/amb/mocks/MockTelepathy.sol";

import "./ExampleCounter.sol";

contract CounterTest is Test {
    uint32 constant SOURCE_CHAIN = 1;
    uint32 constant TARGET_CHAIN = 100;

    MockTelepathy router;
    MockTelepathy receiver;
    SourceCounter source;
    TargetCounter target;

    function setUp() public {
        router = new MockTelepathy(SOURCE_CHAIN);
        receiver = new MockTelepathy(TARGET_CHAIN);
        router.addTelepathyReceiver(TARGET_CHAIN, receiver);

        source = new SourceCounter(address(router), TARGET_CHAIN);
        target = new TargetCounter(address(receiver));
    }

    function test_IncrementOne() public {
        source.increment(1, address(target));
        router.executeNextMessage();
        require(target.counter() == 1);
    }

    function test_IncrementSeveral() public {
        source.increment(2, address(target));
        router.executeNextMessage();
        require(target.counter() == 2);
        source.increment(123456789, address(target));
        router.executeNextMessage();
        require(target.counter() == 123456791);
    }

    function test_IncrementOverflow() public {
        source.increment(
            0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, address(target)
        );
        router.executeNextMessage();
        require(
            target.counter() == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
        );
        source.increment(2, address(target));
        router.executeNextMessage();
        require(target.counter() == 1);
    }
}
```

We specify our `router` and `receiver` to be `MockTelepathy` contracts that simulate the behavior of `TelepathyRouter` on the source and destination chain, respectively. We then specify our counter contracts on the source and contract chains with `source` and `target`.

To test `source` and `target`, we use `source.increment()...` to increment. Then, we *must* call `router.executeNextMessage()` to make our `MockTelepathy` routers relay the call to `target`. In doing so, we implicitly call `target.handleTelepathyImpl(...)`! Without calls to `router.executeNextMessage()`, our messages would be in a queue on the mock source chain, waiting to be relayed to the specified destination chain.

## MockTelepathy Methods

These are methods unique to [`MockTelepathy`](https://github.com/succinctlabs/telepathy-contracts/blob/main/src/amb/mocks/MockTelepathy.sol) that don't exist in `TelepathyRouter`.

* `addTelepathyReceiver(uint32 _chainId, MockTelepathy _receiver)`: This method tells a source chain router which `MockTelepathy` instance to route messages to that are sent to `_chainId`.
* `executeNextMessage()`: Broadcast next message in queue to receiver `MockTelepathy` instance

`MockTelepathy` can be used with `forge` testing. Examples can be found below:

* [Counter tests](https://github.com/succinctlabs/telepathy-contracts/blob/main/examples/example-counter/ExampleCounter.t.sol)
* [Bridge tests](https://github.com/succinctlabs/telepathy-contracts/blob/main/examples/bridge/Bridge.t.sol)
* [Uniswap example tests](https://github.com/succinctlabs/telepathy-contracts/blob/main/examples/uniswap/UniswapExample.t.sol)


# Ethereum Data Oracle

Fetch arbitrary data from other chains.

{% hint style="info" %}
This feature is in beta, so please fill out [this form](https://airtable.com/shrKT6HrHi2oDcmrf) to get in touch with the Succinct team about using it.
{% endhint %}

The Telepathy Ethereum data oracle allows developers to trustlessly request data from Ethereum Mainnet on \*\*\*\* any other chain. This can be useful, for example, to obtain data that makes cross-chain airdrops or cross-chain ENS name usage possible.

## Requesting Data

1\) To request data from a different chain, you simply call the `oracle.requestCrossChain` function. Here is an example of requesting the `ERC721.ownerOf` function for mainnet.

```solidity
telepathyOracle.requestCrossChain(
    address(mainnetContractAddress),
    abi.encodeWithSelector(IERC721.ownerOf.selector, _tokenId),
    address(myCallbackAddress)
);
```

2\) To process the data that the oracle returns, you will want to extend the `OracleCallbackBase` contract and implement the `handleOracleResponse` function. The base contract ensures that the function is only called on a valid response message.

```solidity
abstract contract OracleCallbackBase is IOracleCallbackReceiver {
    error NotFromOracle(address sender);

    address private _oracle;

    constructor(address oracle) {
        _oracle = oracle;
    }

    function rawHandleOracleResponse(
        uint256 nonce,
        bytes memory responseData,
        bool responseSuccess
    ) external override {
        if (msg.sender != _oracle) {
            revert NotFromOracle(msg.sender);
        }
        handleOracleResponse(nonce, responseData, responseSuccess);
    }

    function handleOracleResponse(
        uint256 nonce,
        bytes memory responseData,
        bool responseSuccess
    ) internal virtual;
}
```

{% hint style="info" %}
Ethereum Data Oracle is currently in an experimental state. For updates and assistence, please join our [Discord](https://discord.gg/succinctlabs).
{% endhint %}


# Example: Cross-Chain Airdrop

Allow owners of an ERC721 on Ethereum to claim something on another chain.

{% hint style="info" %}
This feature is in beta, so please fill out [this form](https://airtable.com/shrKT6HrHi2oDcmrf) to get in touch with the Succinct team about using it.
{% endhint %}

We've built a simple example contract that allows users that own an ERC721 on mainnet to receive an airdrop such as a new NFT or other tokens on another chain (ex. Polygon). The contract reads [`ERC721.ownerOf(uint256)`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/e739144cb02dbbd73256a2ae79f024faf3c7ea0a/contracts/token/ERC721/ERC721.sol#LL70C38-L70C38) from mainnet to ensure that the wallet is the correct owner.

Since the `_giveAirdrop` function is abstract, you can add any logic you want for airdrop claimers. This base contract could be used to migrate an NFT to a new chain, give tokens to claimers, or otherwise provide additional utility to ERC721 tokens on mainnet.

Here's an example that gives 1 ETH to airdrop claimers.

```solidity
contract SimpleNFTAirdrop is NFTAirdrop {
    constructor(address _nft, address _oracle)
        payable
        NFTAirdrop(_nft, _oracle)
    {
        require(msg.value == 10 ether);
    }

    function _giveAirdrop(address _to, uint256 _tokenId) internal override {
        (bool success, ) = payable(_to).call{value: 1 ether}("");
        require(success);
    }
}
```

The full source code for the contracts used in this below can be found at the Github [NFTAirdrop.sol](https://github.com/succinctlabs/telepathy-oracle/blob/main/src/examples/nft/NFTAirdrop.sol) example.


# Example: Cross-Chain ENS Resolution

Resolve an Ethereum ENS name on another chain.

What if you want to use your mainnet ENS address on other chains?

In this example, we fetch data from mainnet to resolve an ENS address on chain, then store the results into a mapping. We deploy a custom ENSUtil contract on mainnet that combines `registry.resolver(namehash).addr(namehash)` into a single function, so we only need one cross chain call.

Extra logic could be added to the callback handler, to interact with the resolved address in some way.

```solidity
pragma solidity ^0.8.14;

import {IERC721} from "openzeppelin/token/ERC721/IERC721.sol";
import {TelepathyOracle} from "src/oracle/TelepathyOracle.sol";
import {OracleCallbackBase} from "src/oracle/OracleCallbackBase.sol";
import {ENSUtil} from "src/examples/ens/ENSUtil.sol";

struct Request {
    address sender;
    bytes32 node;
}

struct ResolvedAddress {
    bool success;
    address addr;
    uint88 timestamp;
}

/// @title ENSQueryExample
/// @notice Example contract that queries mainnet to resolve an ENS name on chain
contract ENSQueryExample is OracleCallbackBase {
    address ensUtil;
    TelepathyOracle oracle;

    /// @notice Maps oracle nonce to the address that requested the claim and the requested node
    mapping(uint256 => Request) public requests;
    /// @notice Maps resolved ENS names to their addresses (could be outdated)
    mapping(bytes32 => ResolvedAddress) public addresses;

    constructor(address _oracle, address _ensUtil) OracleCallbackBase(_oracle) {
        oracle = TelepathyOracle(_oracle);
        ensUtil = _ensUtil;
    }

    function sendQuery(bytes32 _node) external returns (uint256) {
        uint256 nonce = oracle.requestCrossChain(
            address(ensUtil),
            abi.encodeWithSelector(ENSUtil.resolve.selector, _node),
            address(this)
        );
        requests[nonce] = Request(msg.sender, _node);
        return nonce;
    }

    function handleOracleResponse(
        uint256 _nonce,
        bytes memory _responseData,
        bool _responseSuccess
    ) internal override {
        address resolved;
        if (_responseSuccess) {
            resolved = abi.decode(_responseData, (address));
        }
        bytes32 node = requests[_nonce].node;
        delete requests[_nonce];
        // node => resolved
        addresses[node] = ResolvedAddress(
            _responseSuccess,
            resolved,
            uint88(block.timestamp)
        );
        // we can add extra effects here if desired
    }
}

```


# Ethereum Consensus Oracle

Consensus data made easily available.

{% hint style="info" %}
This feature is in beta, so please fill out [this form](https://airtable.com/shrKT6HrHi2oDcmrf) to get in touch with the Succinct team about using it.
{% endhint %}

You may want to develop an application that requires information about Ethereum's consensus, such as validator balances or validator slashings. For this, you can use the Telepathy [Light Client](/telepathy-protocol/contracts#light-client) to trustlessly retrieve all of this information on-chain. For the first time, you can access Ethereum consensus data in it's execution layer!


# Example: Validator Balance Data

Verify data by using a Merkle Proof against a state root.

In this example, the contract has a reference to the Telepathy [Light Client](/telepathy-protocol/contracts#light-client) and uses the state root stored for a specific `slot`.&#x20;

Using a Merkle Proof, you can then verify that the data provided is correct for the that `slot`'s state root.

```solidity
contract BeaconValidatorBalance {
    uint256 public constant VALIDATOR_PUBKEY_GINDEX = 0;
    uint256 public constant BALANCE_G_INDEX = 1;

    address lightclient;
    mapping(uint256 => bytes32) public indexToPubkey;
    mapping(uint256 => mapping(uint256 => uint256)) public indexToSlotToBalance;

    constructor(address _lightclient) {
        lightclient = _lightclient;
    }

    function addIndexAndPubkey(
        uint256 slot, uint256 index, bytes32 publicKey, bytes32[] memory proof
    ) public {
        bytes32 headerRoot = ILightClient(lightclient).headers(slot);
        bool isValidProof = SSZ.verifyMerkleBranch(
            publicKey,
            concatGIndex(VALIDATOR_PUBKEY_GINDEX, index),
            proof,
            headerRoot
        );
        require(isValidProof, "Invalid state root");
        indexToPubkey[index] = publicKey;
    }

    function addBalanceAtSlot(uint256 slot, uint256 index, uint256 balance, bytes32[] memory proof) public {
        bytes32 headerRoot = ILightClient(lightclient).headers(slot);
        bool isValidProof = SSZ.verifyMerkleBranch(
            bytes32(balance),
            concatGIndex(BALANCE_G_INDEX, index),
            proof,
            headerRoot
        );
        require(isValidProof, "Invalid state root");
        indexToSlotToBalance[index][slot] = balance;
    }
}
```


# Overview

The steps involved in arbitrary message passing with Telepathy.

<figure><img src="/files/JYziy8zfdtmi6jpoIMvB" alt=""><figcaption></figcaption></figure>

Telepathy, at its core, is an interoperability protocol for Ethereum that is secured by verifying the signatures of Ethereum validators on-chain. In this section, we will walk through at a high-level how Telepathy enables secure arbitrary messaging between Ethereum and any other chain.

## Technical Walkthrough

Alice wants to send a transaction from Ethereum that increments a counter variabe in a smart contract on Gnosis Chain. To enable Alice to accomplish her goal, the first thing Alice must do is integrate her Ethereum contract with the Telepathy [Router](/telepathy-protocol/contracts#router).

### Sending Messages

The Telepathy Router is a contract on Ethereum that allows you to send an arbitrary message to another chain using the Telepathy Protocol. This contract exposes the following function:

```solidity
function send(
    uint32 destinationChainId, bytes32 destinationAddress, bytes calldata data
)
```

The `destinationChainId` and `destinationAddress` parameters are used to determine what the destination contract is. The `data` parameter is used to specify the message that the destination contract receives.

### Proof of Consensus

Once Alice's contract has called [`send`](https://github.com/succinctlabs/telepathy-contracts/blob/fe9566b7837487dfb6d39e708a2c0bfb7c52ceaf/src/amb/SourceAMB.sol#L27), Alice must wait \~12 minutes for her transaction to [finalize](https://hackmd.io/@prysmaticlabs/finality) (this is a fundamental limitation with Ethereum itself--relaying the message any earlier leads to a chance that the transaction does not end up getting included in the canonical chain due to reorgs).\
\
A finalized block header can only be produced if a large percentage of Ethereum validators have signed a message that attests to this block header. To know that a block has been finalized, it suffices to check (among other details) that a block header has signatures from a large percentage of the Ethereum validators (details are being omitted here that will be explained in the next section). Thus, one way we can trustlessly verify Ethereum state (via the block header) on another chain is by verifying this large percentage of signatures inside a smart contract--also known as a [Light Client](https://blog.chainsafe.io/the-road-ahead-for-ethereum-light-clients-b6fdb7c3b603). Note that light clients only verify the consensus of a protocol, but do not verify the execution state transition like a full-node. \
\
Unfortunately, because verifying validator signatures is quite computationally expensive (as the EVM does not support the [elliptic curves](https://en.wikipedia.org/wiki/Elliptic-curve_cryptography) used for the BLS12-381 curve used in validator signatures), running an on-chain light client in this manner would be prehibitively expensive. To solve this problem, Telepathy utilizes a zero-knowledge succinct proof that provides a validity proof that a block header has enough signatures from the Ethereum validators. This zkSNARK can be cheaply verified on-chain, accomplishing the same goal that wasn't originally possible due to high transaction costs.

{% hint style="info" %}
A zkSNARK allows you to generate a proof that some computation has some particular output, in such a way that the proof can be verified extremely quickly even if the underlying computation takes a very long time to run. Read more about them [here](https://vitalik.ca/general/2021/01/26/snarks.html).
{% endhint %}

### Receiving Messages

By verifying these zkSNARKs that contain validity proofs of Ethereum's light client protocol, we now have secure access to Ethereum's block headers on destination chains, inside a contract known as the [Telepathy Light Client](https://google.com). Because these block headers are commitments for the entire state of Ethereum, it allows us to prove that Alice made a call to the Router with a [Merkle proof](https://eips.ethereum.org/EIPS/eip-1186).\
\
The [Relayer](/telepathy-protocol/actors#relayer) is an off-chain actor that provides these Merkle proofs for them to be verified inside the Telepathy Router contract. Once Alice's request has been verified, the Telepathy Router will pass the arbitrary message on Alice's behalf to increment the counter.

In particular, the Telepathy Router assumes that the contract that Alice specified as `destinationAddress` in her call to `send` implements this function:

```solidity
handleTelepathy(uint32 sourceChainId, address sourceAddress, bytes memory data)
```

This enables Alice to implement custom logic based on the received message.

### Security Properties

Because the block headers are only approved if they have enough validator signatures, the security of the protocol borrows directly from the honesty of the Ethereum validator set.\
\
Furthermore, it is important to note that both the [Operator](/telepathy-protocol/actors#operator) and [Relayer](/telepathy-protocol/actors#relayer) are not permissioned. Anyone can generate the zkSNARKs and anyone can provide the Merkle proofs that prove that a specific message was requested to be passed to another chain on Ethereum.

Everything is secured by checks made on-chain, and the core contract functions are also permissionless. The few permissioned access controls that do exist are outlined in our [Guardrails](/telepathy-protocol/guardrails).


# Sync Committee Protocol

The Basics of Ethereum's Light Client Protocol

<figure><img src="/files/GOUyNoW4WmmC4E1VgsIh" alt=""><figcaption><p>The basic procedure by which a Light Client learns about more recent blocks. Taken from the official annotated spec for Ethereum.</p></figcaption></figure>

## Ethereum's Light Client Protocol

In the past sections, we have made a few references to how we borrow security from Ethereum's validator set and how we generate these "proofs of consensus" that can succinctly verify Ethereum consensus on-chain.\
\
While we originally glossed over this detail for the sake of simplicity, it is important to note that our protocol is specifically secured by Ethereum's Light Client protocol, which was introduced in the [Altair hard fork](https://ethereum.org/en/history/#altair).\
\
Ethereum's Light Client protocol is secured by a group of rotating validators known as the **sync committee.** The [official annotated spec](https://github.com/ethereum/annotated-spec/blob/master/altair/sync-protocol.md#introduction) for Ethereum provides a good introductory description of its details:

> The **sync committee** is the "flagship feature" of the Altair hard fork. This is a committee of 512 validators that is randomly selected every **sync committee period (\~1 day)**, and while a validator is part of the currently active sync committee they are expected to continually sign the block header that is the new head of the chain at each slot.
>
> The purpose of the sync committee is to allow **Light Clients** to keep track of the chain of beacon block headers. The other two duties that involve signing block headers, block proposal and block attestation, do not work for this function because computing the proposer or attesters at a given slot requires a calculation on the entire active validator set, which Light Clients do not have access to (if they did, they would not be light!). Sync committees, on the other hand, are (i) updated infrequently, and (ii) saved directly in the beacon state, allowing Light Clients to verify the sync committee with a Merkle branch from a block header that they already know about, and use the public keys in the sync committee to directly authenticate signatures of more recent blocks.

The Light Client protocol uses the sync committee to reduce the computational cost of verifying the validity of Ethereum block headers. Verifying over 400k+ signatures per block can be quite expensive, even in settings outside of blockchain.\
\
It is important to note that the sync committee **does not have the same security as the entire Ethereum validator set.** In particular, there is no slashing for the sync committee, so the security of the sync committee relies on an **honesty assumption** of the sync committee validators. However, as long as a significant portion of the entire Ethereum validator set is honest, it is statistically unlikely with high probability that the sync committee will ever sign off on an invalid block header, as its honesty proportion will reflect the honesty of the entire Ethereum validator set.


# Proof of Consensus

What exactly is a "proof of consensus"? How does it work?

<figure><img src="/files/DE3lGgmmlRUS7L3koz0S" alt=""><figcaption></figcaption></figure>

To communicate information between two blockchains without a trusted intermediary, we can simply verify the consensus of the source chain in the execution environment of the destination chain. This is the exact principle that [Light Client nodes](https://www.parity.io/blog/what-is-a-light-client/) use to keep track of the state of a blockchain in a compute and storage-efficient manner.\
\
For example, running an on-chain Light Client for Ethereum on Gnosis chain would allow for keeping track of Ethereum block headers on Gnosis (and vice-versa) without any *additional* trust assumptions, aside from trusting the security of the consensus of each participating chain (in our case, the sync committee). Once an on-chain Light Client can keep track of block headers of another chain, anyone can supply state proofs to prove any information (balances, storage, transactions, events) about the source chain in the context of the destination chain. With this, building cross-chain applications, such as an arbitrary message bridge or token bridge becomes simple.\
\
Historically, this approach has been difficult because on-chain computation is quite expensive. From a gas perspective, it’s not feasible to run these on-chain Light Clients. For example, Ethereum validators in Ethereum POS consensus use [BLS signatures](https://en.wikipedia.org/wiki/BLS_digital_signature) and the EVM does not have a precompile for the BLS12-381 curve used in these signatures. This renders a naive Solidity implementation of such a Light Client prohibitively expensive.\
\
Recent advances in zero-knowledge proof systems, which allow for succinctly verifiable computation, make this approach feasible today. Similar to how zkSNARKs are powering zkEVM teams to scale execution, verifiable compute can also scale verification of consensus. In particular, “proof of consensus”, is the idea of using zero-knowledge succinct proofs to generate a validity proof of the state of a chain according to its consensus protocol. This validity proof can be used to power a gas-efficient Light Client, which facilitates trust-minimized interoperability. We note that we don’t actually use the *zero-knowledge* property of “zero-knowledge proofs”, we are using the *succinctness* property for scaling.\
\
For more information about how the zero-knowledge circuits work, read this [blog post](https://blog.succinct.xyz/post/2022/10/29/gnosis-bridge/).


# Smart Contracts

An overview of all the smart contracts used in Telepathy.

For more details about any of these contracts, please refer to the source code for the [contracts](https://github.com/succinctlabs/telepathy-contracts) and [circuits](https://github.com/succinctlabs/telepathy-circuits) respectively.

## Light Client

The [Light Client contract](https://github.com/succinctlabs/telepathy-contracts/blob/main/src/lightclient/LightClient.sol) is responsible for keeping up-to-date with the state of Ethereum. In particular, it keeps track of an append-only list of Ethereum block headers based on the verification that >2/3rds of the sync committee has provided signatures.

To add a new block header to a Light Client, one must generate a zkSNARK proof that validates the latter claim and pass the proof as an argument to the [step](https://github.com/succinctlabs/telepathy-contracts/blob/fe9566b7837487dfb6d39e708a2c0bfb7c52ceaf/src/lightclient/LightClient.sol#L94) function:

```solidity
step(LightClientStep memory update)
```

where an [`update`](https://github.com/succinctlabs/telepathy-contracts/blob/fe9566b7837487dfb6d39e708a2c0bfb7c52ceaf/src/lightclient/LightClient.sol#L15) contains all the necessary information for validation:

```solidity
struct LightClientStep {
    uint256 attestedSlot;
    uint256 finalizedSlot;
    uint256 participation;
    bytes32 finalizedHeaderRoot;
    bytes32 executionStateRoot;
    Groth16Proof proof;
}
```

This allows the Light Client to check the following conditions:

* \>2/3rds participation signatures from the current sync committee
* A valid finality proof for the `finalizedHeaderRoot`
* A valid execution state root proof against the `finalizedHeaderRoot`

If those conditions all pass, the current head of the Light Client is set to the `finalizedSlot` with the `finalizedHeaderRoot` stored on-chain.

## Router

The [Router contract](https://github.com/succinctlabs/telepathy-contracts/tree/main/src/amb) is how developers use Telepathy Protocol to achieve cross-chain communication between their contracts. It is responsible for handling both the sending and receiving of messages.

#### Sending

When the Router is used on the source chain to send messages, the entry point is the Router's [`send`](https://github.com/succinctlabs/telepathy-contracts/blob/main/src/amb/TargetAMB.sol#L104) function:

```solidity
send(uint32 destinationChainId, bytes32 destinationAddress, bytes calldata data)
```

The `destinationChainId` and `destinationAddress` parameters are used to specify the chain and the address of the destination contract. The `data` parameter is used to specify the message that the destination contract receives. The `send` function emits a `SentMessage` event, which is used for validation on the destination chain.

#### Receiving

When on the destination chain, the Router contract is responsible for relaying the message by using the light client to verify the message was actually sent on the source chain, and then calling `handleTelepathy` with the correct arguments. This happens when [`executeMessageFromLog`](https://github.com/succinctlabs/telepathy-contracts/blob/main/src/amb/TargetAMB.sol#L104) function is called:

```solidity
executeMessageFromLog(
    bytes calldata srcSlotTxSlotPack,
    bytes calldata messageBytes,
    bytes32[] calldata receiptsRootProof,
    bytes32 receiptsRoot,
    bytes[] calldata receiptProof,
    bytes memory txIndexRLPEncoded,
    uint256 logIndex
)
```

An off-chain actor, known as a [Relayer](/telepathy-protocol/actors#relayer), will call this function with the required information and Merkle proofs that are used to validate the message against the stored header root in the `LightClient`. Once validated, the message is relayed to the specified`destinationAddress` via the [`handleTelepathy`](https://github.com/succinctlabs/telepathy-contracts/blob/fe9566b7837487dfb6d39e708a2c0bfb7c52ceaf/src/amb/interfaces/ITelepathy.sol#L67)function:

```solidity
handleTelepathy(uint32 sourceChainId, address sourceAddress, bytes memory data)
```

A destination contract must implement this function to receive the message.


# Off-chain Actors

An overview of all the off-chain actors used in Telepathy.

The Telepathy protocol relies on off-chain actors to trigger on-chain contract execution:

* The [Operator](#operator) is responsible for generation zkSNARK proofs and updates for the [Light Client](/telepathy-protocol/contracts#light-client) contract.
* The [Relayer](#relayer) executes messages for the [Router](/telepathy-protocol/contracts#router) contract.

These actors are completely permissionless. [Succinct](https://succinct.xyz/) operates their own version of these actors so that the user experience of integrating with Telepathy painless, but as all `LightClient` and `TelepathyRouter` update functions are permissionless (with no access control), anyone who wishes to can run their own versions of an operator and relayer can do so.

## Operator

The Operator is responsible for keeping the Light Clients on each chain up-to-date with Ethereum's latest block headers. To accomplish this, the Operator periodically queries the sync committee for the latest finalized block header, and then generates a zkSNARK ([circuit](https://google.com)) which attests to the fact that enough of the sync committee has provided signatures for this block. The zkSNARK takes around 1-2 minutes to generate on a 32 core machine. This zkSNARK is then passed as an argument into a function called [`step`](https://github.com/succinctlabs/telepathy-contracts/blob/fe9566b7837487dfb6d39e708a2c0bfb7c52ceaf/src/lightclient/LightClient.sol#L94) within the Light Client contract for verification.\
\
The Operator is also responsible for updating the set of validators included in the sync committee within the Light Clients. The sync committee rotates every 27 hours. The Operator periodically generates a zkSNARK ([circuit](https://google.com)) which attests to the proper selection of the next sync committee. This zkSNARK is then passed into a different function called [`rotate`](https://github.com/succinctlabs/telepathy-contracts/blob/fe9566b7837487dfb6d39e708a2c0bfb7c52ceaf/src/lightclient/LightClient.sol#L116) within the Light Client contract for verification.&#x20;

All circuits and contracts are open-source and can be used to generate the above proofs in running one's own operator if desired.

## Relayer

The Relayer is responsible for relaying the arbitrary messages sent from Ethereum to the other chains. The Relayer waits for the Light Clients on each chain to be updated by the Operator, and then relays all the messages that can be validated via a Merkle proof against the latest block header in the Light Client.&#x20;

In particular, the Relayer will gather the necessary Merkle proof for each message and then pass them as an argument to the [Router ](/telepathy-protocol/contracts#router)contract's [`executeMessageFromLog`](https://github.com/succinctlabs/telepathy-contracts/blob/fe9566b7837487dfb6d39e708a2c0bfb7c52ceaf/src/amb/TargetAMB.sol#L104) function:

```solidity
function executeMessageFromLog(
    bytes calldata srcSlotTxSlotPack,
    bytes calldata messageBytes,
    bytes32[] calldata receiptsRootProof,
    bytes32 receiptsRoot,
    bytes[] calldata receiptProof,
    bytes memory txIndexRLPEncoded,
    uint256 logIndex
)
```

Assuming the Merkle proofs are valid, the message will get relayed to the correct destination contract.


# Circuits

A short primer on the zero-knowledge proofs (zkSNARKs) used in Telepathy.

We use the Circom programming language and the Groth16 proving system to generate our zkSNARKs. While a newer proof system (like PLONK arithmetization + KZG or FRI) would improve proving time, we believe Circom is the most production-ready zkSNARK stack today. In particular, Tornado Cash’s circuits are built on top of Circom and have been used for several years. Additionally, the on-chain verification cost of a Groth16 zkSNARK is the cheapest of all proving systems available today.\
\
**All zero-knowledge proofs used in Telepathy are built with the aim of making it more gas-efficient to run an Ethereum Light Client on-chain.** To properly understand the structure of the circuits, we recommend reading the sections on [Sync Committees](/telepathy-protocol/sync-committees) and [Proof of Consensus](/telepathy-protocol/proof-of-consensus).\
\
For more details, please refer to the [source code](https://github.com/succinctlabs/telepathy) itself.

## Step (Aggregate BLS12-381 Signature Verification)

To add a new block header to the Light Client, we must verify that at least two thirds of the sync committee has provided valid signatures for this block header.\
\
Thus, the bulk of the complexity inside this zkSNARK is actually involved in the verification of the specific signature scheme that Ethereum 2.0 validators use. In particular, Ethereum uses BLS12-381 signatures. Cryptographically speaking, verifying the sync committees signatures involves computing 512 elliptic curve adds over the BLS12-381 curve to get the sync committee aggregate public key, and a single elliptic curve pairing for the final BLS signature check.\
\
We also do a few other checks in the circuit, such as validating the Merkle proof for the Ethereum execution state root and checking the finality proof for the finalized block header.

## Rotate (Sync Committee Commitment Mapping)

One key detail about the Light Client contract is that it must know what the current sync committee is. While you could naively store all 512 public keys of these validators, this is extremely gas-inefficient, so a solution is to instead store a **cryptographic commitment** to the sync committee. Thankfully, the Ethereum Light Client protocol already provides such a commitment. Thus, we can pass this as an input to the Step circuit and unpack the commitment to get the public keys of the sync committee. Unfortunately, the hash function used to create this commitment (Sha256) is extremely expensive to compute inside zkSNARKs and unpacking this commitment every time we want to add a new block header can be prohibitively expensive in terms of proving time. For this reason, every 27 hours when there is a new sync committee, we unpack this commitment once and then compute a new commitment to the sync committee using a SNARK-friendly hash function known as Poseidon (within a zkSNARK).\
\
We store this SNARK-friendly commitment on-chain instead so that the Step circuit can very cheaply unpack the commitment inside its circuit. In short, the Rotate circuit maps the sync committee commitment from a SNARK-unfriendly to a SNARK-friendly one.

## Benchmarks

All benchmarks are performed on an r6a.8xlarge AWS instance which has 32 cores and 256GB of RAM.

|                    | Step        | Rotate      |
| ------------------ | ----------- | ----------- |
| Constraints        | 27M         | 68M         |
| Witness Generation | 124 Seconds | 180 Seconds |
| Proving Time       | 118 Seconds | 60 Seconds  |

## Trusted Setup Ceremony

As mentioned above, our circuits are currently using the Groth16 proving system. This proving system normally requires a process called a "trusted setup" that generates a public set of encrypted values (common reference string, aka CRS) that the circuit verifier uses to check for proof validity.

For Groth16, the trusted setup requires two phases. The first phase (referred to as "Powers of Tau") can be used for all circuits using Groth16 (and other proving system), and the second phase needs to be run for each circuit. Both of these phases have a 1 of N trust assumption, where the CRS is still secure as long as at least one of the participants is honest. For more details, please refer to this [document](https://zkproof.org/2021/06/30/setup-ceremonies/).

For the first phase for both circuits, we used [Hermez's powers of tau CRS](https://blog.hermez.io/hermez-cryptographic-setup/). Specifically, we used their [227 CRS](https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_27.ptau), which is the minimum sized one for our rotate circuit. The set of contributor public attestations for that CRS can be seen [here](https://github.com/privacy-scaling-explorations/perpetualpowersoftau).

For the second phase, we ran our own trusted setup ceremony. The details of that ceremony can be seen in this [document](https://succinctlabs.notion.site/Succinct-Trusted-Setup-eb1ee211e5c443a183888fcbf6ec0d57). We had a total of 31 contributions, and the public attestation for those contributions can be seen [here](https://github.com/succinctlabs/succinct-telepathy-ceremony-contributions/blob/main/README.md).


# Guardrails

Telepathy's approach to trust-minimized administrative functionality.

Telepathy attempts to minimize trust assumptions in the protocol by reducing the amount of permissioned functionality to only essential administrative actions. With a transparent overview of access control in Telepathy, you can be informed and fully understand the security implications of using Telepathy.

To provide a reliable arbitrary message bridge that&#x20;

* Protects user applications in the case that an exploit is found
* Stays up-to-date with future updates to the Ethereum protocol

some permissioned functionality is necessary. This functionality is limited to being called by the `Guardian` (a [Gnosis Safe](https://docs.gnosis-safe.io/) multisig) or the `Timelock` controller.

{% hint style="info" %}
For a comprehensive list of the administrative functions, see [TelepathyAccess.sol](https://github.com/succinctlabs/telepathy-contracts/blob/main/src/amb/TelepathyAccess.sol).
{% endhint %}

## Freezing

In the event that a vulnerability is found (either in the [Circuits](/telepathy-protocol/circuits) or [Contracts](/telepathy-protocol/contracts)), Telepathy will pause the functionality of receiving messages in order to protect user applications. This may be on a per-chain basis:

```solidity
function freeze(uint32 chainId) external onlyGuardian
```

or all chains simultaneously:

```solidity
function freezeAll() external onlyGuardian
```

This safety mechanism allows a potential issues to be mitigated and functionality to resume with minimal interruption. This mechanism is only intended to be called by the Guardian multisig if an exploit is suspected or found. As the protocol matures, we intend on removing this permissioned functionality.

## Upgradability

The Telepathy Router is currently upgradable. Similar to how rollups need upgradeability for changes in the Ethereum execution layer, our protocol requires upgradeability for changes in the Ethereum consensus layer. In the short-term, we believe that upgradeability should only be allowed via a Timelock, meaning that the execution of these upgrades only occur after a predetermined amount of time has passed. Our current timelock period is 7 days and controlled by the Succinct Guardian Gnosis SAFE. This time delay adds transparency to the administrative actions being taken and users can verify that the changes are not malicious. Any stakeholders that do not agree with the upgrades proposed can exit the protocol safely before it executes.&#x20;

In the longer-term, we want to decentralize this upgradeability authority beyond the Succinct team.

**Implementation**

Our upgradeability uses the OpenZeppelin's [UUPSUpgradeable](https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable) pattern, which is an audited and standardized method for changing the implementation logic of contracts via:

```solidity
_authorizeUpgrade(address newImplementation) internal override onlyTimelock
```

As mentioned, the upgrades are locked behind a [Timelock](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/3214f6c2567f0e7938f481b0a6b5cd2fe3b13cdb/contracts/governance/TimelockController.sol) controller, which means that the execution of these upgrades only occur after a predetermined amount of time has passed. Read more about why Timelocks are a principled approach to administration [here](https://blog.openzeppelin.com/protect-your-users-with-smart-contract-timelocks/).&#x20;


# Telepathy Explorer

Explore messages sent through Telepathy.

The [Telepathy Explorer](https://explorer.telepathy.xyz/) is a message explorer, showing the status of messages being sent through the Telepathy protocol. The explorer displays messages by the chain they were sent on, with the dropdown on the top-right allowing users to select the desired source chain.

<figure><img src="/files/PU1ogns816wE3pKwERW4" alt=""><figcaption></figcaption></figure>

The explorer shows the nonce, destination chain, and other information about the transactions being relayed. On the "Messages" tab, users can filter by various fields, including source and destination chain, sender and destination address, source address, and date.

Clicking on a message also reveals more information about the source and destination transaction, as well as links to respective block explorers.

## Statuses

There are 3 main message statuses on the [Telepathy Explorer](https://explorer.telepathy.xyz/):

1. **Waiting for block to finalize**

   After a transaction is sent, it still may be reorged out of the canonical chain. Ethereum consensus takes [\~12 minutes to finalize blocks](https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/#finality). Because we can only generate a proof of consensus for a finalized block, we must wait for the block that a message was sent in to finalize before we can relay the message across chains.
2. **Waiting for proof of consensus**

   After a block is finalized, someone needs to generate a zero-knowledge "proof of consensus" that enough validators in the light client protocol have signed off on that block. Generally proof generation with our circuits takes 3-4 minutes, so the user must wait until this proof is generated so that the light client can be updated on the destination chain.
3. **Waiting for relayer**

   Once the proof of consensus has been generated and the light client of the source chain has been updated on the target chain, a relayer must send a transaction to execute the sent message. The `TelepathyRouter` verifies certain Merkle proofs against the header contained in the light client to verify that the message was actually sent on the source chain. Additionally, we wait for the light client update to have been on-chain for at least 2 minutes before it can be "used" by the `TelepathyRouter` as an additional safety delay period. Generally this waiting period lasts 2-3 minutes, but can be longer if many messages have been sent recently.


# Contract Addresses

The canonical list of Telepathy contract addresses across different chains.

## Telepathy Router Addresses

There is one `TelepathyRouter` contract per chain.

<table><thead><tr><th width="263">Chain</th><th>Address</th></tr></thead><tbody><tr><td>Mainnet</td><td><a href="https://etherscan.io/address/0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a">0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a</a></td></tr><tr><td>Arbitrum</td><td><a href="https://arbiscan.io/address/0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a">0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a</a></td></tr><tr><td>Avalanche</td><td><a href="https://snowtrace.io/address/0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a">0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a</a></td></tr><tr><td>Binance</td><td><a href="https://bscscan.com/address/0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a">0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a</a></td></tr><tr><td>Gnosis</td><td><a href="https://gnosisscan.io/address/0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a">0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a</a></td></tr><tr><td>Goerli</td><td><a href="https://goerli.etherscan.io/address/0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a">0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a</a></td></tr><tr><td>Optimism</td><td><a href="https://optimistic.etherscan.io/address/0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a">0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a</a></td></tr><tr><td>Polygon</td><td><a href="https://polygonscan.com/address/0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a">0x41EA857C32c8Cb42EEFa00AF67862eCFf4eB795a</a></td></tr></tbody></table>

## Light Client Addresses

We currently have 3 source chains from which developers can send messages to the 8 destination chains listed above. Note that some chains are both a source and a destination.&#x20;

As described in [Telepathy Protocol](/telepathy-protocol/overview), for each source chain, we run a light client for that chain in a smart contract on the destination chain. For example, we run a Ethereum mainnet light client on Polygon. Because of this, each destination chain has several `LightClient` contract deployments.

For a given source chain, all the destination chain `LightClient` addresses are the same and are listed below. For example, to examine the Mainnet light client on Polygon, we can look at the Polygon block explorer [here](https://polygonscan.com/address/0xaa1383Ee33c81Ef2274419dd5e0Ea5cCe4baF6cC).

| Source Chain | Address                                    |
| ------------ | ------------------------------------------ |
| Mainnet      | 0xaa1383Ee33c81Ef2274419dd5e0Ea5cCe4baF6cC |
| Gnosis       | 0x3F5ab4ac5B4bf6c390cFa857DE5E524D48DA9c3B |
| Goerli       | 0x34b5378DE786389a477b40dD710812c250185f83 |

### Access Controls: Guardian + Timelock&#x20;

The Telepathy Router contracts are upgradeable with a 7-day timelock as noted in the [Guardrails](/telepathy-protocol/guardrails) section. The Succinct Guardian Gnosis SAFE is the only proposer and executor on these timelock contracts.&#x20;

We list the addresses of the timelock controllers on each chain that govern the Telepathy Router contract and the Guardian Gnosis SAFE addresses.

<table><thead><tr><th width="150.33333333333331">Chain</th><th width="292">Guardian</th><th>Timelock</th></tr></thead><tbody><tr><td>Mainnet</td><td>‎<a href="https://etherscan.io/address/0xd1999B562e74d9fbf57b4479b3fe8748BDF4e4A0">0xd1999B562e74d9fbf57b4479b3fe8748BDF4e4A0</a></td><td>‎<a href="https://etherscan.io/address/0xE9d09b2Fc172a6c1803eBcEeD78dD2d0eF44DdC8">0xE9d09b2Fc172a6c1803eBcEeD78dD2d0eF44DdC8</a></td></tr><tr><td>Arbitrum</td><td>‎<a href="https://arbiscan.io/address/0xdC00f2469023a7b0b1D5b6abE2F736F90955e7F3">0xdC00f2469023a7b0b1D5b6abE2F736F90955e7F3</a></td><td>‎<a href="https://arbiscan.io/address/0xD30E68b1423677bC27951e33e0EC660e4D034947">0xD30E68b1423677bC27951e33e0EC660e4D034947</a></td></tr><tr><td>Avalanche</td><td>‎<a href="https://snowtrace.io/address/0xd7B7cDD1Bf92e63844f0d4f51bBE6bE5877B3dcb">0xd7B7cDD1Bf92e63844f0d4f51bBE6bE5877B3dcb</a></td><td>‎<a href="https://snowtrace.io/address/0x34fe9F549C2686d5a055eD2189bb60c44F1c8DAb">0x34fe9F549C2686d5a055eD2189bb60c44F1c8DAb</a></td></tr><tr><td>Binance</td><td>‎<a href="https://bscscan.com/address/0x0555003589d903783C44d99c38315488db945A65">0x0555003589d903783C44d99c38315488db945A65</a></td><td>‎<a href="https://bscscan.com/address/0x5E37785D848FAC932D1c3bA10EF455338bC4Dbe6">0x5E37785D848FAC932D1c3bA10EF455338bC4Dbe6</a></td></tr><tr><td>Gnosis</td><td>‎<a href="https://gnosisscan.io/address/0xc5B66fbB215c49938061EC2C0871018B262e28ce">0xc5B66fbB215c49938061EC2C0871018B262e28ce</a></td><td>‎<a href="https://gnosisscan.io/address/0x6bba9da2eE1e812F30d78e87199F80035853774e">0x6bba9da2eE1e812F30d78e87199F80035853774e</a></td></tr><tr><td>Goerli</td><td>‎<a href="https://goerli.etherscan.io/address/0x0fba07B5a6Db9f26F36B8c2caF8f9D10c02FC3fd">0x0fba07B5a6Db9f26F36B8c2caF8f9D10c02FC3fd</a></td><td>‎<a href="https://goerli.etherscan.io/address/0x2f29633b3eE6B8aAab464a47cCdFd2218057B1D8">0x2f29633b3eE6B8aAab464a47cCdFd2218057B1D8</a></td></tr><tr><td>Optimism</td><td>‎<a href="https://optimistic.etherscan.io/address/0x54E05bDBa158a1DCda0420bc803D35c2FAf6655E">0x54E05bDBa158a1DCda0420bc803D35c2FAf6655E</a></td><td>‎<a href="https://optimistic.etherscan.io/address/0xcf4C5399f51e334A6e9e62B0986D6caE2EE09C11">0xcf4C5399f51e334A6e9e62B0986D6caE2EE09C11</a></td></tr><tr><td>Polygon</td><td>‎<a href="https://polygonscan.com/address/0x83F54A99035DD5c2E3A35f3918eF08Bd31b25A48">0x83F54A99035DD5c2E3A35f3918eF08Bd31b25A48</a></td><td>‎<a href="https://polygonscan.com/address/0xa6b3a136b7baD0C29935a25bb095380F4CCe0Aaa">0xa6b3a136b7baD0C29935a25bb095380F4CCe0Aaa</a></td></tr></tbody></table>

The signers of all of the Guardian Gnosis SAFEs are 4 members of the Succinct team with the Gnosis SAFE threshold being 3 out of 4.


# Brand Assets

Logos for Succinct / Telepathy

{% file src="/files/KTJrXat7M5tcCu8xH8tz" %}

**Usage**

We would like you to use any of the the assets 'as is'. If you need a modified version of any of the logos [reach out to us](mailto:hello@succinct.xyz) and we'll be happy to help.

Please do not use any of the Succinct / Telepathy assets as the logo or in your logo for your app or brand. Thanks!


