0% found this document useful (0 votes)
22 views

Synapse Protocol API Docs

API documentation file for using Synapse Protocol.

Uploaded by

Jordan Durrani
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
22 views

Synapse Protocol API Docs

API documentation file for using Synapse Protocol.

Uploaded by

Jordan Durrani
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 51

Synapse Interchain Network

An optimistic proof-of-stake interchain network.


What is Synapse Interchain Network?

The Synapse Interchain Network (SIN) is an optimistic proof-of-stake interchain network. SIN allows trustless communication & settlement between chains, making all blockchain data &
liquidity accessible and transferable across all chains. The protocol builds on previous research used for Optimistic Layer 2 technologies and is inspired by the work of the Optimism team.
Our design principles:

Trustless — A single honest actor maintains network security, as long as there is at least one honest guard, fraudulent messages aren't executed. The network doesn’t
rely on centralized oracles or permissioned relayers to validate transactions.
Permissionless — Anyone can run an agent, strengthening network security & competing for fees. Furthermore, agents can permissionlessly support new chains.
Modular Security — Different types of use cases have different security needs. SIN’s security architecture supports them all. Apps can leverage the customizable optimistic
period to ensure interchain integrity. This flexible design enables the full range of interchain use cases, from multi-billion dollar Dapp governance to NFT bridging, to all use the
same system with no tradeoffs.

Use Cases

SIN's decentralization and censorship-resistance enables a wide range of new use cases, including:

Chain Abstraction - No more switching chains. Interact with smart contracts and Dapps connected to the Interchain Network regardless of what chain they’re on or where you have
assets.

Unified Liquidity - Liquidity for tokens and lending is fragmented across dozens of chains. SIN allows for interchain transactions on multiple chains at once allowing a user to tap into total
onchain liquidity without leaving their preferred chain.

Interchain Connectivity - Seamlessly connect blockchains with SIN’s permissionless architecture. New chains can onboard users, assets and call smart contracts on any chain.

Table of Contents

Learn about Staking and Fees

Synapse Interchain Network Testnet

Build on the Synapse Interchain Network

Synapse Interchain Network Message Lifecycle

Synapse Interchain Network Contracts

Learn about Off Chain Agents

Note: The above is a vast oversimplification of the system, which includes multiple ways to report fraud (some permissionless). For a more comprehensive overview, please see the
contracts.

Deep Dive: Optimistic POS


The following post attempts to help the user build intuition for Optimistic Proof of stake by taking the reader through the same thought process used to design the protocol. It's not intended
to be a comprehensive guide to SIN, but to help readers understand some of the design decisions made when building the protocol.

The Basics of Optimistic Protocols

At their core, optimistic protocols operate under the assumption that most network participants will act honestly, but that if they don't, dishonesty can always be proven. To date, this type of
design has been implemented within Optimistic Layer 2 Rollups, which work by posting transactions submitted to the rollup to Ethereum L1. Once data has been posted to Ethereum, the
L2 transactions can be proven trustlessly that it did occur, and any dishonest transactions submitted would be disproven.

However, this design is not only restricted to two chains - it is also possible between chains, in an interchain communication context. Anyone is always able to prove that a transaction
occurred on the origin chain, and by submitting it to the destination chain, it creates a ‘base truth’ to prove honest action against.

A simple optimistic example

In this example, Alice submits a transaction to Chain A, and wants Chain B to know what the transaction is.

Alice submits a transaction on Chain A to receive $5 on Chain B.


Chain B is intrinsically unable to know what happened on Chain A.
Bob (an off-chain agent) says that he knows what happened on Chain A, and sends what he believes to be the Chain A transaction to Chain B. Alongside that, Bob posts a
$5 bond.
During a set period of time, anyone is able to report that Bob did not post the correct transaction.
If no one reports that Bob is lying about the transaction, Alice now receives her $5 on Chain B, and the data has been successfully transferred from Chain A to Chain B.
However, anyone can report that Bob is lying about the transaction, since they can prove it compared to the publicly available state of Chain A. If Bob was lying, the reporter
earns Bob’s bond, and no fraudulent data about Chain A is successfully posted to Chain B.
Guards & the Optimistic Period

This basic example works well on a single transaction, but is not suitable as the basis for a scalable interchain network. For one thing, messages have arbitrary value. What kind of bond
would we require the off-chain agent to put up for a governance vote and how would the situation be corrected in the event that the agent was dishonest?

To deal with these complexities, we introduce two new concepts:

The first: an optimistic period, that is, a wait period between which Bob, the optimistic agent from the example above (who we call a Notary, going forward), sends the transaction data to
Chain B, to be reviewed, and in what period of time it can be executed (and finalized).

The second: a Guard. The Guard is an independent agent who institutes checks and balances on the Notary. The Guard can temporarily pause the transaction on the destination chain if
the data the notary is providing is incorrect. Now, in the event of a dispute, both agents can go back to chain A, and at least one of the two agents will have their bond slashed; Either the
Notary for providing invalid data, or the guard for filing a false report, since there cannot be two truths.

But what happened to the transaction on chain B that has now been paused? We've proved fraud on chain A, and slashed an agent, but as of right now we have no way to trustlessly
communicate whether or not the message on chain B was valid.

Optimistic Proof of Stake in the Synapse Interchain Network

Instead of the Notary relaying the state of the origin chain straight to the destination chain, first, any of the Guards in the network submit a message attesting to the state of the origin chain
and the state of Synapse Chain in one message, which is posted and to Synapse Chain. This ensures at least 1 guard is always online and continuously validating the state of every chain.

Only after a guard attests to the state of both the origin chain and the Interchain network as a whole, the Notary bonded to the chain a submit its attestation to Synapse chain, updating the
state of the network with a proof that both a guard and a notary have attested to the state of Chain A (as well as attestations from any other chains in the system).Once a guard attests to
the state of both the origin chain and the entire Interchain network, the bonded Notary on the origin chain can submit its attestation to the Synapse Chain. This updates the state of the
network via a proof, confirming that both a Guard and a Notary have attested to the status of Chain A, along with attestations from any other chains within the system.

A snapshot of the Synapse Chain state is then taken and posted to the destination chain, initiating the "optimistic seconds" countdown until the message is eligible for execution. During
this period, if the notary and the guard colluded to commit fraud, any guard within the system can present a fraud proof to all chains, temporarily blacklisting agents on every chain while the
dispute is resolved.

Only after another Notary & Guard on the origin chain attest that fraud has not been committed, and post it to Synapse Chain (using a longer optimististic period for increased safety), with
the results of the dispute can the message be executed.

The pending disputed message can only be resolved after a separate notary and guard on the Origin Chain both verify that no fraud has occurred, and report this to the Synapse Chain.
This employs a prolonged "optimistic seconds" timeframe to enhance security, and once complete, the dispute is resolved and the message is executed.

By making the results of these disputes globally accessible, the safety of the system increases as more chains are added. This guarantees that every bonded actor in the system will be
looking for fraud all the time, since everyone is forced to attest to the state of the network as whole.

Crucially, bond amounts are set differently for Guards and Notaries to incentivize participation. Since Guards can only grief messages, but cannot independently commit fraud, they are
required to post a much smaller bond compared to notaries. This is because at least one guard is necessary to verify the condition of every message.

On the other hand, Notaries are required to post a substantially larger bond to participate in the consensus. This not only minimizes the chances of a notary committing fraud by raising the
potential financial penalty for notaries, but it also establishes a significant bounty for guards who promptly report any fraud, thus encouraging swift reporting of any fraud.
Synapse Interchain Network Testnet
The Synapse Interchain Network Testnet is live, deployed to the Synapse Chain Testnet, Arbitrum Sepolia, and Ethereum Sepolia. Reach out in Discord for additional testnet chain
support.

The Contracts are deployed on all chains to the same addresses:

Contract Name Address Description

Origin 0x537ab51470984D6D9aDF8953C0D2ed8eDA4050ED Sends messages to other chains.

Destination 0xA944636Ac279e0346AF96Ef7e236025C6cBFE609 Executes messages sent on other chains.

View Synapse Chain Network Information

View Ethereum Sepolia Network Information

View Arbitrum Sepolia Network Information

Staking and Fees


L1 gas fees represent the cost of on-chain compute, SIN transaction fees represent the cost of interchain compute.
Solving the interchain communication problem is important enough to require its own network with its own economics. L1 gas fees represent the cost of on-chain compute, SIN transaction
fees represent the cost of interchain compute.

Agents stake to start validating SIN transactions and participating in the network. As users and Dapps make interchain calls, they pay fees to the network similar to L1 gas fees. These fees
are split among honest agents(Notaries, Guards & Executor) involved in processing that transaction.

This ecosystem of dapps and users paying fees for interchain compute and agents staking to earn fees opens the opportunity for an economic ecosystem built around interchain compute
fees.

For example, in the future, the network could support an LSD (liquid staking derivative) of the token used for staking. As well as a DPOS (delegated proof of stake) model where token
holders who want to earn fees can delegate their tokens to those running agents.

Note: In today's Synapse Interchain Network testnet, fees are paid in origin chain gas and staking is disabled.

Proof Of Stake

Note: The terms stake and bond are used interchangeably.

Both Notaries and Guards are required to stake prior to participating in the system with guards submitting the smaller of the two stakes since the guards stake serves primarily to
disincentivize griefing attacks (filing incorrect fraud reports).

Executors are not required to stake to participate in the network.

All stakes are posted on Synapse Chain and recorded as part of the global state of the SIN using the BondingManager.

In the event agents submit invalid attestations they will be reported and slashed with the reporter receiving the stake of the agent that commited fraud.

Stakers also have to wait a period of time before being able to unstake. How long this ends up being will decided when the staking module is activated.

Synapse Interchain Network Fees

Note: The terms tips and SIN fees are used interchangeably.

In SIN contracts, fees are referred to as Tips. This terminology is used to reflect that while the nature of compensation for off chain agents is dynamic, it does have a lower bound much
the way the base fee & tip cap work in dynamic fee transactions on Ethereum.

All transactions that go through SIN require multiple participants to be compensated for their participation in consensus and gas fees and infrastructure costs. This process is completely
trustless and happens on a per-transaction basis. Fees are dynamically adjusted based on gas prices using the GasOracle with a markup value defined in the oracle contract on a
per-chain basis.

Tip values returned by the GasOracle represent the minimum value required for messages to be included. Client contracts can pay additional fees to ensure faster execution by agents.

The fee distribution per message is as follows:


Guard Tip - Tip paid to the guard for posting a snapshot for a given origin chain.
Notary Tips -
Snapshot Tip - Tip paid to a notary for posting a snapshot of the origin chain.
Receipt Tip - Tip paid to a notary for posting a receipt
Attestation Tip - Tip paid to the notary for posting the attestation to the destination chain
Executor Tips-
Execution Tip- Tip paid to the first executor performing a valid execution attempt (correct proofs, optimistic period over).
Delivery Tip- Tip paid to the executor who successfully delivers the message.

Tips are eligible for collection only after the first valid attempt at message execution.

Build on the Synapse Interchain Network


The Synapse Interchain Network (SIN) is designed to be as easy as possible for developers to integrate. To this end, we've developed an abstract contract called MessageRecipient that
provides much of the already minimal scaffolding required to get started sending cross-chain messages. If you don't want to use this, you can also implement the IMessageRecipient
interface

You can also consider using BaseClient to avoid implementing any receiver checks yourself.

The contract also does some basic checks for us when receiving a message. Let's take a look at the receiveBaseMessage related functions which are used to receive a message from a
destination:
Copy
/**

* @notice Message recipient needs to implement this function in order to

* receive cross-chain messages.

* @dev Message recipient needs to ensure that merkle proof for the message

* is at least as old as the optimistic period that the recipient is using.

* Note: as this point it is checked that the "message optimistic period" has passed,

* however the period value itself could be anything, and thus could differ from the one

* that the recipient would like to enforce.

* @param origin Domain where message originated

* @param nonce Message nonce on the origin domain

* @param sender Sender address on origin chain

* @param proofMaturity Message's merkle proof age in seconds

* @param version Message version specified by sender

* @param content Raw bytes content of message

*/

function receiveBaseMessage(

uint32 origin_,

uint32 nonce,

bytes32 sender,

uint256 proofMaturity,

uint32 version,

bytes memory content

) external payable {

if (msg.sender != destination) revert CallerNotDestination();

if (nonce == 0) revert IncorrectNonce();

if (sender == 0) revert IncorrectSender();

if (proofMaturity == 0) revert ZeroProofMaturity();

_receiveBaseMessageUnsafe(origin_, nonce, sender, proofMaturity, version, content);

/**
* @dev Child contracts should implement the logic for receiving a Base Message in an "unsafe way".

* Following checks HAVE been performed:

* - receiveBaseMessage() was called by Destination (i.e. this is a legit base message).

* - Nonce is not zero.

* - Message sender on origin chain is not a zero address.

* - Proof maturity is not zero.

* Following checks HAVE NOT been performed (thus "unsafe"):

* - Message sender on origin chain could be anything non-zero at this point.

* - Proof maturity could be anything non-zero at this point.

*/

function _receiveBaseMessageUnsafe(

uint32 origin_,

uint32 nonce,

bytes32 sender,

uint256 proofMaturity,

uint32 version,

bytes memory content

) internal virtual;

This means in our test client we're going to have to implement some checks. The first thing you'll notice is both the origin and destination are pre-defined so you'll have to define those in
the constructor like so using the appropriate origin and destination chain addresses for your chain.
Copy
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

// ══════════════════════════════ LIBRARY IMPORTS ══════════════════════════════

import {TypeCasts} from "../libs/TypeCasts.sol";

// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════

import {MessageRecipient} from "./MessageRecipient.sol";

contract TestClient is MessageRecipient {

constructor(address origin_, address destination_) MessageRecipient(origin_, destination_) {}

Next up, you're going to want to define the sender checks and make sure the optimistic seconds period is set correctly. Let's define _receiveBaseMessageUnsafe
Copy
uint256 private constant REQUIRED_OPTIMISTIC_SECONDS = 20

error ProofMaturityTooLow();

error InvalidSender();

/// @inheritdoc MessageRecipient

function _receiveBaseMessageUnsafe(

uint32 origin_,

uint32 nonce,

bytes32 sender,

uint256 proofMaturity,

uint32 version,

bytes memory content


) internal override {

// let's make sure everything is kosher before we accept the message

// first we want to make sure the sender is who we think it is.

if Typecast.addressToBytes32(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045) != sender {

revert InvalidSender();

// and we want to make sure the optimistic seconds period has exceeded

// what we want on our side.

if (proofMaturity != REQUIRED_OPTIMISTIC_SECONDS) {

revert ProofMaturityTooLow();

emit MessageReceived(origin_, nonce, sender, proofMaturity, version, content);

Now we're ready to receive a message, but we still have to send one. Let's define a sendMessage function:
Copy
function sendMessage(

uint32 destination_,

address recipientAddress,

uint32 optimisticSeconds,

uint64 gasLimit,

uint32 version,

bytes memory content

) external payable {

// receipient is the address of this contract, but on the other chain

bytes32 recipient = TypeCasts.addressToBytes32(recipientAddress);

// if you want the user to get dropped gas on the destination chain, can define that here

MessageRequest memory request = MessageRequest({gasDrop: 0, gasLimit: gasLimit, version: version});

(uint32 nonce,) = _sendBaseMessage(destination_, recipient, optimisticSeconds, request, content);

// you've sent he message

emit MessageSent(destination_, nonce, TypeCasts.addressToBytes32(address(this)), recipient, content);

Feel free to reach out in Discord for more help integrating these contracts. As the ecosystem matures, more dev docs will be released as well as more integrations.

In the meantime, you can see examples here and here.

Message Lifecycle
This document provides a quick overview of the lifecycle of a successful cross-chain message.
An application calls sendMessage on the Origin and specifies a destination chain, contract address, optimisticSeconds timer, and call data.

The message is encoded, assigned a nonce, and added to the origin merkle tree. The merkle state at this nonce is then saved.

Any guard observes the new state of the origin chain and creates a state snapshot of the origin chain. This message is then submitted back to the origin chain (which can
confirm its validity).
Any notary bonded to the origin chain can post an "Attestation" of the state of the origin chain to Synapse Chain. This must include the guards signed snapshot.
Any notary for the destination chain proposes the attestation above to the destination chain. The optimistic seconds period timer has now started and guards are verifying
the validity of the update.
Assuming no fraud proof has been presented that would cause the message to be quarantined, and the optimistic seconds period has elapsed, the message is executed. A
message receipt is generated.
The receipt is posted back to Synapse Chain where tips can be claimed by the notary, guard, and executor involved in the given transaction.

Contracts
A variety of contracts power the Synapse Interchain Network, contained in the contracts-core section on GitHub (generated docs available here). The core of the protocol consists of a few
different components:
The Synapse Interchain Network Testnet is now live, get started here: Synapse Interchain Network Testnet

Origin - Interface for sending messages to other chains. Also serves as the source of truth for resolving disputes since the origin chain is the only place where fraud can be
proven with absolute certainty.
Destination - Interface for executing messages sent on other chains. Also has an interface for submitting fraud reports from Guards.
Summit - Contains all states from the entire network, resolves disputes and allows agents to bond.
Gas Oracle - Used for cross-chain dynamic gas pricing.
Agent Manager - used to keep track of all the bonded agents and their statuses.
Bonding Manager - keeps track of all existing agents on the Synapse Chain
Light Manager - Replicates the state of the bonding manager using a single Merkle root.
Inbox - Accepts snapshots & receipts from various chains and passes them to summit
LightInbox - Replicates inbox on other chains to pass messages into Origin and Destination

Security

The contracts have been audited by both Zellic and Trail of bits + reviewed extensively internally. Synapse employs extremely intensive unit, fuzz, and integration tests for the contracts &
has an extremely comprehensive foundry test suite. A full overview of the test suite can be found here.

In addition to automated testing & manual review, Slither, a static analysis tool is ran on each commit & suggestions are considered carefully prior to merge. Other tools, including snyk +
sonarqube are also used to scan for malignant dependencies.

This section will be updated with reporting guidelines for security issues + bounty program once SIN is live. Community members & contributors are encouraged to review protocol critical
changes on github.

Origin
Overview

The Origin contract is deployed on all chains in the interchain network. Each origin contract represents the state of messages sent to that chain at any given time.

The origin contract serves an extremely important role in the optimistic system akin to the L1 in ORU's like optimism. It's the only place where fraud on destination chains can be proven.

This is because the sending chain in any cross-chain system is the only place it can be proven, with absolute certainty, that something actually happened. So, when a fraud report occurs,
this is the only place that the fraud report can be resolved.

Responsibilities

The Origin contract is responsible for the following:

Formatting the sent message payloads, and inserting their hashes into the Origin Merkle Tree.
Keeping track of the historical Origin Merkle Tree containing all the message hashes.
Enforcing minimum tips (fees) values for sent base messages based on the provided execution requests. This is done using a Gas Oracle
Distributing the collected tips upon request from a local contract.

The statue used in this contract is used by the AgentManager

Destination
Overview

The destination contract is used for receiving messages from remote chains and executing them. This is where the last leg of any interchain transaction happens.

Responsibilities:

Enforcing the optimistic seconds period before messages can be executed


Removing messages that have been proven fraudulent
Issue Tip Receipts to compensate off chain agents for participating in message delivery
Accepting Fraud/Success Reports through LightInbox
Accepting State and Receipt Reports to initiate a dispute between Guard and Notary.
Storing all the Guard Reports with the Guard signature leading to a dispute.
Verifying State/State Reports referencing the local chain and slashing the signer if statement is invalid.
Verifying Receipt/Receipt Reports referencing the local chain and slashing the signer if statement is invalid.

Summit
The Summit contract is deployed on Synapse Chain and acts as the "rollup" of SIN for the attested states of all remote chains in the system. The snapshot root of the contract determines
the source of truth for snapshots for all chains and is attested to by every actor in the system every time they perform an action.

The contract works hand in hand with the BondingManager to keep remote chains up-to-date on the state of the agents & forces all agents to attest to the validity of the agents in every
snapshot and ultimately serves as a global state of the interchain network.

Responsibilities

Accepts Guard/Notary Snapshots


Performs state transitions
Contains the updated root of all the bonded agents through the BondingManager
Accepts receipts, which are used to distribute tips (upon the completion of a message by the executor on the Destination contract, a receipt is issues which can be used to claim
tips, these receipts are verifiable against the snapshot).
Verifies Attestations/Reports which ares used to slash off-chain agents using the BondingManager.
Accepting Fraud/Success Reports through Inbox
Accepting State and Receipt Reports to initiate a dispute between Guard and Notary.
Storing all the Guard Reports with the Guard signature leading to a dispute.
Verifying State/State Reports referencing the local chain and slashing the signer if statement is invalid.
Verifying Receipt/Receipt Reports referencing the local chain and slashing the signer if statement is invalid.

AgentManager
Overview

The AgentManager is deployed on all chains and is used to keep track of the status of all Off Chain Agents and resolve disputes. This contract is also used by the Origin, Destination, and
the Summit to determine whether or not an off-chain actor has permissions to perform different actions on the contracts.

The agent manager also has a mechanism for permissionlessly updating its own record of the agents involved in the system using snapshots.

Responsibilities

Keeping track of all the bonded agents and their statuses.


Keeping track of all the disputes between agents.
Notifying relevant contracts about changes to the agent set.

Propagating Agent Updates

Agent updates are propagated via the agent root which represents the current off-chain agents that have posted a bond on Synapse Chain at any given time.

This root is contained in the Attestations posted by Notaries to summit so the agent set can be updated on every chain as soon as a message is sent on any chain.

Much like messages passed by developers building on top of the protocol. the agent root itself has an optimistic period before it can be considered valid, and as such, represents a critical
part of the protocol security.

The propagation of this root at SynChain is necessary since only SynChain knows the state of the agent set at any given time.

Gas Oracle
Gas Oracle

The Gas Oracle is a Smart Contract deployed on each of the chains in the network that tracks the estimated gas prices on other chains. This is needed to estimate the cost of gas to send
a message in the messaging system. The sender of the message pays up front for the transactions required on both the Synapse Chain and the Destination.

Responsibilities

Track gas prices on all chains


Include these prices in the summit so they are attested to by all agents

Future work

In the future, the gas oracle will use tools like tx.gasprice to track time-averaged values for different gas stats.

Off Chain Agents


The Synapse Interchain System has 3 different kinds of off-chain agents.

Notary - Attests to the validity of snapshots submitted by guards.


Guard - Submits snapshots of the state of the network to Origin contracts, watches for fraud.
Executor - Executes completed messages on the destination chain

Deployment

Right now, the easiest way to deploy the agents is with our helm charts available on artifact hub. Work is being done on a one-click devnet in the meantime that uses docker-compose
instead of helm.

Making Changes

Because of the careful incentives around speed & security implicit in the system, it's likely client diversity will emerge very quickly. To enable this, the codebase exports as much
functionality as possible through godoc (including our e2e go-based contract testing suite in ethergo & custom github actions to minimize the probability of issues.

Devcontainers are built daily to enable one click runs from github codespaces for any developer interested in contributing to or hacking on the synapse interchain network standard.

Developers can reach out through GitHub issues or the Synapse Discord with questions.

Security
Supply Chain Security
Similarly to contract security, a number of automated & manual systems are used to minimize risks of root of trust compromise when running any of the off-chain agents.

These include:

Snyk, Sonarqube and Codeql are all used for static analysis + dependency analysis on every pull request and issues are carefully reviewed.
Software Bill of Materials are exported with every release & scanned continuously
All containers are built using either distroless or scratch containers in order to minimize the risk of supply chain attacks on docker images.
All binaries are built using a custom goreleaser image to avoid supply chain attacks in builder images

Devops security

We've also taken extraordinary steps to ensure users have a secure deployment environment for offchain agents. This has included the creation of 3 different terraform providers aimed at
using workload identity authorization for private key management:

Terraform-kubreproxy-provider: Allows the configuration of kubernetes clusters through terraform while utilziing an iap bastion host (something previously impossible)
Terraform-helmproxy-provider: Allows the configuration of deployment of helm charts while utilziing an iap bastion host (something previously impossible)

We've also open-sourced workload-identity based Ethereum ECDSA signers for both GCP and AWS.

RPC Security

One other root of trust in any interchain system is the place data is being received from the origin chain. To this end, we built OmniRPC, a way to specify a threshold of different RPC
providers that need to return the same data about a query before it's trusted.

Operational Security

In order to enable operators of off-chain agents to operate as seamlessly as possible, extensive work has been done around the introduction of distributed tracing through otel + jaeger to
quickly identify & remediate any issues that might result in slashing of offchain agents. This includes a custom pyroscope + Jaeger image for especially hard to find issues + dockerized
tracing.

Additionally, all modules export metrics via Prometheus + Grafana to make setting up alerts as easy as possible.

Executor
Overview

Executors are permissionless & trustless actors responsible for executing messages on the Destination chain once the optimistic seconds period has elapsed. The executors receive tips
(fees) in exchange for processing messages.

Responsibilities

Listen for messages sent on the Origin & store a list of merkle proofs for these messages
Listen for snapshots being submitted to the destination chain and execute messages once the optimistic period has elapsed.
Optional: Report fraud on Origin chains, as this kind of fraud report is completely permissionless
Collect Fees by submitting tip proofs in order to claim tips.

Guard
Overview

Guards are off-chain agents that detect fraud and attest to state on many chains. Guards listen for new messages on the Origin chain and create snapshots that contain the state of the
Origin contract (provable on the origin chain) and the state of Synchain (which contains provable updates from the gas oracle, a list of agents, etc). Once this snapshot is created, it is
posted to Synchain where a notary can attest to its validity or other guards can report fraud.

Responsibilities

Keep track of state on all chains


Keep track of snapshots submitted on all chains
Submit state snapshots to the Origin and Synapse Chains
Report fraud by other notaries or guards to Origin Chain, Synapse Chain, and Destination chains as fast as possible (first reporter gets the reward here to incentives instant
reporting)
Update the agent statuses on each chain after fraud is reported.

Report Types

Guards can report various different types of fraud listed below.

Fraud type Agent Fraud proving Fraud reporting Description


submitStateReportWithSnap Snapshot having State not
Invalid Snapshot Notary/Guard verifyStateWithSnapshot
shot (N) matching Origin

Attestation that was never


Invalid Attestation Notary verifyAttestation submitAttestationReport
created in Summit

Attestation from Invalid submitStateReportWithAttes Attestation having State not


Notary verifyStateWithAttestation
Snapshot tation matching Origin

Attestation from Invalid verifyStateWithSnapshotP submitStateReportWithSnap (Alternative way to


Notary
Snapshot roof shotProof prove/report fraud)

Receipt that doesn't match


Invalid Receipt Notary verifyReceipt submitReceiptReport
Destination

Notary
Overview

A Notary is an off-chain agent assigned to at most 1 chain. The notary is bonded on Synapse Chain and posts a stake in exchange for a percentage of tips (fees) for attesting to messages.

The notary is akin to the sequencer in an L2 blockchain in that they are responsible for posting state to the L1. While they can refuse to post state temporarily, they cannot permanently
censor the network, and they can never lie about the state of a chain in a way that cannot be detected on the destination.

The notary must also first have a snapshot signed by a guard before attesting to state updates and can therefore not unilaterally perform any action, even optimistically.

Responsibilities

The notary does the following:

Observe the Origin contract for new snapshots from a guard.


Sign an attestation attesting to the state of the origin chain + the validity of the guards snapshot at any given time. The attestation contains the following information:
Snapshot Root: Merkle root of the origin roots grouped together in a state snapshot. This gets submitted to the Summit contract.
Agent root: Merkle root that proves the current state of bonded agents. See AgentManager for details.
Nonce: Total number of notary snapshots.
Synapse Chain Block Number: block on the synapse chain that the attestation was registered by a notary
Timestamp of the attestations registration on synapse chain
The Attestation is submitted to Synapse Chain.

Synapse Chain
Synapse Chain is an Ethereum-based Optimistic Rollup, built on the Syn OP Stack, inspired by OP Stack.
Synapse Chain is an Etherum-based optimistic rollup built on the SYN OP stack.

Synapse Chain is EVM compatible, leveraging the rich infrastructure and developer ecosystem built around it. Like Optimism, Synapse Chain settles to Ethereum, enabling the scalable &
trustless environment of Optimistic Rollups. Transactions on Synapse Chain are near instant and cost a fraction of what they would on Ethereum.

Unlike other rollups, by building on the SYN OP Stack, Synapse Chain is a natively interchain rollup, able to communicate other supported chains by the Synapse Interchain Network.
This enables interchain deposits to Synapse Chain and fast withdraws from the Synapse Chain Bridge.

It serves as the data availability layer of the Synapse Interchain Network and as a soverign execution environment for interchain applications.

Synapse Interchain Data Availability Layer

Synapse Chain contains the state of all chains within the SIN. Anytime a message is sent through SIN, it is settled to Synapse Chain and attested to by off-chain agents inside of a global
Merkle tree. Thus, Synapse Chain serves as the data availability layer for the Interchain Network. All messages sent, attestations made, and agent information can be accessed through
Synapse Chain. Since Synapse Chain is an rollup, all of the SIN data gets settled to Ethereum, resulting in the following Synapse stack:

Synapse Interchain Network, enabling trustless interchain communication


Synapse Chain, serving as a high throughput, low cost, data availability layer & execution environment
Ethereum, serving as the finals settlement destination and data availability layer of Synapse Interchain. All Synapse Interchain actions, such as attestations, staking, and
slashing, can occur through L1. Fraud can be resolved through Ethereum for the Synapse Interchain.

This is why Synapse Chain serves as an interchain data availability layer for the SIN, and is the first interchain rollup.

Building Interchain Applications

By serving as the hub of the Interchain Network, Synapse Chain enables application developers to create new primitive applications previously not possible.

Learn more about Building on Synapse Chain & building on the Synapse Interchain Network (SIN).

Synapse Chain Gas Fees

Currently, transaction fees on Synapse Chain are paid in ETH. In the future, this could be paid in any token. See below for additional transaction fee information.

SYN OP Stack
The Syn OP Stack is a natively interchain rollup stack, built on top of Optimism's OP Stack.
The SYN OP Stack is a modified version of Optimism's OP Stack, enabling natively interchain rollups.

The SYN OP Stack has all of the standard benefits of the OP stack, but enshrines the Synapse Interchain Network at the root of the rollup, allowing any SYN OP chain to communicate
with all other supported chains by the Synapse Interchain Network.

Modularized L2 Bridging

The SYN Op Stack allows for interchain deposits and fast withdraws from the standard L2 Bridge via the Synapse Interchain Network. The standard 7-day L2 bridge withdrawal period
works fine for some execution environments, however, many rollups need to be able to customize withdraw periods based on the activities on the rollup. The SYN OP Stack enables rollups
to modularize their bridge logic, whether by token, amount bridged, or throughput.

Built-in interchain communication

The Synapse Interchain Network is in built in the SYN OP Stack. Applications are increasingly interchain and require interacting with other chains. The standard OP Stack only allows for
Ethereum ↔ L2 communication. This was built under the design of one all-encompassing rollup and Ethereum, but 1000s of new blockchains have launched, and rollup must allow
application developers to onboard users and assets from all other chains, not just Ethereum.

Permissionless Interoperability

Due to the permissionless nature of SIN, the SYN OP Stack allows rollups to immediately have native interchain communication with all chains supported by SIN. Any rollup developer can
run their own agents, enabling communication, strengthening network security, and competing for fees.

Synapse Chain Testnet


The Synapse Chain Testnet is now live for developers.
Synapse Chain Sepolia serves as a testnet chain for both the Synapse Interchain Network and application developers alike.

This testnet is under active development and may undergo a regenesis.

Network information can be found below. As a developer, to bridge to Synapse Sepolia, see Bridging to Synapse Chain.

Synapse Chain Testnet Network Information


Name Value

Network Name Synapse Sepolia

Description The public Synapse Chain testnet.


RPC Endpoint https://sepolia.synapseprotocol.com

Chain ID 444

Currency Symbol ETH

Block Explorer https://sepolia.synapsescan.com

Gas Fees
Currently, transaction fees on Synapse Chain are paid in ETH. In the future, this could be modified to use any token.

Transaction fees function similarly to Ethereum in many ways, with some differences.

Every Synapse Chain transaction consists of two costs: an L2 (execution) fee and an L1 (security) fee. The L2 fee is the cost to execute your transaction on the L2, and the L1 fee is the
estimated cost to publish the transaction on the L1. Typically the L1 security fee is higher than the L2 execution fee.

The L1 fee will vary depending on the amount of transactions on the L1. If the timing of your transaction is flexible, you can save costs by submitting transactions during periods of lower
gas on the L1.

Similarly, the L2 fee can increase and decrease depending on how many transactions are being submitted to the L2.

For additional details about fee calculation on Synapse Chain, please refer to the op-stack developer documentation.

Using Synapse Chain


To use Synapse Chain as a developer, you can bridge ETH and interact with it as you would with Ethereum. Network information is located here.

As a user, you should, set up your wallet to support Synapse Chain, acquire Sepolia ETH, and bridge to Synapse Chain.

Wallet Setup
Faucet
Bridging to Synapse Chain

Using Synapse Chain


To use Synapse Chain as a developer, you can bridge ETH and interact with it as you would with Ethereum. Network information is located here.

As a user, you should, set up your wallet to support Synapse Chain, acquire Sepolia ETH, and bridge to Synapse Chain.

Wallet Setup
Faucet
Bridging to Synapse Chain

Faucet
To interact with the Synapse Chain testnet, you'll first need testnet Sepolia ETH, to bridge to the Synapse Sepolia testnet.

Here are a few of the currently available Sepolia facuets:

https://sepoliafaucet.com
https://faucet.quicknode.com/drip
https://faucet.chainstack.com
https://www.infura.io/faucet/sepolia
Once you receive ETH on Sepolia, you should see it in your wallet on the Sepolia Network. It may take a few seconds for them to appear, but you can check the status by looking for a
transaction to your address on a Sepolia Block Explorer.

Bridging to Synapse Chain


The Synapse Chain Testnet is now live on Sepolia for developers.
You can bridge ETH from Sepolia Ethereum to use on Synapse Sepolia by sending Sepolia ETH to the following address:
https://sepolia.etherscan.io/address/0x643B10E697f2763168B810b65d29704b51Df69f2sepolia.etherscan.io
By default, bridging ETH will result in a SELF transaction on the L2, with the from and to addresses equal to the address performing the bridge on the L1. This will also increase that address'
L2 nonce. If you are bridging for the purpose of a deterministic contract deploy that depends on a zero nonce, please bridge using a different address first, and transfer to the
contract deployment address on the L2 after bridging.

Building on Synapse Chain


Building on Synapse Chain is as simple as interacting with any other EVM chain, due to its EVM-compatible Optimistic Rollup design. All standard EVM developer tools and wallets are
compatible, such as Foundy, Hardhat, Remix, Brownie, EthersJS, Wagmi, and more.

Synapse Chain Contracts


Synapse Chain Addresses
Synapse Testnet (Sepolia)
Name Address

Batch Sender 0x82e4D3bF6D71FB8036a4C331624064f0b5699510

Batch Inbox 0x95b1634205399f82649e270b37420faea4834008

Output Proposer 0x571974383754a42E73C5eC9AcF2A4c083Ec2a1ff

L2 Contract Addresses
Synapse Testnet (Sepolia)

Name Address

WETH9 0x4200000000000000000000000000000000000006

L2CrossDomainMessenger 0x4200000000000000000000000000000000000007

L2StandardBridge 0x4200000000000000000000000000000000000010

SequencerFeeVault 0x4200000000000000000000000000000000000011

OptimismMintableERC20Factory 0x4200000000000000000000000000000000000012

GasPriceOracle 0x420000000000000000000000000000000000000F

L1Block 0x4200000000000000000000000000000000000015

L2ToL1MessagePasser 0x4200000000000000000000000000000000000016
L2ERC721Bridge 0x4200000000000000000000000000000000000014

OptimismMintableERC721Factory 0x4200000000000000000000000000000000000017

ProxyAdmin 0x4200000000000000000000000000000000000018

BaseFeeVault 0x4200000000000000000000000000000000000019

L1FeeVault 0x420000000000000000000000000000000000001a

L1 Contract Addresses
Ethereum Testnet (Sepolia)

Name Address

AddressManager 0x3727ef5C3CF38685b1759df99A83D4d229a80199

L1CrossDomainMessenger 0x49ADa0E5AE1FbdAE15A0bFa8CfE2e396Df529c2f

L1ERC721Bridge 0x45d61c43bd26D92a631bebe17A340181AFf78A29

L1StandardBridge 0x0eA234cFd2074Bf5eC64b15637Af8a68e0b2bFab

L2OutputOracle 0x174B9593CaD503D789ebA6dF2CCfce51649c2CC1

OptimismMintableERC20Factory 0xE287c226DC58fd5322B8f292A99793d7203a3A1C

OptimismPortal 0x643B10E697f2763168B810b65d29704b51Df69f2

ProxyAdmin 0x81e95329cCFa3e004633b758cbb2932686188555

SystemConfig 0x15F763Fd4eB94201B7227cfEfe7460b62910AF4f

thirdweb
Using thirdweb to build on Synapse Chain Testnet
Thirdweb is a web3 infrastructure developer suite that gives developers out-of-the-box tooling to easily deploy contracts on Synapse Chain Testnet.

Create a Smart contract


Run the following command in your CLI:
Copy
npx thirdweb create contract

Provide the following in the CLI:


Framework to build with (Forge or HardHat)
Base Contract (choose from ERC721, ERC1155 or ERC20, or empty)
Smart Contract name
Customize additional command line prompts such as:
Assign a name to your project
Include any desired extensions
Once created, navigate to your project's directory and open it in your preferred code editor.
Inside the contracts folder, you'll find your smart contract written in Solidity.
Customize the base-contract to your own preference. Further documentation around the base contracts can be found here.

Deploy a Smart Contract

Use the CLI to deploy your contracts directly to Synapse Chain by running
Copy
npx thirdweb deploy

This command will accomplish the following:


Detect the framework (Hardhat, Foundry or Truffle).
Compile all the contracts in the current directory using your project settings.
Allow you to select which contract(s) you want to deploy.
Upload your contract metadata to IPFS, making sure it matches exactly the encoded IPFS hash in the compiled bytecode.
Detect the extensions on your contract.
Open the deploy flow in the dashboard for you to connect a wallet, input the contract parameters and select a chain to deploy to, without needing to hardcode
private keys!
In the resulting dashboard, fill out remaining parameters such as:
_name

_symbol

_royaltyRecipient

_royaltyBps

Select SynapseChainTestnet as the network

Reach out to thirdweb support with any additional questions.

Alternative Options

You can deploy a prebuilt contract for NFTs, tokens, or marketplace directly from the thirdweb Explore page:
Navigate to the thirdweb Explore page: https://thirdweb.com/explore
Choose the type of contract you want to deploy from the available options: NFTs, tokens, marketplace, and more.
Follow the on-screen prompts to configure and deploy your contract.

Synapse Bridge
The Synapse Bridge is the first user-facing product built on top of the cross-chain communication network.

Synapse Bridge allows users to seamlessly swap on-chain assets across 15+ EVM and non-EVM blockchains in a safe and secure manner. The bridge supports two types of bridging:

Canonical Token Bridging — bridging of wrapped assets across chains


Liquidity-based Bridging — bridging of native assets across cross-chain stableswap pools
RFQ Bridging — bridging using intent based relayers for faster confirmation and competitive quotes

Synapse Bridge is also available for developers who want to integrate cross-chain asset swapping natively into their application. By leveraging the bridge, developers can build truly
cross-chain DeFi applications including cross-chain DEX, lending platforms, margining systems, derivatives markets, yield aggregators, and much more. The cross-chain AMM gives users
access to the deep liquidity, low fees, and minimal slippage.

In a short span of time, the Synapse Bridge has become one of the most widely used and trusted bridges, processed nearly $14 billion in total volume, serviced hundreds of thousands of
users as well as mass scale dapps like DeFi Kingdoms.

All of the bridging contracts can be found here

Transaction Support FAQ


Commonly asked questions regarding an individual bridge transaction.
What does a Bridge transaction look like?

After signing a transaction from your wallet, gas fees are collected, and the transaction is initiated on the origin chain. Once accepted, the bridged asset is removed from your portfolio, and
a progress bar shows the estimated confirmation time.

Once confirmed on the destination chain, the asset is added to your portfolio, and a link to the destination hash can be accessed from the progress menu. The transaction is then indexed
by the Synapse explorer and will appear with your transaction history in the Activity tab.

Gas token airdrops and rebates are delivered to your wallet automatically. However, only bridgeable assets will appear in your Synapse portfolio.

How are assets moved between chains?

How assets move between chains depends on the router being used. Synapse automatically selects the appropriate router based on the tokens and chains being used.

Synapse Bridge

The origin token is added to a Pool on the origin chain where it’s swapped for a compatible destination chain asset, which is then sent to the destination chain.

RFQ

Relayers on the destination chain bid to provide the best exchange rate for a given route. After delivering the destination asset, the winning bidder receives the origin token.

CCTP

A native USDC contract burns and re-mints the token on its respective origin and destination chains.

Troubleshooting
Did my transaction initiate?

Transactions that fail to initiate will return an error message, and your funds will remain in your portfolio. If a transaction fails to initiate, you may safely retry the transaction.

My transaction failed to initiate after several tries

Origin chain activity volume may occasionally prevent a transaction from being accepted. These transactions return an error message, and funds remain safely in your wallet on their
original chain. You may safely retry your transaction at any time; activity spikes will usually return to normal within 30-60 minutes.

Advanced techniques

Activity levels can be tracked on the native block explorers for your source or destination chains (e.g https://etherscan.io/gastracker).
You can update your wallet’s gas settings to make transactions from Ethereum more likely to be accepted during times of peak activity.

Why is my transaction taking so long?

Synapse estimates are based on destination block times. At times, high levels of activity may cause transactions to post in a later block than expected. The block explorer links in the
transaction progress dropdown menu can confirm whether a slow transaction is actually confirmed but not yet updated on the Synapse website.

Transactions that fail to complete are not lost, and are manually addressed by the Synapse support team. You can reach out to them regarding your specific case on the Synapse Discord
channel, which will also have alerts regarding slow or unresponsive chains and resolution times.

For DeFi Kingdoms users: Bridging an NFT may sometimes take up to twice as long as bridging an asset. Contact Synapse Support on Discord if an NFT transaction has been pending for
more than two hours.

I received a different asset than I expected

In the event of a sudden increase in slippage on the destination chain, Synapse prevents unexpected loss by simply delivering the intermediate asset used for the Bridge transaction
instead. This asset will appear in your portfolio and can be safely swapped for the asset of your choice from the destination chain.

Did I receive my rebate or gas airdrop?

While rebates and airdrops appear in your wallet automatically, only bridgeable assets are shown in your Synapse portfolio. If you don’t see an asset you should have received, first check
your wallet while connected to the destination chain for your bridge transaction.

Help!

Don’t panic! Contact Synapse Support on Discord to answer any other questions you might have.

Bridge Liquidity
The engine behind Synapse's cross-chain infrastructure.

What is nUSD?

nUSD, or "nexus" USD, is a cross-chain stablecoin fully backed by the nexus stablecoin liquidity pool on Ethereum consisting of DAI, USDC, and USDT.

The nexus stablecoin pool is a liquidity pool incentivized on every destination chain Synapse offers. LPs accrue value through bridge fees and SYN emissions, giving end users access to
deep liquidity and low slippage on stable pairs

Synapse's nUSD multi-chain stablecoin, paired with native chain stablecoins

When a stablecoin is bridged between Synapse-enabled chains, the funds are automatically converted to nUSD, and bridged to the destination chain. Once there, this nUSD can be
auto-swapped to that chain's native stablecoins using the local nUSD pool.

What is nETH?

nETH, or "nexus" ETH, is a cross-chain asset pegged to ETH and fully backed by the nexus ETH liquidity pool on Ethereum that consists solely (for now) of ETH.
Synapse's nETH multi-chain ETH-pegged asset, paired with native chain ETH

nETH is used to enable fast bridging of ETH to and from L2 networks such as Arbitrum, Boba, and Optimism.

Contract Addresses
Addresses for all relevant Synapse Protocol contracts.
Due to the number of deployments & contracts across multiple chains, this is not currently an exhaustive list.
An exhaustive list as well as all Synapse Protocol contracts & deployment addresses can be found on Github.

The Synapse Router Contract is 0x7E7A0e201FD38d3ADAA9523Da6C109a07118C96a on all chains and the Synapse CCTP Router is 0xd5a597d6e7ddf373a92c8f477daaa673b0902f48 on all chains
that support CCTP. Additionally, the Synapse RFQ Router is 0x00cD000000003f7F682BE4813200893d4e690000 on all chains that support RFQ.

Synapse Token

Chain Address

Arbitrum 0x080f6aed32fc474dd5717105dba5ea57268f46eb

Aurora 0xd80d8688b02B3FD3afb81cDb124F188BB5aD0445

Avalanche 0x1f1E7c893855525b303f99bDF5c3c05Be09ca251
Base 0x432036208d2717394d2614d6697c46DF3Ed69540

Blast 0x9592f08387134e218327E6E8423400eb845EdE0E

Boba 0xb554A55358fF0382Fb21F0a478C3546d1106Be8c

BSC 0xa4080f1778e69467e905b8d6f72f6e441f9e9484

Canto 0x555982d2E211745b96736665e19D9308B615F78e

Cronos 0xFD0F80899983b8D46152aa1717D76cba71a31616

DFK 0xB6b5C854a8f71939556d4f3a2e5829F7FcC1bf2A

Dogechain 0xDfA53EeBA61D69E1D2b56b36d78449368F0265c1

Ethereum 0x0f2D719407FdBeFF09D87557AbB7232601FD9F29

Fantom 0xE55e19Fb4F2D85af758950957714292DAC1e25B2

Harmony 0xE55e19Fb4F2D85af758950957714292DAC1e25B2

Moonbeam 0xF44938b0125A6662f9536281aD2CD6c499F22004

Moonriver 0xd80d8688b02B3FD3afb81cDb124F188BB5aD0445

Optimism 0x5A5fFf6F753d7C11A56A52FE47a177a87e431655

Polygon 0xf8f9efc0db77d8881500bb06ff5d6abc3070e695

Metis 0x67c10c397dd0ba417329543c1a40eb48aaa7cd00

nUSD Token

Chain Address
Arbitrum 0x2913e812cf0dcca30fb28e6cac3d2dcff4497688

Aurora 0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c

Avalanche 0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46

Blast 0x3194B0A295D87fDAA54DF852c248F7a6BAF6c6e0

Boba 0x6B4712AE9797C199edd44F897cA09BC57628a1CF

BSC 0x23b891e5c62e0955ae2bd185990103928ab817b3

Cronos 0x396c9c192dd323995346632581BEF92a31AC623b

DFK 0x52285D426120aB91F378b3dF4A15a036a62200aE

Ethereum 0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F

Fantom 0xed2a7edd7413021d440b09d654f3b87712abab66

Harmony 0xed2a7edd7413021d440b09d654f3b87712abab66

Optimism 0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00

Polygon 0xb6c473756050de474286bed418b77aeac39b02af

Metis 0x961318fc85475e125b99cc9215f62679ae5200ab

nETH Token

Chain Address

Arbitrum 0x3ea9B0ab55F34Fb188824Ee288CeaEfC63cf908e

Base 0xb554A55358fF0382Fb21F0a478C3546d1106Be8c
Blast 0xce971282faac9fabcf121944956da7142cccc855

Boba 0x96419929d7949D6A801A6909c145C8EEf6A40431

Optimism 0x809DC529f07651bD43A172e8dB6f4a7a0d771036

Metis 0x931b8f17764362a3325d30681009f0edd6211231

SynapseBridge

Chain Address

Arbitrum 0x6F4e8eBa4D337f874Ab57478AcC2Cb5BACdc19c9

Aurora 0xaeD5b25BE1c3163c907a471082640450F928DDFE

Avalanche 0xC05e61d0E7a63D27546389B7aD62FdFf5A91aACE

Base 0xf07d1C752fAb503E47FEF309bf14fbDD3E867089

Blast 0x55769baf6ec39b3bf4aae948eb890ea33307ef3c

Boba 0x432036208d2717394d2614d6697c46DF3Ed69540

BSC 0xd123f70AE324d34A9E76b67a27bf77593bA8749f

Canto 0xDde5BEC4815E1CeCf336fb973Ca578e8D83606E0

Cronos 0xE27BFf97CE92C3e1Ff7AA9f86781FDd6D48F5eE9

DFK 0xE05c976d3f045D0E6E7A6f61083d98A15603cF6A

Dogechain 0x9508BF380c1e6f751D97604732eF1Bae6673f299

Ethereum 0x2796317b0fF8538F253012862c06787Adfb8cEb6
Fantom 0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b

Harmony 0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b

Klaytn 0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b

Metis 0x06Fea8513FF03a0d3f61324da709D4cf06F42A5c

Moonriver 0xaeD5b25BE1c3163c907a471082640450F928DDFE

Moonbeam 0x84A420459cd31C3c34583F67E0f0fB191067D32f

Optimism 0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b

Polygon 0x8F5BBB2BB8c2Ee94639E55d5F41de9b4839C1280

MiniChef

Chain Address

Avalanche 0x3a01521F8E7F012eB37eAAf1cb9490a5d9e18249

Aurora 0x809DC529f07651bD43A172e8dB6f4a7a0d771036

Arbitrum 0x73186f2Cf2493f20836b17b21ae79fc12934E207

Base 0xfFC2d603fde1F99ad94026c00B6204Bb9b8c36E9

Blast 0x3100dC8464A8523306c3C5034de24a8927d6E590

Boba 0xd5609cD0e1675331E4Fb1d43207C8d9D83AAb17C

BSC 0x8F5BBB2BB8c2Ee94639E55d5F41de9b4839C1280

Canto 0x93124c923dA389Bc0f13840fB822Ce715ca67ED6
Ethereum 0xd10eF2A513cEE0Db54E959eF16cAc711470B62cF

Fantom 0xaeD5b25BE1c3163c907a471082640450F928DDFE

Harmony 0xaeD5b25BE1c3163c907a471082640450F928DDFE

Metis 0xaB0D8Fc46249DaAcd5cB36c5F0bC4f0DAF34EBf5

Moonriver 0x432036208d2717394d2614d6697c46DF3Ed69540

Optimism 0xe8c610fcb63A4974F02Da52f0B4523937012Aaa0

Polygon 0x7875Af1a6878bdA1C129a4e2356A3fD040418Be5

Synapse Bridge Zaps

Chain Address

Arbitrum 0x37f9aE2e0Ea6742b9CAD5AbCfB6bBC3475b3862B

Aurora 0x2D8Ee8d6951cB4Eecfe4a79eb9C2F973C02596Ed

Avalanche 0x0EF812f4c68DC84c22A4821EF30ba2ffAB9C2f3A

Boba 0x64B4097bCCD27D49BC2A081984C39C3EeC427a2d

BSC 0x749F37Df06A99D6A8E065dd065f8cF947ca23697

Canto 0x8671A0465844a15eb7230C5dd8d6032c26c655B7

Cronos 0x991adb00eF4c4a6D1eA6036811138Db4379377C2

DFK 0x75224b0f245Fe51d5bf47A898DbB6720D4150BA7

Dogechain 0x544450Ffdfa5EA20528F21918E8aAC7B2C733381
Ethereum 0x6571d6be3d8460CF5F7d6711Cd9961860029D85F

Fantom 0xB003e75f7E0B5365e814302192E99b4EE08c0DEd

Harmony 0xB003e75f7E0B5365e814302192E99b4EE08c0DEd

Optimism 0x470f9522ff620eE45DF86C58E54E6A645fE3b4A7

Moonbeam 0x73783F028c60D463bc604cc53852C37C31dEC5e9

Moonriver 0x06Fea8513FF03a0d3f61324da709D4cf06F42A5c

Polygon 0x1c6aE197fF4BF7BA96c66C5FD64Cb22450aF9cC8

ETH Pools
Chain Address

Arbitrum 0xa067668661C84476aFcDc6fA5D758C4c01C34352

Avalanche 0x77a7e60555bC18B4Be44C181b2575eee46212d44

Base 0x6223bD82010E2fB69F329933De20897e7a4C225f

Blast 0x999fcd13C54B26E02a6Ccd185f71550b3a4641c0

Metis 0x09fEC30669d63A13c666d2129230dD5588E2e240

Optimism 0xE27BFf97CE92C3e1Ff7AA9f86781FDd6D48F5eE9

Stableswap Pools
Chain Address

Arbitrum 0x9Dd329F5411466d9e0C488fF72519CA9fEf0cb40

Aurora 0x3CE7AAD78B9eb47Fd2b487c463A17AAeD038B7EC
Avalanche 0xED2a7edd7413021d440b09D654f3b87712abAB66

Blast 0xa4bd1AAD7cF04567c10f38FC4355E91bba32aC9c

BNB Chain 0x28ec0B36F0819ecB5005cAB836F4ED5a2eCa4D13

Boba 0x75FF037256b36F15919369AC58695550bE72fead

Canto 0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c

Ethereum 0x1116898DdA4015eD8dDefb84b6e8Bc24528Af2d8

Fantom 0x85662fd123280827e11C59973Ac9fcBE838dC3B4

Metis 0x555982d2E211745b96736665e19D9308B615F78e

Optimism 0xF44938b0125A6662f9536281aD2CD6c499F22004

Polygon 0x85fCD7Dd0a1e1A9FCD5FD886ED522dE8221C3EE5

Synapse CCTP
Chain Address

Arbitrum 0x12715a66773bd9c54534a01abf01d05f6b4bd35e

Avalanche 0x12715a66773bd9c54534a01abf01d05f6b4bd35e

Base 0x12715a66773bd9c54534a01abf01d05f6b4bd35e

Ethereum 0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E

Optimism 0x12715a66773bd9c54534a01abf01d05f6b4bd35e

Polygon 0x12715a66773bd9c54534a01abf01d05f6b4bd35e

Synapse RFQ
Chain Address

Arbitrum 0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E

Base 0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E

Blast 0x34F52752975222d5994C206cE08C1d5B329f24dD

BNB Chain 0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E

Ethereum 0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E

Linea 0x34F52752975222d5994C206cE08C1d5B329f24dD

Optimism 0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E

Scroll 0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E

Bridge SDK
The quickest & simplest way to access cross-chain liquidity
The Synapse Router SDK is the easiest way for any developer to integrate cross-chain token & liquidity transfers into their application. The SDK is built to support full-fledged frontend
applications, but is fully isomorphic, able to be used both client & server-side.

The Router SDK is built on top of the Synapse Router Contract. To learn more see this page

The Synapse SDK allows protocols to own the user experience within their own dApp
Quick Start
Installation

Pre-reqs: Node v16+. The SDK is only fully tested on Node 16+ or greater. Earlier versions may have errors.

Depending on the package manager of your choice, install the SDK using one of the following:
NPMYarn
Copy
npx install @synapsecns/sdk-router

The SDK package relies on the following dependencies installed from NPM.
Copy
@ethersproject

ethers

Usage
How to use the SDK to create quotes and bridge transactions

Setting up your Environment


In order to begin constructing bridge related transactions, developers need to set up their environment for the relevant chains. The first is configuring chains and providers.

If using ethers v5:


Copy
//Set up providers (RPCs) for each chain desired

const arbitrumProvider: Provider = new ethers.providers.JsonRpcProvider(

'https://arb1.arbitrum.io/rpc'

const avalancheProvider: Provider = new ethers.providers.JsonRpcProvider(

'https://api.avax.network/ext/bc/C/rpc'

//Structure arguments properly

const chainIds = [42161, 43114]

const providers = [arbitrumProvider, avalancheProvider]

//Set up a SynapseSDK instance

const Synapse = new SynapseSDK(chainIds, providers)

If using ethers v6, will be required to install following dependency:


Copy
yarn add @ethersproject/providers@^5.7.2

or
Copy
npm install @ethersproject/providers@^5.7.2
Copy
import {

JsonRpcProvider,

} from '@ethersproject/providers'

//Set up providers (RPCs) for each chain desired

const arbitrumProvider: Provider = new JsonRpcProvider(

'https://arb1.arbitrum.io/rpc'

const avalancheProvider: Provider = new JsonRpcProvider(

'https://api.avax.network/ext/bc/C/rpc'
)

//Structure arguments properly

const chainIds = [42161, 43114]

const providers = [arbitrumProvider, avalancheProvider]

//Set up a SynapseSDK instance

const Synapse = new SynapseSDK(chainIds, providers)


In the code snippet above, we set up the different providers and format them, along with the Ids of the chains we are bridging from/to for our quotes.

bridgeQuote(): Getting a Quote

Getting a bridge quote returns all relevant information regarding a possible transaction. A quote returns important instructions for the bridge in a data type called "Query". The Query type is
defined as follows.

swapAdapter (string): The 0x address of the swap adapter.

tokenOut (string): The 0x address of the outputted token on that chain.

minAmountOut (Ethers BigNumber): The min amount of value exiting the transaction.

deadline (Ethers BigNumber): The deadline for the potential transaction.

rawParams (string): The 0x params for the potential transaction.

For more information on the Query data type, refer to this page. To access these Queries along with a quote for the estimated amount out, we call the function bridgeQuote() with the
relevant args.

The bridgeQuote function requires the following information

fromChain (number): The origin chain id.

toChain (number): The destination chain id.

fromToken (string): The 0x token address on the origin chain.

toToken (string): The 0x token address on the destination chain.

amount (Ethers BigNumber): The amount (with the correct amount of decimals specified by the token on the origin chain)

object (three seperate args):

deadline (Ethers BigNumber): Deadline for the transaction to be initiated on the origin chain, in seconds (optional)

excludedModules (array): List of bridge modules to exclude from the result, optional

originUserAddress (string): Address of the user on the origin chain, optional, mandatory if a smart contract is going to initiate the bridge operation

The bridgeQuote function returns the following information

feeAmount (Ethers BigNumber): The calculated amount of fee to be taken.

bridgeFee (number): The percentage of fee to be taken.

maxAmountOut (Ethers BigNumber): The maximum output amount resulting from the bridge transaction.

originQuery (Query Type): The query to be executed on the origin chain.

destQuery (Query Type): The query to be executed on the destination chain.

Copy
maxAmountOut is the value produced after fees

If the permutation of chains and tokens is not supported, the SDK will inform the user. Note that every bridge transaction requires this step.

Getting a Quote Example


Copy
const quotes = await Synapse.bridgeQuote(

routerAddress
42161, // Origin Chain

43114, // Destination Chain

'0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', // Origin Token Address

'0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664', // Destination Token Address

BigNumber.from('20000000') // Amount in

// Deadline for the transaction to be initiated on the origin chain, in seconds (optional)

deadline: 1234567890,

// List of bridge modules to exclude from the result, optional.

// Empty list means that all modules are included.

excludedModules: ['SynapseBridge', 'SynapseCCTP', 'SynapseRFQ'],

// Address of the user on the origin chain, optional.

// MANDATORY if a smart contract is going to initiate the bridge operation on behalf of the user.

originUserAddress: '0x1234567890abcdef1234567890abcdef12345678',

bridge(): Executing a Bridge Transaction

Once bridge quotes are retrieved, the Origin and Destination Queries can be used to execute the transaction.

The bridge() function requires the following information

toAddress (number): The 0x wallet address on the destination chain.

routerAddress (string): The 0x contract address on the origin chain of the bridge router contract.

fromChain (number): The origin chain id.

toChain (number): The destination chain id.

fromToken (string): The 0x token address on the origin chain.

amount (Ethers BigNumber): The amount (with the correct amount of decimals specified by the token on the origin chain)

originQuery (Query Type): The query to be executed on the origin chain.

destQuery (Query Type): The query to be executed on the destination chain.

The bridge() function returns the following information

to (string): The 0x wallet address on the destination chain.

data (string): The output data in 0x hex format

Executing a bridge transaction example


Copy
await Synapse.bridge(

'0x0AF91FA049A7e1894F480bFE5bBa20142C6c29a9', // To Address

bridgeQuote.routerAddress, // address of the contract to route the txn

42161, // Origin Chain

43114, // Destination Chain

'0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', // Origin Token Address

BigNumber.from('20000000'), // Amount

quote.originQuery, // Origin query from bridgeQuote()

quote.destQuery // Destination query from bridgeQuote()

)
Once this function is called the bridge transaction is initiated and the user should soon receive their funds on the destination chain.

The allBridgeQuotes() function returns a list of all the possible bridge quotes, with the first item in the array being the cheapest route. This function allows for quotes to be returned from
any of the bridge "types" such as RFQ or CCTP. Discover more here.

More information can be found in the synapsecns github repo

Version 0.10.0 breaking changes


A summary of breaking changes made in v0.10.0.
If you were previously using optional deadline parameter in either bridgeQuote or allBridgeQuotes, you need to wrap it into a separate options object, e.g. use

synapseSDK.bridgeQuote(originChainId, destChainId, tokenIn, tokenOut, amountIn, {deadline: 1234567890})

If you were previously using excludeCCTP parameter in bridgeQuote, you can now use synapseSDK.bridgeQuote(originChainId, destChainId, tokenIn, tokenOut, amountIn,

{excludedModules: ["SynapseCCTP"]})

It's possible to exclude multiple modules by providing a list with their names. The supported values are SynapseBridge, SynapseCCTP, SynapseRFQ

If a smart contract is going to initiate the bridge transaction on behalf of the user, you have to pass the user's address on the origin chain in bridgeQuote or allBridgeQuotes like

so: synapseSDK.bridgeQuote(originChainId, destChainId, tokenIn, tokenOut, amountIn, {originUserAdsress: "0x1234..."})

These three options could be combined in any way the consumer desires by excluding/including these in the options object

Old FastBridgeRouter deployment is deprecated, if your integration is using the hardcoded address, please make sure to check the router deployments/deprecated deployments

table here

Example
An implemented example of the Bridge SDK

Example 1:

A basic implementation
Copy
import { JsonRpcProvider } from '@ethersproject/providers'

import { Provider } from '@ethersproject/abstract-provider'

import { SynapseSDK } from '@synapsecns/sdk-router'

import { BigNumber } from '@ethersproject/bignumber'

//Set up providers (RPCs) for each chain desired**

const arbitrumProvider: Provider = new JsonRpcProvider('https://arb1.arbitrum.io/rpc')

const avalancheProvider: Provider = new JsonRpcProvider('https://api.avax.network/ext/bc/C/rpc')

//Structure arguments properly

const chainIds = [42161, 43114]

const providers = [arbitrumProvider, avalancheProvider]

//Set up a SynapseSDK instance

const Synapse = new SynapseSDK(chainIds, providers)

// quote

const quote = await Synapse.bridgeQuote(

42161, // From Chain

43114, // To Chain

'0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', // From Token

'0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664', // To Token

BigNumber.from('20000000'), // Amount

{
deadline: 1234567890,

excludedModules: ['SynapseRFQ'],

originUserAddress: '0x1234567890abcdef1234567890abcdef12345678',

// bridge

await Synapse.bridge(

'0x0AF91FA049A7e1894F480bFE5bBa20142C6c29a9', // To Address

quote.routerAddress, // bridge router contract address

42161, // Origin Chain

43114, // Destination Chain

'0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', // Origin Token Address

BigNumber.from('20000000'), // Amount

quote.originQuery, // Origin query from bridgeQuote()

quote.destQuery // Destination query from bridgeQuote()

Example 2

A More advanced example, using next.js, wagmi, and rainbowkit


Copy
//_ app.tsx

import '@styles/global.css'

import '@rainbow-me/rainbowkit/styles.css'

import { SynapseProvider } from '@/utils/SynapseProvider'

import type { AppProps } from 'next/app'

import { Provider as EthersProvider } from '@ethersproject/abstract-provider'

import { JsonRpcProvider } from '@ethersproject/providers'

import { configureChains, createClient, WagmiConfig } from 'wagmi'

import {

mainnet,

arbitrum,

aurora,

avalanche,

bsc,

canto,

fantom,

harmonyOne,

metis,

moonbeam,

moonriver,

optimism,

polygon,

} from 'wagmi/chains'

import {
getDefaultWallets,

RainbowKitProvider,

darkTheme,

} from '@rainbow-me/rainbowkit'

import { alchemyProvider } from 'wagmi/providers/alchemy'

import { publicProvider } from 'wagmi/providers/public'

export default function App({ Component, pageProps }: AppProps) {

const { chains, provider } = configureChains([mainnet,

arbitrum,

aurora,

avalanche,

bsc,

canto,

fantom,

harmonyOne,

metis,

moonbeam,

moonriver,

optimism,

polygon], [

alchemyProvider({ apiKey: 'API_KEY' }),

publicProvider(),

])

const { connectors } = getDefaultWallets({

appName: 'ExampleApp',

chains,

})

const wagmiClient = createClient({

autoConnect: true,

connectors,

provider,

})

// Synapse client params setup example

let synapseProviders: EthersProvider[] = []

chains.map((chain) => {

let rpc: EthersProvider = new JsonRpcProvider(chain.rpcUrls.default.http[0])

synapseProviders.push(rpc)

})

return (
<WagmiConfig client={wagmiClient}>

<RainbowKitProvider chains={chains} theme={darkTheme()}>

<SynapseProvider

chainIds={chains.map((chain) => chain.id)}

providers={synapseProviders}

>

<Component {...pageProps} />

</SynapseProvider>

</RainbowKitProvider>

</WagmiConfig>

}
Copy
//@/utils/SynapseProvider.tsx

import { createContext, useContext } from 'react'

import { SynapseSDK } from '@synapsecns/sdk-router'

import { Provider } from '@ethersproject/abstract-provider'

export const SynapseContext = createContext<SynapseSDK>(null)

export const SynapseProvider = ({

children,

chainIds,

providers,

}: {

children: React.ReactNode

chainIds: number[]

providers: Provider[]

}) => {

const sdk = new SynapseSDK(chainIds, providers)

return (

<SynapseContext.Provider value={sdk}>{children}</SynapseContext.Provider>

export const useSynapseContext = () => useContext(SynapseContext)


Copy
//homepage/index.tsx

import { useSynapseContext } from '@/utils/SynapseProvider'

import { Zero } from '@ethersproject/constants'

import { useState, useEffect } from 'react'

import { getNetwork } from '@wagmi/core'

import {

DEFAULT_FROM_CHAIN,

DEFAULT_TO_CHAIN,

DEFAULT_FROM_TOKEN,

DEFAULT_TO_TOKEN,
} from '@/constants/bridge'

export default function HomePage({ address }: { address: `0x${string}` }) {

// Get the synapse sdk

const SynapseSDK = useSynapseContext()

// Get the current time

const time = // add logic to get the current unix timestamp

// Example state hooks

const [fromToken, setFromToken] = useState(DEFAULT_FROM_TOKEN)

const [toToken, setToToken] = useState(DEFAULT_TO_TOKEN)

const [fromChainId, setFromChainId] = useState(DEFAULT_FROM_CHAIN)

const [toChainId, setToChainId] = useState(DEFAULT_TO_CHAIN)

const [amount, setAmount] = useState(Zero)

const [bridgeQuote, setBridgeQuote] = useState({

outputAmountString: '',

quotes: { originQuery: null, destQuery: null },

})

// Set connected network when component is mounted

useEffect(() => {

const { chain: fromChainIdRaw } = getNetwork()

setFromChainId(fromChainIdRaw ? fromChainIdRaw?.id : DEFAULT_FROM_CHAIN)

}, [])

// Get Quote function

// Suggestion: this function should be triggered from an useEffect when amount or to/from token/chain is altered

const getQuote = async () = {

SynapseSDK.bridgeQuote(

fromChainId,

toChainId,

fromToken,

toToken,

amount,

deadline = time + 10000

excludedModules: [],

originUserAddress = address

.then(

({ feeAmount, bridgeFee, maxAmountOut, originQuery, destQuery }) => {

let toValueBigNum = maxAmountOut ?? Zero

let toValueBase = toValueBigNum.div(toDecimals).toString()


let toValueMantissa = toValueBigNum.mod(toDecimals).toString()

setBridgeQuote({

outputAmountString: toValueBase + '.' + toValueMantissa,

quotes: {

originQuery,

destQuery,

},

})

// do something

.catch((err) => {

alert('error getting quote', err)

// do something

})

// Execute bridge function

const executeBridge = async () = {

await Synapse.bridge(

toAddress, // To Address

bridgeQuote.routerAddress, // bridge router contract address

fromChainId, // Origin Chain

toChainId, // Destination Chain

fromToken, // Origin Token Address

amount, // Amount

bridgeQuote.quotes.originQuery, // Origin query from bridgeQuote()

bridgeQuote.quotes.destQuery // Destination query from bridgeQuote()

).then(({to, data}) => {

// do something

.catch((err) => {

alert('error bridging', err)

// do something

})

// ...

}
Widget
Implement the Synapse Bridge into your dApp frontend
The Synapse Widget is a trivial implementation of the current Synapse bridge that enables protocols to integrate the bridge into their Frontend with little to no Engineering resources.

Example Widget
This explains how to integrate the bridge widget into your dApp in just a few minutes. This widget enables users to bridge tokens directly on your site, utilizing the Synapse Protocol.

Example use cases include:

Building a custom frontend for the Synapse Protocol


Bridging assets in a DeFi application
Acquiring a token to participate in a web3 game

This guide shows how to customize the widget to seamlessly blend with your app's theme by altering colors, fonts, and the token list. Learn to make the widget appear as an integral part of
your application.

Implementation

The widget is available on npm or yarn.

npm:
Copy
npm install @synapsecns/widget

yarn:
Copy
yarn add @synapsecns/widget

Get Started

To get started, import the Widget React component into your App. You will need a web3Provider parameter to pass to the widget. The demo landing page app, for example, defines this
provider from the ethers library. However, the component supports any similar provider:
Copy
import { Bridge } from '@synapsecns/widget'

const MyApp = () => {

const web3Provider = new ethers.BrowserProvider(window.ethereum)


return <Bridge web3Provider={web3Provider} />

Your site should now display a fully operational bridge widget integrating the routes and tokens supported by the Synapse protocol. By utilizing Synapse's multiple routers, you will be able
to find the best quotes to support your bridging use case.

Bridge Widget Props

The widget accepts a number of props to customize its functionality and appearance. Below is a quick summary with more detailed explanations later on.

web3Provider Web3Provider. Required.

customRpcs Custom JSON-RPC endpoints for your consumer application. Optional but recommended.

customTheme Custom theme for the widget. Optional. If not provided, defaults to light theme.

container HTML element to render the widget in. Optional. If not provided, false.

targetChainIds List of chain IDs for the destination side of your consumer app. Optional. If not provided, defaults to all Synapse Protocol supported networks.

targetTokens List of tokens to display in the widget. These tokens are imported from the widget package. Optional. If not provided, defaults to all Synapse Protocol supported tokens.

protocolName A short name for users of the widget to identify the protocol. Optional. If not provided, defaults to 'Target'.

hideConsoleErrors Boolean to enable suppressing Synapse (SDK, Widget) console.error messages in your consumer app's browser. If not provided, defaults to logging error messages.

A list of targetTokens can be found here

A list of Synapse Protocol supported chains can be found here

web3Provider prop (required)

ethers v6
Copy
const web3Provider = new ethers.BrowserProvider(window.ethereum)

ethers v5
Copy
const web3Provider = new ethers.providers.Web3Provider(window.ethereum, 'any')

Enhanced and Reliable Performance

The bridge widget is a React component designed for straightforward integration into any React-based project. Engineered for immediate functionality, and apart from a web3Provider, it
requires no initial parameters or web3 setup to begin operation. The widget facilitates bridging across all networks where the Synapse Protocol is active.

While the widget is primed for immediate use without configuration as it provides some basic primary and fallback JSON-RPC endpoints, we encourage developers to specify their own for
enhanced performance. This can be done by including a customRpcs parameter in the format of an object with chain ids as keys and their associated RPC endpoints as values.
Copy
import { Bridge, CustomRpcs } from '@synapsecns/widget'

const customRpcs: CustomRpcs = {

1: 'https://ethereum.my-custom-rpc.com',

10: 'https://optimism.my-custom-rpc.com',

42161: 'https://arbitrum.my-custom-rpc.com',

const MyApp = () => {

const web3Provider = new ethers.BrowserProvider(window.ethereum)

return <Bridge web3Provider={web3Provider} customRpcs={customRpcs} />

Token and Chain Customization


To further tailor the bridge widget to meet the specific demands of your project, additional optional targetTokens and targetChainIds parameters are provided. These allow for customizing
which chain and tokens your consuming application will support bridging to. This is effectively a way to filter for specific tokens on destination chain your application's users bridge.
Copy
import { Bridge, CustomRpcs, ETH, USDC, USDT } from '@synapsecns/widget'

const MyApp = () => {

const web3Provider = new ethers.BrowserProvider(window.ethereum)

return (

<Bridge

web3Provider={web3Provider}

targetTokens={[ETH, USDC, USDT]}

targetChainIds={[42161, 43114]}

/>

Note: Token naming convention is based on the tokens provided by @synapsecns/widget. For example, USDC on Metis is METISUSDC instead of simply USDC. The package's
src/constants/bridgeable.ts file contains a detailed list of supported tokens and the chains they live on. Additionally, to see a detailed list of Synapse Protocol supported chains, please
see src/constants/chains.ts.

useBridgeSelections Hook

The widget also provides a useBridgeSelections hook that can be used to access the selected tokens and chains. This hook returns an object of type BridgeSelections which has fields of
originChain, originToken, destinationChain, and destinationToken.

originChain and destinationChain structure:


Copy
{

id,

name,

originToken and destinationToken structure:


Copy
{

symbol,

address

In the consumer app:


Copy
const { originChain, originToken, destinationChain, destinationToken } =

useBridgeSelections()

Theme Customization

The widget is designed to be easily customized to match your app's theme. The widget accepts an optional customTheme configurable bgColor parameter for 'dark', 'light', and custom
color modes:
Copy
<Bridge web3Provider={web3Provider} customTheme={{ bgColor: 'light' }} />

Additionally, the widget supports more complex custom themes with the customTheme property. This allows for more fine-grained control over the widget's colors and fonts.
Copy
const customTheme = {

// Generate from base color, 'dark', or 'light'

bgColor: '#08153a',

// Basic customization

'--synapse-text': 'white',
'--synapse-secondary': '#ffffffb3',

'--synapse-root': '#16182e',

'--synapse-surface': 'linear-gradient(90deg, #1e223de6, #262b47e6)',

'--synapse-border': 'transparent',

// Full customization (Uses based colors by default)

'--synapse-focus': 'var(--synapse-secondary)',

'--synapse-select-bg': 'var(--synapse-root)',

'--synapse-select-text': 'var(--synapse-text)',

'--synapse-select-border': 'var(--synapse-border)',

'--synapse-button-bg': 'var(--synapse-surface)',

'--synapse-button-text': 'var(--synapse-text)',

'--synapse-button-border': 'var(--synapse-border)',

// Transaction progress colors (set bgColor to auto-generate)

'--synapse-progress': 'hsl(265deg 100% 65%)',

'--synapse-progress-flash': 'hsl(215deg 100% 65%)',

'--synapse-progress-success': 'hsl(120deg 100% 30%)',

'--synapse-progress-error': 'hsl(15deg 100% 65%)',

<Bridge

web3Provider={web3Provider}

customTheme={customTheme}

/>

Please see the examples/landing-page folder for more examples.

Container Customization

The widget additionally supports a container property of true or false to adjust its width to the container it's in.
Copy
<Bridge web3Provider={web3Provider} container={true} />

Example Apps

Within the repository's /examples folder, there are three example apps. The landing-page folder contains a fully functional demo with customizations of the widget. The with-react and
with-next folders contain a simple implementation of the widget using React and Next.js, respectively.
If you have any questions or need help implementing the widget, feel free to reach out to the team in Discord!

REST API
Synapse REST API
The Synapse REST API V2 is now live at https://synapse-rest-api-v2.herokuapp.com/

The API supports read-only actions from any on-chain Synapse contracts and generates Swap and Bridge quotes along with additional information. Some use cases this caters to, are for
application front-ends integrating the cross-chain bridge, liquidity providers, or cross-chain arbitragers.
Additionally, if you're unable to integrate the Synapse JavaScript SDK because your app is another language (Python, Rust, etc.), use the REST API.

Please read the FAQs and instructions carefully before reaching out to the on Discord for questions.

Usage
How to interact with the Rest API
The REST API V2 currently has four methods :

GET /swap

Used for getting a quote to swap tokens.

Parameters:

chain (int) - Chain ID of the desired chain

fromToken (string) - Symbol of token to swap from

toToken (string) - Symbol of token to swap to

amount (int) - Amount of fromToken to swap

Example Request:
Copy

/swap?chain=1&fromToken=USDC&toToken=DAI&amount=100

Returns:

routerAddress (string) - The address of the router contract

maxAmountOut (object) - The maximum amount of tokens that can be swapped out. Contains:

type (string) - The data type

hex (string) - The amount encoded in hexidecimal

query (object) - Parameters for the swap query:

0 (string) - Router contract address

1 (string) - Address of tokenIn

2 (object) - Amount of tokenIn to swap (same structure as maxAmountOut)

3 (object) - Minimum amount of tokenOut requested (same structure as maxAmountOut)

4 (string) - Encoded params for swap routing

swapAdapter (string) - Address of the swap adapter contract

tokenOut (string) - Address of tokenOut

minAmountOut (object) - Minimum amount of tokenOut required (same structure as maxAmountOut)

deadline (object) - Deadline parameter for the swap (same structure as maxAmountOut)

rawParams (string) - Encoded hex string containing swap parameters

maxAmountOutStr (string) - The maxAmountOut value formatted as a decimal string

GET /bridge

Used for getting a quote for bridging tokens between chains.

Parameters:

fromChain (int) - Chain ID of origin chain


toChain (int) - Chain ID of destination chain
fromToken (string) - Token symbol to bridge
toToken (string) - Token symbol to bridge to
amount (int) - Amount to bridge
Example:
Copy

/bridge?fromChain=1&toChain=42161&fromToken=USDC&toToken=USDC&amount=1000000

Returns:

feeAmount (object) - The fee amount for the swap. Contains:

type (string) - Data type

hex (string) - Fee amount encoded in hex

feeConfig (array) - Fee configuration parameters, contains:

0 (number) - Gas price

1 (object) - Fee percentage denominator (hex encoded BigNumber)

2 (object) - Protocol fee percentage numerator (hex encoded BigNumber)

routerAddress (string) - Address of the router contract

maxAmountOut (object) - Maximum amount receivable from swap, structure same as above

originQuery (object) - Original swap query parameters, contains:

swapAdapter (string) - Swap adapter address

tokenOut (string) - Address of output token

minAmountOut (object) - Minimum output token amount

deadline (object) - Expiry time

rawParams (string) - Encoded hex params

destQuery (object) - Destination swap query parameters, structure similar to originQuery above.

maxAmountOutStr (string) - maxAmountOut as a decimal string.

bridgeModuleName (string) - the bridge module the transaction will be routed through

gasDropAmount (BigNumber) - the amount of gas airdropped to the user on the dest chain.

GET /swapTxInfo

Used to get transaction data for executing a swap.

Parameters: (same as /swap)

Example:
Copy

/swapTxInfo?chain=1&fromToken=USDC&toToken=DAI&amount=100

Returns:

data: The binary data that forms the input to the transaction.

to: The address of the Synapse Router (the synapse bridge contract)

GET /bridgeTxInfo

Used to get transaction data for executing a bridge.

Parameters: (same as /bridge + destAddress)

Example:
Copy

/bridgeTxInfo?fromChain=1&toChain=42161&fromToken=USDC&toToken=USDC&amount=1000000&destAddress=0xcc78d2f004c9de9694ff6a9bbdee4793d30f3842
Returns:

data: The binary data that forms the input to the transaction.

to: The address of the Synapse Router (the synapse bridge contract)

Please read the FAQs and instructions carefully before reaching out to the on Discord for questions.

Examples
Examples of using the REST API with Javascript
Example 1:

Get the estimated output from executing a bridge


Copy
async function estimateBridgeOutput(

fromChain,

toChain,

fromToken,

toToken,

amountFrom

){

const query_string = `fromChain=${fromChain}&toChain=${toChain}&fromToken=${fromToken}&toToken=${toToken}&amountFrom=${amountFrom}`;

const response = await fetch(

`https://synapse-rest-api-v2.herokuapp.com/bridge?${query_string}`

);

const response_json = await response.json();

return await response_json;

estimateBridgeOutput(

1, // Ethereum Chain ID

42161, //Arbitrum Chain ID

"USDC",

"USDC",

"1000"

);

Example 2:

Generate an unsigned bridge transaction


Copy
async function generateUnsignedBridgeTxn(

fromChain,

toChain,

fromToken,

toToken,

amountFrom,

destAddress

){
const query_string = `fromChain=${fromChain}&toChain=${toChain}&fromToken=${fromToken}&toToken=${toToken}&amount=${amountFrom}&destAddress=${addressTo}`;

const response = await fetch(

`https://synapse-rest-api-v2.herokuapp.com/bridgeTxInfo?${query_string}`

);

const response_json = await response.json();

return await response_json;

generateUnsignedBridgeTxn(

1, // Ethereum Chain ID

42161, //Arbitrum Chain ID

"USDC",

"USDC",

"1000"

"0x2D2c027E0d1A899a1965910Dd272bcaE1cD03c22"

);

Example 3:

A cURL request to get information about a swap transaction.


Copy
curl --request GET \

--url 'https://synapse-rest-api-v2.herokuapp.com/swap?chain=1&fromToken=USDC&toToken=DAI&amount=100'

Synapse Router
A quick overview of the Synapse Router

Overview

The Synapse Router is an overhaul of current Synapse Bridge contracts that abstracts much of the complexity around liquidity based bridging to one simple bridge() function. Previously,
different functions would be used for different types of transactions which made it very complex to bridge at the contract level.

The new Router is comprised of one bridge() function and three supporting functions that help to construct a bridge transaction. All of the mentioned are organized by an important struct
called a “Query”. Before diving into these functions, a deeper understanding of how the bridge actually works is fundamental.

Most Synapse Bridge transactions require an “intermediary token” which is a token the protocol has mint/burn permissions over. Any third-party bridge necessitates such a structure to
properly account for bridged tokens. Find out more about Synapse intermediary tokens nUSD and nETH in the docs.

Thus, when users are not bridging or receiving an intermediary token, their transaction is routed through a liquidity pool on both chains. Determining what is the optimal route is not trivial
most times because of certain considerations: slippage in pools, expected tokenOut/tokenIn. The router condenses this complexity into a “query”.

What is a Query:

A query is a data structure that describes generic swap instructions, containing two items [token, amountReceived]. Given the desired token to bridge, a query identifies what “swap” needs
to happen on the respective chain in order for the bridge to:
A: Identify a route(s) for tokenIn to be swapped into an intermediary token
B: Identify a route(s) for the intermediary token to be swapped into the desired tokenOut on the destination chain.

Two of our supporting functions help do this:


Copy
getOriginAmountOut()

// returns us a list of "Queries" that would enable the desired bridging transaction on the Origin chain

// This should be called on the origin chain for every attempt to construct a bridge transaction

getDestinationAmountOut()

// returns us a list of "Queries" that would enable the desired bridging transaction on the Desitnation chain

// This should be called on the destination chain (after calling getOriginAmountOut on the origin chain) for every attempt to construct a bridge transaction

Note that these functions return a list of queries. This is because their can be several different routes for a bridge transaction. At the application level, a developer can decide which
route(which query) to utilize. In examples further down, the cheapest route is chosen. The developer must generate and select one query for both the Origin and Destination chains to
properly format a bridge() call.

Ultimately, the Query struct enables the ability to do the following:

Specify the tokenIn and its amount elsewhere and will transfer it before calling adapterSwap()

Set a parameter defining the minimumAmountOut of tokenOut

Set a deadline for the swap

Specify the Adapter that the swap will use

*Note that the resulting SwapQuery could be "empty" -- this only happens if the tokenIn or tokenOut is already a bridge token (e.g. nUSD or nETH)

See the Example Page for further information (and arguments) that the functions above require. It is imperative that the program uses the functions here to construct Queries instead of
manually doing so -- this guarantees that the transaction won't be reverted for misconfigured parameters.

Constructing a Bridge Transaction

To construct a bridge transaction there are two main parts, the off-chain to submit the transaction, and the on-chain transaction construction . Here we explore all on-chain interactions to
properly setup a bridge transaction at a high level.

Examples are provided on the ensuing page

The first step is utilizing the getConnectedBridgeTokens() supporting function to help structure getOriginAmountOut() — our method for generating the list of queries for the origin chain. We
call this function with the desired output token and are returned a list of tokens and their symbols.

Once we have our list of supported bridge token symbols we can generate a list of queries for the Origin chain by calling getOriginAmountOut() with the desired input token, the result from
getConnectedBridgeTokens(), and the amount of tokens being bridged. Now for every bridge token from the above list we know the amount of tokens being bridged, if this token is selected
as the intermediary token.

After reformatting the output from the above, we can get our Destination queries by calling getDestinationAmountOut(). The Synapse Router V1 only supports swaps via one of the
Synapse pools are available. Passing other instructions to the Router could result in failed transactions. Additionally, this function doesn't include tokenIn or token amount because those
variables can be populated by the bridge given the arguments to getOriginAmountOut(). By passing the list of bridge tokens together with their bridged amount, we can determine the
amount of tokens a user will receive at the end of the bridge transaction; each possible path having their own respective amount.

Now we have a list of queries for the Origin and Destination chain and need to select one (originQuery, destinationQuery) pair. Below is a simple function to extract the queries with the
lowest slippage (cheapest route).
Copy
maxIndex = destQueries.indexOf(destQueries.maxBy, (query) => { return query.minAmountOut });

originQuery = originQueries[maxIndex];

// destQuery.minAmountOut is the full quote for tokenIn => tokenOut cross-chain swap

destQuery = destQueries[maxIndex];

Finally, the developer can apply slippage and deadline parameters using the applyUserSettings(), and then call the bridge() function with the relevant parameters. Basic examples
displaying this setup can be found here.

Note that these are view functions that live on every supported chain, thus the program needs to call each function on its respective chain using off-chain services (such as an RPC).

At a High Level

On-chain we have two main objective, identify all possible routes (queries) for a specific bridging transaction, and then choose the desired routes so that we can call the bridge() function.
To actually bridge funds we need to call the bridge function using a web3 client.

Synapse Adapter
Exploring the Synapse Adapter
Overview

The SynapseAdapter is a configurable mechanism that facilitates the "swap" action on both the Origin and Destination chains. In SynapseRouterV1, the Adapter only supports Synapse
hosted pools. In future versions, additional Adapters will be configured to support aggregators on different chains. This will enable Any asset to Any asset bridging through the Synapse
Bridge.

Function

The SynapseAdapter provides a custom wrapper around standard "swaps" from one token to another given certain parameters. The Adapter also exposes useful methods to get Quotes
and the Swap Query struct. Additional methods provide views into the supported pools, tokens, and more.

Example
An example of a direct contract integration
Copy

/**

* Struct representing a bridge token.

* @param symbol Bridge token symbol: unique token ID consistent among all chains

* @param token Bridge token address

*/

type BridgeToken = {

symbol: String;

token: Address;

};

/**

* Struct representing a request for a swap quote from a bridge token.

* @param symbol Bridge token symbol: unique token ID consistent among all chains

* @param amountIn Amount of bridge token to start with, before the bridge fee is applied

*/

type DestRequest = {

symbol: String;

amountIn: BigInt;

};

/**

* Struct representing a request swap (list of instructions) for SynapseRouter.

* @param swapAdapter Adapter address that will perform the swap. Address(0) specifies a "no swap" query.

* @param tokenOut Token address to swap to.

* @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted.

* @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted.

* @param rawBytes ABI-encoded params for the swap that will be passed to `swapAdapter`.

*/

type SwapQuery = {

swapAdapter: Address;

tokenOut: Address;

minAmountOut: BigInt;

deadline: BigInt;

rawParams: BytesLike;
};

type UserSettings = {

maxSlippage: BigInt;

deadlineOrigin: BigInt;

deadlineDest: BigInt;

};

interface SynapseRouter {

/**

* Initiate a bridge transaction with an optional swap on both origin and destination chains

* @param to Address to receive tokens on destination chain

* @param chainId Destination chain id

* @param token Initial token for the bridge transaction to be pulled from the user

* @param amount Amount of the initial tokens for the bridge transaction

* @param originQuery Origin swap query. Empty struct indicates no swap is required

* @param destQuery Destination swap query. Empty struct indicates no swap is required

*/

bridge(

to: Address,

chainId: Number,

token: Address,

amount: BigInt,

originQuery: SwapQuery,

destQuery: SwapQuery

): null;

/**

* Gets the list of all bridge tokens (and their symbols), such that destination swap

* from a bridge token to `tokenOut` is possible.

* @param tokenOut Token address to swap to on destination chain

*/

getConnectedBridgeTokens(tokenOut: Address): BridgeToken[];

/**

* Finds the best path between `tokenIn` and every supported bridge token from the given list,

* treating the swap as "origin swap", without putting any restrictions on the swap.

* @param tokenIn Initial token that user wants to bridge/swap

* @param tokenSymbols List of symbols representing bridge tokens

* @param amountIn Amount of tokens user wants to bridge/swap

*/

getOriginAmountOut(

tokenIn: Address,

tokenSymbols: String[],
amountIn: BigInt

): SwapQuery[];

/**

* Finds the best path between every supported bridge token from the given list and `tokenOut`,

* treating the swap as "destination swap", limiting possible actions to those available for every bridge token.

* Will take the bridge fee into account, when returning a quote for every bridge token.

* @param requests List of structs with following information:

* - symbol: unique token ID consistent among all chains

* - amountIn: amount of bridge token to start with, before the bridge fee is applied

* @param tokenOut Token user wants to receive on destination chain

*/

getDestinationAmountOut(

requests: DestRequest[],

tokenOut: Address

): SwapQuery[];

/// Perform a cross-chain swap using Synapse:Bridge

/// Start from `amountIn` worth of `tokenIn` on origin chain

/// Receive `tokenOut` on destination chain

function synapseBridge(

originChainId: Number,

destChainId: Number,

tokenIn: Address,

tokenOut: Address,

amountIn: BigInt,

userOrigin: Address,

userDest: Address,

userSettings: UserSettings

){

// Every cross-chain swap via Synapse:Bridge is fueled by using one of the

// supported "bridge tokens" as the intermediary token.

// A following set of actions will be initiated by a single SynapseRouter.bridge() call:

// - Origin chain: tokenIn -> bToken swap is performed

// - Synapse: bridge bToken from origin chain to destination

// - Destination chain: bToken -> tokenOut is performed

// Here we describe a list of actions to perform such a cross-chain swap, knowing only

// - tokenIn, tokenOut, amountIn

// - SynapseRouter deployments

// - User settings for maximum slippage and deadline

// - User address on origin and destinaion chain (might be equal or different)

// Beware: below is a TypeScript pseudocode.


// 0. Fetch deployments of SynapseRouter on origin and destiantion chains

let routerOrigin = getSynapseRouter(originChainId);

let routerDest = getSynapseRouter(destChainId);

// 1. Determine the set of bridge tokens that could enable "receive tokenOut on destination chain"

// For that we pefrorm a static call to SynapseRouter on destination chain

let bridgeTokens = routerDest.getConnectedBridgeTokens(tokenOut);

// Then we get the list of bridge token symbols

let symbols = bridgeTokens.map((token) => token.symbol);

// 2. Get the list of Queries with possible swap instructions for origin chain

// For that we pefrorm a static call to SynapseRouter on origin chain

// This gets us the quotes from tokenIn to every bridge token (one quote per bridge token in the list)

let originQueries = routerOrigin.getOriginAmountOut(

tokenIn,

symbols,

amountIn

);

// 3. Get the list of Queries with possible swap instructions for destination chain

// First, we form a list of "destiantion requests" by merging

// list of token symbols with list of quotes obtained in step 2.

let requests = symbols.map((value, index) => {

let request: DestRequest = {

symbol: value,

amountIn: originQueries[index].minAmountOut,

};

return request;

});

// Then we perform a static call to SynapseRouter on destination chain

// This gets us the quotes from every bridge token to tokenOut (one quote per bridge token in the list)

// These quotes will take into account the fee for bridging the token to destination chain

let destQueries = routerDest.getDestinationAmountOut(requests, tokenOut);

// 4. Pick a pair of originQueries[i], destQueries[i] to pefrom the cross-chain swap

// In this example we are picking the pair that yeilds the best overall quote

let destQuery = maxBy(destQueries, (query) => query.minAmountOut);

let selectedIndex = destQueries.indexOf(destQuery)

let originQuery = originQueries[selectedIndex]

// Now we apply user slippage and deadline settings

originQuery = applyUserSettings(originQuery, userSettings)

destQuery = applyUserSettings(destQuery, userSettings)


// 5. Call SynapseRouter on origin chain to perform a swap

let amountETH: BigInt;

// 0xEeee address is used to represent native ETH

if (tokenIn == "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE") {

// If user selected "native ETH" as tokenIn, we would need to modify msg.value for the call

amountETH = amountIn;

} else {

// If user selected an ERC-20 token as tokenIn, we would need to use msg.value=0

amountETH = 0

// We also need to check if user approved routerOrigin to spend `tokenIn`

if (allowance(tokenIn, userOrigin, routerOrigin) < amountIn) {

// Users needs to issue a token approval

// tokenIn.approve(routerOrigin, amountIn)

// Perform a call to Synapse Router with all the derevied parameters

// Use previously determined msg.value for this call

// (WETH wrapping is done by the Synapse Router)

routerOrigin.bridge{value: amountETH}(

userDest,

destChainId,

tokenIn,

amountIn,

originQuery,

destQuery

);

Contracts
All of the code for the SynapseRouter is open source on Github

For event indexing, see the SynapseBridge Contract. Events that start with "TokenMint" or "TokenWithdraw" are emitted when the bridge transaction is completed on the destination chain.
The contracts that emit these events can be found in the deployments folder in the respective chain's "SynapseBridge.json"

The Router exists on the following chains :

Ethereum
BNB Chain
Polygon
Fantom
Optimism
Arbitrum
Avalanche
Moonriver
Moonbeam
Cronos
Boba

You might also like