# What is Connext?

Connext is a modular protocol for securely passing funds and data between chains. Developers can use Connext to build crosschain apps (**xApps**) - applications that interact with multiple domains (blockchains and/or rollups) simultaneously.

## Why Do We Need xApps?

Blockchains do not scale to the level of volume needed for mainstream adoption.

Ethereum and other programmable blockchains solve this problem by [moving users, funds, and data to multiple parallel domains](https://ethereum.org/en/layer-2/) (sidechains, rollups, and other chain-like constructions). This, however, creates a new challenge: a *fragmented* user experience.

Similar to how Youtube users do not need to understand Google's distributed database infrastructure, decentralized application users should not need to consciously think about what chain they are on, or how to move between chains. In other words, applications should *abstract* the multichain experience by becoming xApps, and do so while retaining the security and trust-minimization properties of the underlying chains.

## Design Philosophy

Connext is built with three core design principles in mind that make it the best option for developers looking to build xApps without compromising on trustlessness.

### Modularity

Connext utilizes a modular hub-and-spoke architecture, which derives its security from Ethereum L1 and *plugs into* the battle-tested **canonical** messaging bridges that underpin the security of each Ethereum-connected domain.

Messages in Connext are added to a merkle root generated on each spoke domain, which are then optimistically aggregated into a singular root on Ethereum L1. In the event that fraud occurs, the system falls back to using the canonical messaging bridge for each chain ecosystem. In other words, a message passed between Polygon and Optimism is secured by a proof that is posted to Ethereum and verified by the Polygon PoS bridge and Optimism rollup bridge. Similarly, a message passed within the Cosmos ecosystem is verified by IBC.

This mechanism gives developers the **best possible** trust guarantees for whichever chains they want to build on. For fraud to occur in our Polygon to Optimism example, there would need to be a compromise of Polygon or Optimism's canonical bridges *and* Connext's failsafe mechanisms.

### Security

Bridges and crosschain messaging are some of the most critical infrastructure in the space, with a high potential risk for catastrophe in the event of hacks or bugs. In the past 18 months, almost **$2B** has been lost to bridge hacks.

Connext utilizes Watchers, automated offchain actors, that observe the network and halt message passing in the event that fraud or a hack is observed. This ensures that damage due to the failure of any individual part of the network is minimized.

Additionally, Connext follows a strict philosophy of secure development, requiring rigorous external review for code changes and working closely with the security community to educate auditors on bridge security risks and collaborate on creating best practices.

### Simplicity

Migrating to a completely new multichain development workflow can be challenging.

Rather than reinventing the wheel, Connext tries to mimic and extend existing development patterns as much as possible. The protocol implements a single, simple primitive - `xcall` - which allows devs to asynchronously interact with contracts living on another chain/rollup similarly to how you would `call` a contract living on the same chain/rollup.

Connext additionally plugs into the tooling and supporting infrastructure that is *already* widely used in the ecosystem. Have a development workflow that isn't well supported by Connext? [Let us know](https://discord.gg/pef9AyEhNz) and our community can help build support for it!

## What Can I Build With Connext?

In short, pretty much anything! Here are some of the ideas that members of our community are working on:

* Executing the outcome of **DAO votes** across chains
* Lock-and-mint or burn-and-mint **token bridging**
* **Aggregating DEX liquidity** across chains in a single seamless transaction
* Crosschain **vault zaps** and **vault strategy management**
* **Lending funds** on one chain and borrow on another
* Bringing **UniV3 TWAPs** to every chain without introducing oracles
* **NFT bridging** and chain-agnostic NFT marketplaces
* **Storing data on Arweave/Filecoin** directly from within an Ethereum smart contract

## Where Do I Go Next?

If the above information was overwhelming, don't worry! We have plenty more resources to help you learn about Connext:

* If you're a developer and want to start building with `xcall`, check out our Developer Quickstart and Examples.
* Want to learn more about how the protocol works? Read through the How Connext Works section.
* Are you an infrastructure operator? Help run the network by operating a Connext router.
* Still not sure where to go? Join our [Discord](https://discord.gg/pef9AyEhNz) server and chat with us in the `#dev-hub` channel!


# How It Works

{% content-ref url="/pages/VO6do0M1oI3r4GFxz3u9" %}
[Architecture](/concepts/how-it-works/architecture)
{% endcontent-ref %}

{% content-ref url="/pages/u7hTxsZbSvwM2huuU3YR" %}
[Transaction Lifecycle](/concepts/how-it-works/transaction-lifecycle)
{% endcontent-ref %}


# Architecture

The Connext protocol is composed of a set of smart contracts and offchain agents.

## Smart Contracts

Connext's smart contracts are the interfaces between the protocol and the wide gamut of different users in the ecosystem. There are contracts for handling `xcall`, managing asset registration, and provisioning liquidity for routers and stableswap LPs.

The full contract stack consists of the following components.

### Connext

Dispatches and handles messages related to sending funds across chains. Custodies funds for canonical assets, fast liquidity, and stable swaps.

The Connext contract uses the [Diamond](https://eips.ethereum.org/EIPS/eip-2535) pattern so it comprises a set of Facets that act as logical boundaries for groups of functions. Facets share contract storage and can be upgraded separately.

<details>

<summary>Diamond Facets</summary>

#### TokenFacet

Manages asset enrollment, stores mappings of adopted <-> local assets, exposes liquidity caps functions, and specifies stableswaps for assets.

#### BridgeFacet

Implements `xcall` and enables destination-side calldata execution.

#### InboxFacet

Holds all the functionality needed for Connext's messaging layer to reconcile cross-chain transfers.

#### ProposedOwnableFacet

Provides a basic access control mechanism.

#### RelayerFacet

Manages whitelisting of relayers.

#### RoutersFacet

Manages whitelisting of routers and keeps track of router owners/recipients.

#### StableSwapFacet

A StableSwap implementation that custodies closely pegged assets (eg. group of stablecoins).

#### SwapAdminFacet

Manages only-admin controls for the StableSwapFacet.

#### DiamondCutFacet

Functions for adding, removing, and replacing facets.

#### DiamondLoupeFacet

Required by the Diamond standard. Implements the DiamondLoupe interface which allows for inspection of a Diamond contract's various facets and their functions.

</details>

### Messaging

The various contracts required to manage merkle roots containing hashed transfer data and send them through a hub-and-spoke architecture. The messaging architecture includes:

* **Connector.** A connector is an abstraction around an underlying transport layer. The `IConnector` interface requires a `processMessage` method implemented for handling incoming messages. `Connector` is an abstract contract that is inherited by the following contracts:
  * **SpokeConnector.** The `SpokeConnector` is deployed on spoke domains and implements a `send` method to send the Merkle root of all the messages that originate from the spoke domain to the hub domain. For example, `ArbitrumSpokeConnector` is deployed on the Arbitrum L2.
  * **HubConnector.** The `HubConnector` is deployed on hub domains for each spoke and implements a `sendMessage` method to send the aggregated Merkle root of all the received spoke Merkle roots to the configured destination domain. For example `ArbitrumHubConnector` is deployed on Ethereum L1.

{% hint style="info" %}
Each AMB implementation requires us to create and deploy `HubConnector` and `SpokeConnector` contracts for that flavor of AMB, calling into the internal bridge infrastructure.
{% endhint %}

## Offchain Agents

### Routers

Routers are liquidity providers that enable instant liquidity for the user on the destination chain in return for a fee. Anybody can participate in the protocol as a router and there is no minimum liquidity required! Routers provide a crucial service to the Connext protocol.

Learn how to run one in the [Routers](/routers/routers-intro) section.

### Sequencer

The sequencer collects bids from all chains and randomly selects router(s) to fulfill them. Any number of routers can fulfill a single transaction, which is especially useful for large transfers. The sequencer will post batches of these bids to a relayer network to submit them to chain.

### Relayers

Relayers are a decentralized network of infrastructure operators that can execute smart contract transactions on behalf of a user in exchange for a small fee. Because the last leg of a cross-chain transaction requires execution on the destination domain, relayers play an important role in completing the full flow.

We are currently using [Gelato](https://www.gelato.network/) as our relayer service.


# Transaction Lifecycle

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

In this diagram we observe two domains, "origin" and "destination", representing the chains that a cross-chain message will originate from and travel to.

We also see a differentiation between "fast path" and "slow path".

***

## Fast Path

### Requirements

For a cross-chain message to travel through the fast path, it must abide by **BOTH** of these requirements:

1. The transaction is bridging tokens *only* (no calldata) OR the calldata included is unauthenticated (anyone is allowed to call the function on the target contract).

*AND*

2. Routers are providing sufficient liquidity of the bridged token on the destination domain. We sometimes refer to this as the availability of "fast liquidity". If fast liquidity is *not* available, then the message will go through the slow path.

### Examples

* A simple token bridge (like the [Connext Bridge](https://bridge.connext.network/))
* Send funds from origin to destination and then execute a Uniswap `swap()` on destination

### How it works

Connext is able to shortcut the normal AMB messaging delay (sometimes hours or days of latency!) by allowing its network of routers to **front the capital to the user** on the destination domain. Routers wait out the AMB latency in the user's stead, allowing the user to receive funds almost immediately. In exchange for taking on the risk of this temporary liquidity lockup, routers are compensated with a small fee. Note that at the end of the waiting window, routers always get reimbursed.

***

## Slow Path

### Requirements

Essentially the inverse of fast path; if **ANY** of these apply to the cross-chain message, it will travel through the slow path:

1. The transaction includes authenticated calldata (the destination function checks the originating caller from the origin domain).

*OR*

2. There is insufficient router liquidity of the bridged token on the destination domain.

### Examples

* Execute DAO votes across chains
* Change protocol settings from any chain
* Generally, do anything that has an `onlyOwner` modifier or equivalently must validate the origin caller

### How it works

The message takes on the full AMB delay, allowing the AMB verification process to complete. This is why slow path messages can trust that the origin caller is correct since data integrity is maintained.

***

## Detailed Flow Summary

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

A transaction flowing through Connext will have the following lifecycle:

* User will initiate the transaction by calling an `xcall` function on the Connext contract, passing in funds, gas details, arbitrary data, and a target address object (includes chain info).
  * *Note: `xcall` is meant to mimic solidity's lower level call as best as possible.*
* The Connext contracts will:
  * If needed, swap the passed in token to the AMB version of the same asset.
  * Call the AMB contracts with a hash of the transaction details to initiate the 60 minute message latency across chains.
  * Emit an event with the transaction details.
* Routers observing the origin chain with funds on the destination chain will:
  * Simulate the transaction (if this fails, the assumption is that this is a more "expressive" crosschain message that requires authentication and so must go through the AMB: the slow path).
  * Prepare a signed transaction object using funds on the receiving chain.
  * Post this object (a "bid") to the sequencer.
  * *Note: if the router does not have enough funds for the transfer, they may also provide only part of the transfer's value.*
* The sequencer will be observing all of the underlying chains. Every X blocks, the sequencer will collect bids for transactions. The sequencer will be responsible for selecting the correct router (or routers!) for a given transaction (can be random). The sequencer will post batches of these bids to a relayer network to submit them to chain.
* When a given bid is submitted to chain, the contracts will do the following:
  * Check that there are enough funds available for the transaction.
  * Swap the router's AMB-flavored funds for the canonical asset of the chain if needed.
  * Send the swapped funds to the correct target (if it is a contract, this will also execute `calldata` against the target).
  * Hash the router's params and store a mapping of this hash to the router's address in the contract.
    * *At this point, the user's transaction has already been completed!*
* Later, when the slow path message arrives, a heavily batched transaction can be submitted to take all pending hashes received over the AMB and look up whether they have corresponding router addresses in the hash -> router address mapping. If they do, then AMB assets are minted and given to the router.
  * *Note: if the router gives the incorrect amount of funds to a user or if they execute the wrong calldata, then the router's param hash will not match the hash coming over the AMB and the router will not get reimbursed. This is the core security mechanism that ensures that routers behave correctly.*
  * *Note: Routers will take a 60 minute lockup on their funds when relaying transactions. While this theoretically reduces capital efficiency compared to the existing system, in practice the lack of need to rebalance will mean that routers have more capital available more often regardless.*


# Background

{% content-ref url="/pages/8ttb4vT7D1un0H7CHr86" %}
[What is a Bridge?](/concepts/background/what-is-a-bridge)
{% endcontent-ref %}

{% content-ref url="/pages/gIRQj4Tj97RWa297j5rx" %}
[Modular Bridges](/concepts/background/modular-bridges)
{% endcontent-ref %}

{% content-ref url="/pages/xcxDuZlRQBzO4wLZEX0d" %}
[Message Verification](/concepts/background/verification-mechanisms)
{% endcontent-ref %}


# What is a Bridge?

A bridge (AKA crosschain messaging protocol or interoperability network) is a system that relays information between blockchains.

While there are many bridges out there today, every bridge has the same core structure and components.

| **Layer**    | **Function**                                                                                                      |
| ------------ | ----------------------------------------------------------------------------------------------------------------- |
| Transport    | Read root or hash of data on origin chain and post to destination.                                                |
| Verification | Ensure that the above data is correct.                                                                            |
| Execution    | Generate merkle roots or hashes on origin. Prove against the root and execute the target function on destination. |
| Application  | Handle specific usecases such as token transfers, NFTs, governance, and more!                                     |

## Messaging Layers

### Transport

Transport is how a payload of data gets read from one domain and posted to another, making no assumptions about the correctness of the data.

This is typically done by one or many offchain actors that watch an *outbox* of data on the origin chain, and then post the corresponding data to an *inbox* on a connected destination domain. To keep this process scalable, protocols will typically relay merkle roots rather than data in raw form.

### Verification

Verification is how crosschain communication is *secured*. After a payload is transported across chains, a bridge will verify the data prior to it becoming usable on the destination domain.

There are many different ways to verify messages across domains, each with their own tradeoffs on trust, cost, and latency. See Message Verification for a longer discussion about this step.

### Execution

Once a verified payload is available on the destination, some offchain infrastructure is needed to "push" that payload into a target function.

Bridge execution layers are the interface that developers will typically interact with when integrating with a bridge. Execution layers collect gas fees (in Connext, this is collected as additional gas paid on the origin chain) and use those fees to execute a transaction on the destination chain against the target function the developer intends to interact with.

Execution layers may additionally be responsible for generating merkle roots on each origin domain, and then generating a merkle proof against that root on a destination domain.

## Application Layers

In addition to the above messaging layers, bridges may implement one or many **application layers** that define how specific usecases are enabled across domains.

## Additional Resources

* [The Messaging Bridge Stack](https://blog.connext.network/the-messaging-bridge-stack-a9ae849858e0)


# Modular Bridges

The vast majority of generalized bridges out there today are *monolithic*. This means that they are tied to a specific verification mechanism that is implemented as a core part of the bridge construction.

Connext is the first example of a generalized crosschain messaging mechanism that is *modular*, plugging into the best available verification method for a given ecosystem.

## Clusters

To understand why Connext is designed the way that it is, let us first explore the concept of [Clusters](https://blog.celestia.org/clusters/).

A cluster is a sovereign set of domains that share security and can communicate with one another using trust-minimized methods. For example, Ethereum and its rollups form a cluster. Cosmos chains are another cluster. Singular "monolithic" chains, such as Solana are also their own cluster.

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

In the [Message Verification](/concepts/background/verification-mechanisms) section, we broke down the different methods that exist to secure a message that travels between two domains. As we noted, the **best available mechanism** for verifying messages is different based on the specific pair of domains we want to communicate between. Another way to say this is that there is a *heterogenous* topology for message verification mechanisms across all networks.

For example, between a rollup and its L1, the most secure verification mechanism is to use the rollup bridge itself.

{% hint style="danger" %}
The security of a domain is always the security of its weakest link. This means that *any* method of passing messages to a rollup that isn't the rollup bridge introduces at least *some* trust assumptions and security overhead, which in turn weakens the benefit of using a rollup (vs a less secure domain such as a sidechain) in the first place.
{% endhint %}

On the other hand, the most secure way to verify a message that passes between two discrete chains is a light client (zk or otherwise). Light client implementations exist in some places, but not everywhere yet - and it's highly unlikely that a single light client protocol will "win" every single pathway between sovereign chains.

## Pluggable Verification

Modular bridges make the verification layer (and potentially other parts!) of the [bridging stack](/concepts/background/what-is-a-bridge) pluggable. By doing this, they can leverage *existing* methods of message verification wherever possible. This gives the best possible security for applications that may want to interact with multiple (heterogenous) domains simultaneously.


# Message Verification

{% hint style="info" %}
We recommend checking out the [What is a Bridge?](/concepts/background/what-is-a-bridge) section if you haven't already!
{% endhint %}

The most important component of any crosschain messaging system is how messages are *verified* across domains. There are a number of different verification methods, [each with their own tradeoffs](https://blog.connext.network/the-interoperability-trilemma-657c2cf69f17).

## Summary Table

|                   | **External**                                                                                           | **Optimistic**                                                                                                                                                           | **Local**                                                                                                                       | **Native**                                                                                                          | **Rollups**                                                                                               |
| ----------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Mechanism         | External actors attest message correctness.                                                            | Messages incur 30 min delay and are disputable by Watchers.                                                                                                              | Messages are verified by the parties who they affect. E.g. a swap is verified by both counterparties.                           | Messages are verified directly by chain validator set, typically using a light client + using ZKPs for scalability. | Special case: L1 runs a "full node" directly verifying state transitions (not just consensus!) of rollup. |
| Trust assumptions | Fully assumes honesty of verifier set. PoS systems have better security than MPC or Oracle/Relay ones. | Assumes that at least 1 Watcher is online, honest, and able to send a transaction to chain.                                                                              | Assumes liveness of all parties within a certain period to verify message.                                                      | Light clients verify *consensus* but not state - assume honest relayer can give them the correct block to verify.   | Reasonable/minimal assumptions around L1 censorability and ZK proof schema.                               |
| Tradeoffs         | Most trusted option compared to underlying chains.                                                     | (1) Message latency - can be combined with local verification to cut latency in many cases. (2) Censorship risk of Watchers - can be mitigated with economic incentives. | Doesn't really scale beyond 2 parties. This means only *some* types of messages are supported - see Authentication for details. | Difficult to build + needs custom work for each chain. Doesn't really work for rollups.                             | Proving state transitions only works for rollups, not between two separate chains.                        |
| Examples          | LayerZero, Celer, Multichain, Axelar                                                                   | Nomad, Hyperlane, Hop (also local), Across (also local)                                                                                                                  | Connext v0, Hop (also optimistic), Across (also optimistic)                                                                     | IBC, Succinct Labs, zkBridge, zkIBC                                                                                 | Optimism, Arbitrum, ZkSync, Starkware                                                                     |

## External

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

Externally verified bridges rely on an 3rd-party set of actors to attest the correctness of data that is transported between chains. This is typically represented as an multisig, MPC system, PoS validator set, or oracle/relay.

External verification is easy to extend to any chain, and can support generalized messages at low latency. However, the security of a message passed between chains *fully* relies on the verifier set - this means that security scales with the size of the set. In most cases, externally verified bridges have far less security than the underlying chains, implying that they are not trust-minimized.

{% hint style="info" %}
External bridges with large validator sets and staked incentives (e.g. PoS systems) are more secure than their smaller counterparts.
{% endhint %}

## Optimistic

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

[Optimistic bridges](https://blog.connext.network/optimistic-bridges-fb800dc7b0e0) use **fraud proofs** to ensure the validity of data relayed across chains. Every message that passes through an optimistic bridge remains in a “pending” state during the dispute window until it is considered valid. During this time, **watchers** can dispute the message if the data is incorrect.

Optimistic bridges assume that there is at least one Watcher online somewhere (1-of-n assumption) that can send a transaction to chain. They also assume that Watchers will not abuse their position to censor the system. Censorship risk can be minimized using economic (dis)incentives.

## Native

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

In a natively verified bridge, a chain's own validator set verifies information coming in from another chain. This happens by implementing a [light client](https://geth.ethereum.org/docs/fundamentals/les) of one chain within the VM of another, typically within a zk-snark to keep expensive computation offchain.

Because light clients verify *consensus* but not the state transitions of the block itself (the latter requires [Data Availability](https://ethereum.org/en/developers/docs/data-availability/)), they assume the existence of an **honest relayer** who can [give them the "correct" block from a connected chain](https://blog.connext.network/validity-proofs-are-not-effective-for-bridging-blockchains-85b5e3b22a35). This makes them vulnerable to attacks where the validators of one chain produce an invalid block (for example, through a 51% attack) to spoof a message through the bridge.

{% hint style="info" %}
This honest relayer assumption of light client based bridges is the key point behind Vitalik's often-discussed [Multichain not Crosschain](https://twitter.com/VitalikButerin/status/1479501366192132099?s=20) post.
{% endhint %}

Natively verified bridges have an additional tradeoff in that they require custom work for each chain with a new consensus mechanism. For some domains, for example with rollups, there may not be a light-client based strategy for bridging that works without additional trust tradeoffs.

Despite the above, natively verified bridges (regardless of whether they use ZKPs) are the best mechanism for moving data between two discrete blockchains.

## Special Case: Local

<figure><img src="/files/5uJmulNvI0o3nqMUnoUs" alt=""><figcaption></figcaption></figure>

Locally verified bridges are a special case of bridging where only the parties involved in a given cross-domain interaction verify the interaction. This is typically done through schemes like atomic swaps.

The challenge of locally verified bridges is that they require the liveness of all parties involved in a transaction, degrading UX and making it impractical to execute transactions that involve more than 2 parties. This further means that only some types of transactions are supported. For example, a peer-to-peer transfer is possible because there are only two parties involved, but many types of peer-to-contract interactions would not be possible as *every* chain user would be involved.

Local verification techniques are typically used in conjunction with other mechanisms such as optimistic bridges to improve latency. You can learn more about what types of messages can be executed immediately through local verification in our Authentication section.

## Special Case: Rollups

Rollups are another special case. Rollups exist specifically to solve for the "honest relayer" assumption that exists with light client based bridges. They do this by creating a strict heirarchy between the parent chain (the L1) and the rollup, with all rollup data being available on the L1. This allows the L1 to run a "full client", directly verifying the state transitions of the rollup itself.

Rollup bridges are **the most secure** mechanism to communicate between any two domains. Period.

However, they can only exist between a rollup and its L1. See our section on Intercluster vs Intracluster communication for details.


# Use Cases

{% content-ref url="/pages/X3BHcatHk8rlJEISTf62" %}
[xERC20](/usecases/xerc20)
{% endcontent-ref %}

{% content-ref url="/pages/tvmDd8DqMrrGJIRLbawO" %}
[xGovernance](/usecases/xgovernance)
{% endcontent-ref %}

{% content-ref url="/pages/m9C1BnWlk6UmQlOxX5wU" %}
[Chain Abstraction](/usecases/chain-abstraction)
{% endcontent-ref %}


# FAQ

## Where can I find the the supported chains, assets, domainIDs, and Connext contracts?

An updated list can be found in [Deployments](broken://pages/bbG6im4BlayCJRkYxOkN).

## What are token "flavors"?

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

## How do I find the canonical details of a token?

The canonical domainId and tokenId of a token can be found by calling the [`getTokenId`](https://github.com/connext/monorepo/blob/3d0af2251b2d8d244d2617be6fb738c09a571022/packages/deployments/contracts/contracts/core/connext/helpers/TokenRegistry.sol#L176) function of `TokenRegistry`.

Example:

* The token of interest is TestERC20 (`0x7ea6eA49B0b0Ae9c5db7907d139D9Cd3439862a1`) on Goerli. We want to figure out its canonical domainId and tokenId.
* Find the Connext contract address on Goerli [from here](broken://pages/bbG6im4BlayCJRkYxOkN#goerli-domain-id-1735353714-chain-id-5), click its link to open up the Diamond Inspector on Louper.
* Find the `TokenFacet` and click the "Read" button.
* Select the `getTokenId` method and input the TestERC20 address to obtain its canonical details.

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

* Alternatively, you can call `getTokenId` using a tool like Foundry's `cast` to read from the contract.

  ```bash
  cast call --chain goerli 0x99A784d082476E551E5fc918ce3d849f2b8e89B6 "getTokenId(address)(uint32,bytes32)" "0x7ea6eA49B0b0Ae9c5db7907d139D9Cd3439862a1" --rpc-url <goerli_rpc_url>
  ```

  Returns:

  ```bash
  1735353714 # the canonical domainId is Goerli
  0x0000000000000000000000007ea6ea49b0b0ae9c5db7907d139d9cd3439862a1 # the canonical bytes32 tokenId
  ```

## What if I just want to test the destination-side target function?

If there’s no token transfer involved then just set `transactingAssetId: address(0)` and `amount: 0`.

## Do I need to do anything with the AMB contracts?

No, you do not need to deploy or even interact with AMB contracts directly.


# Introduction

Connext provides the simplest possible experience for building xchain applications (**xApps**).

Building a xApp requires only two straightforward steps:

1. Implement `xReceive` in the destination chain contract. This is the function that receives the payload you pass across chains.
2. Call `xcall` on the origin chain, passing in your payload and relayer fees.

Get started with the [Quickstart](/developers/quickstart)!

***

## Important Concepts

### Fast Path vs. Slow Path

Take a moment to review the [Transaction Lifecycle](/concepts/how-it-works/transaction-lifecycle). Here we introduce the concept of "fast path" and "slow path" (authenticated) transfers. The differentiation is crucial to understand for any cross-chain project. Then, try it out with our [Authentication guide](/developers/guides/authentication).

### Relayer Fees

Check out our [guide on relayer fees](/developers/guides/estimating-fees) and how to estimate them.

### Handling Failures

You should always build in contingency for [failed calls](/developers/guides/handling-failures).

### Tracking xCalls

Dive into the c[urrent status of an `xcall`](/developers/guides/xcall-status).

### Nested xCalls

You can even [chain `xcall`](/developers/guides/nested-xcalls)`s` across domains! :open\_mouth:

### Chain Abstraction

Create [seamless cross-chain interactions](/usecases/chain-abstraction/chain-abstraction-guide) without having to switch chains! 🤯

***

## Help

Have questions or need support? Our core team and vibrant community members are highly active in our Discord server!

[Chat with us!](https://discord.gg/everclear)


# Quickstart

## Quickstart (Greeter)

This quickstart will teach you how to use `xcall`, the cross-chain communication primitive, to send funds and data across chains.

***

### Introduction

In this guide, we will build a cross-chain Greeter. The `DestinationGreeter` contract on the destination chain has an `updateGreeting` function that changes a stored `greeting` variable. The `SourceGreeter` contract on the origin chain uses `xcall` to send encoded calldata for `updateGreeting`.

To demonstrate a combination of an asset transfer and an arbitrary call in a single `xcall`, the `updateGreeting` function will require a payment to update the greeting. For this example, the contract will be okay with any amount greater than 0.

`updateGreeting` is implemented as an unauthenticated call (there are no checks to determine *who* is calling the function). Therefore, this type of `xcall` will be go through the "Fast Path".

{% hint style="success" %}
If you prefer to fork a repo instead of following this step-by-step guide, our [xapp-starter](https://github.com/connext/xapp-starter) kit contains a full example of this quickstart (plus more) and is compatible with both Hardhat and Foundry.
{% endhint %}

### Prerequisites

* Node v18 installed

  Follow the instructions to install [Node.js](https://nodejs.dev/en/learn/how-to-install-nodejs/) and use **Node.js v18**. We also recommend installing `nvm`, a node version manager, which will make switching versions easier.
* An Ethereum development environment like Foundry, Hardhat, Truffle, etc.

  This guide will be using Hardhat. Follow the instructions to install [Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started#overview).
* If you don't already have gas funds on Goerli, try these faucets to get some:
  * <https://goerli-faucet.mudit.blog/> (Requires Twitter account)
  * <https://goerlifaucet.com/> (Requires signing up with Alchemy)

### Create a new project

Create a new project by running the following command:

```bash
$ npx hardhat
888    888                      888 888               888
888    888                      888 888               888
888    888                      888 888               888
8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
888    888     "88b 888P"  d88" 888 888 "88b     "88b 888
888    888 .d888888 888    888  888 888  888 .d888888 888
888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888

👷 Welcome to Hardhat v2.12.1 👷‍

? What do you want to do? …
❯ Create a JavaScript project
  Create a TypeScript project
  Create an empty hardhat.config.js
  Quit
```

Choose a Javascript project. Choose `y` on all of the prompts.

Install the latest version of Connext contracts package in your project:

```bash
npm install @connext/interfaces
```

Next, install the OpenZeppelin contract package:

```bash
npm install @openzeppelin/contracts
```

You'll need to manually install the library `@openzeppelin/contracts-upgradeable`

```bash
npm install @openzeppelin/contracts-upgradeable 
```

Install `dotenv` to protect your private key needed to deploy your contract:

```bash
npm install dotenv
```

In the root of your project, create a new `.env` file. Here you will store your private key used to deploy your contract.

Update `.env` with the following line:

```console
PRIVATE_KEY = YOUR-PRIVATE-KEY-HERE
```

***

### Source Contract

The source contract initiates the cross-chain operation with `xcall` and passes the encoded greeting into the call. All `xcall` params are detailed here.

In the `/contracts` directory, create a new contract called `SourceGreeter.sol`:

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;

import {IConnext} from "@connext/interfaces/core/IConnext.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * @title SourceGreeter
 * @notice Example source contract that updates a greeting on DestinationGreeter.
 */
contract SourceGreeter {
  // The Connext contract on this domain
  IConnext public immutable connext;

  // The token to be paid on this domain
  IERC20 public immutable token;
  
  // Slippage (in BPS) for the transfer set to 100% for this example
  uint256 public immutable slippage = 10000;

  constructor(address _connext, address _token) {
    connext = IConnext(_connext);
    token = IERC20(_token);
  }

  /** @notice Updates a greeting variable on the DestinationGreeter contract.
    * @param target Address of the DestinationGreeter contract.
    * @param destinationDomain The destination domain ID.
    * @param newGreeting New greeting to update to.
    * @param relayerFee The fee offered to relayers.
    */
  function xUpdateGreeting (
    address target, 
    uint32 destinationDomain,
    string memory newGreeting,
    uint256 amount,
    uint256 relayerFee
  ) external payable {
    require(
      token.allowance(msg.sender, address(this)) >= amount,
      "User must approve amount"
    );

    // User sends funds to this contract
    token.transferFrom(msg.sender, address(this), amount);

    // This contract approves transfer to Connext
    token.approve(address(connext), amount);

    // Encode calldata for the target contract call
    bytes memory callData = abi.encode(newGreeting);

    connext.xcall{value: relayerFee}(
      destinationDomain, // _destination: Domain ID of the destination chain
      target,            // _to: address of the target contract
      address(token),    // _asset: address of the token contract
      msg.sender,        // _delegate: address that can revert or forceLocal on destination
      amount,            // _amount: amount of tokens to transfer
      slippage,          // _slippage: max slippage the user will accept in BPS (e.g. 300 = 3%)
      callData           // _callData: the encoded calldata to send
    );
  }
}
```

`xUpdateGreeting` is what the user will call on origin to initiate the `xcall`.

{% hint style="info" %}
One important detail to note is that `xUpdateGreeting` is a `payable` method. This is necessary because a `relayerFee` in native gas is passed into the `xcall`. More on how this fee is determined later.
{% endhint %}

#### Compile Contract

Make sure the solidity compiler version in your `hardhat.config.js` is at least `0.8.17`.

```js
module.exports = {
  solidity: "0.8.17",
};
```

Compile the contract with the following command:

```bash
npx hardhat compile
```

> Note: Hardhat may require you to manually install dependencies for @nomicfoundation/hardhat-toolbox. If you get an error about missing dependencies for that plugin, run the following command:

```bash
npm install --save-dev "@nomicfoundation/hardhat-network-helpers@^1.0.0" "@nomicfoundation/hardhat-chai-matchers@^1.0.0" "@nomiclabs/hardhat-ethers@^2.0.0" "@nomiclabs/hardhat-etherscan@^3.0.0" "@types/chai@^4.2.0" "@types/mocha@^9.1.0" "@typechain/ethers-v5@^10.1.0" "@typechain/hardhat@^6.1.2" "solidity-coverage@^0.8.1" "ts-node@>=8.0.0" "typescript@>=4.5.0"
```

#### Deploy Contract

Update the `hardhat.config.js` file:

```js
require("@nomicfoundation/hardhat-toolbox");
require('@openzeppelin/hardhat-upgrades');
require('dotenv').config();
 
module.exports = {
  solidity: "0.8.17",
  networks:{
    goerli:{
      url: "https://rpc.ankr.com/eth_goerli",
      accounts: [`0x${process.env.PRIVATE_KEY}`]
    }
  }
};
```

Create a `/scripts/deploySource.js` file with the following:

```js
const main = async () => {
  const sourceGreeterContract = await hre.ethers.deployContract(
    "SourceGreeter",
    [
      "0xFCa08024A6D4bCc87275b1E4A1E22B71fAD7f649", // Connext on Goerli
      "0x7ea6eA49B0b0Ae9c5db7907d139D9Cd3439862a1" // TEST on Goerli
    ]
  );
  await sourceGreeterContract.waitForDeployment();
  console.log("Contract deployed to:", await sourceGreeterContract.getAddress());
};

const runMain = async () => {
  try {
    await main();
    process.exit(0);
  } catch (error) {
    console.log(error);
    process.exit(1);
  }
};

runMain();
```

The addresses for Connext and supported tokens in different domains can be referenced [here](https://docs.connext.network/resources/deployments). We'll be using Goerli as our origin domain and the TEST token for this contract.

Now run the deploy script:

```bash
npx hardhat run scripts/deploySource.js --network goerli
```

> Output: `Contract deployed to: 0x9Af84578B89FcA019580af02326388987A074ca1`

#### Verify Contract

Add an `etherScan` section to `hardhat.config.js` with your `goerli` api key (note: Etherscan API keys for the mainnet explorers will work for testnets):

```js
module.exports = {
  solidity: "0.8.17",
  networks:{
    goerli:{
      url: "https://rpc.ankr.com/eth_goerli",
     // PRIVATE_KEY loaded from .env file
      accounts: [`0x${process.env.PRIVATE_KEY}`]
    }
  },
  //highlight-start
  etherscan: {
    apiKey: {
      goerli: "YOUR-API-KEY-HERE",
    }
  }
  //highlight-end
};
```

Using the contract address you just deployed, run the `hardhat verify` command, including the contract address and its constructor arguments:

```bash
npx hardhat verify --network goerli 0x9Af84578B89FcA019580af02326388987A074ca1 0xFCa08024A6D4bCc87275b1E4A1E22B71fAD7f649 0x7ea6eA49B0b0Ae9c5db7907d139D9Cd3439862a1
```

If you run into any errors like `ProviderError: Too Many Requests`, then replace the public RPC url in `hardhat.config.js` with another one from <https://chainlist.org/> or use your own private RPC from a provider like Infura or Alchemy.

### Target Contract

In the `/contracts` directory, create another contract called `DestinationGreeter.sol`:

All target contracts must implement Connext's `IXReceiver` interface. This interface ensures that Connext can call the contract and pass necessary data.

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;

import {IXReceiver} from "@connext/interfaces/core/IXReceiver.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * @title DestinationGreeter
 * @notice Example destination contract that stores a greeting.
 */
contract DestinationGreeter is IXReceiver {
  string public greeting;

  // The token to be paid on this domain
  IERC20 public immutable token;

  constructor(address _token) {
    token = IERC20(_token);
  }

  /** @notice The receiver function as required by the IXReceiver interface.
    * @dev The Connext bridge contract will call this function.
    */
  function xReceive(
    bytes32 _transferId,
    uint256 _amount,
    address _asset,
    address _originSender,
    uint32 _origin,
    bytes memory _callData
  ) external returns (bytes memory) {
    // Check for the right token
    require(
      _asset == address(token),
      "Wrong asset received"
    );
    // Enforce a cost to update the greeting
    require(
      _amount > 0,
      "Must pay at least 1 wei"
    );

    // Unpack the _callData
    string memory newGreeting = abi.decode(_callData, (string));

    _updateGreeting(newGreeting);
  }

  /** @notice Internal function to update the greeting.
    * @param newGreeting The new greeting.
    */
  function _updateGreeting(string memory newGreeting) internal {
    greeting = newGreeting;
  }
}
```

#### Compile Contract

Compile:

```bash
npx hardhat compile
```

#### Deploy Contract

Add another entry to `hardhat.config.js`, this time for Optimism-Goerli.

```js
module.exports = {
  solidity: "0.8.17",
  networks:{
    "goerli":{
      url: "https://rpc.ankr.com/eth_goerli",
      accounts: [`0x${process.env.PRIVATE_KEY}`]
    },
    //highlight-start
    "optimism-goerli":{
      url: "https://goerli.optimism.io",
      accounts: [`0x${process.env.PRIVATE_KEY}`],
      // gasPrice: 800000 // you may need to set this manually if you get "transaction underpriced"
    }
    //highlight-end
  }
};
```

Create a `scripts/deployTarget.js` file with the following:

```js
const main = async () => {
  const destinationGreeterContract = await hre.ethers.deployContract(
    "DestinationGreeter",
    [
      "0x68Db1c8d85C09d546097C65ec7DCBFF4D6497CbF" // TEST on Optimism-Goerli
    ]
  );
  await destinationGreeterContract.waitForDeployment();
  console.log("Contract deployed to:", await destinationGreeterContract.getAddress());
};

const runMain = async () => {
  try {
    await main();
    process.exit(0);
  } catch (error) {
    console.log(error);
    process.exit(1);
  }
};

runMain();
```

Then run the deploy script:

```bash
npx hardhat run scripts/deployTarget.js --network optimism-goerli
```

> Output: `Contract deployed to: 0xC4e508cEe84499958a84C3562e92bD9e71d7D38a`

#### Verify Contract

Add an `apiKey` to `hardhat.config.js` for `optimism-goerli`:

```js
module.exports = {
  solidity: "0.8.17",
  networks:{
    goerli:{
      url: "https://rpc.ankr.com/eth_goerli",
     // PRIVATE_KEY loaded from .env file
      accounts: [`0x${process.env.PRIVATE_KEY}`]
    }
  },
  etherscan: {
    apiKey: {
      goerli: "YOUR-API-KEY-HERE",
      //highlight-start
      optimisticGoerli: "YOUR-API-KEY-HERE",
      //highlight-end
    }
  }
};
```

Using the contract address you just deployed, verify it:

```bash
npx hardhat verify --network optimism-goerli 0xC4e508cEe84499958a84C3562e92bD9e71d7D38a 0x68Db1c8d85C09d546097C65ec7DCBFF4D6497CbF
```

If you run into any errors like `ProviderError: Too Many Requests`, then replace the public RPC url in `hardhat.config.js` with another one from <https://chainlist.org/> or use your own private RPC with a provider like Infura or Alchemy.

### Executing the Transaction

You should try the following steps on your own deployed contracts. For the lazy ones, you can just use these contracts we've deployed already:

* [`SourceGreeter.sol`](https://goerli.etherscan.io/address/0x9Af84578B89FcA019580af02326388987A074ca1)
* [`DestinationGreeter.sol`](https://goerli-optimism.etherscan.io/address/0xC4e508cEe84499958a84C3562e92bD9e71d7D38a)

#### Mint TEST Tokens

First, you will need some TEST tokens. Recall that the destination contract requires a payment > 0 TEST in order to update its greeting.

Since you'll be updating the greeting from the origin chain, you will need to acquire some TEST tokens on the origin chain.

You can use Etherscan to call functions on (verified) contracts. Go to the [TEST Token on Etherscan](https://goerli.etherscan.io/address/0x7ea6eA49B0b0Ae9c5db7907d139D9Cd3439862a1#writeContract) and click on the "Write Contract" button.

A new tab will show up with all write functions of the contract. Connect your wallet, switch to the Goerli network, and enter the parameters for the `mint` function:

* `account`: \<YOUR\_WALLET\_ADDRESS>
* `amount`: 10000000000000000000
  * 10 TEST. You can actually mint however much you want.

#### Approve TEST Tokens

Tokens will move from `User's wallet` => `SourceGreeter` => `Connext` => `DestinationGreeter`.

The user must first approve a spending allowance of the TEST ERC20 to the `SourceGreeter` contract. The `require` clause starting on line 39 checks for this allowance.

Again, on the Etherscan page for the TEST token, enter the parameters for the `approve` function:

* `spender`: 0x9Af84578B89FcA019580af02326388987A074ca1
  * This is the address of `SourceGreeter`.
* `amount`: 10000000000000000000

Then "Write" to the `approve` function.

#### Execute `xUpdateGreeting`

Similarly to the approval function for TEST, navigate to the `SourceGreeter` contract on Etherscan. Fill out the `xUpdateGreeting` function parameters and "Write" to the contract.

Let's walk through the different parameters.

* `xUpdateGreeting` (payableAmount): 0.03
  * This is the native gas that you're sending into the `xcall`. This value must match what you pass in as `relayerFee`, but note that it's in ETH units here and wei units in `relayerFee`.
* `target`: 0xC4e508cEe84499958a84C3562e92bD9e71d7D38a
  * The address of `DestinationGreeter`.
* `destinationDomain`: 1735356532
  * The Domain ID of the destination chain. You can find a mapping of Domain IDs here. For this example, `DestinationGreeter` is deployed to Optimism-Goerli.
* `newGreeting`: hello chain!
  * Whatever string you want to update the greeting to.
* `amount`: 1000000000000000000
  * The amount of TEST tokens to pay. We send 1 TEST here.
* `relayerFee`: 30000000000000000
  * 0.03 goerli ETH, in wei units. Just a conservative estimate for relayers on testnet.
  * **IMPORTANT!** This is a fee paid to relayers, which are off-chain agents that help execute the final leg of the cross-chain transfer on destination. Relayers get paid in the origin chain's native asset. This is why `SourceGreeter` passes the fee like so:

    ```solidity
    connext.xcall{value: relayerFee}(...)
    ```

{% hint style="info" %}
As a xApp developer, you have some tools available to estimate what this `relayerFee` should be. For now, there are offchain methods for doing so - check out the guide on [Estimating Fees](/developers/guides/estimating-fees).
{% endhint %}

#### Track the xcall

After executing `updateGreeting`, you can use [Connextscan (testnet)](https://testnet.connextscan.io) to check the status of the `xcall`. Just search up the transaction hash from the execution transaction.&#x20;

Note that if your `relayerFee` was too low, the explorer will prompt you to increase it.

#### Check `DestinationGreeter`

`DestinationGreeter` should be updated in just a few minutes (because this call is unauthenticated!). Cross-chain calls are not always this fast - see our guide on Authentication.

Head over to the `DestinationGreeter` contract on Etherscan. This time, we'll go to the `Read Contract` tab and look at the value of `greeting`. It has updated!

Send a couple more updates from `SourceGreeter` but make it a different string. At some point, your TEST allowance to `HelloSource` will run out and you'll need to do the approval dance again.

Congrats! You've gone cross-chain!

***

### Next Steps

* Try [tracking the status](/developers/guides/xcall-status) of an `xcall` after you send it.
* Learn about [authentication](/developers/guides/authentication) and important security considerations.
* See how [nested xcalls](/developers/guides/nested-xcalls) can open up infinite cross-chain possibilities.
* Fork the [xApp Starter Kit](https://github.com/connext/xapp-starter/) (includes code for this example) and build your own xApp.


# Guides


# Frontend

A common pattern for the SDK is to use it in a frontend application. This guide will walk you through the steps to integrate the SDK with a frontend application.

## Next.JS

Next.JS is a popular frontend framework that allows you to build server-rendered React applications. It is a great choice for building a frontend application that uses the SDK.

### 1. Setup

Create a new Next.JS application using the `create-next-app` command (Typescript is recommended).

```bash
npx create-next-app@latest --typescript
# or
yarn create next-app --typescript
# or
pnpm create next-app --typescript
```

Follow the instructions to create a new Next.JS application.

### 2. Install the SDK

Install the SDK using your package manager of choice.

```bash
npm install @connext/sdk
```

### 3. Configure Next.JS

The Connext SDK contains some dependencies that must be polyfilled to work on client-side applications. At minimum, your `next.config.js` file should contain the following configuration:

```js
/** @type {import('next').NextConfig} */
const nextConfig = {
  webpack: (config) => {
    config.resolve.fallback = { fs: false };
    return config;
  },
};

module.exports = nextConfig;
```

Now you are ready to use the SDK in your Next.JS application!

For example:

```tsx
"use client"; // this is a client component 👈🏽

import styles from "./page.module.css";
import { create, SdkConfig } from "@connext/sdk";
import { useEffect } from "react";

const inter = Inter({ subsets: ["latin"] });

const sdkConfig: SdkConfig = {
  signerAddress: "0x2b8aA42fFb2c9c7B9f0B1e1b935F7D8331b6dC7c",
  // Use `mainnet` when you're ready...
  network: "testnet",
  // Add more chains here! Use mainnet domains if `network: mainnet`.
  // This information can be found at https://docs.connext.network/resources/supported-chains
  chains: {
    1735353714: { // Goerli domain ID
      providers: ["https://rpc.ankr.com/eth_goerli"],
    },
    1735356532: { // Optimism-Goerli domain ID
      providers: ["https://goerli.optimism.io"],
    },
  },
};

export default function Home() {
  useEffect(() => {
    const run = async () => {
      const { sdkBase } = await create(sdkConfig);
      console.log('sdkBase: ', sdkBase);
    }
    run();
  })
  return (
    <main className={styles.main}>
      <div className={styles.description}>
        <p>
          Get started by editing&nbsp;
          <code className={styles.code}>src/app/page.tsx</code>
        </p>
      </div>
    </main>
  )
```

## Create React App (CRA)

React is a popular frontend framework that allows you to build client-rendered React applications. However, CRA is at end of life and the React team recommends other frameworks instead.

We highly recommend using NextJS for easier integration. If, however, you still want to use CRA for your project then you should follow these steps.

### 1. Setup

Create a new CRA using the `create-react-app` command.

```bash
npx create-react-app my-app
# or
yarn create react-app my-app --template typescript
```

### 2. Install the SDK

Install the SDK using your package manager of choice.

```bash
npm install @connext/sdk
```

### 3. Configure CRA

1. Install necessary dependencies

```
yarn add -D @craco/craco zlib-browserify 
```

2. Create a `craco.config.js` in your project root with the following contents.

```js
const webpack from 'webpack';

module.exports = {
  webpack: {
    configure: webpackConfig => {
      webpackConfig['resolve'] = {
        fallback: {
          fs: false,
          path: false,
          os: false,
          zlib: require.resolve("zlib-browserify"),
        },
      }
      return webpackConfig;
    },
		plugins: [
      // Work around for Buffer is undefined:
      // https://github.com/webpack/changelog-v5/issues/10
      new webpack.ProvidePlugin({
          Buffer: ['buffer', 'Buffer'],
      }),
      new webpack.ProvidePlugin({
          process: 'process/browser',
      }),
    ],
  },
};
```

3. Change scripts in `package.json` to use `craco` commands instead of `react-scripts`.

```json
"scripts": {
-  "start": "react-scripts start",
-  "build": "react-scripts build",
-  "test": "react-scripts test"
+  "start": "craco start",
+  "build": "craco build",
+  "test": "craco test"
}
```


# SDK

The Connext SDK allows developers to interact with the Connext protocol in standard Node.js or web environments. See [here](/developers/reference) for a reference of all SDK methods.

## Cross-Chain Transfer

This example demonstrates how to execute an `xcall` to transfer funds from a wallet on the source domain to the same address on the destination domain.

### 1. Setup

Install [Node.js](https://nodejs.dev/en/learn/how-to-install-nodejs/) and use **Node.js v18**. Follow the instructions to install `nvm`, a node version manager, which will make switching versions easier.

Create a project folder and initialize the package. Fill out the project information as you please.

```bash
mkdir connext-sdk-example && cd connext-sdk-example
npm init
```

We'll be using TypeScript so install the following and generate the `tsconfig.json` file.

```bash
npm install --save-dev @types/node @types/chai @types/mocha typescript 
npx tsc --init # or `yarn tsc --init`
```

We want to use top-level await so we'll set the compiler options accordingly in `tsconfig.json`:

```json
{
  "compilerOptions": {
    "outDir": "./dist",
    "target": "es2017",
    "module": "esnext",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "skipLibCheck": true
  },
  "exclude": ["node_modules"]
}
```

Add `type` and `scripts` as root-level entries to `package.json` - they may already exist, so just replace them with the following.

```json
{
  ...
  "type": "module",
  "scripts": {
    "xtransfer": "tsc && node dist/xtransfer.js"
  }
  ...
}
```

### 2. Install dependencies

Install the latest beta version of Connext SDK and ethers.

```bash
npm install @connext/sdk
npm install ethers@^5
```

### 3. The code

First, we'll configure the SDK. Create a `config.ts` file with the following contents.

{% code lineNumbers="true" %}

```ts
import { SdkConfig } from "@connext/sdk";
import { ethers } from "ethers";

// Create a Signer and connect it to a Provider on the sending chain
const privateKey = "<PRIVATE_KEY>";

let signer = new ethers.Wallet(privateKey);

// Use the RPC url for the origin chain
const provider = new ethers.providers.JsonRpcProvider("https://public.stackup.sh/api/v1/node/ethereum-sepolia");
signer = signer.connect(provider);
const signerAddress = await signer.getAddress();

const sdkConfig: SdkConfig = {
  signerAddress: signerAddress,
  // Use `mainnet` when you're ready...
  network: "testnet",
  environment:"production",

  // Add more chains here! Use mainnet domains if `network: mainnet`.
  // This information can be found at https://docs.connext.network/resources/supported-chains
  chains: {
    1936027759: {
      providers:["https://public.stackup.sh/api/v1/node/ethereum-sepolia"]
    },
    1869640549: {
      providers: ['https://sepolia.optimism.io']
    },
  },
};

export { signer, sdkConfig };
```

{% endcode %}

Replace `<PRIVATE_KEY>` with your own private key on line 5.

Notice that the config supports Goerli and Optimism-Goerli. We've also hard-coded the origin chain provider on line 10.

Now create a `xtransfer.ts` file with the following:

```ts
import { create } from "@connext/sdk";
import { BigNumber } from "ethers";
import { signer, sdkConfig } from "./config.js";

const {sdkBase} = await create(sdkConfig);

const signerAddress = await signer.getAddress();

// xcall parameters
const originDomain = "1936027759";
const destinationDomain = "1869640549";
const originAsset = "0xd26e3540A0A368845B234736A0700E0a5A821bBA";
const amount = "100000000000000";
const slippage = "10000";



// Estimate the relayer fee
const relayerFee = (
  await sdkBase.estimateRelayerFee({
    originDomain, 
    destinationDomain
  })
).toString();

// Prepare the xcall params
const xcallParams = {
  origin: originDomain,           // send from Sepolia
  destination: destinationDomain, // to Op-Sepolia
  to: signerAddress,              // the address that should receive the funds on destination
  asset: originAsset,             // address of the token contract
  delegate: signerAddress,        // address allowed to execute transaction on destination side in addition to relayers
  amount: amount,                 // amount of tokens to transfer
  slippage: slippage,             // the maximum amount of slippage the user will accept in BPS (e.g. 30 = 0.3%)
  callData: "0x",                 // empty calldata for a simple transfer (byte-encoded)
  relayerFee: relayerFee,         // fee paid to relayers 
};

// Approve the asset transfer if the current allowance is lower than the amount.
// Necessary because funds will first be sent to the Connext contract in xcall.
const approveTxReq = await sdkBase.approveIfNeeded(
  originDomain,
  originAsset,
  amount
)

if (approveTxReq) {
  const approveTxReceipt = await signer.sendTransaction(approveTxReq);
  await approveTxReceipt.wait();
}

// Send the xcall
const xcallTxReq = await sdkBase.xcall(xcallParams);
xcallTxReq.gasLimit = BigNumber.from("20000000"); 
const xcallTxReceipt = await signer.sendTransaction(xcallTxReq);
console.log(xcallTxReceipt);
await xcallTxReceipt.wait();
```

Most of the parameters are hardcoded in this example. For a detailed description of each parameter, see the [SDK reference for `xcall`](/developers/reference/sdk/sdkbase#xcall).

Information like asset addresses be found in the [Deployments](/resources/deployments) page.

### 4. Run it

Fire off the cross-chain transfer!

```bash
npm run xtransfer
```

### 5. Track the `xcall`

We can now use the transaction `hash` from the logged transaction receipt to [track the status](/developers/guides/xcall-status) of this `xcall`.

After the transfer is `status: Executed` on the destination side, the transferred tokens should show up in the recipient wallet.


# Estimating Fees

There are two types of fees paid to offchain agents for each applicable `xcall`.

* **Router Fee**: 0.05% of the transferred asset will be levied by routers on destination for their service as fast liquidity providers.
  * The 'fast path' is possible when routers have liquidity in the destination asset and are able to provide those assets to the user. This allows users to receive their desired destination assets almost *immediately*.
  * Routers will take on the bridge delay and wait for the optimistic period to pass. Once complete, the bridge will 'reconcile' by minting the local destination assets to the routers, making them whole again.
  * If an `xcall` goes through the 'slow path' (authenticated), then users do not pay the router fee.
  * Note that routers always provide *and* receive minted assets of the local destination flavor - they never have to rebalance funds!
* **Relayer Fee**: A fee charged by relayers on top of normal gas costs in exchange for providing a meta-transaction service.
  * Relayers execute transactions on the destination chain on behalf of users.
  * Users offer a fee bounty to incentivize relayers to execute their destination calls.
  * Relayer fees are paid in the origin native asset or the transacting asset and need to be estimated when `xcall` is initiated. Some relayers provide endpoints that can help with estimation.

Router fees are fixed and hardcoded into the Connext protocol. Relayer fees, on the other hand, can vary between chains and the service provider.

## Estimating Relayer Fees

For now, we need to rely on offchain tools to estimate relayer fees. The Connext SDK abstracts away some of this complexity.

The `SdkBase` class includes an `estimateRelayerFee` method that estimates total gas fees including a bump to account for Gelato relayer fees.

The relayer fee can be paid in either the native asset or the transacting asset (the asset being bridged in the `xcall`).

### Pay in native asset

The resulting estimate will be converted to the native origin asset.

```typescript
const {sdkBase} = await create(nxtpConfig);

const params = {
  originDomain: "<ORIGIN_DOMAIN>",
  destinationDomain: "<DESTINATION_DOMAIN>",
}

const relayerFee = await sdkBase.estimateRelayerFee(params);
```

The estimate should be used as the `relayerFee` param for an `xcall` using the SDK.

```typescript
const xcallTxReq = await sdkBase.xcall(
  ...,
  relayerFee: relayerFee
);
```

Or passed in as the `value` for an `xcall` in a smart contract.

```solidity
contract Source {
  ...
  function crossChainCall() {
    ...
    connext.xcall{value: relayerFee}(...);
  }
}
```

### Pay in transacting asset

The resulting estimate will be the relayer fee in USD.

```typescript
const {sdkBase} = await create(nxtpConfig);

const params = {
  originDomain: "<ORIGIN_DOMAIN>",
  destinationDomain: "<DESTINATION_DOMAIN>",
  priceIn: "usd" // use this if you want the estimate in USD
}

const relayerFeeInTransactingAsset = await sdkBase.estimateRelayerFee(params);
```

The estimate in USD should be converted to the value of the transacting asset (e.g. by using a price feed) and supplied as the `relayerFeeInTransactingAsset` param for an `xcall` using the SDK.

```typescript
const xcallTxReq = await sdkBase.xcall(
  ...,
  relayerFeeInTransactingAsset: relayerFeeInTransactingAsset
);
```

Or passed in as the `_relayerFee` for an `xcall` in a smart contract.

```solidity
contract Source {
  ...
  function crossChainCall() {
    ...
    connext.xcall(
      ...,
      relayerFeeInTransactingAsset
    );
  }
}
```

## Bumping Relayer Fees

Since gas conditions are impossible to predict, transactions can potentially stay pending on destination if fees aren't high enough. Connext allows the user (or anyone if they are feeling charitable) to increase the original fee until sufficient for relayers.

Anyone can call the Connext contract function `bumpTransfer` to increase the original relayer fee for an `xcall`.

### Bump in native asset

To bump using SDK:

```typescript
const bumpTxReq = await sdkBase.bumpTransfer(
  domainId: originDomain,
  transferId: transferId,
  asset: <native_asset_address>,
  relayerFee: <relayerFee_in_native>
);
```

To bump from a contract call:

```solidity
function bumpTransfer(bytes32 _transferId) external payable;
```

### Bump in transacting asset

To bump using SDK:

```typescript
const bumpTxReq = await sdkBase.bumpTransfer(
  domainId: originDomain,
  transferId: transferId,
  asset: <transacting_asset_address>,
  relayerFee: <relayerFee_in_transacting>
);
```

To bump from a contract call:

```solidity
function bumpTransfer(bytes32 _transferId, address _relayerFeeAsset, uint256 _relayerFee) external payable;
```

To find the `transferId`, see Tracking xCalls.


# Tracking xCalls

Every `xcall` is associated with a unique `transferId` that can be used to track its lifecycle through a cross-chain transaction.

## Connextscan

The easiest option to track an `xcall` is by using [Connextscan](https://testnet.connextscan.io/) to look up any `transferId`. In the top right search box, enter the `transferId` of interest.

Connextscan will pull up current status of the associated `xcall`.&#x20;

## Querying Subgraphs

You can also query the [hosted subgraphs](/resources/subgraphs#mainnet-subgraphs) on each chain to check the transaction status.&#x20;

1. Make note of the transaction hash that interacted with the Connext contract.
2. Navigate to the hosted subgraph for the origin domain and query by the xcall's transaction hash or the transfer ID.

```graphql
query OriginTransfer {
  originTransfers(
    where: {
      # Query by the transaction hash of the xcall
      transactionHash: "<TRANSACTION_HASH>",
      # Or by the xcall's transfer ID
      transferId: "<TRANSFER_ID>"
    }
  ) {
    # Meta Data
    chainId
    nonce
    transferId
    to
    delegate
    receiveLocal
    callData
    slippage
    originSender
    originDomain
    destinationDomain
    transactionHash
    bridgedAmt
    status
    timestamp
    normalizedIn
    # Asset Data
    asset {
      id
      adoptedAsset
      canonicalId
      canonicalDomain
    }
  }
}
```

3. Navigate to the hosted subgraph for the destination domain and query by the `transferId` obtained from the origin domain subgraph.

```graphql
query DestinationTransfer {
  destinationTransfers(
    where: {
      transferId: "<TRANSFER_ID>"
    }
  ) {
    # Meta Data
    chainId
    nonce
    transferId
    to
    callData
    originDomain
    destinationDomain
    delegate
    # Asset Data
    asset {
      id
    }
    bridgedAmt
    # Executed event Data
    status
    routers {
      id
    }
    originSender
    # Executed Transaction
    executedCaller
    executedTransactionHash
    executedTimestamp
    executedGasPrice
    executedGasLimit
    executedBlockNumber
    # Reconciled Transaction
    reconciledCaller
    reconciledTransactionHash
    reconciledTimestamp
    reconciledGasPrice
    reconciledGasLimit
    reconciledBlockNumber
    routersFee
    slippage
  }
}
```

4. If there was a nested `xcall` involved on the destination side, the `executedTransactionHash` from step 3 can be used as the *new* origin-side transaction hash. To trace the nested `xcall`, go back to step 1 using this `executedTransactionHash` but instead consider the current destination domain as the origin domain.

## XCall Status

| Status        | Description                                                                                                                                                               |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| XCalled       | Transaction has been initiated on origin.                                                                                                                                 |
| Executed      | Funds have been delivered and calldata executed on destination, if applicable. If this happens before Reconciled, then this was a fast path transfer (non-authenticated). |
| Reconciled    | Funds have been reimbursed to routers. If this happens before Executed, then this was a slow path transfer (authenticated).                                               |
| CompletedFast | Transaction has been Executed and then Reconciled.                                                                                                                        |
| CompletedSlow | Transaction has been Reconciled and then Executed.                                                                                                                        |


# Authentication

Authentication is a critical concept to understand when building xApps. In the context of smart contracts, an authenticated call is one that passes permissioning constraints set by the protocol developer. In most cases this manifests as a modifier that allows a certain set of addresses to call specific smart contract functions - in other words, we are talking about access control.

For example:

* Uniswap's `swap` [function](https://docs.uniswap.org/protocol/reference/core/UniswapV3Pool#swap) is **unauthenticated** because it is a public function that can be called by anyone.
* Uniswap's `setFeeProtocol` [function](https://docs.uniswap.org/protocol/reference/core/UniswapV3Pool#setfeeprotocol) is **authenticated** because it uses the `onlyFactoryOwner` modifier that prevents anyone but the owner of the contract factory from calling it. You can read more about this at [OpenZeppelin's Ownable contracts](https://docs.openzeppelin.com/contracts/2.x/api/ownership).

The major impact that an authenticated call can have on users is latency. Authenticated calls are a crucial feature for xApps but in order to validate data transferred between chains, Connext must allow *some* time to elapse before accepting messages as authenticated on destination chains. This latency is both a drawback and a security mechanism of optimistic bridges.

## Checking Origin Data

Suppose a target contract on the destination domain has a function that should only be callable by a specific source contract on a specific origin domain.

A custom modifier like `onlySource` below can conduct all the necessary checks to uphold this permissioning constraint.

```solidity
contract Target is IXReceiver {
  /** @notice A modifier for authenticated calls.
   *  This is an important security consideration. If the target contract
   *  function should be authenticated, it must check three things:
   *    1) The originating call comes from the expected origin domain.
   *    2) The originating call comes from the expected source contract.
   *    3) The call to this contract comes from Connext.
   */
  modifier onlySource(address _originSender, uint32 _origin) {
    require(
      _origin == <ORIGIN_DOMAIN> &&
        _originSender == <SOURCE_CONTRACT_ADDRESS> &&
        msg.sender == <CONNEXT_CONTRACT_ADDRESS>,
      "Expected source contract on origin domain called by Connext"
    );
    _;
  }

  function xReceive(
    bytes32 _transferId,
    uint256 _amount,
    address _asset,
    address _originSender,
    uint32 _origin,
    bytes memory _callData
  ) external onlySource(_originSender, _origin) returns (bytes memory) {
    // Do stuff that requires authentication
  }
}
```

You can find a full example of this at [Authenticated Greeter.](/developers/examples/authenticated-hello)


# Handling Failed xCalls

There are a few failure conditions to watch out for when using `xcall`.

## High Slippage Conditions

When tokens are bridged through Connext, slippage can impact the `xcall` during the swap on origin or destination. If slippage is too high on the origin swap, the `xcall` will just revert. If slippage is too high on the destination swap (after it has already gone through the origin swap), then there are a couple options to consider.

* Cancel the transfer and bridge back to origin (sender will lose any funds related to the origin slippage) \[not available yet].
* Wait it out until slippage conditions improve (relayers will continuously re-attempt the transfer execution).
* Increase the slippage tolerance.

### Increasing Slippage Tolerance

The `_delegate` parameter of `xcall` is an address that has rights to update the original slippage tolerance by calling Connext's [forceUpdateSlippage](https://github.com/connext/monorepo/blob/27bbf7871a78b03d8613b06ece2675a57309d573/packages/deployments/contracts/contracts/core/connext/facets/BridgeFacet.sol#L395) function with the following signature:

```solidity
function forceUpdateSlippage(TransferInfo calldata _params, uint256 _slippage) external;
```

The `TransferInfo` struct that must be supplied:

```solidity
struct TransferInfo {
  uint32 originDomain;
  uint32 destinationDomain;
  uint32 canonicalDomain;
  address to;
  address delegate;
  bool receiveLocal;
  bytes callData;
  uint256 slippage;
  address originSender;
  uint256 bridgedAmt;
  uint256 normalizedIn;
  uint256 nonce;
  bytes32 canonicalId;
}
```

The parameters in `TransferInfo` must match the same parameters used in the original `xcall`. It's possible to obtain the original parameters by querying the subgraph (origin *or* destination) with the `transferId` associated with the `xcall`.

The Connext SDK also exposes an [updateSlippage](/developers/reference/sdk/sdkbase#updateslippage) method for this.

## Low Relayer Fee

If the estimated relayer fee paid was too low, then users may have to [increase the relayer fee](/developers/guides/estimating-fees#bumping-relayer-fees) after the `xcall` has been sent.

## Reverts on Receiver Contract

If the call on the receiver contract (also referred to as "target" contract) reverts, funds sent in with the call will end up on the receiver contract. To avoid situations where user funds get stuck on the receivers, developers should build any contract implementing `IXReceive` defensively.

Ultimately, the goal should be to handle any revert-susceptible code and ensure that the logical owner of funds *always* maintains agency over them.

### Try/Catch with External Calls

One way to guard against unexpected reverts is to use `try/catch` statements which allow contracts to handle errors on external function calls.

```solidity
contract TargetContract {
  ...
  function xReceive(
    bytes32 _transferId,
    uint256 _amount,
    address _asset,
    address _originSender,
    uint32 _origin,
    bytes memory _callData
  ) external returns (bytes memory) {
    try {
      someExternalCall();
    } catch { 
      // Make sure funds are delivered to logical owner on failing external calls
    }
  }
}
```

### Options for Funds on Receiver

We recommend that xApp developers consider recovery options in case of reverting calls on the receiver. For example, there could be an internal accounting structure to record `transferId`s and allow rightful `originSender`s to rescue their funds from the receiver contract. Note that this approach requires authentication and would cause `xcall`s to go through the slow path.

Alternatively, the protocol can implement an allowlist for addresses that are able to rescue funds and redirect them to users.

Connext is actively researching standards and best practices for receiver contracts. Reach out to us if you questions!


# Nested xCalls

Cross-chain calls can easily be composed together by `xcall`ing within the `xReceive` function of a target contract. In effect, the target contract becomes the source contract of that nested `xcall`.

## xCall in xReceive

```solidity
contract Target is IXReceiver {
  function xReceive(
    bytes32 _transferId,
    uint256 _amount,
    address _asset,
    address _originSender,
    uint32 _origin,
    bytes memory _callData
  ) external returns (bytes memory) {
    // After handling the first xcall...
    ...

    // Send another xcall within the xReceive function!
    connext.xcall{value: relayerFee}(...);
  }
}
```

There are many ways to use nested `xcall`s to extend cross-chain functionality. With this technique, it's possible to:

* Emulate the behavior of a "callback" between chains to verify state changes and/or followup asynchronously
* Disperse data to multiple different chains at once

See this in action in the [Ping Pong](/developers/examples/ping-pong) example.


# Reference

The Connext SDK allows developers to interact with the Connext protocol in standard Node.js or web environments.

{% tabs %}
{% tab title="npm" %}

<pre class="language-bash"><code class="lang-bash"><strong>npm install @connext/sdk
</strong></code></pre>

{% endtab %}

{% tab title="yarn" %}

<pre class="language-bash"><code class="lang-bash"><strong>yarn install @connext/sdk
</strong></code></pre>

{% endtab %}
{% endtabs %}


# SDK


# SdkShared

SDK class encapsulating shared logic to be inherited.

## Hierarchy

* **`SdkShared`**

  ↳ `SdkBase`

  ↳ `SdkRouter`

  ↳ `SdkPool`

  ↳ `SdkUtils`

## Methods

### approveIfNeeded

▸ **approveIfNeeded**(`domainId`, `assetId`, `amount`, `infiniteApprove?`): `Promise`<`undefined` | `TransactionRequest`>

Returns the transaction request for an allowance approval.

**Parameters**

| Name              | Type      | Default value | Description                                       |
| ----------------- | --------- | ------------- | ------------------------------------------------- |
| `domainId`        | `string`  | `undefined`   | The domain ID.                                    |
| `assetId`         | `string`  | `undefined`   | The address of the token.                         |
| `amount`          | `string`  | `undefined`   | The amount of the token.                          |
| `infiniteApprove` | `boolean` | `true`        | (optional) Whether to approve an infinite amount. |

**Returns**

`Promise`<`undefined` | `TransactionRequest`>

providers.TransactionRequest object.

***

### calculateCanonicalKey

▸ **calculateCanonicalKey**(`domainId`, `canonicalId`): `string`

Returns the hash of the canonical ID + canonical domain.

**`Remarks`**

This key is used as the unique identifier for a canonical token, across all domains.

**Parameters**

| Name          | Type     | Description                           |
| ------------- | -------- | ------------------------------------- |
| `domainId`    | `string` | The canonical domain ID of the token. |
| `canonicalId` | `string` | The canonical ID of the token.        |

**Returns**

`string`

***

### changeSignerAddress

▸ **changeSignerAddress**(`signerAddress`): `Promise`<`void`>

Switches the signer address in the SDK config.

**Parameters**

| Name            | Type     | Description             |
| --------------- | -------- | ----------------------- |
| `signerAddress` | `string` | The new signer address. |

**Returns**

`Promise`<`void`>

***

### getAssetsData

▸ **getAssetsData**(): `Promise`<`AssetData`\[]>

Fetches the list of registered assets.

**Returns**

`Promise`<`AssetData`\[]>

Array of objects containing assets registered to the network, in the form of:

```ts
{
  "local": "0x2983bf5c334743aa6657ad70a55041d720d225db",
  "adopted": "0x82af49447d8a07e3bd95bd0d56f35241523fbab1",
  "canonical_id": "0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
  "canonical_domain": "6648936",
  "domain": "1634886255",
  "key": "0x12acadfa38ab02479ae587196a9043ee4d8bf52fcb96b7f8d2ba240f03bcd08a",
  "id": "0x2983bf5c334743aa6657ad70a55041d720d225db"
},
```

***

### getAssetsDataByDomainAndKey

▸ **getAssetsDataByDomainAndKey**(`domainId`, `key`): `Promise`<`undefined` | `AssetData`>

Retrieve the asset data for a specific domain and key.

**Parameters**

| Name       | Type     | Description                                |
| ---------- | -------- | ------------------------------------------ |
| `domainId` | `string` | The domain ID.                             |
| `key`      | `string` | The canonical hash of the canonical token. |

**Returns**

`Promise`<`undefined` | `AssetData`>

The object containing asset data.

***

### getCanonicalTokenId

▸ **getCanonicalTokenId**(`domainId`, `tokenAddress`): `Promise`<\[`string`, `string`]>

Returns the canonical ID and canonical domain of a token.

**Parameters**

| Name           | Type     | Description                           |
| -------------- | -------- | ------------------------------------- |
| `domainId`     | `string` | The canonical domain ID of the token. |
| `tokenAddress` | `string` | The address of the token.             |

**Returns**

`Promise`<\[`string`, `string`]>

***

### getSupported

▸ **getSupported**(): `Promise`<`ConnextSupport`\[]>

Fetches the list of supported networks and assets.

**Returns**

`Promise`<`ConnextSupport`\[]>

Array of objects containing networks and assets supported by the protocol, in the form of:

```ts
{
  "name": "arbitrum",
  "chainId": 42161,
  "domainId": "1634886255",
  "assets": [
    "0x82af49447d8a07e3bd95bd0d56f35241523fbab1",
    "0xff970a61a04b1ca14834a43f5de4533ebddb5cc8"
  ]
},
```

***

### isNextAsset

▸ **isNextAsset**(`tokenAddress`): `Promise`<`undefined` | `boolean`>

Returns whether the specified token is a Connext-issued (local) token.

**Parameters**

| Name           | Type     | Description               |
| -------------- | -------- | ------------------------- |
| `tokenAddress` | `string` | The address of the token. |

**Returns**

`Promise`<`undefined` | `boolean`>

Boolean or undefined if the specified token is not registered.

***

### parseConnextTransactionReceipt

▸ **parseConnextTransactionReceipt**(`transactionReceipt`): `any`

Parses a providers.TransactionReceipt for the logs.

**Parameters**

| Name                 | Type                 | Description                          |
| -------------------- | -------------------- | ------------------------------------ |
| `transactionReceipt` | `TransactionReceipt` | providers.TransactionReceipt object. |

**Returns**

`any`

Array of providers.Log objects.

***

### domainToChainName

▸ `Static` **domainToChainName**(`domainId`): `string`

Returns the chain name for a specified domain.

**Parameters**

| Name       | Type     | Description    |
| ---------- | -------- | -------------- |
| `domainId` | `string` | The domain ID. |

**Returns**

`string`

The chain name.

***

### chainIdToDomain

▸ `Static` chainIdToDomain(`chainId`): `number`

Returns the chain name for a specified domain.

**Parameters**

| Name      | Type     | Description    |
| --------- | -------- | -------------- |
| `chainId` | `number` | The domain ID. |

**Returns**

`number`

The domain of the chain.

***

### getBlockNumberFromUnixTimestamp

▸ `Static` **getBlockNumberFromUnixTimestamp**(`domainId`, `unixTimestamp`): `Promise`<`number`>

Uses an external API to fetch the block number from a unix timestamp.

**Parameters**

| Name            | Type     | Description         |
| --------------- | -------- | ------------------- |
| `domainId`      | `string` | The domain ID.      |
| `unixTimestamp` | `number` | The unix timestamp. |

**Returns**

`Promise`<`number`>


# SdkBase

SDK class encapsulating bridge functions.

## Hierarchy

* `SdkShared`

  ↳ **`SdkBase`**

## Methods

### bumpTransfer

▸ **bumpTransfer**(`params`): `Promise`<`TransactionRequest`>

Increases the relayer fee for a specific transfer on origin; anyone is allowed to bump for any transfer.

**`Example`**

```ts
// call SdkBase.create(), instantiate a signer

const params = {
  domainId: "6648936",
  transferId: "0xdd252f58a45dc78fee1ac12a628782bda6a98315b286aadf76e4d7322bf135ca",
  asset: "0x0000000000000000000000000000000000000000", // can be either native asset or transacting asset
  relayerFee: "10000",
};

const txRequest = sdkBase.bumpTransfer(params);
signer.sendTransaction(txRequest);
```

**Parameters**

| Name                | Type     | Description                                                                                         |
| ------------------- | -------- | --------------------------------------------------------------------------------------------------- |
| `params`            | `Object` | SdkBumpTransferParams object.                                                                       |
| `params.asset`      | `string` | The asset address you want to pay in (use "0x0000000000000000000000000000000000000000" for native). |
| `params.domainId`   | `string` | The origin domain ID of the transfer.                                                               |
| `params.relayerFee` | `string` | The additional relayer fee to increase the transfer by, in the specified asset.                     |
| `params.transferId` | `string` | The transfer ID.                                                                                    |

**Returns**

`Promise`<`TransactionRequest`>

providers.TransactionRequest object.

***

### calculateAmountReceived

▸ **calculateAmountReceived**(`originDomain`, `destinationDomain`, `originTokenAddress`, `amount`, `receiveLocal?`, `checkFastLiquidity?`): `Promise`<{ `amountReceived`: `BigNumberish` ; `destinationSlippage`: `BigNumberish` ; `originSlippage`: `BigNumberish` ; `routerFee`: `BigNumberish` ; `isFastPath`: `boolean` }>

Calculates the estimated amount received on the destination domain for a bridge transaction.

**Parameters**

| Name                 | Type           | Default value | Description                                                                               |
| -------------------- | -------------- | ------------- | ----------------------------------------------------------------------------------------- |
| `originDomain`       | `string`       | `undefined`   | The domain ID of the origin chain.                                                        |
| `destinationDomain`  | `string`       | `undefined`   | The domain ID of the destination chain.                                                   |
| `originTokenAddress` | `string`       | `undefined`   | The address of the token to be bridged from origin.                                       |
| `amount`             | `BigNumberish` | `undefined`   | The amount of the origin token to bridge, in the origin token's native decimal precision. |
| `receiveLocal`       | `boolean`      | `false`       | (optional) Whether the desired destination token is the local asset ("nextAsset").        |
| `checkFastLiquidity` | `boolean`      | `false`       | (optional) Whether to check current router liquidity for fast path availability.          |

**Returns**

`Promise`<{ `amountReceived`: `BigNumberish` ; `destinationSlippage`: `BigNumberish` ; `originSlippage`: `BigNumberish` ; `routerFee`: `BigNumberish` ; `isFastPath`: `boolean` }>

Estimated amount received for local/adopted assets, if applicable, in their native decimal precisions.

***

### estimateRelayerFee

▸ **estimateRelayerFee**(`params`): `Promise`<`BigNumber`>

Calculates an estimated relayer fee in the native asset of the origin domain to be used in xcall.

**`Example`**

```ts
// call SdkBase.create(), instantiate a signer

const params = {
  originDomain: "6648936",
  destinationDomain: "1869640809",
};

const txRequest = sdkBase.estimateRelayerFee(params);
signer.sendTransaction(txRequest);
```

**Parameters**

| Name                                 | Type     | Default value                                      | Description                                                                                  |
| ------------------------------------ | -------- | -------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `params`                             | `Object` | undefined                                          | SdkEstimateRelayerFeeParams object.                                                          |
| `params.originDomain`                | `string` | undefined                                          | The origin domain ID of the transfer.                                                        |
| `params.destinationDomain`           | `string` | undefined                                          | The destination domain ID of the transfer.                                                   |
| `params.callDataGasAmount`           | `string` | undefined                                          | The gas amount needed for calldata.                                                          |
| `params.originNativeToken`           | `string` | "0x0000000000000000000000000000000000000000"       | (optional) The native token of the origin domain.                                            |
| `params.priceIn`                     | `string` | "native"                                           | (optional) "native" for native asset denomination or "usd" to get the estimate in USD value. |
| `params.destinationNativeToken`      | `string` | "0x0000000000000000000000000000000000000000"       | (optional) The native token of the destination domain.                                       |
| `params.originNativeTokenPrice`      | `number` | (uses external estimate - increases response time) | (optional) The USD price of the origin native token.                                         |
| `params.destinationNativeTokenPrice` | `number` | (uses external estimate - increases response time) | (optional) The USD price of the destination native token.                                    |
| `params.destinationGasPrice`         | `string` | (uses external estimate - increases response time) | (optional) The gas price of the destination chain, in gwei units.                            |

**Returns**

`Promise`<`BigNumber`>

The relayer fee in native asset of the origin domain or USD equivalent.

***

### xcall

▸ **xcall**(`params`): `Promise`<`TransactionRequest`>

Prepares xcall inputs and encodes the calldata. Returns an ethers TransactionRequest object, ready to be sent to an RPC provider.

**`Example`**

```ts
// call SdkBase.create(), instantiate a signer

const params = {
  origin: "6648936"
  destination: "1869640809"
  to: "0x3cEe6c5c0fB713925BdA590829EA574b7b4f96b6"
  asset: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
  delegate: "0x3cEe6c5c0fB713925BdA590829EA574b7b4f96b6"
  amount: "1000000"
  slippage: "300"
  callData: "0x",
  relayerFee: "10000000000000"
};

const txRequest = sdkBase.xcall(params);
signer.sendTransaction(txRequest);
```

**Parameters**

| Name                                  | Type                     | Description                                                                                                                                                                                                                                                                                                                                                                                                                     |
| ------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `params`                              | `Object`                 | SdkXCallParams object.                                                                                                                                                                                                                                                                                                                                                                                                          |
| `params.amount`                       | `undefined` \| `string`  | (optional) The amount of tokens (in specified asset) to send with the xcall. If `wrapNativeOnOrigin` is true, this will be used as the amount of native token to deposit into the wrapper contract and withdraw as wrapped native token for sending (e.g. deposit ETH to the WETH contract in exchange for the WETH ERC20).                                                                                                     |
| `params.asset`                        | `undefined` \| `string`  | (optional) The target asset to send with the xcall. Can be set to `address(0)` if this is a 0-value transfer. If `wrapNativeOnOrigin` is true, this should be the target wrapper contract (e.g. WETH) address.                                                                                                                                                                                                                  |
| `params.callData`                     | `undefined` \| `string`  | (optional) Calldata to execute (can be empty: "0x").                                                                                                                                                                                                                                                                                                                                                                            |
| `params.delegate`                     | `undefined` \| `string`  | (optional) Address allowed to cancel an xcall on destination.                                                                                                                                                                                                                                                                                                                                                                   |
| `params.destination`                  | `string`                 | The destination domain ID.                                                                                                                                                                                                                                                                                                                                                                                                      |
| `params.origin`                       | `string`                 | The origin domain ID.                                                                                                                                                                                                                                                                                                                                                                                                           |
| `params.receiveLocal`                 | `undefined` \| `boolean` | (optional) Whether to receive the local asset ("nextAsset").                                                                                                                                                                                                                                                                                                                                                                    |
| `params.relayerFee`                   | `undefined` \| `string`  | (optional) Fee paid to relayers, in native asset on origin. Use `calculateRelayerFee` to estimate.                                                                                                                                                                                                                                                                                                                              |
| `params.relayerFeeInTransactingAsset` | `undefined` \| `string`  | (optional) Fee paid to relayers, in transacting asset on origin. Use `calculateRelayerFee` to estimate.                                                                                                                                                                                                                                                                                                                         |
| `params.slippage`                     | `undefined` \| `string`  | (optional) Maximum acceptable slippage in BPS. For example, a value of 30 means 0.3% slippage.                                                                                                                                                                                                                                                                                                                                  |
| `params.to`                           | `string`                 | Address receiving funds or the target contract.                                                                                                                                                                                                                                                                                                                                                                                 |
| `params.wrapNativeOnOrigin`           | `undefined` \| `boolean` | (optional) Whether we should wrap the native token before sending the xcall. This will use the Multisend utility contract to deposit ETH, approve Connext as a spender, and call xcall. If set true, `asset` should be the target wrapper contract (e.g. WETH) address.                                                                                                                                                         |
| `params.unwrapNativeOnDestination`    | `undefined` \| `boolean` | (optional) Whether we should unwrap the wrapped native token when the transfer reaches its destination. By default, if sending a wrapped native token, the wrapped token is what gets delivered at the destination. Setting this to `true` means we should overwrite `callData` to target the Unwrapper utility contract, which will unwrap the wrapped native token and deliver it to the target recipient (the `to` address). |

**Returns**

`Promise`<`TransactionRequest`>

providers.TransactionRequest object.

***

### create

▸ `Static` **create**(`_config`): `Promise`<`SdkBase`>

Create a singleton instance of the SdkBase class.

**Parameters**

| Name                    | Type                                                                                   | Default value | Description                                             |
| ----------------------- | -------------------------------------------------------------------------------------- | ------------- | ------------------------------------------------------- |
| `_config`               | `Object`                                                                               | undefined     | SdkConfig object.                                       |
| `_config.chains`        | `Record`<`string`, { providers: string\[] }>                                           | undefined     | Chain config, at minimum with providers for each chain. |
| `_config.signerAddress` | `string`                                                                               | undefined     | Signer address for transactions.                        |
| `_config.logLevel`      | `"fatal"` \| `"error"` \| `"warn"` \| `"info"` \| `"debug"` \| `"trace"` \| `"silent"` | "info"        | (optional) Logging severity level.                      |
| `_config.network`       | `"testnet"` \| `"mainnet"`                                                             | "mainnet"     | (optional) Blockchain environment to interact with.     |

**Returns**

`Promise`<`SdkBase`>

providers.TransactionRequest object.

**`Example`**

```ts
import { SdkBase } from "@connext/sdk";

const config = {
  signerAddress: "<wallet_address>",
  network: "mainnet",
  chains: {
    6648936: { // the domain ID for Ethereum Mainnet
      providers: ["https://rpc.ankr.com/eth"],
    },
    1869640809: { // the domain ID for Optimism
      providers: ["https://mainnet.optimism.io"]
    },
    1886350457: { // the domain ID for Polygon
      providers: ["https://polygon-rpc.com"]
    },
  },
}

const sdkBase = await SdkBase.create(config);
```

{% hint style="info" %}
See the [Deployments](broken://pages/bbG6im4BlayCJRkYxOkN) page for all domain IDs and asset addresses.
{% endhint %}

***

### updateSlippage

▸ **updateSlippage**(`params`): `Promise`<`TransactionRequest`>

Updates the slippage tolerance for a specific transfer on origin; only the origin sender is allowed to do so.

**`Example`**

```ts
// call SdkBase.create(), instantiate a signer

const params = {
  domainId: "6648936",
  transferId: "0xdd252f58a45dc78fee1ac12a628782bda6a98315b286aadf76e4d7322bf135ca",
  relayerFee: "1000",
};

const txRequest = sdkBase.updateSlippage(params);
signer.sendTransaction(txRequest);
```

**Parameters**

| Name                | Type     | Description                                           |
| ------------------- | -------- | ----------------------------------------------------- |
| `params`            | `Object` | SdkUpdateSlippageParams object.                       |
| `params.domainId`   | `string` | The origin domain ID of the transfer.                 |
| `params.slippage`   | `string` | The new relayer fee to use for this transfer, in BPS. |
| `params.transferId` | `string` | The transfer ID.                                      |

**Returns**

`Promise`<`TransactionRequest`>

providers.TransactionRequest object.


# SdkPool

SDK class encapsulating stableswap pool functions.

This class will either interact with internal StableSwapFacet pools or external StableSwap pools depending on which type of pool is being used for each asset. Note: SDK currently only supports internal StableSwapFacet pools.

## Hierarchy

* `SdkShared`

  ↳ **`SdkPool`**

## Methods

### addLiquidity

▸ **addLiquidity**(`domainId`, `tokenAddress`, `amounts`, `minToMint?`, `deadline?`): `Promise`<`TransactionRequest`>

Prepares the transaction request for adding liquidity to a pool.

**Parameters**

| Name           | Type        | Default value | Description                                                    |
| -------------- | ----------- | ------------- | -------------------------------------------------------------- |
| `domainId`     | `string`    | `undefined`   | The domain ID of the pool.                                     |
| `tokenAddress` | `string`    | `undefined`   | The address of local or adopted token.                         |
| `amounts`      | `string`\[] | `undefined`   | The amounts of the tokens to swap.                             |
| `minToMint`    | `string`    | `"0"`         | (optional) The minimum acceptable amount of LP tokens to mint. |
| `deadline`     | `number`    | `undefined`   | (optional) The deadline for the operation.                     |

**Returns**

`Promise`<`TransactionRequest`>

providers.TransactionRequest object.

***

### calculateAddLiquidityPriceImpact

▸ **calculateAddLiquidityPriceImpact**(`domainId`, `tokenAddress`, `amountX`, `amountY`): `Promise`<`undefined` | `BigNumber`>

Calculates the price impact of adding liquidity to a pool.

**Parameters**

| Name           | Type     | Description                                                                   |
| -------------- | -------- | ----------------------------------------------------------------------------- |
| `domainId`     | `string` | The domain ID of the pool.                                                    |
| `tokenAddress` | `string` | The address of local or adopted token.                                        |
| `amountX`      | `string` | The amount of token X (index 0 of the pool), in the token's native precision. |
| `amountY`      | `string` | The amount of token Y (index 1 of the pool), in the token's native precision. |

**Returns**

`Promise`<`undefined` | `BigNumber`>

Price impact for adding liquidity, in 1e18 precision.

***

### calculatePriceImpact

▸ **calculatePriceImpact**(`tokenInputAmount`, `tokenOutputAmount`, `virtualPrice?`, `isDeposit?`): `BigNumber`

Calculates the price impact depending on whether liquidity is being deposited or withdrawn.

**Parameters**

| Name                | Type        | Default value | Description                                                                                                            |
| ------------------- | ----------- | ------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `tokenInputAmount`  | `BigNumber` | `undefined`   | The amount of inbound tokens (LP tokens for withdrawals, total tokens for deposits, dx for swaps), in 1e18 precision.  |
| `tokenOutputAmount` | `BigNumber` | `undefined`   | The amount of outbound tokens (total tokens for withdrawals, LP tokens for deposits, dy for swaps), in 1e18 precision. |
| `virtualPrice`      | `BigNumber` | `undefined`   | (optional) The current virtual price of the pool.                                                                      |
| `isDeposit`         | `boolean`   | `true`        | (optional) Whether this is a deposit or withdrawal.                                                                    |

**Returns**

`BigNumber`

The price impact.

***

### calculateRemoveLiquidityPriceImpact

▸ **calculateRemoveLiquidityPriceImpact**(`domainId`, `tokenAddress`, `amountX`, `amountY`): `Promise`<`undefined` | `BigNumber`>

Returns the price impact of removing liquidity from a pool.

**Parameters**

| Name           | Type     | Description                                                                   |
| -------------- | -------- | ----------------------------------------------------------------------------- |
| `domainId`     | `string` | The domain ID of the pool.                                                    |
| `tokenAddress` | `string` | The address of local or adopted token.                                        |
| `amountX`      | `string` | The amount of token X (index 0 of the pool), in the token's native precision. |
| `amountY`      | `string` | The amount of token Y (index 1 of the pool), in the token's native precision. |

**Returns**

`Promise`<`undefined` | `BigNumber`>

The price impact for removing liquidity, in 1e18 precision.

***

### calculateRemoveSwapLiquidityOneToken

▸ **calculateRemoveSwapLiquidity**(`domainId`, `tokenAddress`, `amount`, `index`): `Promise`<`BigNumber`>

Calculates the amounts of underlying tokens returned.

**Parameters**

| Name           | Type     | Description                                       |
| -------------- | -------- | ------------------------------------------------- |
| `domainId`     | `string` | The domain ID of the pool.                        |
| `tokenAddress` | `string` | The address of local or adopted token.            |
| `amount`       | `string` | The amount of the LP token to burn on withdrawal. |
| `index`        | `number` | The index of the token to withdraw.               |

**Returns**

`Promise`<`BigNumber`>

Calculated amount of underlying token returned.

***

### calculateRemoveSwapLiquidity

▸ **calculateRemoveSwapLiquidity**(`domainId`, `tokenAddress`, `amount`): `Promise`<`BigNumber`\[]>

Calculates the amounts of underlying tokens returned.

**Parameters**

| Name           | Type     | Description                                       |
| -------------- | -------- | ------------------------------------------------- |
| `domainId`     | `string` | The domain ID of the pool.                        |
| `tokenAddress` | `string` | The address of local or adopted token.            |
| `amount`       | `string` | The amount of the LP token to burn on withdrawal. |

**Returns**

`Promise`<`BigNumber`\[]>

Array containing amount of each underlying token returned, in correct index order.

***

### calculateSwap

▸ **calculateSwap**(`domainId`, `tokenAddress`, `tokenIndexFrom`, `tokenIndexTo`, `amount`): `Promise`<`BigNumber`>

Calculates the amount of tokens received on a swap.

**Parameters**

| Name             | Type           | Description                                                                   |
| ---------------- | -------------- | ----------------------------------------------------------------------------- |
| `domainId`       | `string`       | The domain ID of the pool.                                                    |
| `tokenAddress`   | `string`       | The address of local or adopted token.                                        |
| `tokenIndexFrom` | `number`       | The index of the token to sell.                                               |
| `tokenIndexTo`   | `number`       | The index of the token to buy.                                                |
| `amount`         | `BigNumberish` | The number of tokens to sell, in the "From" token's native decimal precision. |

**Returns**

`Promise`<`BigNumber`>

Minimum amount received, in the "To" token's native decimal precision.

***

### calculateSwapPriceImpact

▸ **calculateSwapPriceImpact**(`domainId`, `amountX`, `tokenX`, `tokenY`): `Promise`<`BigNumber`>

Calculates the price impact of a swap.

**Parameters**

| Name       | Type     | Description                                                    |
| ---------- | -------- | -------------------------------------------------------------- |
| `domainId` | `string` | The domain ID of the pool.                                     |
| `amountX`  | `string` | The amount of tokens to swap, in the token's native precision. |
| `tokenX`   | `string` | The address of the token to swap from.                         |
| `tokenY`   | `string` | The address of the token to swap to.                           |

**Returns**

`Promise`<`BigNumber`>

The price impact for swapping, in 1e18 precision.

***

### calculateTokenAmount

▸ **calculateTokenAmount**(`domainId`, `tokenAddress`, `amounts`, `isDeposit?`): `Promise`<`BigNumber`>

Calculates the minimum LP token amount from deposits or withdrawals.

**Parameters**

| Name           | Type        | Default value | Description                                                                                                     |
| -------------- | ----------- | ------------- | --------------------------------------------------------------------------------------------------------------- |
| `domainId`     | `string`    | `undefined`   | The domain ID of the pool.                                                                                      |
| `tokenAddress` | `string`    | `undefined`   | The address of local or adopted token.                                                                          |
| `amounts`      | `string`\[] | `undefined`   | The amounts of the tokens to deposit/withdraw, in the correct index order and in each token's native precision. |
| `isDeposit`    | `boolean`   | `true`        | (optional) Whether this is a deposit or withdrawal.                                                             |

**Returns**

`Promise`<`BigNumber`>

Minimum LP tokens received, in 1e18 precision.

***

### calculateYield

▸ **calculateYield**(`feesEarned`, `principal`, `days`): `Object`

Calculates apr and apy.

**Parameters**

| Name         | Type     | Description                                      |
| ------------ | -------- | ------------------------------------------------ |
| `feesEarned` | `number` | The total fees earned in the period.             |
| `principal`  | `number` | The principal amount at the start of the period. |
| `days`       | `number` | The number of days to look back.                 |

**Returns**

`Object`

Object containing apr and apy.

| Name  | Type     |
| ----- | -------- |
| `apr` | `number` |
| `apy` | `number` |

***

### getAdopted

▸ **getAdopted**(`domainId`, `tokenAddress`): `Promise`<`string`>

Reads the adopted token.

**Parameters**

| Name           | Type     | Description                            |
| -------------- | -------- | -------------------------------------- |
| `domainId`     | `string` | The domain ID of the pool.             |
| `tokenAddress` | `string` | The address of local or adopted token. |

**Returns**

`Promise`<`string`>

The adopted token.

***

### getDefaultDeadline

▸ **getDefaultDeadline**(): `number`

Set to 1 hour from current time.

**Returns**

`number`

The default deadline, in unix time.

***

### getLPTokenAddress

▸ **getLPTokenAddress**(`domainId`, `tokenAddress`): `Promise`<`string`>

Reads the LP token address of a pool.

**Parameters**

| Name           | Type     | Description                            |
| -------------- | -------- | -------------------------------------- |
| `domainId`     | `string` | The domain ID of the pool.             |
| `tokenAddress` | `string` | The address of local or adopted token. |

**Returns**

`Promise`<`string`>

The LP token address.

***

### getPoolTokenAddress

▸ **getPoolTokenAddress**(`domainId`, `tokenAddress`, `index`): `Promise`<`string`>

Reads the token address of a specified index in a pool.

**Parameters**

| Name           | Type     | Description                            |
| -------------- | -------- | -------------------------------------- |
| `domainId`     | `string` | The domain ID of the pool.             |
| `tokenAddress` | `string` | The address of local or adopted token. |
| `index`        | `number` | The index of the token in the pool.    |

**Returns**

`Promise`<`string`>

The address of the specified token in the pool.

***

### getPoolTokenBalance

▸ **getPoolTokenBalance**(`domainId`, `tokenAddress`, `poolTokenAddress`): `Promise`<`BigNumber`>

Reads the balance of a pool token.

**Parameters**

| Name               | Type     | Description                            |
| ------------------ | -------- | -------------------------------------- |
| `domainId`         | `string` | The domain ID of the pool.             |
| `tokenAddress`     | `string` | The address of local or adopted token. |
| `poolTokenAddress` | `string` | The address of the pool token.         |

**Returns**

`Promise`<`BigNumber`>

The balance of the pool token.

***

### getPoolTokenIndex

▸ **getPoolTokenIndex**(`domainId`, `tokenAddress`, `poolTokenAddress`): `Promise`<`number`>

Reads the index of a token in a pool.

**Parameters**

| Name               | Type     | Description                                                |
| ------------------ | -------- | ---------------------------------------------------------- |
| `domainId`         | `string` | The domain ID of the pool.                                 |
| `tokenAddress`     | `string` | The address of the local or adopted token.                 |
| `poolTokenAddress` | `string` | The address of the token in the pool to get the index for. |

**Returns**

`Promise`<`number`>

The index of the specified token in the pool or -1 if not found.

***

### getPoolTokenDecimals

▸ **getPoolTokenDecimals**(`domainId`, `tokenAddress`, `poolTokenAddress`): `Promise`<`number`>

Reads the decimal precision of a token in a pool.

**Parameters**

| Name               | Type     | Description                                                    |
| ------------------ | -------- | -------------------------------------------------------------- |
| `domainId`         | `string` | The domain id of the pool.                                     |
| `tokenAddress`     | `string` | The address of local or adopted token.                         |
| `poolTokenAddress` | `string` | The address of the token in the pool to get the precision for. |

**Returns**

`Promise`<`number`>

The decimal precision of the specified token in the pool or -1 if not found.

***

### getRepresentation

▸ **getRepresentation**(`domainId`, `tokenAddress`): `Promise`<`string`>

Reads the representation asset of the pool. The representation asset is the adopted asset on the canonical domain and local (nextAsset) otherwise.

**Parameters**

| Name           | Type     | Description                            |
| -------------- | -------- | -------------------------------------- |
| `domainId`     | `string` | The domain ID of the pool.             |
| `tokenAddress` | `string` | The address of local or adopted token. |

**Returns**

`Promise`<`string`>

The representation (local or adopted) token.

***

### getTokenPrice

▸ **getTokenPrice**(`tokenSymbol`): `Promise`<`any`>

Fetches the current price of a token.

**Parameters**

| Name          | Type     | Description               |
| ------------- | -------- | ------------------------- |
| `tokenSymbol` | `string` | The symbol for the token. |

**Returns**

`Promise`<`any`>

The price of the token.

***

### getTokenSupply

▸ **getTokenSupply**(`domainId`, `tokenAddress`): `Promise`<`BigNumber`>

Reads the ERC20 token supply.

**Parameters**

| Name           | Type     | Description                       |
| -------------- | -------- | --------------------------------- |
| `domainId`     | `string` | The domain ID of the ERC20 token. |
| `tokenAddress` | `string` | The address of the ERC20 token.   |

**Returns**

`Promise`<`BigNumber`>

The balance of the address.

***

### getTokenUserBalance

▸ **getTokenUserBalance**(`domainId`, `tokenAddress`, `userAddress`): `Promise`<`BigNumber`>

Reads the ERC20 token balance of an address.

**Parameters**

| Name           | Type     | Description                        |
| -------------- | -------- | ---------------------------------- |
| `domainId`     | `string` | The domain ID of the ERC20 token.  |
| `tokenAddress` | `string` | The address of the ERC20 token.    |
| `userAddress`  | `string` | The address to get the balance of. |

**Returns**

`Promise`<`BigNumber`>

The balance of the address.

***

### getUserPools

▸ **getUserPools**(`domainId`, `userAddress`): `Promise`<{ `info`: `Pool` ; `lpTokenBalance`: `BigNumber` ; `poolTokenBalances`: `BigNumber`\[] }\[]>

Retrieves the Pools that a user has LP tokens for.

**Parameters**

| Name          | Type     | Description                                   |
| ------------- | -------- | --------------------------------------------- |
| `domainId`    | `string` | The domain ID of the pool.                    |
| `userAddress` | `string` | The address of the user to get the pools for. |

**Returns**

`Promise`<{ `info`: `Pool` ; `lpTokenBalance`: `BigNumber` ; `poolTokenBalances`: `BigNumber`\[] }\[]>

Array of Pool objects.

***

### getVirtualPrice

▸ **getVirtualPrice**(`domainId`, `tokenAddress`): `Promise`<`BigNumber`>

Reads the virtual price of a pool.

**Parameters**

| Name           | Type     | Description                            |
| -------------- | -------- | -------------------------------------- |
| `domainId`     | `string` | The domain ID of the pool.             |
| `tokenAddress` | `string` | The address of local or adopted token. |

**Returns**

`Promise`<`BigNumber`>

The virtual price, scaled to the pool's decimal precision (10^18).

***

### getYieldStatsForDays

▸ **getYieldStatsForDays**(`domainId`, `tokenAddress`, `unixTimestamp`, `days`): `Promise`<`undefined` | { `totalFeesFormatted`: `number` ; `totalLiquidityFormatted`: `number` ; `totalVolume`: `BigNumber` ; `totalVolumeFormatted`: `number` }>

Calculates the fees, liquidity, and volume of a pool for the days prior to the specified unix time.

**Parameters**

| Name            | Type     | Description                                |
| --------------- | -------- | ------------------------------------------ |
| `domainId`      | `string` | The domain ID of the pool.                 |
| `tokenAddress`  | `string` | The address of local or adopted token.     |
| `unixTimestamp` | `number` | The unix time to start the look back from. |
| `days`          | `number` | The number of days to look back.           |

**Returns**

`Promise`<`undefined` | { `totalFeesFormatted`: `number` ; `totalLiquidityFormatted`: `number` ; `totalVolume`: `BigNumber` ; `totalVolumeFormatted`: `number` }>

Object containing fees, liquidity, and volume, in 1e18 precision.

***

### removeLiquidity

▸ **removeLiquidity**(`domainId`, `tokenAddress`, `amount`, `minAmounts?`, `deadline?`): `Promise`<`TransactionRequest`>

Returns the transaction request for removing liquidity from a pool.

**Parameters**

| Name           | Type        | Description                                               |
| -------------- | ----------- | --------------------------------------------------------- |
| `domainId`     | `string`    | The domain ID of the pool.                                |
| `tokenAddress` | `string`    | The address of local or adopted token.                    |
| `amount`       | `string`    | The amount of LP tokens to burn.                          |
| `minAmounts`   | `string`\[] | (optional) The minimum amounts of each token to withdraw. |
| `deadline`     | `number`    | (optional) The deadline for the operation.                |

**Returns**

`Promise`<`TransactionRequest`>

providers.TransactionRequest object.

***

### removeLiquidityOneToken

▸ **removeLiquidityOneToken**(`domainId`, `tokenAddress`, `amount`, `minAmount?`, `deadline?`): `Promise`<`TransactionRequest`>

Returns the transaction request for removing liquidity from a pool.

**Parameters**

| Name                   | Type     | Default value               | Description                                                        |
| ---------------------- | -------- | --------------------------- | ------------------------------------------------------------------ |
| `domainId`             | `string` | `undefined`                 | The domain ID of the pool.                                         |
| `tokenAddress`         | `string` | `undefined`                 | The address of local or adopted token.                             |
| `withdrawTokenAddress` | `string` | `undefined`                 | The address of the token to withdraw.                              |
| `amount`               | `string` | `undefined`                 | The amount of LP tokens to burn.                                   |
| `minAmount`            | `string` | "0"                         | (optional) The minimum acceptable amount of the token to withdraw. |
| `deadline`             | `number` | One hour from current time. | (optional) The deadline for the operation.                         |

**Returns**

`Promise`<`TransactionRequest`>

providers.TransactionRequest object.

***

### removeLiquidityImbalance

▸ **removeLiquidityImbalance**(`domainId`, `tokenAddress`, `amounts`, `maxBurnAmount?`, `deadline?`): `Promise`<`TransactionRequest`>

Returns the transaction request for removing liquidity from a pool.

**Parameters**

| Name            | Type        | Default value               | Description                                               |
| --------------- | ----------- | --------------------------- | --------------------------------------------------------- |
| `domainId`      | `string`    | `undefined`                 | The domain ID of the pool.                                |
| `tokenAddress`  | `string`    | `undefined`                 | The address of local or adopted token.                    |
| `amounts`       | `string`\[] | `undefined`                 | The amount of LP tokens to burn.                          |
| `maxBurnAmount` | `string`    | "0" (Use total LP balance)  | (optional) The max LP tokens the user is willing to burn. |
| `deadline`      | `number`    | One hour from current time. | (optional) The deadline for the operation.                |

**Returns**

`Promise`<`TransactionRequest`>

providers.TransactionRequest object.

***

### swap

▸ **swap**(`domainId`, `tokenAddress`, `from`, `to`, `amount`, `minDy?`, `deadline?`): `Promise`<`TransactionRequest`>

Returns the transaction request for performing a swap in a pool.

**Parameters**

| Name           | Type     | Default value | Description                                                   |
| -------------- | -------- | ------------- | ------------------------------------------------------------- |
| `domainId`     | `string` | `undefined`   | The domain ID of the pool.                                    |
| `tokenAddress` | `string` | `undefined`   | The address of local or adopted token.                        |
| `from`         | `string` | `undefined`   | The address of the token to sell.                             |
| `to`           | `string` | `undefined`   | The address of the token to buy.                              |
| `amount`       | `string` | `undefined`   | The amount of the selling token to swap.                      |
| `minDy`        | `number` | `0`           | (optional) The minimum amount of the buying token to receive. |
| `deadline`     | `number` | `undefined`   | (optional) The deadline for the operation.                    |

**Returns**

`Promise`<`TransactionRequest`>

providers.TransactionRequest object.

***

### create

▸ `Static` **create**(`_config`): `Promise`<`SdkPool`>

Create a singleton instance of the SdkPool class.

**Parameters**

| Name                    | Type                                                                                   | Default value | Description                                             |
| ----------------------- | -------------------------------------------------------------------------------------- | ------------- | ------------------------------------------------------- |
| `_config`               | `Object`                                                                               | undefined     | SdkConfig object.                                       |
| `_config.chains`        | `Record`<`string`, { providers: string\[] }>                                           | undefined     | Chain config, at minimum with providers for each chain. |
| `_config.signerAddress` | `string`                                                                               | undefined     | Signer address for transactions.                        |
| `_config.logLevel`      | `"fatal"` \| `"error"` \| `"warn"` \| `"info"` \| `"debug"` \| `"trace"` \| `"silent"` | "info"        | (optional) Logging severity level.                      |
| `_config.network`       | `"testnet"` \| `"mainnet"`                                                             | "mainnet"     | (optional) Blockchain environment to interact with.     |

**Returns**

`Promise`<`SdkPool`>

providers.TransactionRequest object.

**`Example`**

```ts
import { SdkPool } from "@connext/sdk";

const config = {
  signerAddress: "<wallet_address>",
  network: "mainnet",
  chains: {
    6648936: { // the domain ID for Ethereum Mainnet
      providers: ["https://rpc.ankr.com/eth"],
    },
    1869640809: { // the domain ID for Optimism
      providers: ["https://mainnet.optimism.io"]
    },
    1886350457: { // the domain ID for Polygon
      providers: ["https://polygon-rpc.com"]
    },
  },
}

const sdkPool = await SdkPool.create(config);
```

{% hint style="info" %}
See the [Deployments](broken://pages/bbG6im4BlayCJRkYxOkN) page for all domain IDs and asset addresses.
{% endhint %}


# SdkRouter

SDK class encapsulating router functions.

## Hierarchy

* `SdkShared`

  ↳ **`SdkRouter`**

## Methods

### addLiquidityForRouter

▸ **addLiquidityForRouter**(`params`): `Promise`<`TransactionRequest`>

Returns the transaction request for adding liquidity to a router.

**Parameters**

| Name                  | Type     | Description                              |
| --------------------- | -------- | ---------------------------------------- |
| `params`              | `Object` | addLiquidityForRouter parameters object. |
| `params.amount`       | `string` | The amount of the token to add.          |
| `params.domainId`     | `string` | The domain ID.                           |
| `params.router`       | `string` | The address of the router.               |
| `params.tokenAddress` | `string` | The address of the token.                |

**Returns**

`Promise`<`TransactionRequest`>

providers.TransactionRequest object.

***

### removeRouterLiquidity

▸ **removeRouterLiquidity**(`params`): `Promise`<`TransactionRequest`>

Returns the transaction request for removing liquidity from a router.

**`Remarks`**

This function is permissioned to the router owner only.

**Parameters**

| Name                  | Type     | Description                                            |
| --------------------- | -------- | ------------------------------------------------------ |
| `params`              | `Object` | removeRouterLiquidity parameters object.               |
| `params.amount`       | `string` | The amount of the token to add.                        |
| `params.domainId`     | `string` | The domain ID.                                         |
| `params.recipient`    | `string` | The address where the removed funds will be delivered. |
| `params.tokenAddress` | `string` | The address of the token.                              |

**Returns**

`Promise`<`TransactionRequest`>

providers.TransactionRequest object.

***

### removeRouterLiquidityFor

▸ **removeRouterLiquidityFor**(`params`): `Promise`<`TransactionRequest`>

**Parameters**

| Name                  | Type     |
| --------------------- | -------- |
| `params`              | `Object` |
| `params.amount`       | `string` |
| `params.domainId`     | `string` |
| `params.recipient`    | `string` |
| `params.router`       | `string` |
| `params.tokenAddress` | `string` |

**Returns**

`Promise`<`TransactionRequest`>

***

### create

▸ `Static` **create**(`_config`): `Promise`<`SdkRouter`>

Create a singleton instance of the SdkRouter class.

**Parameters**

| Name                    | Type                                                                                   | Default value | Description                                             |
| ----------------------- | -------------------------------------------------------------------------------------- | ------------- | ------------------------------------------------------- |
| `_config`               | `Object`                                                                               | undefined     | SdkConfig object.                                       |
| `_config.chains`        | `Record`<`string`, { providers: string\[] }>                                           | undefined     | Chain config, at minimum with providers for each chain. |
| `_config.signerAddress` | `string`                                                                               | undefined     | Signer address for transactions.                        |
| `_config.logLevel`      | `"fatal"` \| `"error"` \| `"warn"` \| `"info"` \| `"debug"` \| `"trace"` \| `"silent"` | "info"        | (optional) Logging severity level.                      |
| `_config.network`       | `"testnet"` \| `"mainnet"`                                                             | "mainnet"     | (optional) Blockchain environment to interact with.     |

**Returns**

`Promise`<`SdkRouter`>

providers.TransactionRequest object.

**`Example`**

```ts
import { SdkRouter } from "@connext/sdk";

const config = {
  signerAddress: "<wallet_address>",
  network: "mainnet",
  chains: {
    6648936: { // the domain ID for Ethereum Mainnet
      providers: ["https://rpc.ankr.com/eth"],
    },
    1869640809: { // the domain ID for Optimism
      providers: ["https://mainnet.optimism.io"]
    },
    1886350457: { // the domain ID for Polygon
      providers: ["https://polygon-rpc.com"]
    },
  },
}

const sdkRouter = await SdkRouter.create(config);
```

{% hint style="info" %}
See the [Deployments](broken://pages/bbG6im4BlayCJRkYxOkN) page for all domain IDs and asset addresses.
{% endhint %}


# SdkUtils

SDK class encapsulating utility functions.

## Hierarchy

* `SdkShared`

  ↳ **`SdkUtils`**

## Methods

### getRoutersData

▸ **getRoutersData**(): `Promise`<`any`>

Fetches a list of router liquidity data.

**Returns**

`Promise`<`any`>

Array of objects containing the router address and liquidity information, in the form of:

```ts
{
  "address": "0xf26c772c0ff3a6036bddabdaba22cf65eca9f97c",
  "asset_canonical_id": "0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
  "asset_domain": "1869640809",
  "router_address": "0xf26c772c0ff3a6036bddabdaba22cf65eca9f97c",
  "balance": 8816006545,
  "local": "0x67e51f46e8e14d4e4cab9df48c59ad8f512486dd",
  "adopted": "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
  "canonical_id": "0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
  "canonical_domain": "6648936",
  "domain": "1869640809",
  "key": "0x6d9af4a33ed4034765652ab0f44205952bc6d92198d3ef78fe3fb2b078d0941c",
  "id": "0x67e51f46e8e14d4e4cab9df48c59ad8f512486dd",
  "fees_earned": 7249237
}
```

***

### getTransfers

▸ **getTransfers**(`params`): `Promise`<`any`>

Fetches the transfers that match filter criteria.

**Parameters**

| Name                      | Type                   |
| ------------------------- | ---------------------- |
| `params`                  | `Object`               |
| `params.range?`           | `Object`               |
| `params.range.limit?`     | `number`               |
| `params.range.offset?`    | `number`               |
| `params.routerAddress?`   | `string`               |
| `params.status?`          | `XTransferStatus`      |
| `params.transactionHash?` | `string`               |
| `params.transferId?`      | `string`               |
| `params.userAddress?`     | `string`               |
| `params.errorStatus?`     | `XTransferErrorStatus` |

**Returns**

`Promise`<`any`>

The object containing transfer data in the form of:

```ts
{
  "transfer_id": "0x4a379d3367bb589ddc00dd7c2d7d6557bed75c9595e5cd6a4369d85e587ec386",
  "nonce": 34,
  "to": "0x6d2a06543d23cc6523ae5046add8bb60817e0a94",
  "call_data": "0x",
  "origin_domain": "6778479",
  "destination_domain": "6648936",
  "receive_local": false,
  "origin_chain": "100",
  "origin_transacting_asset": "0x6a023ccd1ff6f2045c3309768ead9e68f978f6e1",
  "origin_transacting_amount": "100000000000000",
  "origin_bridged_asset": "0x538e2ddbfdf476d24ccb1477a518a82c9ea81326",
  "origin_bridged_amount": "99407526243394",
  "xcall_caller": "0x6d2a06543d23cc6523ae5046add8bb60817e0a94",
  "xcall_transaction_hash": "0xd1b4f723c1f7453bc38e8dd64f56830ed1e907b95c8b5eba55a9f1a26d867ea8",
  "xcall_timestamp": 1672964955,
  "xcall_gas_price": "4654771330",
  "xcall_gas_limit": "511921",
  "xcall_block_number": 25819530,
  "destination_chain": "1",
  "status": "CompletedFast",
  "routers": [
    "0xf26c772c0ff3a6036bddabdaba22cf65eca9f97c"
  ],
  "destination_transacting_asset": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
  "destination_transacting_amount": "99357822480272",
  "destination_local_asset": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
  "destination_local_amount": "99407526243394",
  "execute_caller": "0x75c6a865c30da54e365cb5def728890b3dd8bdc4",
  "execute_transaction_hash": "0x7e02bd79087ec48d9588f655474ba7f38921b46ab8ff812f2b2a8b97bad9fa72",
  "execute_timestamp": 1672965155,
  "execute_gas_price": "32181349289",
  "execute_gas_limit": "4000000",
  "execute_block_number": 16344186,
  "execute_origin_sender": "0x6d2a06543d23cc6523ae5046add8bb60817e0a94",
  "reconcile_caller": "0xf7c4d7dcec2c09a15f2db5831d6d25eaef0a296c",
  "reconcile_transaction_hash": "0xe3c8042bcd2e943df1d3a38c75bfee132827f3a8a3a1efedf1e4e96cecd72e6b",
  "reconcile_timestamp": 1672986047,
  "reconcile_gas_price": "19184906166",
  "reconcile_gas_limit": "4000000",
  "reconcile_block_number": 16345915,
  "update_time": "2023-01-12T04:56:14.72407",
  "delegate": "0x6d2a06543d23cc6523ae5046add8bb60817e0a94",
  "message_hash": "0x327618edf7bab0e7c6b97ecee50ad6572e9c069a85db3083c942e0c0ddc469b7",
  "canonical_domain": "6648936",
  "slippage": 300,
  "origin_sender": "0x6d2a06543d23cc6523ae5046add8bb60817e0a94",
  "bridged_amt": "99407526243394",
  "normalized_in": "100000000000000",
  "canonical_id": "0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
  "router_fee": null,
  "xcall_tx_origin": "0x6d2a06543d23cc6523ae5046add8bb60817e0a94",
  "execute_tx_origin": "0x29d33fcd30240d55b9280362599d5066c1a2cf10",
  "reconcile_tx_origin": "0x29d33fcd30240d55b9280362599d5066c1a2cf10",
  "relayer_fee": "8424181656635272573"
}
```

***

### create

▸ `Static` **create**(`_config`): `Promise`<`SdkUtils`>

Create a singleton instance of the SdkUtils class.

**Parameters**

| Name                    | Type                                                                                   | Default value | Description                                             |
| ----------------------- | -------------------------------------------------------------------------------------- | ------------- | ------------------------------------------------------- |
| `_config`               | `Object`                                                                               | undefined     | SdkConfig object.                                       |
| `_config.chains`        | `Record`<`string`, { providers: string\[] }>                                           | undefined     | Chain config, at minimum with providers for each chain. |
| `_config.signerAddress` | `string`                                                                               | undefined     | Signer address for transactions.                        |
| `_config.logLevel`      | `"fatal"` \| `"error"` \| `"warn"` \| `"info"` \| `"debug"` \| `"trace"` \| `"silent"` | "info"        | (optional) Logging severity level.                      |
| `_config.network`       | `"testnet"` \| `"mainnet"`                                                             | "mainnet"     | (optional) Blockchain environment to interact with.     |

**Returns**

`Promise`<`SdkUtils`>

providers.TransactionRequest object.

**`Example`**

```ts
import { SdkUtils } from "@connext/sdk";

const config = {
  signerAddress: "<wallet_address>",
  network: "mainnet",
  chains: {
    6648936: { // the domain ID for Ethereum Mainnet
      providers: ["https://rpc.ankr.com/eth"],
    },
    1869640809: { // the domain ID for Optimism
      providers: ["https://mainnet.optimism.io"]
    },
    1886350457: { // the domain ID for Polygon
      providers: ["https://polygon-rpc.com"]
    },
  },
}

const sdkBase = await SdkUtils.create(config);
```

{% hint style="info" %}
See the [Deployments](broken://pages/bbG6im4BlayCJRkYxOkN) page for all domain IDs and asset addresses.
{% endhint %}


# Types

### AssetData

Ƭ **AssetData**: `Object`

**Type declaration**

| Name               | Type     |
| ------------------ | -------- |
| `local`            | `string` |
| `adopted`          | `string` |
| `canonical_id`     | `string` |
| `canonical_domain` | `string` |
| `domain`           | `string` |
| `key`              | `string` |
| `id`               | `string` |

***

### Pool

Ƭ **Pool**: `Object`

**Type declaration**

| Name             | Type        |
| ---------------- | ----------- |
| `domainId`       | `string`    |
| `name`           | `string`    |
| `symbol`         | `string`    |
| `local`          | `PoolAsset` |
| `adopted`        | `PoolAsset` |
| `lpTokenAddress` | `string`    |
| `canonicalHash`  | `string`    |
| `swapFee`        | `string`    |
| `adminFee`       | `string`    |
| `address?`       | `string`    |

***

### PoolAsset

Ƭ **PoolAsset**: `Object`

**Type declaration**

| Name       | Type        |
| ---------- | ----------- |
| `address`  | `string`    |
| `name`     | `string`    |
| `symbol`   | `string`    |
| `decimals` | `number`    |
| `index`    | `number`    |
| `balance`  | `BigNumber` |

***

### ConnextSupport

Ƭ **ConnextSupport**: `Object`

**Type declaration**

| Name       | Type        |
| ---------- | ----------- |
| `assets`   | `string`\[] |
| `chainId`  | `number`    |
| `domainId` | `string`    |
| `name`     | `string`    |

***

### XTransferStatus

Ƭ **XTransferStatus**: `Object`

**Type declaration**

<table><thead><tr><th>Name</th><th width="106.66666666666666">Type</th><th>Value</th></tr></thead><tbody><tr><td><code>XCalled</code></td><td><code>string</code></td><td>"XCalled"</td></tr><tr><td><code>Executed</code></td><td><code>string</code></td><td>"Executed"</td></tr><tr><td><code>Reconciled</code></td><td><code>string</code></td><td>"Reconciled"</td></tr><tr><td><code>CompletedFast</code></td><td><code>string</code></td><td>"CompletedFast"</td></tr><tr><td><code>CompletedSlow</code></td><td><code>string</code></td><td>"CompletedSlow"</td></tr></tbody></table>

### XTransferErrorStatus

Ƭ **XTransferStatus**: `Object`

**Type declaration**

| Name           | Type   | Value            |
| -------------- | ------ | ---------------- |
| LowSlippage    | string | "LowSlippage"    |
| LowRelayerFee  | string | "LowRelayerFee"  |
| ExecutionError | string | "ExecutionError" |
| NoBidsReceived | string | "NoBidsReceived" |


# Contracts


# Calls

This section contains a full API reference of all public functions & events related to making and tracking xchain calls.

## Events

### XCalled

```solidity
event XCalled(bytes32 transferId, uint256 nonce, bytes32 messageHash, struct TransferInfo params, address asset, uint256 amount, address local)
```

Emitted when `xcall` is called on the origin domain of a transfer.

#### Parameters

| Name        | Type                | Description                                                                       |
| ----------- | ------------------- | --------------------------------------------------------------------------------- |
| transferId  | bytes32             | - The unique identifier of the crosschain transfer.                               |
| nonce       | uint256             | - The bridge nonce of the transfer on the origin domain.                          |
| messageHash | bytes32             | - The hash of the message bytes (containing all transfer info) that were bridged. |
| params      | struct TransferInfo | - The `TransferInfo` provided to the function.                                    |
| asset       | address             | - The asset sent in with xcall                                                    |
| amount      | uint256             | - The amount sent in with xcall                                                   |
| local       | address             | - The local asset that is controlled by the bridge and can be burned/minted       |

### ExternalCalldataExecuted

```solidity
event ExternalCalldataExecuted(bytes32 transferId, bool success, bytes returnData)
```

Emitted when a transfer has its external data executed

#### Parameters

| Name       | Type    | Description                                         |
| ---------- | ------- | --------------------------------------------------- |
| transferId | bytes32 | - The unique identifier of the crosschain transfer. |
| success    | bool    | - Whether calldata succeeded                        |
| returnData | bytes   | - Return bytes from the IXReceiver                  |

### Executed

```solidity
event Executed(bytes32 transferId, address to, address asset, struct ExecuteArgs args, address local, uint256 amount, address caller)
```

Emitted when `execute` is called on the destination domain of a transfer.

*`execute` may be called when providing fast liquidity or when processing a reconciled (slow) transfer.*

#### Parameters

| Name       | Type               | Description                                                                                                                                                                                     |
| ---------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| transferId | bytes32            | - The unique identifier of the crosschain transfer.                                                                                                                                             |
| to         | address            | - The recipient `TransferInfo.to` provided, created as indexed parameter.                                                                                                                       |
| asset      | address            | - The asset the recipient is given or the external call is executed with. Should be the adopted asset on that chain.                                                                            |
| args       | struct ExecuteArgs | - The `ExecuteArgs` provided to the function.                                                                                                                                                   |
| local      | address            | - The local asset that was either supplied by the router for a fast-liquidity transfer or minted by the bridge in a reconciled (slow) transfer. Could be the same as the adopted `asset` param. |
| amount     | uint256            | - The amount of transferring asset the recipient address receives or the external call is executed with.                                                                                        |
| caller     | address            | - The account that called the function.                                                                                                                                                         |

### TransferRelayerFeesIncreased

```solidity
event TransferRelayerFeesIncreased(bytes32 transferId, uint256 increase, address caller)
```

Emitted when `_bumpTransfer` is called by an user on the origin domain both in `xcall` and `bumpTransfer`

#### Parameters

| Name       | Type    | Description                                           |
| ---------- | ------- | ----------------------------------------------------- |
| transferId | bytes32 | - The unique identifier of the crosschain transaction |
| increase   | uint256 | - The additional amount fees increased by             |
| caller     | address | - The account that called the function                |

### SlippageUpdated

```solidity
event SlippageUpdated(bytes32 transferId, uint256 slippage)
```

Emitted when `forceUpdateSlippage` is called by an user on the destination domain

#### Parameters

| Name       | Type    | Description                                           |
| ---------- | ------- | ----------------------------------------------------- |
| transferId | bytes32 | - The unique identifier of the crosschain transaction |
| slippage   | uint256 | - The updated slippage boundary                       |

***

## Getters

### routedTransfers

```solidity
function routedTransfers(bytes32 _transferId) public view returns (address[])
```

Gets a list of routers that routed a transfer by `transferId`.

#### Parameters

| Name         | Type    | Description                               |
| ------------ | ------- | ----------------------------------------- |
| \_transferId | bytes32 | Unique transfer ID of a given transaction |

#### Return Values

| Name | Type       | Description                           |
| ---- | ---------- | ------------------------------------- |
| \[0] | address\[] | Array containing addresses of routers |

### transferStatus

```solidity
function transferStatus(bytes32 _transferId) public view returns (enum DestinationTransferStatus)
```

Gets a transfer's status by `transferId`. Note - this function MUST be called on the destination chain.

#### Parameters

| Name         | Type    | Description                               |
| ------------ | ------- | ----------------------------------------- |
| \_transferId | bytes32 | Unique transfer ID of a given transaction |

#### Return Values

| Name | Type | Description            |
| ---- | ---- | ---------------------- |
| \[0] | enum | Status of the transfer |

### domain

```solidity
function domain() public view returns (uint32)
```

Gets the `domain` identifier of the chain.

#### Parameters

#### Return Values

| Name | Type   | Description                    |
| ---- | ------ | ------------------------------ |
| \[0] | uint32 | Domain identifier of the chain |

## Functions

### xcall

```solidity
function xcall(uint32 _destination, address _to, address _asset, address _delegate, uint256 _amount, uint256 _slippage, bytes _callData, uint256 _relayerFee) external payable returns (bytes32)
```

Initiates a cross-chain transfer of funds, calldata, and/or various named properties.

For ERC20 transfers, this contract must have approval to transfer the input (transacting) assets. The adopted assets will be swapped for their local (connext-flavored) asset counterparts (i.e. bridgeable tokens) via the configured AMM if necessary. In the event that the adopted assets *are* local assets, no swap is needed. The local tokens will then be sent via the bridge router. If the local assets are representational for an asset on another chain, we will burn the tokens here. If the local assets are canonical (meaning that the adopted to local asset pairing is native to this chain), we will custody the tokens here.

#### Parameters

| Name           | Type      | Description                                                                                                                                                                                                                                           |
| -------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `_destination` | `uint32`  | The destination chain's Domain ID (*not* equivalent to “Chain ID”). See \[Domains] for details TODO                                                                                                                                                   |
| `_to`          | `address` | The target address on the destination chain. xcall will send funds to whatever address is specified here regardless of whether it is a contract or EOA. If calldata is provided, xcall will additionally attempt to call `xReceive` on this contract. |
| `_asset`       | `address` | The contract address of the asset to be bridged. If the `xcall` is calldata-only (e.g. doesn't bridge any funds), any registered asset can be used here as long as `amount: 0`.                                                                       |
| `_delegate`    | `address` | An address on destination domain that has rights to update slippage tolerance, retry transactions, or revert back to origin in the event that a transaction fails at the destination.                                                                 |
| `_amount`      | `uint256` | The amount of tokens to bridge specified in wei units (i.e. to send 1 USDC, a token with 10^6 decimals, you must specify the amount as `1000000`).                                                                                                    |
| `_slippage`    | `uint256` | The maximum slippage a user is willing to take, in BPS, due to the StableSwap Pool(s), if applicable. For example, to achieve 0.03% slippage tolerance this will be `3`.                                                                              |
| `_callData`    | `bytes`   | In the case of bridging funds only, this should be empty bytes ("0x"). If calldata is sent, then the encoded calldata must be passed here.                                                                                                            |
| `_relayerFee`  | `uint256` | (Optional) This is available in an overloaded `xcall`. If provided, the relayer fee will be taken in `_asset` rather than the native asset.                                                                                                           |

#### Return Values

| Name | Type    | Description                                                         |
| ---- | ------- | ------------------------------------------------------------------- |
| \[0] | bytes32 | bytes32 - The transfer ID of the newly created crosschain transfer. |

### xcallIntoLocal

```solidity
function xcallIntoLocal(uint32 _destination, address _to, address _asset, address _delegate, uint256 _amount, uint256 _slippage, bytes _callData, uint256 _relayerFee) external payable returns (bytes32)
```

Helper function that xcalls as normal but forces the receipt of the local (Connext-flavored) asset at destination. This function is used typically to generate nextAssets that can be used to LP into the destination chain stableswap. Params and returned data function exactly the same way as `xcall`.

### execute

```solidity
function execute(struct ExecuteArgs _args) external returns (bytes32)
```

Called on a destination domain to disburse correct assets to end recipient and execute any included calldata.

*Can be called before or after `handle` \[reconcile] is called (regarding the same transfer), depending on whether the fast liquidity route (i.e. funds provided by routers) is being used for this transfer. As a result, executed calldata (including properties like `originSender`) may or may not be verified depending on whether the reconcile has been completed (i.e. the optimistic confirmation period has elapsed).*

#### Parameters

| Name   | Type               | Description              |
| ------ | ------------------ | ------------------------ |
| \_args | struct ExecuteArgs | - ExecuteArgs arguments. |

#### Return Values

| Name | Type    | Description                                                                                                                      |
| ---- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
| \[0] | bytes32 | bytes32 - The transfer ID of the crosschain transfer. Should match the xcall's transfer ID in order for reconciliation to occur. |

### bumpTransfer (native asset)

```solidity
function bumpTransfer(bytes32 _transferId) external payable
```

Anyone can call this function on the origin domain to increase the relayer fee for a transfer. MUST be called on the origin domain.

#### Parameters

| Name         | Type    | Description                                           |
| ------------ | ------- | ----------------------------------------------------- |
| \_transferId | bytes32 | - The unique identifier of the crosschain transaction |

### bumpTransfer (transacting asset)

```solidity
function bumpTransfer(bytes32 _transferId, address _relayerFeeAsset, uint256 _relayerFee) external payable
```

Anyone can call this function to increase the relayer fee for a transfer (using the \_relayerFeeAsset specified). MUST be called on the origin domain.

**Parameters**

| Name              | Type    | Description                                           |
| ----------------- | ------- | ----------------------------------------------------- |
| \_transferId      | bytes32 | - The unique identifier of the crosschain transaction |
| \_relayerfeeAsset | address | - The asset you are bumping fee with                  |
| \_relayerFee      | uint256 | - The amount you want to bump transfer fee with       |

### forceUpdateSlippage

```solidity
function forceUpdateSlippage(struct TransferInfo _params, uint256 _slippage) external
```

Allows a user-specified account (`delegate` in `xcall`) to update the slippage they are willing to take on destination transfers. MUST be called on the destination chain.

#### Parameters

| Name       | Type                | Description                               |
| ---------- | ------------------- | ----------------------------------------- |
| \_params   | struct TransferInfo | TransferInfo associated with the transfer |
| \_slippage | uint256             | The updated slippage                      |

***

## Interfaces

### xReceive

```solidity
function xReceive(bytes32 _transferId, uint256 _amount, address _asset, address _originSender, uint32 _origin, bytes _callData) external returns (bytes)
```

Interface that the Connext contracts call into on the `_to` address specified during `xcall`. Developers MUST implement this on the destination chain to receive incoming calldata.

#### Parameters

| Name           | Type    | Description                                                                                                                                                                                                                                       |
| -------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| \_transferId   | bytes32 | Unique id of the xchain transaction                                                                                                                                                                                                               |
| \_amount       | uint256 | Amount of token, if any, passed into the contract in Wei units                                                                                                                                                                                    |
| \_asset        | address | Address of token, if any, passed into the contract                                                                                                                                                                                                |
| \_originSender | address | Address of the contract or EOA that called `xcall` on the origin chain. NOTE: this param will *only* be populated if the transaction went through the slow path rather than being executed immediately by a Connext router (see TODO for details) |
| \_origin       | uint32  | Domain ID of the chain that the transaction is coming from                                                                                                                                                                                        |
| \_calldata     | bytes   | Data, in bytes, that is passed into `xcall` on the origin chain                                                                                                                                                                                   |


# Routers

This section contains a full API reference of all public functions & events related to routers and router liquidity management.

## Events

### RouterAdded

```solidity
event RouterAdded(address router, address caller)
```

Emitted when a new router is added

#### Parameters

| Name   | Type    | Description                            |
| ------ | ------- | -------------------------------------- |
| router | address | - The address of the added router      |
| caller | address | - The account that called the function |

### RouterRemoved

```solidity
event RouterRemoved(address router, address caller)
```

Emitted when an existing router is removed

#### Parameters

| Name   | Type    | Description                            |
| ------ | ------- | -------------------------------------- |
| router | address | - The address of the removed router    |
| caller | address | - The account that called the function |

### RouterRecipientSet

```solidity
event RouterRecipientSet(address router, address prevRecipient, address newRecipient)
```

Emitted when the recipient of router is updated

#### Parameters

| Name          | Type    | Description                                           |
| ------------- | ------- | ----------------------------------------------------- |
| router        | address | - The address of the added router                     |
| prevRecipient | address | - The address of the previous recipient of the router |
| newRecipient  | address | - The address of the new recipient of the router      |

### RouterOwnerProposed

```solidity
event RouterOwnerProposed(address router, address prevProposed, address newProposed)
```

Emitted when the owner of router is proposed

#### Parameters

| Name         | Type    | Description                            |
| ------------ | ------- | -------------------------------------- |
| router       | address | - The address of the added router      |
| prevProposed | address | - The address of the previous proposed |
| newProposed  | address | - The address of the new proposed      |

### RouterOwnerAccepted

```solidity
event RouterOwnerAccepted(address router, address prevOwner, address newOwner)
```

Emitted when the owner of router is accepted

#### Parameters

| Name      | Type    | Description                                       |
| --------- | ------- | ------------------------------------------------- |
| router    | address | - The address of the added router                 |
| prevOwner | address | - The address of the previous owner of the router |
| newOwner  | address | - The address of the new owner of the router      |

### RouterLiquidityAdded

```solidity
event RouterLiquidityAdded(address router, address local, bytes32 key, uint256 amount, address caller)
```

Emitted when a router adds liquidity to the contract

#### Parameters

| Name   | Type    | Description                                                          |
| ------ | ------- | -------------------------------------------------------------------- |
| router | address | - The address of the router the funds were credited to               |
| local  | address | - The address of the token added (all liquidity held in local asset) |
| key    | bytes32 | - The hash of the canonical id and domain                            |
| amount | uint256 | - The amount of liquidity added                                      |
| caller | address | - The account that called the function                               |

### RouterLiquidityRemoved

```solidity
event RouterLiquidityRemoved(address router, address to, address local, bytes32 key, uint256 amount, address caller)
```

Emitted when a router withdraws liquidity from the contract

#### Parameters

| Name   | Type    | Description                                  |
| ------ | ------- | -------------------------------------------- |
| router | address | - The router you are removing liquidity from |
| to     | address | - The address the funds were withdrawn to    |
| local  | address | - The address of the token withdrawn         |
| key    | bytes32 |                                              |
| amount | uint256 | - The amount of liquidity withdrawn          |
| caller | address | - The account that called the function       |

***

## Getters

### getRouterApproval

```solidity
function getRouterApproval(address _router) public view returns (bool)
```

Returns the approval status of a router for the given router address.

#### Parameters

| Name     | Type    | Description                 |
| -------- | ------- | --------------------------- |
| \_router | address | The relevant router address |

#### Return Values

| Name | Type | Description                |
| ---- | ---- | -------------------------- |
| \[0] | bool | True if router is approved |

### getRouterRecipient

```solidity
function getRouterRecipient(address _router) public view returns (address)
```

Returns the recipient for the specified router

*The recipient (if set) receives all funds when router liquidity is removed*

#### Parameters

| Name     | Type    | Description                 |
| -------- | ------- | --------------------------- |
| \_router | address | The relevant router address |

#### Return Values

| Name | Type    | Description                  |
| ---- | ------- | ---------------------------- |
| \[0] | address | Recipient address for router |

### getRouterOwner

```solidity
function getRouterOwner(address _router) public view returns (address)
```

Returns the router owner if it is set, or the router itself if not

*Uses logic function here to handle the case where router owner is not set. Other getters within this interface use explicitly the stored value*

#### Parameters

| Name     | Type    | Description                 |
| -------- | ------- | --------------------------- |
| \_router | address | The relevant router address |

#### Return Values

| Name | Type    | Description             |
| ---- | ------- | ----------------------- |
| \[0] | address | Owner address of router |

### getProposedRouterOwner

```solidity
function getProposedRouterOwner(address _router) public view returns (address)
```

Returns the currently proposed router owner

*All routers must wait for the delay timeout before accepting a new owner*

#### Parameters

| Name     | Type    | Description                 |
| -------- | ------- | --------------------------- |
| \_router | address | The relevant router address |

#### Return Values

| Name | Type    | Description                      |
| ---- | ------- | -------------------------------- |
| \[0] | address | Proposed owner address of router |

### getProposedRouterOwnerTimestamp

```solidity
function getProposedRouterOwnerTimestamp(address _router) public view returns (uint256)
```

Returns the currently proposed router owner timestamp

*All routers must wait for the delay timeout before accepting a new owner*

#### Parameters

| Name     | Type    | Description                 |
| -------- | ------- | --------------------------- |
| \_router | address | The relevant router address |

#### Return Values

| Name | Type    | Description                               |
| ---- | ------- | ----------------------------------------- |
| \[0] | uint256 | Currently proposed router owner timestamp |

### routerBalances

```solidity
function routerBalances(address _router, address _asset) public view returns (uint256)
```

Gets balance of asset for the specified router.

#### Parameters

| Name     | Type    | Description                 |
| -------- | ------- | --------------------------- |
| \_router | address | The relevant router address |
| \_asset  | address | The relevant asset          |

#### Return Values

| Name | Type    | Description                      |
| ---- | ------- | -------------------------------- |
| \[0] | uint256 | Balance the router owns of asset |

***

## Functions

### setupRouter

```solidity
function setupRouter(address router, address owner, address recipient) external
```

Used to set router initial properties

#### Parameters

| Name      | Type    | Description                 |
| --------- | ------- | --------------------------- |
| router    | address | Router address to setup     |
| owner     | address | Initial Owner of router     |
| recipient | address | Initial Recipient of router |

### removeRouter

```solidity
function removeRouter(address router) external
```

Used to remove routers that can transact crosschain

#### Parameters

| Name   | Type    | Description              |
| ------ | ------- | ------------------------ |
| router | address | Router address to remove |

### setRouterRecipient

```solidity
function setRouterRecipient(address router, address recipient) external
```

Sets the designated recipient for a router

*Router should only be able to set this once otherwise if router key compromised, no problem is solved since attacker could just update recipient*

#### Parameters

| Name      | Type    | Description                        |
| --------- | ------- | ---------------------------------- |
| router    | address | Router address to set recipient    |
| recipient | address | Recipient Address to set to router |

### proposeRouterOwner

```solidity
function proposeRouterOwner(address router, address proposed) external
```

Current owner or router may propose a new router owner

#### Parameters

| Name     | Type    | Description                             |
| -------- | ------- | --------------------------------------- |
| router   | address | Router address to set recipient         |
| proposed | address | Proposed owner Address to set to router |

### acceptProposedRouterOwner

```solidity
function acceptProposedRouterOwner(address router) external
```

New router owner must accept role, or previous if proposed is 0x0

#### Parameters

| Name   | Type    | Description                     |
| ------ | ------- | ------------------------------- |
| router | address | Router address to set recipient |

### addRouterLiquidityFor

```solidity
function addRouterLiquidityFor(uint256 _amount, address _local, address _router) external payable
```

This is used by anyone to increase a router's available liquidity for a given asset.

*The liquidity will be held in the local asset, which is the representation if you are not on the canonical domain, and the canonical asset otherwise.*

#### Parameters

| Name     | Type    | Description                                                                                                                                        |
| -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| \_amount | uint256 | - The amount of liquidity to add for the router                                                                                                    |
| \_local  | address | - The address of the asset you're adding liquidity for. If adding liquidity of the native asset, routers may use `address(0)` or the wrapped asset |
| \_router | address | The router you are adding liquidity on behalf of                                                                                                   |

### addRouterLiquidity

```solidity
function addRouterLiquidity(uint256 _amount, address _local) external payable
```

This is used by any router to increase their available liquidity for a given asset.

*The liquidity will be held in the local asset, which is the representation if you are not on the canonical domain, and the canonical asset otherwise.*

#### Parameters

| Name     | Type    | Description                                                                                                                                        |
| -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| \_amount | uint256 | - The amount of liquidity to add for the router                                                                                                    |
| \_local  | address | - The address of the asset you're adding liquidity for. If adding liquidity of the native asset, routers may use `address(0)` or the wrapped asset |

### removeRouterLiquidityFor

```solidity
function removeRouterLiquidityFor(uint256 _amount, address _local, address payable _to, address _router) external
```

This is used by any router owner to decrease their available liquidity for a given asset.

#### Parameters

| Name     | Type            | Description                                                                                                                                             |
| -------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| \_amount | uint256         | - The amount of liquidity to remove for the router                                                                                                      |
| \_local  | address         | - The address of the asset you're removing liquidity from. If removing liquidity of the native asset, routers may use `address(0)` or the wrapped asset |
| \_to     | address payable | The address that will receive the liquidity being removed                                                                                               |
| \_router | address         | The address of the router                                                                                                                               |

### removeRouterLiquidity

```solidity
function removeRouterLiquidity(uint256 _amount, address _local, address payable _to) external
```

This is used by any router to decrease their available liquidity for a given asset.

#### Parameters

| Name     | Type            | Description                                                                                                                                             |
| -------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| \_amount | uint256         | - The amount of liquidity to remove for the router                                                                                                      |
| \_local  | address         | - The address of the asset you're removing liquidity from. If removing liquidity of the native asset, routers may use `address(0)` or the wrapped asset |
| \_to     | address payable | The address that will receive the liquidity being removed if no router recipient exists.                                                                |


# Stableswap

This section contains a full API reference of all public functions & events related to Connext's stableswap contracts.

## Events

### TokenSwap

```solidity
event TokenSwap(bytes32 key, address buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId)
```

### AddLiquidity

```solidity
event AddLiquidity(bytes32 key, address provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply)
```

### RemoveLiquidity

```solidity
event RemoveLiquidity(bytes32 key, address provider, uint256[] tokenAmounts, uint256 lpTokenSupply)
```

### RemoveLiquidityOne

```solidity
event RemoveLiquidityOne(bytes32 key, address provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought)
```

***

## Getters

### getSwapStorage

```solidity
function getSwapStorage(bytes32 key) external view returns (struct SwapUtils.Swap)
```

Return Stable swap storage

#### Parameters

| Name | Type    | Description                       |
| ---- | ------- | --------------------------------- |
| key  | bytes32 | Hash of the canonical id + domain |

#### Return Values

| Name | Type                  | Description    |
| ---- | --------------------- | -------------- |
| \[0] | struct SwapUtils.Swap | SwapUtils.Swap |

### getSwapLPToken

```solidity
function getSwapLPToken(bytes32 key) external view returns (address)
```

Return LP token for canonical Id

#### Parameters

| Name | Type    | Description                       |
| ---- | ------- | --------------------------------- |
| key  | bytes32 | Hash of the canonical id + domain |

#### Return Values

| Name | Type    | Description |
| ---- | ------- | ----------- |
| \[0] | address | LPToken     |

### getSwapA

```solidity
function getSwapA(bytes32 key) external view returns (uint256)
```

Return A, the amplification coefficient \_ n \_ (n - 1)

*See the StableSwap paper for details*

#### Parameters

| Name | Type    | Description                       |
| ---- | ------- | --------------------------------- |
| key  | bytes32 | Hash of the canonical id + domain |

#### Return Values

| Name | Type    | Description |
| ---- | ------- | ----------- |
| \[0] | uint256 | A parameter |

### getSwapAPrecise

```solidity
function getSwapAPrecise(bytes32 key) external view returns (uint256)
```

Return A in its raw precision form

*See the StableSwap paper for details*

#### Parameters

| Name | Type    | Description                       |
| ---- | ------- | --------------------------------- |
| key  | bytes32 | Hash of the canonical id + domain |

#### Return Values

| Name | Type    | Description                           |
| ---- | ------- | ------------------------------------- |
| \[0] | uint256 | A parameter in its raw precision form |

### getSwapToken

```solidity
function getSwapToken(bytes32 key, uint8 index) public view returns (contract IERC20)
```

Return address of the pooled token at given index. Reverts if tokenIndex is out of range.

#### Parameters

| Name  | Type    | Description                       |
| ----- | ------- | --------------------------------- |
| key   | bytes32 | Hash of the canonical id + domain |
| index | uint8   | the index of the token            |

#### Return Values

| Name | Type            | Description                         |
| ---- | --------------- | ----------------------------------- |
| \[0] | contract IERC20 | address of the token at given index |

### getSwapTokenIndex

```solidity
function getSwapTokenIndex(bytes32 key, address tokenAddress) public view returns (uint8)
```

Return the index of the given token address. Reverts if no matching token is found.

#### Parameters

| Name         | Type    | Description                       |
| ------------ | ------- | --------------------------------- |
| key          | bytes32 | Hash of the canonical id + domain |
| tokenAddress | address | address of the token              |

#### Return Values

| Name | Type  | Description                          |
| ---- | ----- | ------------------------------------ |
| \[0] | uint8 | the index of the given token address |

### getSwapTokenBalance

```solidity
function getSwapTokenBalance(bytes32 key, uint8 index) external view returns (uint256)
```

Return current balance of the pooled token at given index

#### Parameters

| Name  | Type    | Description                       |
| ----- | ------- | --------------------------------- |
| key   | bytes32 | Hash of the canonical id + domain |
| index | uint8   | the index of the token            |

#### Return Values

| Name | Type    | Description                                                                      |
| ---- | ------- | -------------------------------------------------------------------------------- |
| \[0] | uint256 | current balance of the pooled token at given index with token's native precision |

### getSwapVirtualPrice

```solidity
function getSwapVirtualPrice(bytes32 key) external view returns (uint256)
```

Get the virtual price, to help calculate profit

#### Parameters

| Name | Type    | Description                       |
| ---- | ------- | --------------------------------- |
| key  | bytes32 | Hash of the canonical id + domain |

#### Return Values

| Name | Type    | Description                                                |
| ---- | ------- | ---------------------------------------------------------- |
| \[0] | uint256 | the virtual price, scaled to the POOL\_PRECISION\_DECIMALS |

### calculateSwap

```solidity
function calculateSwap(bytes32 key, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx) external view returns (uint256)
```

Calculate amount of tokens you receive on swap

#### Parameters

| Name           | Type    | Description                                                                                                                               |
| -------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| key            | bytes32 | Hash of the canonical id + domain                                                                                                         |
| tokenIndexFrom | uint8   | the token the user wants to sell                                                                                                          |
| tokenIndexTo   | uint8   | the token the user wants to buy                                                                                                           |
| dx             | uint256 | the amount of tokens the user wants to sell. If the token charges a fee on transfers, use the amount that gets transferred after the fee. |

#### Return Values

| Name | Type    | Description                            |
| ---- | ------- | -------------------------------------- |
| \[0] | uint256 | amount of tokens the user will receive |

### calculateSwapTokenAmount

```solidity
function calculateSwapTokenAmount(bytes32 key, uint256[] amounts, bool deposit) external view returns (uint256)
```

A simple method to calculate prices from deposits or withdrawals, excluding fees but including slippage. This is helpful as an input into the various "min" parameters on calls to fight front-running

*This shouldn't be used outside frontends for user estimates.*

#### Parameters

| Name    | Type       | Description                                                                                                                                                                                                                                 |
| ------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| key     | bytes32    | Hash of the canonical id + domain                                                                                                                                                                                                           |
| amounts | uint256\[] | an array of token amounts to deposit or withdrawal, corresponding to pooledTokens. The amount should be in each pooled token's native precision. If a token charges a fee on transfers, use the amount that gets transferred after the fee. |
| deposit | bool       | whether this is a deposit or a withdrawal                                                                                                                                                                                                   |

#### Return Values

| Name | Type    | Description                        |
| ---- | ------- | ---------------------------------- |
| \[0] | uint256 | token amount the user will receive |

### calculateRemoveSwapLiquidity

```solidity
function calculateRemoveSwapLiquidity(bytes32 key, uint256 amount) external view returns (uint256[])
```

A simple method to calculate amount of each underlying tokens that is returned upon burning given amount of LP tokens

#### Parameters

| Name   | Type    | Description                                                |
| ------ | ------- | ---------------------------------------------------------- |
| key    | bytes32 | Hash of the canonical id + domain                          |
| amount | uint256 | the amount of LP tokens that would be burned on withdrawal |

#### Return Values

| Name | Type       | Description                                        |
| ---- | ---------- | -------------------------------------------------- |
| \[0] | uint256\[] | array of token balances that the user will receive |

### calculateRemoveSwapLiquidityOneToken

```solidity
function calculateRemoveSwapLiquidityOneToken(bytes32 key, uint256 tokenAmount, uint8 tokenIndex) external view returns (uint256 availableTokenAmount)
```

Calculate the amount of underlying token available to withdraw when withdrawing via only single token

#### Parameters

| Name        | Type    | Description                            |
| ----------- | ------- | -------------------------------------- |
| key         | bytes32 | Hash of the canonical id + domain      |
| tokenAmount | uint256 | the amount of LP token to burn         |
| tokenIndex  | uint8   | index of which token will be withdrawn |

#### Return Values

| Name                 | Type    | Description                                                 |
| -------------------- | ------- | ----------------------------------------------------------- |
| availableTokenAmount | uint256 | calculated amount of underlying token available to withdraw |

***

## Functions

### swap

```solidity
function swap(bytes32 key, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline) external returns (uint256)
```

Swap two tokens using this pool

#### Parameters

| Name           | Type    | Description                                               |
| -------------- | ------- | --------------------------------------------------------- |
| key            | bytes32 | Hash of the canonical id + domain                         |
| tokenIndexFrom | uint8   | the token the user wants to swap from                     |
| tokenIndexTo   | uint8   | the token the user wants to swap to                       |
| dx             | uint256 | the amount of tokens the user wants to swap from          |
| minDy          | uint256 | the min amount the user would like to receive, or revert. |
| deadline       | uint256 | latest timestamp to accept this transaction               |

### swapExact

```solidity
function swapExact(bytes32 key, uint256 amountIn, address assetIn, address assetOut, uint256 minAmountOut, uint256 deadline) external returns (uint256)
```

Swap two tokens using this pool

#### Parameters

| Name         | Type    | Description                                      |
| ------------ | ------- | ------------------------------------------------ |
| key          | bytes32 | Hash of the canonical id + domain                |
| amountIn     | uint256 | the amount of tokens the user wants to swap from |
| assetIn      | address | the token the user wants to swap from            |
| assetOut     | address | the token the user wants to swap to              |
| minAmountOut | uint256 |                                                  |
| deadline     | uint256 |                                                  |

### swapExactOut

```solidity
function swapExactOut(bytes32 key, uint256 amountOut, address assetIn, address assetOut, uint256 maxAmountIn, uint256 deadline) external returns (uint256)
```

Swap two tokens using this pool

#### Parameters

| Name        | Type    | Description                                    |
| ----------- | ------- | ---------------------------------------------- |
| key         | bytes32 | Hash of the canonical id + domain              |
| amountOut   | uint256 | the amount of tokens the user wants to swap to |
| assetIn     | address | the token the user wants to swap from          |
| assetOut    | address | the token the user wants to swap to            |
| maxAmountIn | uint256 |                                                |
| deadline    | uint256 |                                                |

### addSwapLiquidity

```solidity
function addSwapLiquidity(bytes32 key, uint256[] amounts, uint256 minToMint, uint256 deadline) external returns (uint256)
```

Add liquidity to the pool with the given amounts of tokens

#### Parameters

| Name      | Type       | Description                                                                                                             |
| --------- | ---------- | ----------------------------------------------------------------------------------------------------------------------- |
| key       | bytes32    | Hash of the canonical id + domain                                                                                       |
| amounts   | uint256\[] | the amounts of each token to add, in their native precision                                                             |
| minToMint | uint256    | the minimum LP tokens adding this amount of liquidity should mint, otherwise revert. Handy for front-running mitigation |
| deadline  | uint256    | latest timestamp to accept this transaction                                                                             |

#### Return Values

| Name | Type    | Description                                 |
| ---- | ------- | ------------------------------------------- |
| \[0] | uint256 | amount of LP token user minted and received |

### removeSwapLiquidity

```solidity
function removeSwapLiquidity(bytes32 key, uint256 amount, uint256[] minAmounts, uint256 deadline) external returns (uint256[])
```

Burn LP tokens to remove liquidity from the pool. Withdraw fee that decays linearly over period of 4 weeks since last deposit will apply.

*Liquidity can always be removed, even when the pool is paused.*

#### Parameters

| Name       | Type       | Description                                                                                                  |
| ---------- | ---------- | ------------------------------------------------------------------------------------------------------------ |
| key        | bytes32    | Hash of the canonical id + domain                                                                            |
| amount     | uint256    | the amount of LP tokens to burn                                                                              |
| minAmounts | uint256\[] | the minimum amounts of each token in the pool acceptable for this burn. Useful as a front-running mitigation |
| deadline   | uint256    | latest timestamp to accept this transaction                                                                  |

#### Return Values

| Name | Type       | Description                     |
| ---- | ---------- | ------------------------------- |
| \[0] | uint256\[] | amounts of tokens user received |

### removeSwapLiquidityOneToken

```solidity
function removeSwapLiquidityOneToken(bytes32 key, uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline) external returns (uint256)
```

Remove liquidity from the pool all in one token. Withdraw fee that decays linearly over period of 4 weeks since last deposit will apply.

#### Parameters

| Name        | Type    | Description                                      |
| ----------- | ------- | ------------------------------------------------ |
| key         | bytes32 | Hash of the canonical id + domain                |
| tokenAmount | uint256 | the amount of the token you want to receive      |
| tokenIndex  | uint8   | the index of the token you want to receive       |
| minAmount   | uint256 | the minimum amount to withdraw, otherwise revert |
| deadline    | uint256 | latest timestamp to accept this transaction      |

#### Return Values

| Name | Type    | Description                          |
| ---- | ------- | ------------------------------------ |
| \[0] | uint256 | amount of chosen token user received |


# Subgraphs


# Entities

### Asset

```graphql
type Asset @entity {
  id: ID! "The local asset address."
  key: Bytes "The key associated with the local asset."
  canonicalId: Bytes "The canonical identifier for the asset."
  canonicalDomain: BigInt "The domain of the canonical identifier for the asset."
  adoptedAsset: Bytes "The address of the adopted asset, if applicable."
  localAsset: Bytes "The address of the local asset."
  blockNumber: BigInt "The block # associated with the local asset, needed in case multiple locals are stored under the same canonicalId."
  status: AssetStatus "The status of the asset, as defined by the AssetStatus enumeration."
}

```

### AssetStatus

```graphql
type AssetStatus @entity {
  id: ID! "The key associated with the asset status."
  status: Boolean "The status of the asset, represented as a boolean value. True means the asset is active, while false means it is inactive."
}

```

### AssetBalance

```graphql
type AssetBalance @entity {
  id: ID! "The unique identifier for the asset balance, constructed as 'key-router_address'."
  amount: BigInt! "The amount of the asset held in the balance."
  router: Router! "The router associated with the asset balance."
  asset: Asset! "The asset associated with the balance."
  feesEarned: BigInt! "The total amount of fees earned for the asset balance."
}

```

### Router

```graphql
type Router @entity {
  id: ID! "The unique identifier for the router."
  isActive: Boolean! "A boolean indicating whether the router is currently active or not."
  owner: Bytes "The address of the current owner of the router."
  recipient: Bytes "The address of the current recipient of router fees."
  proposedOwner: Bytes "The address of the proposed new owner of the router, if any."
  proposedTimestamp: BigInt "The timestamp of the proposed ownership transfer, if any."
  assetBalances: [AssetBalance!]! @derivedFrom(field: "router") "A list of asset balances associated with the router."
}

```

### Setting

```graphql
type Setting @entity {
  id: ID! "The unique identifier for the setting."
  maxRoutersPerTransfer: BigInt! "The maximum number of routers allowed in a single transfer."
  caller: Bytes! "The address of the caller associated with the setting."
}

```

### Relayer

```graphql
type Relayer @entity {
  id: ID! "The ID of the relayer"
  isActive: Boolean! "Indicates whether the relayer is currently active"
  relayer: Bytes "The address of the relayer"
}

```

### Sequencer

```graphql
type Sequencer @entity {
  id: ID!  "Unique identifier for the sequencer entity"
  isActive: Boolean! "Flag indicating if the sequencer is active or not"
  sequencer: Bytes "Address of the sequencer"
}

```

### TransferStatus

```graphql
enum TransferStatus {
  XCalled
  Executed
  Reconciled
  CompletedSlow
  CompletedFast
}
```

### OriginTransfer

```graphql
type OriginTransfer @entity {
  id: ID! "The unique identifier for the origin transfer."

  # Meta
  chainId: BigInt "The ID of the chain on which the transfer is taking place."
  transferId: Bytes "The unique identifier for the transfer."
  nonce: BigInt "A unique nonce for the transfer, used to prevent replay attacks."
  status: TransferStatus "The status of the transfer, represented as a TransferStatus enum."
  messageHash: Bytes "The hash of the transfer message."

  # CallParams
  originDomain: BigInt "The domain of the origin chain."
  destinationDomain: BigInt "The domain of the destination chain."
  canonicalDomain: BigInt "The domain of the canonical asset."
  to: Bytes "The address of the destination contract."
  delegate: Bytes "The address of the delegate contract."
  receiveLocal: Boolean "A boolean indicating whether the transfer is receiving a local asset."
  callData: Bytes "The calldata associated with the transfer."
  slippage: BigInt "The maximum amount of slippage allowed for the transfer."
  originSender: Bytes "The address of the sender on the origin chain."
  bridgedAmt: BigInt "The amount of the asset being transferred."
  normalizedIn: BigInt "The normalized amount of the asset being transferred."
  canonicalId: Bytes "The canonical ID of the asset being transferred."

  # Asset
  asset: Asset "The asset being transferred."

  # Message
  message: OriginMessage "The message associated with the transfer."

  # Relayer Fee paid by user
  relayerFee: BigInt "The fee paid by the user for the transfer."
  bumpRelayerFeeCount: BigInt "The number of times the relayer fee has been bumped."

  # XCalled Transaction
  caller: Bytes "The address of the caller."
  transactionHash: Bytes "The hash of the transaction."
  timestamp: BigInt "The timestamp of the transaction."
  gasPrice: BigInt "The price of gas for the transaction."
  gasLimit: BigInt "The limit for gas usage in the transaction."
  blockNumber: BigInt "The number of the block in which the transaction was included."
  txOrigin: Bytes "The address of the original transaction sender."
}

```

### DestinationTransfer

```graphql
type DestinationTransfer @entity {
  id: ID!  "unique identifier of the destination transfer"

  # Meta
  chainId: BigInt  "chain id of the transfer"
  transferId: Bytes  "unique identifier for the transfer"
  nonce: BigInt  "number used to prevent replay attacks"
  status: TransferStatus  "status of the transfer"
  routers: [Router!]!  "list of routers used for the transfer"

  # CallParams
  originDomain: BigInt  "domain of the origin chain"
  destinationDomain: BigInt  "domain of the destination chain"
  canonicalDomain: BigInt  "canonical domain of the asset"
  to: Bytes  "recipient address on the destination chain"
  delegate: Bytes  "optional delegate address on the destination chain"
  receiveLocal: Boolean  "whether or not to receive the asset locally on the destination chain"
  callData: Bytes  "optional call data for the recipient"
  slippage: BigInt  "slippage allowance for routers to adjust exchange rate"
  bumpSlippageCount: BigInt  "count of times the slippage allowance was increased"
  originSender: Bytes  "sender address on the origin chain"
  bridgedAmt: BigInt  "amount of asset bridged from the origin chain"
  normalizedIn: BigInt  "amount of asset normalized to its canonical representation"
  canonicalId: Bytes  "unique identifier for the asset"

  # Asset
  asset: Asset  "asset being transferred"
  amount: BigInt  "amount of asset being transferred"

  # calculated
  routersFee: BigInt   "total fee paid to routers for the transfer"

  # Executed Transaction
  executedCaller: Bytes  "address of the user who initiated the transaction"
  executedTransactionHash: Bytes  "hash of the executed transaction"
  executedTimestamp: BigInt  "timestamp of the executed transaction"
  executedGasPrice: BigInt  "gas price used for the executed transaction"
  executedGasLimit: BigInt  "gas limit used for the executed transaction"
  executedBlockNumber: BigInt  "block number of the executed transaction"
  executedTxOrigin: Bytes  "address of the user who initiated the transaction on the destination chain"

  # Reconciled Transaction
  reconciledCaller: Bytes  "address of the user who initiated the reconciled transaction"
  reconciledTransactionHash: Bytes  "hash of the reconciled transaction"
  reconciledTimestamp: BigInt  "timestamp of the reconciled transaction"
  reconciledGasPrice: BigInt  "gas price used for the reconciled transaction"
  reconciledGasLimit: BigInt  "gas limit used for the reconciled transaction"
  reconciledBlockNumber: BigInt  "block number of the reconciled transaction"
  reconciledTxOrigin: Bytes  "address of the user who initiated the reconciled transaction on the origin chain"
}

```

### OriginMessage

```graphql
type OriginMessage @entity {
  id: ID! "unique identifier for each instance of the OriginMessage"

  # origin transfer data
  transferId: Bytes  "ID of the origin transfer"
  destinationDomain: BigInt  "domain where the transfer is being sent to"

  # Dispatch Transaction
  leaf: Bytes  "leaf of the Merkle tree of the message"
  index: BigInt  "index of the message in the Merkle tree"
  message: Bytes  "the message data"
  root: Bytes  "root of the Merkle tree of the message"
  transactionHash: Bytes  "hash of the transaction that dispatched the message"
  blockNumber: BigInt "block number of the transaction that dispatched the message"

  # root count RD
  rootCount: RootCount  "reference to the root count for this message"
}

```

### AggregateRoot

```graphql
type AggregateRoot @entity {
  id: ID!  "Unique identifier of the entity"
  root: Bytes!  "The root hash of a Merkle tree containing transaction messages"
  blockNumber: BigInt!  "The block number where the root was aggregated"
}

```

### ConnectorMeta

```graphql
type ConnectorMeta @entity {
  id: ID!  "ConnectorMeta"
  spokeDomain: BigInt  "domain of the spoke network"
  hubDomain: BigInt  "domain of the hub network"

  amb: Bytes  "address of the AMB contract used for bridging"
  rootManager: Bytes  "address of the root manager contract used for managing bridge roots"
  mirrorConnector: Bytes  "address of the MirrorConnector contract used for interacting with the Mirror network"
}
```

### RootCount

```graphql
type RootCount @entity {
  id: ID!  "unique identifier for the root count, typically a concatenation of the spoke and hub domain IDs"
  count: BigInt  "the current root count for the given spoke and hub domain pair"
}

```

### RootMessageSent

```graphql
type RootMessageSent @entity {
  id: ID! "Unique identifier for the root message sent"

  spokeDomain: BigInt  "Domain ID for the spoke chain"
  hubDomain: BigInt  "Domain ID for the hub chain"
  root: Bytes  "Root hash for the message"
  count: BigInt  "Number of messages in the root"

  # MessageSent Transaction
  caller: Bytes  "Address of the transaction sender"
  transactionHash: Bytes  "Hash of the transaction"
  timestamp: BigInt  "Timestamp of the transaction"
  gasPrice: BigInt  "Gas price for the transaction"
  gasLimit: BigInt   "Gas limit for the transaction"
  blockNumber: BigInt  "Block number of the transaction"
}

```


# Sample Queries

Below are some sample queries you can use to gather information from the Connext contracts.

You can build your own queries using a [GraphQL Explorer](https://graphiql-online.com/graphiql) and enter your endpoint to limit the data to exactly what you need.

## Get origin domain details of an xcall

```graphql
query OriginTransfer {
  originTransfers(
    where: {
      # Query by the transaction hash of the xcall
      transactionHash: "<TRANSACTION_HASH>"
      # Or by the xcall's transfer ID
      transferId: "<TRANSFER_ID>"
    }
  ) {
    # Meta Data
    chainId
    nonce
    transferId
    to
    delegate
    receiveLocal
    callData
    slippage
    originSender
    originDomain
    destinationDomain
    transactionHash
    bridgedAmt
    status
    timestamp
    normalizedIn
    # Asset Data
    asset {
      id
      adoptedAsset
      canonicalId
      canonicalDomain
    }
  }
}
```

## Get destination domain details of an xcall

```graphql
query DestinationTransfer {
  destinationTransfers(where: { transferId: "<TRANSFER_ID>" }) {
    # Meta Data
    chainId
    nonce
    transferId
    to
    callData
    originDomain
    destinationDomain
    delegate
    # Asset Data
    asset {
      id
    }
    bridgedAmt
    # Executed event Data
    status
    routers {
      id
    }
    originSender
    # Executed Transaction
    executedCaller
    executedTransactionHash
    executedTimestamp
    executedGasPrice
    executedGasLimit
    executedBlockNumber
    # Reconciled Transaction
    reconciledCaller
    reconciledTransactionHash
    reconciledTimestamp
    reconciledGasPrice
    reconciledGasLimit
    reconciledBlockNumber
    routersFee
    slippage
  }
}
```


# Subgraph Resources

Connext has a GraphQL API Endpoint hosted by [The Graph](https://thegraph.com/docs/about/introduction#what-the-graph-is) called a subgraph for indexing and organizing data from the Connext smart contracts. Subgraph information is serviced by a decentralized group of server operators called Indexers.

This subgraph is can be used to query Connext bridge transactions, transactions statuses and more.

## Subgraphs

<details>

<summary>Mainnet Subgraphs</summary>

</details>

<details>

<summary>Testnet Subgraphs</summary>

</details>

## Helpful Links

[Creating an API Key Video Tutorial](https://www.youtube.com/watch?v=UrfIpm-Vlgs)

[Managing your API Key & Setting your indexer preferences](https://thegraph.com/docs/en/studio/managing-api-keys/)

[Explorer Page](https://thegraph.com/explorer/subgraph?id=DfD1tZSmDtjCGC2LeYEQbVzj9j8kNqKAQEsYL27Vg6Sw\&view=Playground)

[Code repo with Connext's subgraph implementation](https://github.com/connext/monorepo/tree/56a166f3ecb50cc10356dd96c257e2e4d47f29e3/packages/deployments/subgraph/src/amarok-runtime-v0)


# Integration


# Adapters

Adapters are contracts that can hook into the normal flow of cross-chain transactions and augment their capabilities without requiring changes in existing contracts. The [connext/integration](https://github.com/connext/connext-integration) repository contains adapters that can be inherited for these purposes.

## SwapAdapter

This adapter contains the logic for swapping tokens. The `SwapAdapter` can be used on either the origin or the destination side to execute a swap.

### Using on Origin

`SwapAndXCall` is a contract that implements `SwapAdapter` and is meant to be used on the origin chain. It swaps the input tokens into desired output tokens before initiating the cross-chain transaction with `xcall`. This is useful in cases where you want users to be able to send any token to your contract and bridge them through Connext.

### Using on Destination

`SwapForwarderXReceiver` also implements `SwapAdapter` but it's used on the destination chain. It swaps the tokens received from the bridge into desired output tokens before proceeding with the "forward call", which contains the rest of the logic that follows on the destination side. The `ForwarderXReceiver` that it implements is detailed in the next section for Receivers.

### Swappers

The `SwapAdapter` holds a registry of `allowedSwappers` which are contracts that implement the `ISwapper` interface:

```solidity
interface ISwapper {
  function swap(
    uint256 _amountIn,
    address _tokenIn,
    address _tokenOut,
    bytes calldata _swapData
  ) external payable returns (uint256 amountOut);
}
```

For example, the `UniV3Swapper` implements `swap` which internally calls Uniswap's `ISwapRouter.exactInputSingle` to execute the swap via Uniswap.

Currently, Connext provides the following Swappers:

* `OneInchUniswapV3`
* `UniV2Swapper`
* `UniV3Swapper`


# Receivers

Receivers are contracts that implement the `IXReceiver` interface:

```solidity
interface IXReceiver {
  function xReceive(
    bytes32 _transferId,
    uint256 _amount,
    address _asset,
    address _originSender,
    uint32 _origin,
    bytes memory _callData
  ) external returns (bytes memory);
}
```

These are the main contracts that integrators implement. Connext provides a few abstract receivers that should be inherited in order to gain the built-in security and error handling.

## ForwarderXReceiver

The `ForwarderXReceiver` is used for unauthenticated calls. The receiver has two virtual functions that the integrator should implement to 1) prepare and 2) forward the call.

### \_prepare

```solidity
function _prepare(
  bytes32 _transferId,
  bytes memory _data,
  uint256 _amount,
  address _asset
) internal virtual returns (bytes memory)
```

Preparation steps should be performed in `_prepare`, which can include operations like swapping funds.

### \_forwardFunctionCall

```solidity
function _forwardFunctionCall(
  bytes memory _preparedData,
  bytes32 _transferId,
  uint256 _amount,
  address _asset
) internal virtual returns (bool)
```

The `_forwardFunctionCall` should contain the logic that calls the destination target contract.

## AuthForwarderXReceiver

The `AuthForwarderXReceiver` is used for authenticated calls. It follows the same two-step pattern as the `ForwarderXReceiver` (`_prepare` + `_forwardFunctionCall`).

### onlyOrigin

In addition, the `xReceive` method is guarded by an `onlyOrigin` modifier which ensures that:

1. The originating call comes from a registered origin domain.
2. The originating call comes from the expected origin contract of the origin domain.
3. The call to this contract comes from Connext.

These checks guarantee that any successful call to an `AuthForwarderXReceiver` contains validated data. For more information on authentication, see the Authentication section.

### originRegistry

This contract also holds an `originRegistry` which maps origin domain IDs to origin sender contracts. This registry is checked to uphold requirement #2 for the modifier above.

The contract is `Ownable` and only the owner can `addOrigin` and `removeOrigin`. All expected origins and senders should be registered in this mapping.


# Examples


# Simple Bridge

The `SimpleBridge` just transfers tokens from a user to another wallet (could be themselves) on a different chain. Since no calldata is involved, no target contract is needed.

In this example, `SimpleBridge` has two functions:

* `xTransfer` bridges any ERC20 token
  * The user must first approve a spending allowance of the token to the `SimpleBridge` contract.
  * `relayerFee` is paid in native ETH so when `xTransfer` is called, `msg.value` MUST be passed in equal to the specified `relayerFee`. Informaation for calculating relayer fees can be found on Estimating Fees page.
* `xTransferEth` bridges ETH (for origin/destination chains whose native asset is ETH)
  * To send and receive native ETH, the flow is a bit different. Since Connext doesn't accept native ETH as the bridged asset, ETH should be first wrapped into WETH on the origin domain and then the delivered WETH on destination should be unwrapped back to ETH.
  * An Unwrapper contract that implements `IXReceive` already exists on all supported networks to be used as the `_to` target in `xcall`. The final recipient on destination should be encoded into the `callData` param for the Unwrapper to send ETH to (demonstrated on line 92 below).
  * When sending ETH, `msg.value` = `relayerFee` + `amount`. See example below (note: in Etherscan, the payable field is in `ether` while the other fields are specified in `wei`).

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;

import {IConnext} from "@connext/interfaces/core/IConnext.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IWETH {
  function deposit() external payable;
  function approve(address guy, uint wad) external returns (bool);
}

/**
 * @title SimpleBridge
 * @notice Example of a cross-domain token transfer.
 */
contract SimpleBridge {
  // The connext contract on the origin domain
  IConnext public immutable connext;

  constructor(address _connext) {
    connext = IConnext(_connext);
  }

  /**
   * @notice Transfers non-native assets from one chain to another.
   * @dev User should approve a spending allowance before calling this.
   * @param token Address of the token on this domain.
   * @param amount The amount to transfer.
   * @param recipient The destination address (e.g. a wallet).
   * @param destinationDomain The destination domain ID.
   * @param slippage The maximum amount of slippage the user will accept in BPS.
   * @param relayerFee The fee offered to relayers.
   */
  function xTransfer(
    address token,
    uint256 amount,
    address recipient,
    uint32 destinationDomain,
    uint256 slippage,
    uint256 relayerFee
  ) external payable {
    IERC20 _token = IERC20(token);

    require(
      _token.allowance(msg.sender, address(this)) >= amount,
      "User must approve amount"
    );

    // User sends funds to this contract
    _token.transferFrom(msg.sender, address(this), amount);

    // This contract approves transfer to Connext
    _token.approve(address(connext), amount);

    connext.xcall{value: relayerFee}(
      destinationDomain, // _destination: Domain ID of the destination chain
      recipient,         // _to: address receiving the funds on the destination
      token,             // _asset: address of the token contract
      msg.sender,        // _delegate: address that can revert or forceLocal on destination
      amount,            // _amount: amount of tokens to transfer
      slippage,          // _slippage: the maximum amount of slippage the user will accept in BPS (e.g. 30 = 0.3%)
      bytes("")          // _callData: empty bytes because we're only sending funds
    );  
  }

  /**
   * @notice Transfers native assets from one chain to another.
   * @param destinationUnwrapper Address of the Unwrapper contract on destination.
   * @param weth Address of the WETH contract on this domain.
   * @param amount The amount to transfer.
   * @param recipient The destination address (e.g. a wallet).
   * @param destinationDomain The destination domain ID.
   * @param slippage The maximum amount of slippage the user will accept in BPS.
   * @param relayerFee The fee offered to relayers.
   */
  function xTransferEth(
    address destinationUnwrapper,
    address weth,
    uint256 amount,
    address recipient,
    uint32 destinationDomain,
    uint256 slippage,
    uint256 relayerFee
  ) external payable {
    // Wrap ETH into WETH to send with the xcall
    IWETH(weth).deposit{value: amount}();

    // This contract approves transfer to Connext
    IWETH(weth).approve(address(connext), amount);

    // Encode the recipient address for calldata
    bytes memory callData = abi.encode(recipient);

    // xcall the Unwrapper contract to unwrap WETH into ETH on destination
    connext.xcall{value: relayerFee}(
      destinationDomain,    // _destination: Domain ID of the destination chain
      destinationUnwrapper, // _to: Unwrapper contract
      weth,                 // _asset: address of the WETH contract
      msg.sender,           // _delegate: address that can revert or forceLocal on destination
      amount,               // _amount: amount of tokens to transfer
      slippage,             // _slippage: the maximum amount of slippage the user will accept in BPS (e.g. 30 = 0.3%)
      callData              // _callData: calldata with encoded recipient address
    );  
  }
}
```

Information like asset addresses be found in the Deployments page.


# Authenticated Greeter

The `DestinationGreeterAuthenticated` contract sets some permissioning constraints. It only allows its `greeting` to be updated from `SourceGreeterAuthenticated`. In order to enforce this, the contract checks that the caller is the original sender from the origin domain.

## Target Contract

The target contract must implement some checks to uphold its security constraints.

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;

import {IXReceiver} from "@connext/interfaces/core/IXReceiver.sol";

/**
 * @title DestinationGreeterAuthenticated
 * @notice Example destination contract that stores a greeting and only allows source to update it.
 */
contract DestinationGreeterAuthenticated is IXReceiver {
  // The Connext contract on this domain
  address public immutable connext;

  // The domain ID where the source contract is deployed
  uint32 public immutable originDomain;

  // The address of the source contract
  address public immutable source;

  string public greeting;

  /** @notice A modifier for authenticated calls.
   * This is an important security consideration. If the target contract
   * function should be authenticated, it must check three things:
   *    1) The originating call comes from the expected origin domain.
   *    2) The originating call comes from the expected source contract.
   *    3) The call to this contract comes from Connext.
   */
  modifier onlySource(address _originSender, uint32 _origin) {
    require(
      _origin == originDomain &&
        _originSender == source &&
        msg.sender == connext,
      "Expected original caller to be source contract on origin domain and this to be called by Connext"
    );
    _;
  }

  constructor(
    uint32 _originDomain,
    address _source,
    address _connext
  ) {
    originDomain = _originDomain;
    source = _source;
    connext = _connext;
  }

  /** @notice Authenticated receiver function.
    * @param _callData Calldata containing the new greeting.
    */
  function xReceive(
    bytes32 _transferId,
    uint256 _amount,
    address _asset,
    address _originSender,
    uint32 _origin,
    bytes memory _callData
  ) external onlySource(_originSender, _origin) returns (bytes memory) {
    // Unpack the _callData
    string memory newGreeting = abi.decode(_callData, (string));

    _updateGreeting(newGreeting);
  }

  /** @notice Internal function to update the greeting.
    * @param newGreeting The new greeting.
    */
  function _updateGreeting(string memory newGreeting) internal {
    greeting = newGreeting;
  }
}
```

## Source Contract

Nothing special has to be accounted for on the source contract.

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;

import {IConnext} from "@connext/interfaces/core/IConnext.sol";

/**
 * @title SourceGreeterAuthenticated
 * @notice Example source contract that updates a greeting in DestinationGreeterAuthenticated.
 */
contract SourceGreeterAuthenticated {
  // The connext contract on the origin domain.
  IConnext public immutable connext;

  constructor(address _connext) {
    connext = IConnext(_connext);
  }

  /** @notice Updates a greeting variable on the DestinationGreeterAuthenticated contract.
    * @param target Address of the DestinationGreeterAuthenticated contract.
    * @param destinationDomain The destination domain ID.
    * @param newGreeting New greeting to update to.
    * @param relayerFee The fee offered to relayers.
    */
  function xUpdateGreeting (
    address target, 
    uint32 destinationDomain,
    string memory newGreeting,
    uint256 relayerFee
  ) external payable {
    // Encode the data needed for the target contract call.
    bytes memory callData = abi.encode(newGreeting);

    connext.xcall{value: relayerFee}(
      destinationDomain, // _destination: Domain ID of the destination chain
      target,            // _to: address of the target contract
      address(0),        // _asset: use address zero for 0-value transfers
      msg.sender,        // _delegate: address that can revert or forceLocal on destination
      0,                 // _amount: 0 because no funds are being transferred
      0,                 // _slippage: can be anything between 0-10000 because no funds are being transferred
      callData           // _callData: the encoded calldata to send
    );
  }
}
```

Note that `HelloSource` should be deployed before `HelloTargetAuthenticated` because the latter needs the address of the former in its constructor.

Now we've enforced that the greeting in `HelloTargetAuthenticated` can only be updated through `HelloSource`!


# Ping Pong

`Ping` is a contract on some domain and `Pong` is a contract on some other domain. The user sends a ping and a pong will be sent back!

This example demonstrates how to use nested `xcall`s and a single direction emulating "callback" behavior.

## Ping Contract

The `Ping` contract contains an external `startPingPong` function that the user will call to initiate the flow. It *also* implements `IXReceiver` because it will act as the "target" of an `xcall` from `Pong`. The `xReceive` function here is essentially a callback function - we can verify that something happened in `Pong`'s domain and handle any results passed back.

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;

import {IConnext} from "@connext/interfaces/core/IConnext.sol";
import {IXReceiver} from "@connext/interfaces/core/IXReceiver.sol";

/**
 * @title Ping
 * @notice Ping side of a PingPong example.
 */
contract Ping is IXReceiver {
  // The Connext contract on this domain
  IConnext public immutable connext;

  // Number of pings this contract has received
  uint256 public pings;

  constructor(address _connext) {
    connext = IConnext(_connext);
  }

  /** 
   * @notice Starts the ping pong s. equence.
   * @param destinationDomain The destination domain ID. 
   * @param target Address of the Pong contract on the destination domain.
   * @param relayerFee The fee offered to relayers.
   */
  function startPingPong(
    address target, 
    uint32 destinationDomain, 
    uint256 relayerFee
  ) external payable {
    require(
      msg.value == relayerFee,
      "Must send gas equal to the specified relayer fee"
    );

    // Include the relayerFee so Pong will use the same fee 
    // Include the address of this contract so Pong will know where to send the "callback"
    bytes memory callData = abi.encode(pings, address(this), relayerFee);

    connext.xcall{value: relayerFee}(
      destinationDomain, // _destination: domain ID of the destination chain
      target,            // _to: address of the target contract (Pong)
      address(0),        // _asset: use address zero for 0-value transfers
      msg.sender,        // _delegate: address that can revert or forceLocal on destination
      0,                 // _amount: 0 because no funds are being transferred
      0,                 // _slippage: can be anything between 0-10000 because no funds are being transferred
      callData           // _callData: the encoded calldata to send
    );
  }

  /** @notice The receiver function as required by the IXReceiver interface.
   * @dev The "callback" function for this example. Will be triggered after Pong xcalls back.
   */
  function xReceive(
    bytes32 _transferId,
    uint256 _amount,
    address _asset,
    address _originSender,
    uint32 _origin,
    bytes memory _callData
  ) external returns (bytes memory) {
    uint256 _pongs = abi.decode(_callData, (uint256));

    pings++;
  }
}
```

## Pong Contract

`Pong` will send a nested `xcall` back to `Ping`, including some information that can be acted on.

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;

import {IConnext} from "@connext/interfaces/core/IConnext.sol";
import {IXReceiver} from "@connext/interfaces/core/IXReceiver.sol";

interface IPong {
  function sendPong(
    uint32 destinationDomain, 
    address target,
    uint256 relayerFee
  ) external payable;
}

/**
 * @title Pong
 * @notice Pong side of a PingPong example.
 */
contract Pong is IXReceiver {
  // The Connext contract on this domain
  IConnext public immutable connext;

  // Number of pongs this contract has received
  uint256 public pongs;

  constructor(address _connext) {
    connext = IConnext(_connext);
  }

  /** 
   * @notice Sends a pong to the Ping contract.
   * @param destinationDomain The destination domain ID.
   * @param target Address of the Ping contract on the destination domain.
   * @param relayerFee The fee offered to relayers. 
   */
  function sendPong(
    uint32 destinationDomain, 
    address target,
    uint256 relayerFee
  ) internal {
    // Include some data we can use back on Ping
    bytes memory callData = abi.encode(pongs);

    connext.xcall{value: relayerFee}(
      destinationDomain, // _destination: Domain ID of the destination chain
      target,            // _to: address of the target contract (Ping)
      address(0),        // _asset: use address zero for 0-value transfers
      msg.sender,        // _delegate: address that can revert or forceLocal on destination
      0,                 // _amount: 0 because no funds are being transferred
      0,                 // _slippage: can be anything between 0-10000 because no funds are being transferred
      callData           // _callData: the encoded calldata to send
    );
  }

  /** 
   * @notice The receiver function as required by the IXReceiver interface.
   * @dev The Connext bridge contract will call this function.
   */
  function xReceive(
    bytes32 _transferId,
    uint256 _amount,
    address _asset,
    address _originSender,
    uint32 _origin,
    bytes memory _callData
  ) external returns (bytes memory) {
    // Because this call is *not* authenticated, the _originSender will be the Zero Address
    // Ping's address was sent with the xcall so it can be decoded and used for the nested xcall
    (
      uint256 _pings, 
      address _pingContract, 
      uint256 _relayerFee
    ) = abi.decode(_callData, (uint256, address, uint256));
    
    pongs++;

    // This contract sends a nested xcall with the same relayerFee value used for Ping. That means
    // it must own at least that much in native gas to pay for the next xcall.
    require(
      address(this).balance >= _relayerFee,
      "Not enough gas to pay for relayer fee"
    );

    // The nested xcall
    sendPong(_origin, _pingContract, _relayerFee);
  }

  /** 
   * @notice This contract can receive gas to pay for nested xcall relayer fees.
   */
  receive() external payable {}
  
  fallback() external payable {}
}
```

An important note for `Pong` is that `sendPong` is *not* `payable` and neither is `xReceive`. So in order for the 2nd `xcall` to work with `relayerFees`, the someone has to send native gas on destination to `Pong`. In practice, this can take the form of a "gas tank" mechanism that can be filled by users or subsidized by protocols.

Connext is working on an upgrade that will soon allow nested relayer fees to be deducted from the transacting asset, eliminating the need to fund receivers in their native gas token.


# xERC20

ERC-7281: Sovereign Bridged Tokens

## What are xERC20 tokens?

xERC20 ([`ERC-7281`](https://ethereum-magicians.org/t/erc-7281-sovereign-bridged-tokens/14979)) tokens are crosschain ERC-20 which can be transferred with no slippage across chains without compromising on security.

Today, when you want to make your token available on multiple chains, you have to either:

1. Provide significant liquidity to a bridge on each chain (and require users to take **slippage** when transferring across chains); or
2. Mint a ***new*** representation of your token through the bridge, locking yourself into that bridge and its security model forever.

xERC20s are **natively crosschain without compromises.** This makes your token:

* Transferrable across chains with no slippage.
* Deployed and fully controlled by you, the token issuer, including the ability to set rate limits on a per-bridge basis.
* \[When bridged through Connext] Secured fully by Ethereum L1 and the canonical bridges of each chain.
* \[Coming Soon] Fungible against the token representations minted by canonical bridges (rollup bridges, Polygon PoS bridge, etc.)

For more information about the open standard, please check out <https://www.xerc20.com/>.

{% hint style="info" %}
Connext officially supports industry adoption of [ERC-7281: Sovereign Bridged Tokens](https://ethereum-magicians.org/t/erc-7281-sovereign-bridged-tokens/14979), which will allow token issuers to retain full sovereign control and flexibility over their tokens, regardless of which bridges they choose.&#x20;

The following sections of this guide will walk through how to set up xERC20s. Secondarily, it provides instructions on how to configure Connext as one of your token's allowed bridges.
{% endhint %}


# Setup Overview

The steps to set up your xERC20 are very straightforward:

1. Choose a Home chain for your tokens (or whatever chain your token is already deployed to!)
2. Deploy [Mintable/Burnable](https://github.com/defi-wonderland/xERC20/) Token representations on the other chains you want to support.
3. Allow bridges to support your xERC20 with custom rate limits.&#x20;

To enable Connext as one of your allowlisted bridges:

1. Send your token addresses to the Connext Labs team.
2. Give the Connext contracts rights to mint your token.
3. Work with the team to signal to Connext routers to supply fast-liquidity for your token.

💡 It is entirely possible for you to run your own router to supply fast path liquidity in Connext if you’d like! If this is prohibitively complex, however, we ***already*** have an ecosystem of routers that can work with you to add liquidity for your token.

Routers are **highly** capital efficient (and earn money from fees), and so the network only needs about 1/8th of your expected 24h volume in liquidity in your token. Please contact us and we can help put you in touch with a router partner!

{% hint style="info" %}
You can expose bridging functionality to your users either through the [Connext Bridge](https://bridge.connext.network/) or by integrating directly into your project’s website/app. We recommend the latter because it makes your UX as smooth as possible! 😄
{% endhint %}

In the next section, we will go through all these steps in detail.


# Detailed Setup Guide

Hello! 👋 This section will guide you through the process of setting up your `xERC20` token.

## Prerequisites

Let's begin by getting a comprehensive understanding of the required steps based on your token's current situation.

#### 1. Categorize your token

Determine which of the following categories best describes your token's current state.

* **Category A**: The token is new and is not deployed anywhere.
* **Category B**: The token already exists on one chain.
* **Category C**: The token already exists on multiple chains.

{% hint style="info" %}
If you want to spin up an `xERC20` on testnet, the category may be different from your mainnet token. We recommend emulating your mainnet token setup if your goal is to testrun the process on testnet.\
\
For example: on mainnet, your token `TKN`is currently only deployed on Ethereum (**Category B**). Then on testnet, you should deploy a `TKN` to Goerli and follow the steps for a **Category B** token.
{% endhint %}

#### 2. Define your token's "home" chain

Based on your token's category:

* **Category A**: Choose one chain to be the home chain.&#x20;
* **Category B**: The chain where your token is currently deployed will be the home chain.&#x20;
* **Category C**: Choose one chain (among the ones your token is currently deployed) to be the home chain.

#### 3. Prepare to deploy tokens

* **Category A**:
  * You will deploy an `xERC20` on each chain you wish to support, including the home chain.
* **Category B** and **Category C**:
  * On each chain with an existing token, you need to figure out if a Lockbox setup is needed (next section).
  * On all other chains you wish to support, you will just deploy an `xERC20`.

## Lockbox Setup

A Lockbox allows any existing ERC20 to become compatible with the ERC-7281 (xERC20) standard. The Lockbox is just a simple wrapper contract, analogous to Wrapped ETH.

For **Category B** and **Category C**, there are tokens that already exist on certain chains. For each of these tokens, follow this flowchart to determine if you need to have a Lockbox setup on that token's chain.

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

#### Example: NEXT token

To better understand how a Lockbox setup operates, consider the `NEXT` token as a real-world example.

* On Ethereum: Both the `NEXT` token and `xNEXT` token are deployed with a `Lockbox`.
* On Arbitrum: Only the `xNEXT` token is deployed.

Bridging from Ethereum to Arbitrum:

* \[Ethereum] User deposits `NEXT` into the Lockbox and receives `xNEXT`
* \[Ethereum] User calls the bridge using `xNEXT`&#x20;
* \[Arbitrum] Bridge delivers `xNEXT` to the user

Bridging from Arbitrum to Ethereum:

* \[Arbitrum] User calls the bridge using `xNEXT`
* \[Ethereum] Bridge withdraws `NEXT` from the Lockbox using the bridged `xNEXT`
* \[Ethereum] Bridge delivers `NEXT` to the user

## Deploying Contracts

Now that you have an idea of how your tokens should be set up, let's move on to the actual deployment procedures.&#x20;

#### xERC20s and Lockboxes

The Wonderland team provides an [xERC20 Github repository](https://github.com/defi-wonderland/xERC20/tree/main) that contains fully compliant implementations of `xERC20`, `Lockbox`, and scripts to deploy them. Factory contracts have already been deployed on each chain listed under `/broadcast/MultichainDeploy.sol/{chain_id}/run-latest.json`.

We suggest you deploy from a fork of this repo, please see the `README` for instructions. You will configure the scripts based on which chains you need to have Lockbox setups.

If you wish to roll your own version of an xERC20, make sure your custom implementation is compliant with the standard.

{% hint style="info" %}
The [ERC-7281](https://github.com/ethereum/EIPs/pull/7281) specification requires compliant tokens to implement ERC-20 along with mint/burn and some additional rate limit interfaces. The absolute *minimal* interface needed is the ERC-20 interface plus mint/burn:

```solidity
/**
 * @notice Mints tokens for a user
 * @dev Can only be called by a bridge
 * @param _user The address of the user who needs tokens minted
 * @param _amount The amount of tokens being minted
 */
function mint(address _user, uint256 _amount) external;

/**
 * @notice Burns tokens for a user
 * @dev Can only be called by a bridge
 * @param _user The address of the user who needs tokens burned
 * @param _amount The amount of tokens being burned
 */
function burn(address _user, uint256 _amount) external;
```

{% endhint %}

#### LockboxAdapter

You might have noticed there's a `LockBoxAdapter` contract in the diagram above when you have a Lockbox setup. This contract facilitates the unwrapping of `xERC20 -> ERC20` on the destination chain and is needed for UIs to do this step automatically for users.

The Connext team has a `LockboxAdapter` deployed to all our supported chains (the implementation is available [here](https://github.com/connext/chain-abstraction-integration/blob/c35fbe757946cc76a826d93595cd99fd0db39c27/contracts/integration/LockboxAdapter.sol)).&#x20;

#### Whitelisting bridges

As the token issuer, you have the power to decide which bridges can mint/burn your token and the ability to set rate limits per bridge:

```
/**
 * @notice Updates the limits of any bridge
 * @dev Can only be called by the owner
 * @param _mintingLimit The updated minting limit we are setting to the bridge
 * @param _burningLimit The updated burning limit we are setting to the bridge
 * @param _bridge The address of the bridge we are setting the limits too
 */
function setLimits(address _bridge, uint256 _mintingLimit, uint256 _burningLimit) external;
```

These limits will replenish after`_DURATION` (by default the repo deploys `xERC20` with a value of 1 day).

Once your token is deployed, you can call `setLimits` to grant any bridge the privilege to mint/burn your token on that chain.

## Enabling Connext as a Bridge

If you want Connext to be able to bridge your token ([here's our pitch in the next section](/usecases/xerc20/how-xerc20-tokens-work-with-connext)), please go through the following steps.

1. Set rate limits for Connext.
   * For each chain where your xERC20 is deployed, call `setLimits` as the owner/governor. Use the appropriate Connext address listed under "Core Contract" [here](/resources/deployments) as the `_bridge` parameter.
2. Submit a PR to our [ChainData mappings](https://github.com/connext/chaindata/blob/main/crossChain.json).
   * For each chain where your xERC20 is deployed, add an object keyed by its address like this:

     ```json
     "0x4c781E4D22cfaAdA520cAe4aF9097C5ecf9C3A71": {
       "name": "xDappRadar",
       "symbol": "xRADAR",
       "decimals": 18
     }
     ```
3. Submit a PR to our [allowlisting scripts](https://github.com/connext/monorepo/blob/main/packages/deployments/contracts/src/cli/init/config/mainnet/production.ts).
   * Under the `assets` key in the configuration object, add another object to the list like this:

     ```json
     {
       name: "RADAR",
       canonical: {
         domain: "11111",
         address: "0x202426c15a18a0e0fE3294415E66421891E2EB7C",
         decimals: 18,
       },
       representations: {
         /// ETHEREUM
         "6648936": {
           local: "0x202426c15a18a0e0fE3294415E66421891E2EB7C",
           adopted: "0x202426c15a18a0e0fE3294415E66421891E2EB7C",
         },
         /// BSC
         "6450786": {
           local: "0x489580eB70a50515296eF31E8179fF3e77E24965",
           adopted: "0x489580eB70a50515296eF31E8179fF3e77E24965",
         },
       },
     },
     ```
   * The `canonical` object should always have `11111` as the `domain`. Change the `address` and `decimals` to match your home chain xERC20. For example, `RADAR`'s home chain is Ethereum.
   * For each chain where your xERC20 is deployed (including the home chain), add an entry into the `representations` field keyed on the chain's `domainId`.&#x20;
     * [You can look up each chain's domainId here](broken://pages/liIVrkErrhWUP9H4SUiV). We encourage commenting the chain name above each entry.
     * `local` and `adopted` should both be set to the xERC20 address. These exist as separate fields for non-xERC20 assets.
     * *Note: `domainId` is a Connext-specific identifier per chain that exists for forward compatibility with non-evm chains.*

{% hint style="info" %}
📌 Please reference the ChainData PR in the allowlisting script PR to expedite the review process!
{% endhint %}

Once this is done, the Connext Labs team will review your PRs to sanity check deployment details. Once your PR is approved, your tokens will be whitelisted and transferrable across chains!

## Connextscan and Bridge UI Support

The Connext team will take care of listing your token on our [Bridge UI](https://bridge.connext.network) and be able to track transfers in the [Connext explorer](https://connextscan.io/).

## Router Liquidity

At this point, your token should be transferrable across chains with no added fees or slippage. However, because of how Connext’s model works, these transfers will happen in large batches through Ethereum L1 roughly once every 2-3 hours.

{% hint style="info" %}
💡 Learn more about [fast and slow path execution here](/concepts/how-it-works/transaction-lifecycle).
{% endhint %}

If your usecase requires fast (i.e. <2 minute) transfers across chains, you will need some routers in our network to supply some liquidity to execute transactions immediately on behalf of users.

Please reach out to the Connext team and we can help work through options here with our router partners.


# Connext and xERC20s

Connext combines the security of bridging through canonical bridges (Arbitrum, Optimism, etc.) into a single, easy to use, developer interface.&#x20;

Connext batches data associated with crosschain token transfers into merkle roots. These roots are passed to Ethereum L1 from each chain through canonical bridges, further batched on L1, and then passed back to each other chain. This forms a cheap, trust-minimized **message highway** through which you can communicate between chains.

{% hint style="success" %}
For tokens specifically, Connext supports burning and minting across chains by communicating through the underlying messaging highway.
{% endhint %}

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

Messages through Connext that are passed through Ethereum can take 1-3 hours to arrive across chains, which is too slow to provide a great user experience. Connext routers (the node operators of our network), however, cut this time down to 45-180 seconds, by “fronting” liquidity to the user immediately, and being repaid by the protocol. Routers charge a 5 bps flat fee for this service.

## Why Connext?

Why should you use Connext for crosschain tokens? There are a few important ways that Connext is different from other options.

#### Security

Connext has a long history of prioritizing security and trust-minimization over all else.

* By delegating crosschain message verification to canonical bridges, Connext is the **only** messaging bridge that gives users the trust guarantees of the underlying chain. For cases where no canonical bridge is available, Connext expects to plug into something like [Hashi](https://ethresear.ch/t/hashi-a-principled-approach-to-bridges/14725).
* The latest upgrade of Connext has been [rigorously audited](https://github.com/connext/audits), and features a system of watchers that monitor usage and [proactively pause the network if they detect problems](https://github.com/connext/monorepo/tree/main/packages/agents/watcher).

#### Sovereignty & Fungibility

Unlike other token bridges, Connext prioritizes giving you sovereign control over your projects’ assets.

* We advocate for you to deploy and retain control over your own token implementations on each chain. This means you retain the ability to delist Connext if we’re not fulfilling your needs or list other bridges if you’d like to have multiple options.
* **\[Coming soon]** With the above, we also make it possible for our token to be fungible against token representations deployed by the canonical bridges themselves. This means that regardless of whether a user sends a token through Connext to Optimism, or through the official Optimism Bridge, they are guaranteed to get the same asset.

Please contact [@maxlomu](https://t.me/maxlomu) or [@arjunbhuptani](https://t.me/arjunbhuptani) on Telegram if you have any questions!


# Chain Abstraction

Chain abstraction is one of the flagship use cases of Connext. Chain abstraction allows a dApp to execute logic from any chain without requiring users to switch networks, sign transactions on a different chain, and spend gas on a different chain. This pattern can be used at higher layers to fully abstract chains from the user, removing the need for users to consciously have to think about what chain they are on.

For example, a chain abstraction layer for Aave would involve a simple, two-step process.

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

1. The Connext SDK is used to construct an `xcall` transaction to be sent by the user on their origin chain.
2. An adapter contract deployed on the destination chain forwards the `deposit` call to Aave.

In the end, no changes are needed on the Aave contracts themselves and cross-chain deposits are enabled for users with maximally simplified UX.

To get started, check out the [Chain Abstraction guide](/usecases/chain-abstraction/chain-abstraction-guide).


# Chain Abstraction Guide

A chain abstraction integration will generally involve two steps: writing an adapter contract and constructing `calldata` for users to send.

The [Chain Abstraction Reference](https://github.com/connext/chain-abstraction-reference) is an example repository with adapter contracts and a frontend that implements the full stack of the integration. We'll be referencing code from it in this guide.

## Adapter Contract

The adapter is a smart contract that integrators must build. It should implement the [`xReceive`](broken://pages/ttQFx7maskmWsluXbw4s#xreceive) interface and be deployed to all destination chains that the integrating protocol supports. After the user's transaction has been dispatched and their funds/data arrive on the destination chain, this contract will be called to handle all the execution logic for the protocol.

The execution logic includes a swap and a "forward call". The swap converts the Connext-bridged asset into the asset that will be used for the protocol's function call (a.k.a "target function"). The forward call is where the adapter contract actually calls the "target function".

The following sections will walk through how to implement these two parts.

### Installation

Assuming you're using [Foundry](https://book.getfoundry.sh/), you can add the Connext contracts to your project by running the following command to install the [`connext-integration`](https://github.com/connext/connext-integration/tree/main) repository as a submodule:

```bash
forge install connext/connext-integration
```

The library will be installed to `lib/connext-integration`.

### SwapForwarderXReceiver

The [`SwapForwarderXReceiver`](https://github.com/connext/chain-abstraction-integration/blob/main/contracts/destination/xreceivers/Swap/SwapForwarderXReceiver.sol#L11) is an audited abstract contract that should be implemented by adapter contracts.

```solidity
abstract contract SwapForwarderXReceiver is ForwarderXReceiver, SwapAdapter {
  using Address for address;

  /// @dev The address of the Connext contract on this domain.
  constructor(address _connext) ForwarderXReceiver(_connext) {}

  /// INTERNAL
  /**
   * @notice Prepare the data by calling to the swap adapter. Return the data to be swapped.
   * @dev This is called by the xReceive function so the input data is provided by the Connext bridge.
   * @param _transferId The transferId of the transfer.
   * @param _data The data to be swapped.
   * @param _amount The amount to be swapped.
   * @param _asset The incoming asset to be swapped.
   */
  function _prepare(
    bytes32 _transferId,
    bytes memory _data,
    uint256 _amount,
    address _asset
  ) internal override returns (bytes memory) {
    //highlight-start
    (address _swapper, address _toAsset, bytes memory _swapData, bytes memory _forwardCallData) = abi.decode(
      _data,
      (address, address, bytes, bytes)
    );
    //highlight-end

    uint256 _amountOut = this.exactSwap(_swapper, _amount, _asset, _toAsset, _swapData);

    return abi.encode(_forwardCallData, _amountOut, _asset, _toAsset, _transferId);
  }
}
```

Notice that the `_prepare` function expects a `bytes memory _data` parameter that will be decoded into:

* `_swapper`: The specific swapper that will be used for the swap on the destination domain.
* `_toAsset`: The asset that should be swapped into.
* `_swapData`: The encoded swap data that will be constructed offchain (using the [Chain Abstraction SDK](https://github.com/connext/monorepo/tree/main/packages/agents/chain-abstraction) in the next section).
* `_forwardCallData`: The forward call data that the receiver contract will call on the destination domain.

### Adapter Contract

The adapter inherits `SwapForwarderXReceiver` and implements the `_forwardFunctionCall` method.&#x20;

For example, let's take a look at the `Greeter` from the [Chain Abstraction Reference](https://github.com/connext/chain-abstraction-reference) repo and pretend that this is the existing contract for a protocol that wants to use the chain abstraction pattern.

```solidity
pragma solidity ^0.8.19;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IGreeter {
  function greetWithTokens(address _token, uint256 _amount, string calldata _greeting) external;
}

contract Greeter is IGreeter {
  string public greeting;
  IERC20 public WETH;

  event GreetingUpdated(string _greeting);

  constructor(address _WETH) {
    WETH = IERC20(_WETH);
  }

  function greetWithTokens(address _token, uint256 _amount, string calldata _greeting) external override {
    IERC20 token = IERC20(_token);

    require(_token == address(WETH), "Token must be WETH");
    require(_amount > 0, "Amount cannot be zero");
    require(
      token.allowance(msg.sender, address(this)) >= _amount,
      "User must approve amount"
    );

    token.transferFrom(msg.sender, address(this), _amount);

    greeting = _greeting;
    emit GreetingUpdated(_greeting);
  }
}
```

The "target function" we want to call cross-chain is `greetWithTokens` which takes payment in any amount of WETH to update the greeting.

So `GreeterAdapter` below is the adapter contract that needs to be created:

```solidity
pragma solidity ^0.8.19;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SwapForwarderXReceiver} from "lib/chain-abstraction-integration/contracts/destination/xreceivers/Swap/SwapForwarderXReceiver.sol";
import {IGreeter} from "./Greeter.sol";

contract GreeterAdapter is SwapForwarderXReceiver {
  IGreeter public immutable greeter;

  constructor(address _connext, address _greeter) SwapForwarderXReceiver(_connext) {
    greeter = IGreeter(_greeter);
  }

  function _greetWithTokens(address _token, uint256 _amount, string memory _greeting) internal {
    IERC20 token = IERC20(_token);
    token.approve(address(greeter), _amount);
    greeter.greetWithTokens(_token, _amount, _greeting);
  }

  function _forwardFunctionCall(
    bytes memory _preparedData,
    bytes32 /*_transferId*/,
    uint256 /*_amount*/,
    address /*_asset*/
  ) internal override returns (bool) {
    (bytes memory _forwardCallData, uint256 _amountOut, ,) = abi.decode(
      _preparedData,
      (bytes, uint256, address, address)
    );
    (address _token, string memory _greeting) = abi.decode(_forwardCallData, (address, string));

    // Forward the call
    _greetWithTokens(_token, _amountOut, _greeting);

    return true;
  }
}

```

The `_forwardFunctionCall` function unwraps data which includes arguments for the forward call (`greetWithTokens`) and the amount of tokens received after the swap. It then "forwards" the call to the `greeter` contract.

That's it!

## Origin Domain Transaction

The second part of the integration is to set up the transaction that users send on the origin domain. This involves a few steps but Connext's [Chain Abstraction SDK](https://www.npmjs.com/package/@connext/chain-abstraction) makes this process easier:

1. Use `getPoolFeeForUniV3` to fetch the `poolFee` for a specific swap route on the destination domain
2. Construct the `forwardCallData` for the integrating protocol's "target function"
3. Use `getXCallCallData` to construct the `calldata` that will be passed into `xcall`
4. Use `prepareSwapAndXCall` to combine the swap and the `xcall` into a single transaction request

With Connext's [Core SDK](https://www.npmjs.com/package/@connext/sdk):

1. Use `estimateRelayerFee` to fetch the `relayerFee` for the `xcall`
2. Use `calculateAmountReceived` to show users the expected amount received on the destination domain

### Installation

For installing the SDKs, use **Node.js v18**.

```bash
npm install @connext/chain-abstraction @connext/sdk
```

### 1) Get the pool fee

First we need to retrieve inputs for the destination swap. The function `getPoolFeeForUniV3` returns the poolFee of the UniV3 pool for a given token pair which will be used in the UniV3 router execution. The poolFee is the fee that is charged by the pool for trading tokens.

```ts
export const getPoolFeeForUniV3 = async (
  domainId: string,
  rpc: string,
  token0: string,
  token1: string,
):
```

The function takes four parameters:

* `domainId`: The target domain ID.
* `rpc`: The RPC endpoint for a given domain.
* `token0`: The first token address.
* `token1`: The second token address.

The function returns a `Promise` that resolves to a string representing the poolFee of the UniV3 pool.

**Example**

```ts
// asset address
const POLYGON_WETH = "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619";
const POLYGON_USDC = "0x2791bca1f2de4661ed88a30c99a7a9449aa84174";
// Domain details
const POLYGON_DOMAIN_ID = "1886350457";
const POLYGON_RPC_URL = "https://polygon.llamarpc.com";

const poolFee = await getPoolFeeForUniV3(POLYGON_DOMAIN_ID, POLYGON_RPC_URL, POLYGON_WETH, POLYGON_USDC);
```

### 2) Encode the forward call

This step depends on the target function. In our `Greeter` example, the encoded calldata is quite simple:

```typescript
const forwardCallData = utils.defaultAbiCoder.encode(
  ["address", "string"],
  [POLYGON_WETH, "hello world"],
);
```

### 3) Construct the xcall

The `getXCallCallData` function generates calldata to be passed into `xcall`. This combines the destination swap and the forward call.

```ts
export const getXCallCallData = async (
  domainId: string,
  swapper: Swapper,
  forwardCallData: string,
  params: DestinationCallDataParams,
)
```

It takes four parameters.

* `domainId`: A string representing the destination domain ID.
* `swapper`: A string representing which swapper should be used. It can be `UniV2`, `UniV3`, or `OneInch`.
* `forwardCallData`: encoded data for passing into the target contract using `abiencoder`.
* `params`: An object containing the following fields.

  ```ts
  {
    fallback: string;
    swapForwarderData: {
      toAsset: string;
      swapData: {
        amountOutMin: string;
      } | {
        amountOutMin: string;
        poolFee: string;
      };
      forwardCallData: {
        cTokenAddress: string;
        underlying: string;
        minter: string;
      } | {} | {};
    }
  }
  ```
* `fallback`: The fallback address to send funds to if the forward call fails on the destination domain.
* `swapForwarderData`: An object with the following fields.
  * `toAsset`: Address of the token to swap into on the destination domain.
  * `swapData`: Calldata that the swapper contract on the destination domain will use to perform the swap.
  * `forwardCallData`: Calldata that the xReceive target on the destination domain will use in the forward call.

The function returns the encoded calldata as a string.

**Example**

```ts
const params: DestinationCallDataParams = {
  fallback: USER_ADDRESS as `0x${string}`,
  swapForwarderData: {
    toAsset: POLYGON_WETH,
    swapData: {
      amountOutMin: "0",
      poolFee,
    },
  },
};

const xCallData = await connextService.getXCallCallDataHelper(
  destinationDomain,
  forwardCallData,
  params,
);
```

### 4) Prepare swap and xcall

The `prepareSwapAndXCall` function constructs the `TransactionRequest` that contains the origin swap and `xcall`.

```ts
export const prepareSwapAndXCall = async (
  signerAddress: string,
  params: SwapAndXCallParams,
):
```

It takes two parameters:

* `signerAddress` (required): The address of the signer to send a transaction from.
* `params`: An object containing the following fields:
  * `originDomain` (required): The origin domain ID.
  * `destinationDomain` (required): The destination domain ID.
  * `fromAsset` (required): The address of the asset to swap from.
  * `toAsset` (required): The address of the asset to swap to.
  * `amountIn` (required): The number of fromAsset tokens.
  * `to` (required): The address to send the asset and call with the calldata on the destination.
  * `delegate` (optional): The fallback address on the destination domain which defaults to `to`.
  * `slippage` (optional): Maximum acceptable slippage in BPS which defaults to 300. For example, a value of 300 means 3% slippage.
  * `route` (optional): The address of the swapper contract and the data to call the swapper contract with.
  * `callData` (optional): The calldata to execute (can be empty: "0x").
  * `relayerFeeInNativeAsset` (optional): The fee amount in native asset.
  * `relayerFeeInTransactingAsset` (optional): The fee amount in the transacting asset.

The function returns a Promise that resolves to a `TransactionRequest` object to be sent to the RPC provider.

**Example**

```ts
const swapAndXCallParams = {
  originDomain: "1886350457",
  destinationDomain: "6450786",
  fromAsset: OPTIMISM_WETH
  toAsset: OPTIMISM_USDC,
  amountIn: "1000000000000000000"; // 1 WETH
  to: signerAddress,
  relayerFeeInTransactingAsset: "100000", // 0.1 USDC
};

const txRequest = await prepareSwapAndXCall(swapAndXCallParams, signerAddress);
```

### 5) Estimate relayer fee

Relayer fees must be paid by the user initiating `xcall`. The Core SDK exposes an `estimateRelayerFee` function (see [Estimating Fees](#5-estimate-relayer-fee)) that returns an estimate given current gas prices on the origin and destination domains.

### 6) Estimate amount received

Showing an estimate of the final amount to be used in the destination target function can be informative for users. The `getEstimateAmountReceived` function returns this estimate which accounts for slippage from the origin and destination swaps as well as the bridge operation itself.&#x20;


# xGovernance

Coming soon: specifics on how to implement cross-chain governance with Connext!


# Subgraphs

Connext has a GraphQL API Endpoint hosted by [The Graph](https://thegraph.com/docs/about/introduction#what-the-graph-is) called a subgraph for indexing and organizing data from the Connext smart contracts. Subgraph information is serviced by a decentralized group of server operators called Indexers.

This subgraph is can be used to query Connext bridge transactions, transactions statuses and more.

## Mainnet Subgraphs

| Chain               | Subgraph                                                                                               |
| ------------------- | ------------------------------------------------------------------------------------------------------ |
| Ethereum            | [v0-Mainnet](https://thegraph.com/hosted-service/subgraph/connext/amarok-runtime-v0-mainnet)           |
| Optimism            | [v0-Optimism](https://thegraph.com/hosted-service/subgraph/connext/amarok-runtime-v0-optimism)         |
| Arbitrum            | [v0-Arbitrum-One](https://thegraph.com/hosted-service/subgraph/connext/amarok-runtime-v0-arbitrum-one) |
| Polygon             | [v0-Polygon](https://thegraph.com/hosted-service/subgraph/connext/amarok-runtime-v0-polygon)           |
| Binance Smart Chain | [v0-Bnb](https://thegraph.com/hosted-service/subgraph/connext/amarok-runtime-v0-bnb)                   |
| Gnosis              | [v0-Gnosis](https://thegraph.com/hosted-service/subgraph/connext/amarok-runtime-v0-gnosis)             |
| Linea               | [v0-Linea](https://connext.bwarelabs.com/subgraphs/name/connext/amarok-runtime-v0-linea)               |

## Testnet Subgraphs

| Chain            | Subgraph                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Sepolia          | [v0-Sepolia](https://api.thegraph.com/subgraphs/name/connext/runtime-v1-sepolia/graphql?query=%23%0A%23+Welcome+to+The+GraphiQL%0A%23%0A%23+The+GraphiQL+is+an+in-browser+tool+for+writing,+validating,+and%0A%23+testing+GraphQL+queries.%0A%23%0A%23+Type+queries+into+this+side+of+the+screen,+and+you+will+see+intelligent%0A%23+typeaheads+aware+of+the+current+GraphQL+type+schema+and+live+syntax+and%0A%23+validation+errors+highlighted+within+the+text.%0A%23%0A%23+GraphQL+queries+typically+start+with+a+%22%7B%22+character.+Lines+that+start%0A%23+with+a+%23+are+ignored.%0A%23%0A%23+An+example+GraphQL+query+might+look+like:%0A%23%0A%23+++++%7B%0A%23+++++++field\(arg:+%22value%22\)+%7B%0A%23+++++++++subField%0A%23+++++++%7D%0A%23+++++%7D%0A%23%0A%23+Keyboard+shortcuts:%0A%23%0A%23++Prettify+Query:++Shift-Ctrl-P+\(or+press+the+prettify+button+above\)%0A%23%0A%23+++++Merge+Query:++Shift-Ctrl-M+\(or+press+the+merge+button+above\)%0A%23%0A%23+++++++Run+Query:++Ctrl-Enter+\(or+press+the+play+button+above\)%0A%23%0A%23+++Auto+Complete:++Ctrl-Space+\(or+just+start+typing\)%0A%23%0A) |
| Optimism-Sepolia | [v0-Op-Sepolia](https://api.studio.thegraph.com/query/60851/optimism-sepolia/version/latest)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| Arb-Sepolia      | [v0-Arb-Sepolia](https://api.thegraph.com/subgraphs/name/connext/runtime-v1-arb-sepolia)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |

## Helpful Links

[Creating an API Key Video Tutorial](https://www.youtube.com/watch?v=UrfIpm-Vlgs)

[Managing your API Key & Setting your indexer preferences](https://thegraph.com/docs/en/studio/managing-api-keys/)

[Explorer Page](https://thegraph.com/explorer/subgraph?id=DfD1tZSmDtjCGC2LeYEQbVzj9j8kNqKAQEsYL27Vg6Sw\&view=Playground)

[Code repo with Connext's subgraph implementation](https://github.com/connext/monorepo/tree/56a166f3ecb50cc10356dd96c257e2e4d47f29e3/packages/deployments/subgraph/src/amarok-runtime-v0)


# Deployments

## Contract Deployments

A full list of deployed contracts can be found in the [deployments.json](https://github.com/connext/monorepo/blob/main/packages/deployments/contracts/deployments.json) file. This contains deployments for all environments and is difficult to parse through manually. You should only need to reference it for automation or as a source of truth. For convenience, we extracted the important contract addresses and listed them here.

## Mainnet Contracts

### Ethereum

> Domain ID: 6648936

> Chain ID: 1

| Core Contract                                                                                    | Address                                    |
| ------------------------------------------------------------------------------------------------ | ------------------------------------------ |
| [Connext](https://louper.dev/diamond/0x8898B472C54c31894e3B9bb83cEA802a5d0e63C6?network=mainnet) | 0x8898B472C54c31894e3B9bb83cEA802a5d0e63C6 |

| Asset Contract                                                                  | Address                                    | Flavor    |
| ------------------------------------------------------------------------------- | ------------------------------------------ | --------- |
| [NEXT](https://etherscan.io/token/0xFE67A4450907459c3e1FFf623aA927dD4e28c67a)   | 0xFE67A4450907459c3e1FFf623aA927dD4e28c67a | Canonical |
| [USDC](https://etherscan.io/address/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) | 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 | Canonical |
| [WETH](https://etherscan.io/address/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 | Canonical |
| [DAI](https://etherscan.io/address/0x6b175474e89094c44da98b954eedeac495271d0f)  | 0x6B175474E89094C44Da98b954EedeAC495271d0F | Canonical |
| [USDT](https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7) | 0xdAC17F958D2ee523a2206206994597C13D831ec7 | Canonical |

| Peripheral Contract                                                                  | Address                                    | Description    |
| ------------------------------------------------------------------------------------ | ------------------------------------------ | -------------- |
| [Unwrapper](https://etherscan.io/address/0x268682b7D9992aE7e2ca4A8bCc9D9655FB06056F) | 0x268682b7D9992aE7e2ca4A8bCc9D9655FB06056F | WETH Unwrapper |

### Optimism

> Domain ID: 1869640809

> Chain ID: 10

| Core Contract                                                                                     | Address                                    |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------ |
| [Connext](https://louper.dev/diamond/0x8f7492DE823025b4CfaAB1D34c58963F2af5DEDA?network=optimism) | 0x8f7492DE823025b4CfaAB1D34c58963F2af5DEDA |

| Asset Contract                                                                                  | Address                                    | Flavor        |
| ----------------------------------------------------------------------------------------------- | ------------------------------------------ | ------------- |
| [NEXT](https://optimistic.etherscan.io/address/0x58b9cB810A68a7f3e1E4f8Cb45D1B9B3c79705E8#code) | 0x58b9cB810A68a7f3e1E4f8Cb45D1B9B3c79705E8 | Local/Adopted |
| [nextUSDC](https://optimistic.etherscan.io/address/0x67E51f46e8e14D4E4cab9dF48c59ad8F512486DD)  | 0x67E51f46e8e14D4E4cab9dF48c59ad8F512486DD | Local         |
| [USDC](https://optimistic.etherscan.io/address/0x7F5c764cBc14f9669B88837ca1490cCa17c31607)      | 0x7F5c764cBc14f9669B88837ca1490cCa17c31607 | Adopted       |
| [nextWETH](https://optimistic.etherscan.io/address/0xbAD5B3c68F855EaEcE68203312Fd88AD3D365e50)  | 0xbAD5B3c68F855EaEcE68203312Fd88AD3D365e50 | Local         |
| [WETH](https://optimistic.etherscan.io/address/0x4200000000000000000000000000000000000006)      | 0x4200000000000000000000000000000000000006 | Adopted       |
| [nextDAI](https://optimistic.etherscan.io/address/0xd64Bd028b560bbFc732eA18f282c64B86F3468e0)   | 0xd64Bd028b560bbFc732eA18f282c64B86F3468e0 | Local         |
| [DAI](https://optimistic.etherscan.io/address/0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1)       | 0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1 | Adopted       |
| [nextUSDT](https://optimistic.etherscan.io/address/0x4cBB28FA12264cD8E87C62F4E1d9f5955Ce67D20)  | 0x4cBB28FA12264cD8E87C62F4E1d9f5955Ce67D20 | Local         |
| [USDT](https://optimistic.etherscan.io/address/0x94b008aA00579c1307B0EF2c499aD98a8ce58e58)      | 0x94b008aA00579c1307B0EF2c499aD98a8ce58e58 | Adopted       |

| Peripheral Contract                                                                             | Address                                    | Description    |
| ----------------------------------------------------------------------------------------------- | ------------------------------------------ | -------------- |
| [Unwrapper](https://optimistic.etherscan.io/address/0x7Fe09d217d646a6213e51b237670Bc326188cB93) | 0x7Fe09d217d646a6213e51b237670Bc326188cB93 | WETH Unwrapper |

### Polygon

> Domain ID: 1886350457

> Chain ID: 137

| Core Contract                                                                                    | Address                                    |
| ------------------------------------------------------------------------------------------------ | ------------------------------------------ |
| [Connext](https://louper.dev/diamond/0x11984dc4465481512eb5b777E44061C158CF2259?network=polygon) | 0x11984dc4465481512eb5b777E44061C158CF2259 |

| Asset Contract                                                                         | Address                                    | Flavor        |
| -------------------------------------------------------------------------------------- | ------------------------------------------ | ------------- |
| [NEXT](https://polygonscan.com/address/0x58b9cB810A68a7f3e1E4f8Cb45D1B9B3c79705E8)     | 0x58b9cB810A68a7f3e1E4f8Cb45D1B9B3c79705E8 | Local/Adopted |
| [nextUSDC](https://polygonscan.com/address/0xF96C6d2537e1af1a9503852eB2A4AF264272a5B6) | 0xF96C6d2537e1af1a9503852eB2A4AF264272a5B6 | Local         |
| [USDC](https://polygonscan.com/address/0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174)     | 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174 | Adopted       |
| [nextWETH](https://polygonscan.com/address/0x4b8BaC8Dd1CAA52E32C07755c17eFadeD6A0bbD0) | 0x4b8BaC8Dd1CAA52E32C07755c17eFadeD6A0bbD0 | Local         |
| [WETH](https://polygonscan.com/address/0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619)     | 0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619 | Adopted       |
| [nextDAI](https://polygonscan.com/address/0xaDCe87b14d570665222C1172D18a221BF7690d5a)  | 0xaDCe87b14d570665222C1172D18a221BF7690d5a | Local         |
| [DAI](https://polygonscan.com/address/0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063)      | 0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063 | Adopted       |
| [nextUSDT](https://polygonscan.com/address/0xE221C5A2a8348f12dcb2b0e88693522EbAD2690f) | 0xE221C5A2a8348f12dcb2b0e88693522EbAD2690f | Local         |
| [USDT](https://polygonscan.com/address/0xc2132D05D31c914a87C6611C10748AEb04B58e8F)     | 0xc2132D05D31c914a87C6611C10748AEb04B58e8F | Adopted       |

| Peripheral Contract                                                                     | Address                                    | Description    |
| --------------------------------------------------------------------------------------- | ------------------------------------------ | -------------- |
| [Unwrapper](https://polygonscan.com/address/0x7E8F8B2dA3dc5Ad9c9Dfd1A832331A039d4f3f74) | 0x7E8F8B2dA3dc5Ad9c9Dfd1A832331A039d4f3f74 | WETH Unwrapper |

### Arbitrum One

> Domain ID: 1634886255

> Chain ID: 42161

| Core Contract                                                                                     | Address                                    |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------ |
| [Connext](https://louper.dev/diamond/0xEE9deC2712cCE65174B561151701Bf54b99C24C8?network=arbitrum) | 0xEE9deC2712cCE65174B561151701Bf54b99C24C8 |

| Asset Contract                                                                     | Address                                    | Flavor        |
| ---------------------------------------------------------------------------------- | ------------------------------------------ | ------------- |
| [NEXT](https://arbiscan.io/address/0x58b9cB810A68a7f3e1E4f8Cb45D1B9B3c79705E8)     | 0x58b9cB810A68a7f3e1E4f8Cb45D1B9B3c79705E8 | Local/Adopted |
| [nextUSDC](https://arbiscan.io/address/0x8c556cF37faa0eeDAC7aE665f1Bb0FbD4b2eae36) | 0x8c556cF37faa0eeDAC7aE665f1Bb0FbD4b2eae36 | Local         |
| [USDC](https://arbiscan.io/address/0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8)     | 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8 | Adopted       |
| [nextWETH](https://arbiscan.io/address/0x2983bf5c334743Aa6657AD70A55041d720d225dB) | 0x2983bf5c334743Aa6657AD70A55041d720d225dB | Local         |
| [WETH](https://arbiscan.io/address/0x82aF49447D8a07e3bd95BD0d56f35241523fBab1)     | 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1 | Adopted       |
| [nextDAI](https://arbiscan.io/address/0xfDe99b3B3fbB69553D7DaE105EF34Ba4FE971190)  | 0xfDe99b3B3fbB69553D7DaE105EF34Ba4FE971190 | Local         |
| [DAI](https://arbiscan.io/address/0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1)      | 0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1 | Adopted       |
| [nextUSDT](https://arbiscan.io/address/0x2fD7E61033b3904c65AA9A9B83DCd344Fa19Ffd2) | 0x2fD7E61033b3904c65AA9A9B83DCd344Fa19Ffd2 | Local         |
| [USDT](https://arbiscan.io/address/0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9)     | 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9 | Adopted       |

| Peripheral Contract                                                                 | Address                                    | Description    |
| ----------------------------------------------------------------------------------- | ------------------------------------------ | -------------- |
| [Unwrapper](https://arbiscan.io/address/0x429b9eb01362b2799131EfCC44319689b662999D) | 0x429b9eb01362b2799131EfCC44319689b662999D | WETH Unwrapper |

### Binance Smart Chain

> Domain ID: 6450786

> Chain ID: 56

| Core Contract                                                                                    | Address                                    |
| ------------------------------------------------------------------------------------------------ | ------------------------------------------ |
| [Connext](https://louper.dev/diamond/0xCd401c10afa37d641d2F594852DA94C700e4F2CE?network=binance) | 0xCd401c10afa37d641d2F594852DA94C700e4F2CE |

| Asset Contract                                                                     | Address                                    | Flavor        |
| ---------------------------------------------------------------------------------- | ------------------------------------------ | ------------- |
| [NEXT](https://bscscan.com/address/0x58b9cB810A68a7f3e1E4f8Cb45D1B9B3c79705E8)     | 0x58b9cB810A68a7f3e1E4f8Cb45D1B9B3c79705E8 | Local/Adopted |
| [nextUSDC](https://bscscan.com/address/0x5e7D83dA751F4C9694b13aF351B30aC108f32C38) | 0x5e7D83dA751F4C9694b13aF351B30aC108f32C38 | Local         |
| [USDC](https://bscscan.com/address/0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d)     | 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d | Adopted       |
| [nextWETH](https://bscscan.com/address/0xA9CB51C666D2AF451d87442Be50747B31BB7d805) | 0xA9CB51C666D2AF451d87442Be50747B31BB7d805 | Local         |
| [WETH](https://bscscan.com/address/0x2170Ed0880ac9A755fd29B2688956BD959F933F8)     | 0x2170Ed0880ac9A755fd29B2688956BD959F933F8 | Adopted       |
| [nextDAI](https://bscscan.com/address/0x86a343BCF17D79C475d300eed35F0145F137D0c9)  | 0x86a343BCF17D79C475d300eed35F0145F137D0c9 | Local         |
| [DAI](https://bscscan.com/address/0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3)      | 0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3 | Adopted       |
| [nextUSDT](https://bscscan.com/address/0xD609f26B5547d5E31562B29150769Cb7c774B97a) | 0xD609f26B5547d5E31562B29150769Cb7c774B97a | Local         |
| [USDT](https://bscscan.com/address/0x55d398326f99059fF775485246999027B3197955)     | 0x55d398326f99059fF775485246999027B3197955 | Adopted       |

| Peripheral Contract                                                                 | Address                                    | Description    |
| ----------------------------------------------------------------------------------- | ------------------------------------------ | -------------- |
| [Unwrapper](https://bscscan.com/address/0x2c7B8c1a13F2a7854B9299E4d22809A8B1E05De5) | 0x2c7B8c1a13F2a7854B9299E4d22809A8B1E05De5 | WETH Unwrapper |

### Gnosis

> Domain ID: 6778479

> Chain ID: 100

| Core Contract                                                                                 | Address                                    |
| --------------------------------------------------------------------------------------------- | ------------------------------------------ |
| [Connext](https://louper.dev/diamond/0x5bB83e95f63217CDa6aE3D181BA580Ef377D2109?network=xdai) | 0x5bB83e95f63217CDa6aE3D181BA580Ef377D2109 |

| Asset Contract                                                                       | Address                                    | Flavor        |
| ------------------------------------------------------------------------------------ | ------------------------------------------ | ------------- |
| [NEXT](https://gnosisscan.io/address/0x58b9cb810a68a7f3e1e4f8cb45d1b9b3c79705e8)     | 0x58b9cB810A68a7f3e1E4f8Cb45D1B9B3c79705E8 | Local/Adopted |
| [nextUSDC](https://gnosisscan.io/address/0x44CF74238d840a5fEBB0eAa089D05b763B73faB8) | 0x44CF74238d840a5fEBB0eAa089D05b763B73faB8 | Local         |
| [USDC](https://gnosisscan.io/address/0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83)     | 0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83 | Adopted       |
| [nextWETH](https://gnosisscan.io/address/0x538E2dDbfDf476D24cCb1477A518A82C9EA81326) | 0x538E2dDbfDf476D24cCb1477A518A82C9EA81326 | Local         |
| [WETH](https://gnosisscan.io/address/0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1)     | 0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1 | Adopted       |
| [nextDAI](https://gnosisscan.io/address/0x0e1D5Bcd2Ac5CF2f71841A9667afC1E995CaAf4F)  | 0x0e1D5Bcd2Ac5CF2f71841A9667afC1E995CaAf4F | Local         |
| [DAI](https://gnosisscan.io/address/0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d)      | 0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d | Adopted       |
| [nextUSDT](https://gnosisscan.io/address/0xF4d944883D6FddC56d3534986feF82105CaDbfA1) | 0xF4d944883D6FddC56d3534986feF82105CaDbfA1 | Local         |
| [USDT](https://gnosisscan.io/address/0x4ECaBa5870353805a9F068101A40E0f32ed605C6)     | 0x4ECaBa5870353805a9F068101A40E0f32ed605C6 | Adopted       |

| Peripheral Contract                                                                   | Address                                    | Description    |
| ------------------------------------------------------------------------------------- | ------------------------------------------ | -------------- |
| [Unwrapper](https://gnosisscan.io/address/0x642c27a96dFFB6f21443A89b789a3194Ff8399fa) | 0x642c27a96dFFB6f21443A89b789a3194Ff8399fa | WETH Unwrapper |

### Linea

> Domain ID: 1818848877

> Chain ID: 59144

| Core Contract                                                                         | Address                                    |
| ------------------------------------------------------------------------------------- | ------------------------------------------ |
| [Connext](https://lineascan.build/address/0xa05eF29e9aC8C75c530c2795Fa6A800e188dE0a9) | 0xa05eF29e9aC8C75c530c2795Fa6A800e188dE0a9 |

<table><thead><tr><th>Asset Contract</th><th width="251.33333333333331">Address</th><th>Flavor</th></tr></thead><tbody><tr><td><a href="https://lineascan.build/address/0x331152ca43B50B39F3a9f203685B98dbb9b42342">nextUSDC</a></td><td>0x331152ca43B50B39F3a9f203685B98dbb9b42342</td><td>Local</td></tr><tr><td><a href="https://lineascan.build/address/0x176211869cA2b568f2A7D4EE941E073a821EE1ff">USDC</a></td><td>0x176211869cA2b568f2A7D4EE941E073a821EE1ff</td><td>Adopted</td></tr><tr><td><a href="https://lineascan.build/address/0x0573AD07cA4f74757e5B2417Bf225BEbeBcF66D9">nextWETH</a></td><td>0x0573AD07cA4f74757e5B2417Bf225BEbeBcF66D9</td><td>Local</td></tr><tr><td><a href="https://lineascan.build/address/0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f">WETH</a></td><td>0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f</td><td>Adopted</td></tr><tr><td><a href="https://lineascan.build/address/0x7360a597290612787833EE924C449C61Cc0689E4">nextDAI</a></td><td>0x7360a597290612787833EE924C449C61Cc0689E4</td><td>Local</td></tr><tr><td><a href="https://lineascan.build/address/0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5">DAI</a></td><td>0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5</td><td>Adopted</td></tr><tr><td><a href="https://lineascan.build/address/0xbD7eAEd30936670C931B718F5D9014AFf82fC767">nextUSDT</a></td><td>0xbD7eAEd30936670C931B718F5D9014AFf82fC767</td><td>Local</td></tr><tr><td><a href="https://lineascan.build/address/0xA219439258ca9da29E9Cc4cE5596924745e12B93">USDT</a></td><td>0xA219439258ca9da29E9Cc4cE5596924745e12B93</td><td>Adopted</td></tr></tbody></table>

| Peripheral Contract                                                                     | Address                                    | Description    |
| --------------------------------------------------------------------------------------- | ------------------------------------------ | -------------- |
| [Unwrapper](https://lineascan.build/address/0x5A53576DDE5071719a9A3a9E78e68cbcDf863253) | 0x5A53576DDE5071719a9A3a9E78e68cbcDf863253 | WETH Unwrapper |

### Base

> Domain ID: 1650553709

> Chain ID: 8453

| Core Contract                                                                      | Address                                    |
| ---------------------------------------------------------------------------------- | ------------------------------------------ |
| [Connext](https://basescan.org/address/0xB8448C6f7f7887D36DcA487370778e419e9ebE3F) | 0xB8448C6f7f7887D36DcA487370778e419e9ebE3F |

<table><thead><tr><th>Asset Contract</th><th width="261.3333333333333">Address</th><th>Flavor</th></tr></thead><tbody><tr><td><a href="https://basescan.org/address/0xE08D4907b2C7aa5458aC86596b6D17B1feA03F7E">nextWETH</a></td><td>0xE08D4907b2C7aa5458aC86596b6D17B1feA03F7E</td><td>Local</td></tr><tr><td><a href="https://basescan.org/address/0x4200000000000000000000000000000000000006">WETH</a></td><td>0x4200000000000000000000000000000000000006</td><td>Adopted</td></tr><tr><td><a href="https://basescan.org/address/0xC90a82e926d3a87899b3717aba0262BF66Ef53E8">nextDAI</a></td><td>0xC90a82e926d3a87899b3717aba0262BF66Ef53E8</td><td>Local</td></tr><tr><td><a href="https://basescan.org/address/0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb">DAI</a></td><td>0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb</td><td>Adopted</td></tr><tr><td><a href="https://basescan.org/address/0x1ede59e0d39B14c038698B1036BDE9a4819C86D4">nextUSDC</a></td><td>0x1ede59e0d39B14c038698B1036BDE9a4819C86D4</td><td>Local</td></tr><tr><td><a href="https://basescan.org/address/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913">USDC</a></td><td>0x833589fcd6edb6e08f4c7c32d4f71b54bda02913</td><td>Adopted</td></tr></tbody></table>

<table><thead><tr><th>Peripheral Contract</th><th width="254.33333333333331">Address</th><th>Description</th></tr></thead><tbody><tr><td><a href="https://basescan.org/address/0x01EdE4Fdf8CF7Ef9942a935305C3145f8dAa180A">Unwrapper</a></td><td>0x01EdE4Fdf8CF7Ef9942a935305C3145f8dAa180A</td><td>WETH Unwrapper</td></tr></tbody></table>

### Metis

> Domain ID: 1835365481

> Chain ID: 1088

| Core Contract                                                                                     | Address                                    |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------ |
| [Connext](https://andromeda-explorer.metis.io/address/0x6B142227A277CE62808E0Df93202483547Ec0188) | 0x6B142227A277CE62808E0Df93202483547Ec0188 |

<table><thead><tr><th>Asset Contract</th><th width="261.3333333333333">Address</th><th>Flavor</th></tr></thead><tbody><tr><td><a href="https://andromeda-explorer.metis.io/address/0x3883B5Bdd61BA1b687de69eE50c9738D5ec501E9">nextWETH</a></td><td>0x3883B5Bdd61BA1b687de69eE50c9738D5ec501E9</td><td>Local</td></tr><tr><td><a href="https://andromeda-explorer.metis.io/address/0x420000000000000000000000000000000000000A">WETH</a></td><td>0x420000000000000000000000000000000000000a</td><td>Adopted</td></tr><tr><td><a href="https://andromeda-explorer.metis.io/address/0xa6A8d22D5da43C9f6E5cF7b4e50941784e70F688">nextUSDT</a></td><td>0xa6A8d22D5da43C9f6E5cF7b4e50941784e70F688</td><td>Local</td></tr><tr><td><a href="https://andromeda-explorer.metis.io/address/0xbB06DCA3AE6887fAbF931640f67cab3e3a16F4dC">USDT</a></td><td>0xbB06DCA3AE6887fAbF931640f67cab3e3a16F4dC</td><td>Adopted</td></tr><tr><td><a href="https://andromeda-explorer.metis.io/address/0x9ac9aD5A82Ccd0Ab7584a037A7A2334Dc3715Be2">nextUSDC</a></td><td>0x9ac9aD5A82Ccd0Ab7584a037A7A2334Dc3715Be2</td><td>Local</td></tr><tr><td><a href="https://andromeda-explorer.metis.io/address/0xEA32A96608495e54156Ae48931A7c20f0dcc1a21">USDC</a></td><td>0xEA32A96608495e54156Ae48931A7c20f0dcc1a21</td><td>Adopted</td></tr></tbody></table>

<table><thead><tr><th>Peripheral Contract</th><th width="254.33333333333331">Address</th><th>Description</th></tr></thead><tbody><tr><td><a href="https://andromeda-explorer.metis.io/address/0x8Ef3E1300857FDF616dfE2fcBced4ac6a61Fe774">Unwrapper</a></td><td>0x8Ef3E1300857FDF616dfE2fcBced4ac6a61Fe774</td><td>WETH Unwrapper</td></tr></tbody></table>

### Mode

> Domain ID: 1836016741

> Chain ID: 34443

| Core Contract                                                                               | Address                                    |
| ------------------------------------------------------------------------------------------- | ------------------------------------------ |
| [Connext](https://explorer.mode.network/address/0x7380511493DD4c2f1dD75E9CCe5bD52C787D4B51) | 0x7380511493DD4c2f1dD75E9CCe5bD52C787D4B51 |

<table><thead><tr><th>Asset Contract</th><th width="261.3333333333333">Address</th><th>Flavor</th></tr></thead><tbody><tr><td><a href="https://explorer.mode.network/address/0x609aEfb9FB2Ee8f2FDAd5dc48efb8fA4EE0e80fB">nextWETH</a></td><td>0x609aEfb9FB2Ee8f2FDAd5dc48efb8fA4EE0e80fB</td><td>Local</td></tr><tr><td><a href="https://explorer.mode.network/address/0x4200000000000000000000000000000000000006">WETH</a></td><td>0x4200000000000000000000000000000000000006</td><td>Adopted</td></tr></tbody></table>

<table><thead><tr><th>Peripheral Contract</th><th width="254.33333333333331">Address</th><th>Description</th></tr></thead><tbody><tr><td><a href="https://explorer.mode.network/address/0x2c8DA9c3166085acebc70Ad9491cA2bfa10C8b9F">Unwrapper</a></td><td>0x2c8DA9c3166085acebc70Ad9491cA2bfa10C8b9F</td><td>WETH Unwrapper</td></tr></tbody></table>

### XLayer

> Domain ID: 2020368761

> Chain ID: 196

| Core Contract                                                                               | Address                                    |
| ------------------------------------------------------------------------------------------- | ------------------------------------------ |
| [Connext](https://explorer.mode.network/address/0x7380511493DD4c2f1dD75E9CCe5bD52C787D4B51) | 0x63A4fdD5184c6cCDF9c8e550c02bC815b687d7aC |

<table><thead><tr><th>Asset Contract</th><th width="261.3333333333333">Address</th><th>Flavor</th></tr></thead><tbody><tr><td><a href="https://explorer.mode.network/address/0x609aEfb9FB2Ee8f2FDAd5dc48efb8fA4EE0e80fB">nextWETH</a></td><td>0x705c53246a116b4b10dac9ea429041ef2610a783</td><td>Local</td></tr><tr><td><a href="https://explorer.mode.network/address/0x4200000000000000000000000000000000000006">WETH</a></td><td>0x5a77f1443d16ee5761d310e38b62f77f726bc71c</td><td>Adopted</td></tr></tbody></table>

<table><thead><tr><th>Peripheral Contract</th><th width="254.33333333333331">Address</th><th>Description</th></tr></thead><tbody><tr><td><a href="https://explorer.mode.network/address/0x2c8DA9c3166085acebc70Ad9491cA2bfa10C8b9F">Unwrapper</a></td><td>0xaE3a0b1C17504A193e6137bba2d063b1798049A5</td><td>WETH Unwrapper</td></tr></tbody></table>

## Testnet Contracts

Note that the Test Token is a mintable ERC20. The open `mint` function has the signature `mint(address account, uint256 amount)` and can be freely called.

### Sepolia

> Domain ID: 1936027759

> Chain ID: 11155111

| Core Contract | Address                                    |
| ------------- | ------------------------------------------ |
| Connext       | 0x445fbf9cCbaf7d557fd771d56937E94397f43965 |

| Asset Contract | Address                                    | Flavor    |
| -------------- | ------------------------------------------ | --------- |
| TEST           | 0xd26e3540A0A368845B234736A0700E0a5A821bBA | Canonical |

### Optimism-Sepolia

> Domain ID: 1869640549

> Chain ID: 11155420

| Core Contract | Address                                    |
| ------------- | ------------------------------------------ |
| Connext       | 0x8247ed6d0a344eeae4edBC7e44572F1B70ECA82A |

| Asset Contract | Address                                    | Flavor    |
| -------------- | ------------------------------------------ | --------- |
| TEST           | 0x7Fa13D6CB44164ea09dF8BCc673A8849092D435b | Canonical |

### Arbitrum-Sepolia

> Domain ID: 1633842021

> Chain ID: 421614

| Core Contract | Address                                    |
| ------------- | ------------------------------------------ |
| Connext       | 0x1780Ac087Cbe84CA8feb75C0Fb61878971175eb8 |

| Asset Contract | Address                                    | Flavor    |
| -------------- | ------------------------------------------ | --------- |
| TEST           | 0xaBF282c88DeD3e386701a322e76456c062468Ac2 | Canonical |

### X1-Testnet

> Domain ID: 2016506996

> Chain ID: 195

| Core Contract | Address                                    |
| ------------- | ------------------------------------------ |
| Connext       | 0xDB8310cAa57B052ab270A573B512dc5644558f0A |

| Asset Contract | Address                                    | Flavor    |
| -------------- | ------------------------------------------ | --------- |
| TEST           | 0x471F702E7D96E541488140042bCD1206Ae55CCa5 | Canonical |

## User Interfaces

### Bridge UI

A bridge UI where users can transfer assets across domains. Here you can also mint `TEST` tokens with the faucet.

[https://testnet.bridge.connext.network](https://testnet.bridge.connext.network/)

### Connextscan

This is the testnet scanner site where you can track the status of transfers by `transferId`.

[https://testnet.connextscan.io](https://testnet.connextscan.io/)


# Basics

Routers are the active liquidity providers & nodes of the Connext network.

## How do routers work?

1. Before becoming active in the network, routers provide liquidity on each chain and for each asset they want to support. This liquidity is denominated in `nextAssets` - a Connext-specific unit-of-account that acts as an IOU of locked funds on Ethereum L1.
2. Routers observe all chains in the network. For `xcall`s involving their supported chains & assets, routers simulate the transaction on destination, create a **bid** (a signed transaction that executes the destination chain interaction), and submit that bid to the Connext **sequencer** .
3. The sequencer waits a fixed period of time to collect bids from routers and then randomly selects from among them. For every batch of transactions, the sequencer will send a corresponding batch of winning bids to a relayer network (e.g. Gelato) to submit the transaction to the destination chain.
4. For router transactions that are submitted by the sequencer immediately (see fast path), the router effectively *fronts* the transaction funds and calldata on the destination, being repaid by the protocol after the slow path completes *if they submitted the transaction with the parameters provided in the origin chain `xcall`*.

## Risks

Routers are largely designed to be as passive and safe for operators as possible. However, there are some risks to be aware of:

1. **Hot wallet:** Routers are effectively a "hot wallet" of funds owned by the router operator that can unilaterally spend owned funds in the protocol. This means that proper key management practices are a must for routers that want to operate in production.
2. **Misconfigured environments:** Router operators should also ensure that they are careful to not expose the router's private API as part of setting up their enviroment.
3. **Protocol security**: As with any protocol, router operators are ultimately exposed to the risk of Connext's underlying implementation. While this risk is never 0, Connext follows best practices for [auditing](https://github.com/connext/audits), [security bounties](https://immunefi.com/bounty/connext/), and operational practices to keep routers safe.

Refer to security.md and [router community call](https://www.youtube.com/watch?v=rjNcdm1mjCQ) for best practices to mitigate these risks.

## Business Model

The router’s primary business model is to earn transaction fees for providing liquidity and relaying data across chains.

Routers earn a fee of 5 basis points (0.05%) on all liquidity that is provided for a user transaction. Router liquidity is then subsequently locked up until it can be claimed against the slow path. In effect, this is as if the router is giving a protocol-level loan to the user for a period of up to 2 hours. In this model, router ROI scales with user demand - routers earn the highest returns if a high percentage of their capital is frequently locked up.

Routers currently do not take a fee for relaying data itself. There are future plans to implement an EIP-1559-style tip, that can supplement router income for data-only transactions.


# Spinning Up

### Checklist for Running a Router

* Spin up the router and configure for testnets.
* Provide liquidity and gas fees on testnets.
* Test the router on testnets.
* Change configuration to mainnets (use a different private key!), or spin up a new mainnet router.
* Provide liquidity on mainnets.
* Monitor router logs.

***

### :warning: Requirements

> **Minimum Hardware Requirements**\
> :black\_square\_button: 8GB RAM\
> :black\_square\_button: 30GB Storage<br>

### Preparation

1. **Private key of your wallet** from Metamask.\
   For safety reason create a new wallet address for router. You can create it in Metamask extention or get it automatically during installation.<br>
2. **Setup provider endpoints.** You have to add it to `config.json` file to use your own endpoints. For that we will use the nodes provided by the service [Infura](https://infura.io/).

> You can use also [blastapi.io](https://blastapi.io) as RPC privider to get endpoints for almost any network ([the guide how to get it](https://medium.com/@alexzhurba/adding-rpcs-for-connext-36094191ae4f)). Many other RPC provider services exist as well.

2.1 Register at [infura.io](https://infura.io/) and create new project:

![screenshot](https://user-images.githubusercontent.com/88688304/170812549-0cc07f55-abae-4ad4-9ede-6a9ba7d812ce.png) ![screenshot](https://user-images.githubusercontent.com/88688304/170812576-f7d57b0f-b455-4cab-b6fb-8cdf48f148b8.png)

2.2 Open settings:

![screenshot](https://user-images.githubusercontent.com/88688304/170812595-66f5557e-8fc3-42c8-a08e-82ff270bcab2.png)

2.3 And copy your project ID. It will be the same on any network

![screenshot](https://user-images.githubusercontent.com/88688304/170812613-de163f51-3cd6-4a47-aeda-680d812e3b53.png)

**Keep this data handy, you will need it for further installation**

***

### Manual Setup With Docker Compose

Refer to <https://github.com/connext/router-docker-compose> for instructions on spinning up using Docker Compose!

## #Next Steps

* See the management guide for details on router administration.
* See the liquidity guide for details on how to add liquidity to your router.
* See the security guide reference for details on the security for the router.

***

### Useful links

* How to deploy your router using helm — [Guide](https://github.com/connext/router-helm)<br>


# Guides


# Community Guides

## Articles

### English

1. [Running a Router Guide](https://medium.com/@roojthemighty/how-to-spin-up-a-router-on-connext-network-ver-eng-4ff391b05d94) (Digital Ocean)
2. [Running a Router Guide](https://teletype.in/@moodman/s83IWlWwfsm)
3. [Running a Router Guide](https://mirror.xyz/cyberg.eth/vxkEyroJ0vCnAXEuTl36-5UUrYjCf0V6tf59_PPGLQ0)
4. [Running a Router Guide](https://dramatic-fox-ea1.notion.site/Spinning-up-Connext-Router-20591e06bf2149f0b9d41fa6754469c0#6415e09af8454f78b4233ac8fcacac79)
5. [Running a Ruter Guide](https://github.com/louwo/Guide-for-routers)
6. [Adding Liquidity Guide](https://medium.com/@nizeimbaboy.2/how-to-add-liquidity-in-connext-f0f6bfedeabc)

### French

1. [Running a Router Guide](https://mirror.xyz/0x5214F449553f572F30dE3717CaCA29088A386eEb/BJaHlfi2PoMGN349sIZhim-U1_aa79sIacyZV4ON4As)

### Russian

1. [Running a Router Guide](https://7nda.medium.com/%D0%B7%D0%B0%D0%BF%D1%83%D1%81%D0%BA-%D1%80%D0%BE%D1%83%D1%82%D0%B5%D1%80%D0%B0-connext-d6335e7e962e)
2. [Running a Router Guide](https://github.com/cybernekit/RouterSetupGuide)
3. [Running a Router Guide](https://teletype.in/@landeros/zd69DV9Z1lY)
4. [Running a Router Guide](https://medium.com/@alexzhurba/spinning-up-connext-router-fe3260912f0a)

### Thai

1. [Running a Router Guide](https://medium.com/@roojthemighty/how-to-spin-up-a-router-on-connext-network-ver-%E0%B9%84%E0%B8%97%E0%B8%A2-f5405ac3a6dc) (Digital Ocean)
2. [Running a Router Guide](https://medium.com/@nizeimbaboy.2/how-to-run-node-connext-node-v-%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2-6a6cd3406e19) (Digital Ocean)
3. [Running a Router Guide](https://github.com/PS-PSN/Connext/blob/main/Setup%20Routers%20\(Thai\))
4. [Running a Router Guide](https://medium.com/@arsarawutpad/%E0%B8%84%E0%B8%B9%E0%B9%88%E0%B8%A1%E0%B8%B7%E0%B8%AD%E0%B8%81%E0%B8%B2%E0%B8%A3%E0%B8%95%E0%B8%B4%E0%B8%94%E0%B8%95%E0%B8%B1%E0%B9%89%E0%B8%87-connext-router-testnet-17f941252652)
5. [Minting $TEST Tokens](https://medium.com/@roojthemighty/how-to-mint-test-tokens-on-connext-testnet-e14c5fbafafe)
6. [Running a Router and Adding Liquidity Guide](https://docs.google.com/document/d/1mmoVazi3hOC6nAqRJMK1VuKPPomi64zv9oxA0nSncFk/edit?usp=sharing)
7. [Adding Liquidity Guide](https://medium.com/@nizeimbaboy.2/how-to-add-liquidity-in-connext-2655559eea46)
8. [Running a Router Guide](https://medium.com/@airdropmaglionaire/run-connext-router-step-by-step-8c565c082b2e) (Google Cloud)

### Chinese

1. [Running a Router Guide](https://gist.github.com/bynil/ae29155040c0d6c84ddf497a8462d3d6)
2. [Running a Router Guide](https://mirror.xyz/bullcoin.eth/c-ZKFX4_IsyRM-TJgg8KHoMjtm7E8nov_x9EcErzDeo)
3. [Running a Router Guide](https://mirror.xyz/exploring.eth/fwb657xWhr5Q3mvoNes0eYT75yZtE6_hqVgOF3dVZSY)

### Spanish

1. [Running a Router Guide](https://github.com/VArtOff/Connext-Guide/blob/main/Spanish%20Guide.md)

## Videos

### English

1. [Running a Router Guide](https://youtu.be/jwRR45-ycSw)
2. [Running a Router Guide](https://drive.google.com/drive/folders/1y9a3QDr7z0wvxhiauOSePz9ThkspUdFG) (Local Home Computer)
3. [Running a Router Guide](https://www.youtube.com/watch?v=TTbssVrhL2s) (Hetzner)

### Russian

1. [Running a Router Guide](https://www.youtube.com/watch?v=2_OAz9nIls8)

### Thai

1. [Running a Router Guide](https://www.youtube.com/watch?v=ShNRtdV4URA\&t=873s) (Digital Ocean) (Easy to follow)
2. [Running a Router Guide](https://www.youtube.com/watch?v=Tt6zBupfbF0) (Digital Ocean)
3. [Running a Router Guide](https://www.youtube.com/watch?v=Do3z5Ikp5ac)
4. [Adding Liquidity Guide](https://youtu.be/xJ16II1axjU)

### Chinese

1. Running a Router Guide ([Youtube](https://www.youtube.com/watch?v=E-zGm45dWsc) / [Bilibili](https://www.bilibili.com/video/BV1q3411G7pd/))

### Ukraine

1. [Running a Router Guide](https://medium.com/@88vgk88/%D0%BD%D0%B0%D0%BB%D0%B0%D1%88%D1%82%D1%83%D0%B2%D0%B0%D0%BD%D0%BD%D1%8F-%D1%80%D0%BE%D1%83%D1%82%D0%B5%D1%80%D0%B0-connext-3cd0fbdc5596)

## Script

1. [Auto setup and update script](https://github.com/NunoyHaxxana/nxtp-router-setup)
   * Script for install Connext Router with the latest release and auto update function
2. [Shell script to download and update to the latest version](https://github.com/NunoyHaxxana/Router-Connext-Quests-2)
   * Auto install script with automatic private key generation and auto update function

## Tools

1. [Discord alerting system](https://github.com/NunoyHaxxana/Router-Connext-Quests-3)
   * Script for monitoring docker and send alert to Discord


# Providing Liquidity

### How it works

At a high level, routers provide active liquidity with local NextAssets on each destination chain and receive **5bps**/transfer as a fee.

From a router perspective, you can just just monitor your funds and APY.

Where necessary, for end users Connext will swap into and out of local and adopted assets through our AMMs. Below is the most complex version of this, where both chains have local≠adopted.

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

### How can I add active liquidity to Amarok-v2?

💡 Note: At launch routers will be allowlisted, please reach out if you are interested or have questions! The allowlist is temporary during our initial Beta phase.

Using USDC as an example, to provide active liquidity on Amarok you will need to get local nextUSDC on the desired chain.

**Instructions below:**

1. Move USDC to Ethereum mainnet
   1. \[If moving funds from nxtp-v1] Remove desired amount of USDC from chain on nxtp-v1 with [remove-liquidity endpoint](https://github.com/connext/monorepo/blob/1fc5f3d47e146f67957f8a6943cc8888cb392936/packages/router/example.http#L12-L22). \*\*
      1. (Note: If removing ALL funds from v1 router, add `"cleanupMode":"true"` and wait until amount f locked tokens equals 0. We phasing this out and and will closely communicate)
2. Bridge USDC to the desired chain through Connext, specifying to ‘Receive nextAsset‘ in the Bridge UI settings to receive nextUSDC and avoid swaps/slippage. ![](/files/UWBjdsCTlDzIEYwtG6w9)
3. Navigate to the ‘Router’ tab on Connextscan 2.0 and click your router ID ![](/files/Gb6MdeNCrRa0Qc2xxmz2)
4. Click ‘Manage Router, and add funds using our new ‘Add Liquidity’ UI feature ![](/files/DslFaYYA1tZPvhUhWRRg)![](/files/1huSfj1wBvo76WJe0b4r)


# Managing Your Router

Routers have three distinct roles for management:

1. *Router Signer* - The address associated with the private key that is available to the router at runtime to sign payloads which authorize liquidity to be used to facilitate cross-chain transfers. The *Router Signer* also has the ability to remove the router’s liquidity to the designated recipient.

   ⚠️ The \*Router Signer\* key is effectively a hot wallet that must be accessible by an internet-connected router at all times, making it the least secure key in the system. It is important to understand the ramifications of a key compromise and configure the other roles appropriately to mitigate risks.
2. *Recipient* - The address that will receive funds when liquidity is removed. Example secure options are: a DAO treasury, a secure multisig, a custodial centralized exchange account which you trust. Since this is only a receiver address, it is not required to be able to sign and send transactions (i.e. in the custodial case).

   ℹ️ There is an option to \*not set\* the recipient by setting it to \`address(0)\`. This will allow the \`removeRouterLiquidity\` functions to specify a recipient of their choice. This is a security risk and \*\*not recommended\*\* unless you are fully aware of the risks and have specific needs to do this.
3. *Router Owner* - The address that is able to set and change the *Recipient* address, as well as change itself to a different *Router Owner* address. The *Router Owner* can also remove the router’s liquidity to the designated recipient. This address should be a secure multisig, since it needs to be able to sign transactions to carry out the functionality.

   ℹ️ Router Ownership can be \*burned\* (set to \`address(0)\`). This is of course the most secure way to manage a router, but the implications are you can never change the designated recipient.

## Initialize a Router

Use this section if the router has never been initialized.

### Using the UI

Coming soon!

### Using the Smart Contract Directly

1. Use the Connext repo to find the deployments for the chain you want to set up the router on:

[monorepo/packages/deployments/contracts/deployments at main · connext/monorepo](https://github.com/connext/monorepo/tree/main/packages/deployments/contracts/deployments)

1. Find the `Connext.json` deployment for that chain and copy the address.
2. Go to the website [louper.dev](https://louper.dev/) and enter the Connext contract address and choose the correct network.
3. Scroll to the `RoutersFacet` section and select the option for `WRITE` and connect with the *Router Signer* account.
4. Select the method `initializeRouter` and fill in the details for `_owner` and `_recipient` and execute the transaction.
5. Notify the Connext team to allowlist your router (this is still a permissioned role!).

ℹ️ Note: This is the easiest way to call the contract function but since it is an onchain function it can be called from any mechanism to send the correct function with the appropriate arguments.

## Modify Router Roles

Use these instructions to modify a router that has already been initialized.

### Using the UI

Coming soon!

### Using the Smart Contract Directly

1. Use the [above instructions](https://www.notion.so/Router-Management-b7deba54c150424c978aa6abd4fec7f6) to connect the *Router Owner* wallet to the Connext contract.
2. Use the functions `setRouterRecipient`, `proposeRouterOwner`, and `acceptProposedRouterOwner` on the `RoutersFacet` to modify the params as necessary.

## Remove Liquidity

### Using the UI

1. Navigate to the ‘Router’ tab on Connextscan 2.0 and click your router ID ![](/files/Gb6MdeNCrRa0Qc2xxmz2)
2. Click ‘Manage Router, and remove funds using our ’Remove Liquidity’ UI feature ![](/files/DslFaYYA1tZPvhUhWRRg) ![](/files/1177FcVxNFOywYYcsjKF)

### Using the Smart Contract Directly

1. Use the [above instructions](https://www.notion.so/Router-Management-b7deba54c150424c978aa6abd4fec7f6) to connect the *Router Owner* wallet to the Connext contract.
2. Find the canonical `TokenId` by calling the `adoptedToCanonical` function on the `TokenFacet` and filling in the `_adopted` with the address of the token contract of the “adopted asset” (i.e. USDC).
3. With this information, call the `removeRouterLiquidity` function.


# Security

Being mindful of security is an essential part of operating any Web3 infrastructure, and running a Connext Router is no exception. Here are some things to be aware of: /

## Miscellaneous

#### Setting Recipient and Owner Addresses

<figure><img src="/files/1PEgVymHUx7Ivx29ZvgN" alt=""><figcaption></figcaption></figure>

In addition to its own signing address, each Router has a **Recipient** and **Owner** address set in the respective `Connext Handler` contracts of each chain.

* `Recipient`: Whenever you remove liquidity from the Router, the funds will always be sent to the **Recipient** address. If your Router were somehow compromised, at best the attacker would only be able to withdraw your funds to the Recipient address. We highly advise using a separate hardware wallet for this address.
* `Owner`: Only the **Owner** has the ability to change the Recipient and Owner addresses. Again, we recommend using a separate hardware wallet for this address.

The Recipient and Owner addresses can be changed by calling the corresponding write methods on the Connext Handler contract from the **Owner** address (`setRouterRecipient` and `proposeRouterOwner`/`acceptProposedRouterOwner`). This needs to be done on each chain supported.

It's strongly recommended that you set your Recipient and Owner addresses to something different from your Router's signing address. Since the Recipient and Owner private keys are not accessed by or stored on the Router, simply compromising your Router would not be enough for an attacker to access your liquidity -- they would also need control of the Recipient and/or Owner wallet.

Please keep in mind that each Router's Recipient and Owner addresses can be publicly queried from the Connext Handler contracts.

#### Protecting Your Admin Token

Each Router has an **Admin Token** -- an authorization string chosen by the operator and set in the `config.json` file.

The Admin Token is used to authenticate requests made to the Router's REST API endpoint and must be kept secret.

If your Router's API endpoint is left exposed to the public and your Admin Token is either compromised or vulnerable to brute forcing, someone could use your token perform unauthorized operations with your Router.

Use a sufficiently long token (50 characters or more) to protect against brute force attacks. You can easily generate a secure token using `pwgen` with the following command:

`pwgen -s 50 1`

The Admin Token is stored in plaintext in the Router's `config.json` file. You can follow the method below to load your **config.json** into **tmpfs** before starting the Router, and unmount it after the Router is started. Using this method will require you to generate and move your configuration into **tmpfs** on each Router restart. The old contents will be lost -- so don't forget to backup any important information first (eg: your wallet mnemonic!)

1. Create **tmpfs**:\
   `mount -t tmpfs -o size=100m tmpfs /mnt/tmpfs`
2. Move config file to **tmpfs**:\
   `mv config.json /mnt/tmpfs/config.json`
3. Change the volume point in docker-compose (use type [bind](https://github.com/docker/compose/issues/2781#issuecomment-441653347)):\
   `- /mnt/tmpfs/config.json:/home/node/Router/config.json`
4. Run `docker-compose`
5. Finally, unmount the **tmpfs** dir. After this step all data in **/mnt/tmpfs/** will be lost:\
   `umount /mnt/tmpfs`

#### Docker Overrides Iptables (UFW)

By default, Docker overrides any iptables (UFW) rules. If you're using the Docker Router image and your VM has a direct connection to the Internet, make sure you're not accidentally exposing your Router API endpoint externally.

**Example:**\
If UFW on our Router is configured to block all traffic on the `ROUTER_EXTERNAL_PORT` (port **8000** by default), then we would expect the following to fail from an external machine:

`curl http://x.x.x.x:8000/config`

Instead, we receive a response indicating that the port is open.

`{"signerAddress":"0x26Ad85....."}`

One simple solution to edit the `docker-compose.yml` file to bind the exposed port to **localhost**, by changing:

```
...
  ports:
    - $ROUTER_EXTERNAL_PORT:8080
...
```

to:

```
...
  ports:
    - 127.0.0.1:$ROUTER_EXTERNAL_PORT:8080
...
```

Restart the Docker-Compose stack after making the change. The endpoint should now only be available from the machine running the Router -- try again from an external machine to make sure the change was successful.

## Protecting Your Router's Private Key

Whenever you are joining a crypto project we advise that you should use a brand new wallet each time. You can generate a private key using the following command:\
`openssl rand -hex 32 > private_key.json`

Avoid operating your Router with your private key or mnemonic stored in plaintext. While it's possible to use a mnemonic in `config.json` or a raw key stored unencrypted in a `key.yaml` file, these should be considered for testing purposes only.

Instead, use one of the supported [Web3Signer methods](https://docs.web3signer.consensys.net/en/latest/HowTo/Use-Signing-Keys/). Using an external KMS that explicitly whitelists Web3Signer will allow you to move your private key out of plaintext and off your Router server entirely.

Web3Signer has native support for several key vaults and HSMs, as well as encrypted keystore files. Consult the [official docs](https://docs.web3signer.consensys.net/en/latest/Reference/Key-Configuration-Files/) to get started, or ask in our Discord server.

## Integrate web3signer

#### Example: Using Google Secrets Manager with Keystore Files

Web3Signer doesn't have built-in support for Google Secrets Manager, but we can use a strategy like the one below that combines Secrets Manager with an encrypted keystore file. Web3Signer expects the key password to be given in a text file. But instead of storing the password on our hard drive, we'll store it in Secrets Manager and write the needed file to *tmpfs* on demand, removing it again after Web3Signer has started and loaded the key into memory. This approach is a little more secure than storing the private key directly in Secrets Manager, because two pieces of information from two different sources are needed to learn the key. Also, the generated keystore (json) file is fully encrypted/portable and can be safely backed up or moved to a new VM as needed.

1. Grant your Web3Signer VM the needed permissions to read secrets.
2. Generate a secure password for your keystore file:\
   `echo $(pwgen -s 50 1) > pwd.password`
3. Store the password in Secrets Manager.
4. Use the password file to generate a keystore file. For example, using [geth](https://geth.ethereum.org/docs/install-and-build/installing-geth):\
   `geth account new --password pwd.password`\
   You can copy the keystore file to a more convenient directory.
5. Securely delete the password file.\
   `shred -uvz -n 3 pwd.password`
6. Create your key config file. Note that *keystorePasswordFile* refers to the directory inside the container:

```
type: "file-keystore"
keyType: "SECP256K1"
keystoreFile: "key.json"
keystorePasswordFile: "/home/node/signer/pwd/pwd.password"
```

7. Whenever we need to run Web3Signer, we'll retrieve the password from Secrets Manager directly to *tmpfs* first. We'll run the container using a bind mount for the password file (in *tmpfs*) and another for the directory containing the keystore json and config files, deleting the password file after the container has started and loaded the key into memory. See below:

```
# create tmpfs
sudo mkdir /mnt/tmpfs
sudo mount -t tmpfs -o size=100m tmpfs /mnt/tmpfs

# retrieve keystore password from Secrets Manager
echo $(gcloud secrets versions access --secret {name of your secret} latest) > /mnt/tmpfs/pwd.password

# run web3signer on port 9000
# bind mount password directory (tmpfs)
# second bind mount for keystore json and config files
docker run -d -p 9000:9000 --name web3signer \
-v {your config directory}:/home/node/signer \
-v /mnt/tmpfs:/home/node/signer/pwd \
consensys/web3signer:develop \
--config-file=/home/node/signer/config.yaml eth1

# should show 1 key loaded into memory and ready to to handle signing requests
echo $(docker logs web3signer | grep -o "Total signers (keys) currently loaded in memory: .")
echo $(docker logs web3signer | grep "Runner | Web3Signer has started")

# delete password file
rm -rf /mnt/tmpfs/pwd.password
sudo umount /mnt/tmpfs

# should now be 'file not found' in both cases
cat /mnt/tmpfs/pwd.password
docker exec web3signer cat /home/node/signer/pwd/pwd.password

```

## Best practices for generating and managing ssh-keys

Key based authentication for SSH is more secure than using a password. SSH keys help protect you against brute force attacks, and using public-key encryption is safer than sending passwords across the network.

#### Choosing a key type:

Ed25519 keys are recommended over RSA, since they offer better security and performance.\
`ssh-keygen -t ed25519 -a 100`

If you still want to use RSA keys (eg: for compatibility reasons), use a minimum length of 4096 bits.\
`ssh-keygen -b 4096 -o -a 100`

*-o specifies OpenSSH format, already implied for Ed25519*\
*-a 100 specifies 100 rounds of key derivation*

When creating the key pair, protect it with a passphrase that's at least 15-20 characters long.

#### 2-Factor Auth for SSH

2-Factor authorization is an easy way to greatly increase your level of protection.

* Google Authentication Module links with the Authenticator App on your mobile device. Anyone attempting to login will need to provide both your SSH key and a 6 digit code from your linked app. The code is only valid once and changes every 30 seconds.
* An even more secure option is U2F authentication using a physical token -- for example a Yubikey that authenticates via USB or NFC

## Hardening ssh config

Consider making these changes in the SSH server config file, `/etc/ssh/sshd_config`. Don't forget to restart the SSH service for the changes to take effect.

* Change the SSH port from the default of 22 to any other port (for example 9922) -- there are a lot of bots scraping on the Internet for port 22
* Disable SSH root login
* Disable password login, and disallow empty passwords (*PermitEmptyPasswords no*)
* Explicitly whitelist users for SSH access
* Set *AllowAgentForwarding*, *AllowStreamLocalForwarding*, and *X11Forwarding* to *no*
* Check that *IgnoreRhosts* is set to *yes* and *HostbasedAuthentication* is set to *no*

Rate-limiting (eg: **fail2ban**) can be used to time out an offending IP address after a certain number of failed login attempts. You can also use iptables (UFW) to restrict SSH access to only allow your public IP. (Be careful not to lock yourself out!)

## Hardening current docker-compose file

## How to create and use bastion instance for accessing routers infrastructure

Instead of allowing a direct connection from your Router to the Internet, it's more secure to place your sensitive components (eg: Router, Web3Signer) in a private subnet. Create a bastion server (aka: jump server) to access the subnet, and a NAT to allow the Router to access the Internet.

Many cloud providers have built-in support for features like private networks and Cloud NATs to make this easier to achieve.\
*AWS docs:* [Private Instances](https://aws.amazon.com/vpc/), [Cloud NAT](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html)\
*GCP docs:* [Private VPC](https://cloud.google.com/data-fusion/docs/how-to/create-private-ip), [Cloud NAT](https://cloud.google.com/nat/docs/overview)

#### Private network deep dive:

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

As you can see in the diagram above, your Router doesn't expose an external IP address and can't be accessed directly. SSH access to the Router (or any other VMs in the private subnet) can only be done by connecting through the bastion. The Router accesses the Internet through NAT, using it as gateway.

Using this configuration is less prone to attacks that would compromise your Router host.

#### Connecting to the bastion securely:

How you use the bastion to connect to the private subnet is important.

*Wrong: Connecting with an intermediate key pair*\
Don't under any circumstances store the SSH key to the Router on the bastion, like this:

```
# from client, using first key pair
ssh [bastion external IP]

# from bastion shell, using second key pair
ssh [Router internal IP]
```

The bastion is our most exposed point and the last thing we want to do is store anything sensitive (like SSH keys) on it.

*Wrong: Using agent forwarding*\
You may have seen this method used and/or recommended:

```
# from client
ssh-add [path to key]
ssh -A [bastion external IP]

# from bastion shell (now using client's ssh agent)
ssh [Router internal IP]
```

In this example, we store the SSH key for Router access in the client's SSH-agent. SSH-agent is a helper process that stores keys in memory for later use by the main SSH process. Then we use the `-A` flag to pass a reference to the client SSH-agent (containing the key) to the bastion, to use as its own when connecting to the Router. This is better than the first example, since the bastion is not storing or directly accessing any private keys. But it's still not entirely secure because if someone gains root access to the bastion, they can read the reference to the client SSH-agent, hijack it for their own purposes, and have full access to the Router.

*Right: Use TCP forwarding (ProxyJump)*\
A better approach is to SSH from the client to the bastion and then establish TCP forwarding to the Router. Thankfully, recent versions of OpenSSH have built-in support to make this easy for us:

```
# from client
ssh -J [bastion external IP] [Router internal IP]
```

The client authenticates both hops to the Router, so the bastion does not need to store or have even indirect access to any private keys.

#### Hardening the bastion

* To reduce the attack surface, disable any unnecessary services and only allow ingress on the SSH port/egress to the subnet. You can also disallow interactive shell access on the bastion.
* Apply the general SSH hardening tips from the section above
* By default, unattended-upgrades will not automatically reboot even if some updates require it. You can change this in the config file `/etc/apt/apt.conf.d/50unattended-upgrades` by setting **Automatic-Reboot** to true. You can also change **Automatic-Reboot-Time** to schedule the reboots for a certain time. You will also have to install the package *update-notifier-common* (if it's not already installed)

#### Teleport

As an alternative to OpenSSH, a great tool to directly access your host machine is [Teleport](https://goteleport.com/), which adds features like a web UI and certificate based authentication.


# Reference


# Configuration

The router is accepts configuration using the config file `config.json` in the root directory of the [docker-compose repo](https://github.com/connext/router-docker-compose).

The JSON schema accepts the following keys:

* `redis`: *Required*. Object containing the following keys to configure an external redis instance:
  * `host`: *Required*. The hostname of the redis instance.
  * `port`: *Required*. The port of the redis instance.
* `server`: *Required*. Object containing the following keys to configure the HTTP server:
  * `adminToken`: *Required*. Secret token used to authenticate admin requests.
  * `port`: *Optional*. The port the router will listen on. Defaults to `8080`.
  * `host`: *Optional*. The host the router will listen on. Defaults to `0.0.0.0`.
* `web3SignerUrl`: *Recommended*. The URL for a running [Web3Signer](https://docs.web3signer.consensys.net/en/latest/) instance. This is the recommended approach to private key storage.
* `mnemonic`: *Optional, Discouraged*. The mnemonic used to generate the private key. Using the mnemonic directly in the config file is unsafe and not recommended.
* `chains`: *Required*. The chain configuration. A JSON object with the following keyed by Domain IDs with the following object schema as value:
  * `providers`: *Required*. An array of providers URLs for a chain. Use a minimum of 1 URL, but additional URLs provide more fallback protection against provider issues.
  * `assets`: *Required*. An array of assets. Each asset is a JSON object with the following keys:
    * `assetId`: *Required*. The asset ID (ERC20 token address). This needs to represent the "local" asset which is the Connext bridge minted asset.
    * `name`: *Required*. The Asset Name.
* `network`: *Required*. `mainnet` or `testnet`.
* `environment`: *Optional*. `production` or `staging`. `mainnet` network config will always be `production`.
* `logLevel`: *Optional*. The log level. Defaults to `info`. Accepts `debug`, `info`, `warn`, `error`, `fatal`, `trace`, `silent`.
* `sequencerUrl`: *Optional*. The URL for the sequencer. Only used to override defaults.
* `cartographerUrl`: *Optional*. The URL for the cartographer. Only used to override defaults.

## Example Configuration File

*These are example RPC URLs. Please get your own RPC URLs!*

```json
{
  "chains": {
    "1634886255": {
      "assets": [
        {
          "address": "0x8c556cF37faa0eeDAC7aE665f1Bb0FbD4b2eae36",
          "name": "USDC"
        },
        {
          "address": "0x2983bf5c334743Aa6657AD70A55041d720d225dB",
          "name": "WETH"
        }
      ],
      "providers": [
        "https://arb-mainnet.g.alchemy.com/v2/...",
        "https://rpc.ankr.com/arbitrum"
      ]
    },
    "1869640809": {
      "assets": [
        {
          "address": "0x67E51f46e8e14D4E4cab9dF48c59ad8F512486DD",
          "name": "USDC"
        },
        {
          "address": "0xbAD5B3c68F855EaEcE68203312Fd88AD3D365e50",
          "name": "WETH"
        }
      ],
      "providers": [
        "https://opt-mainnet.g.alchemy.com/v2/...",
        "https://rpc.ankr.com/optimism"
      ]
    },
    "1886350457": {
      "assets": [
        {
          "address": "0xF96C6d2537e1af1a9503852eB2A4AF264272a5B6",
          "name": "USDC"
        },
        {
          "address": "0x4b8BaC8Dd1CAA52E32C07755c17eFadeD6A0bbD0",
          "name": "WETH"
        }
      ],
      "providers": [
        "https://polygon-mainnet.g.alchemy.com/v2/...",
        "https://rpc.ankr.com/polygon"
      ]
    },
    "6450786": {
      "assets": [
        {
          "address": "0x5e7D83dA751F4C9694b13aF351B30aC108f32C38",
          "name": "USDC"
        },
        {
          "address": "0xA9CB51C666D2AF451d87442Be50747B31BB7d805",
          "name": "WETH"
        }
      ],
      "providers": [
        "https://bsc-dataseed1.binance.org",
        "https://bsc-dataseed2.binance.org",
        "https://rpc.ankr.com/bsc"
      ]
    },
    "6648936": {
      "assets": [
        {
          "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
          "name": "USDC"
        },
        {
          "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
          "name": "WETH"
        }
      ],
      "providers": [
        "https://eth-mainnet.alchemyapi.io/v2/...",
        "https://rpc.ankr.com/eth"
      ]
    },
    "6778479": {
      "assets": [
        {
          "address": "0x44CF74238d840a5fEBB0eAa089D05b763B73faB8",
          "name": "USDC"
        },
        {
          "address": "0x538E2dDbfDf476D24cCb1477A518A82C9EA81326",
          "name": "WETH"
        }
      ],
      "providers": [
        "https://rpc.gnosischain.com",
        "https://rpc.ankr.com/gnosis"
      ]
    }
  },
  "environment": "production",
  "logLevel": "debug",
  "messageQueue": {
    "uri": "amqp://guest:guest@rabbitmq:5672"
  },
  "redis": {
    "host": "redis",
    "port": 6379
  },
  "server": {
    "adminToken": "supersecret"
  },
  "web3signer": "http://signer:9000",
  "network": "mainnet"
}
```


