Across is now live on BNB Smart Chain!
Bridge Now!
Across Documentation
V3 Developer Docs
V3 Developer Docs
  • 👋Introduction
    • Welcome to Across
    • What is Across?
    • Technical FAQ
    • Migration Guides
      • Migration from V2 to V3
      • Migration to CCTP
        • Migration Guide for Relayers
        • Migration Guide for API Users
      • Migration Guide for Non-EVM and Prefills
        • Breaking Changes for Indexers
        • Breaking Changes for API Users
        • Breaking Changes for Relayers
        • Testnet Environment for Migration
      • Solana Migration Guide
      • BNB Smart Chain Migration Guide
  • 🌟EXCLUSIVE
    • Integrate BNB Smart Chain
  • 🔗Use Cases
    • Instant Bridging in your Application
      • Bridge Integration Guide
      • Multichain Bridge UI Guide
      • Single Chain Bridge UI Guide
    • Embedded Crosschain Actions
      • Crosschain Actions Integration Guide
        • Using the Generic Multicaller Handler Contract
        • Using a Custom Handler Contract
      • Crosschain Actions UI Guide
    • Settle Crosschain Intents
    • ERC-7683 in Production
  • 🧠Concepts
    • What are Crosschain Intents?
    • Intents Architecture in Across
    • Intent Lifecycle in Across
    • Canonical Asset Maximalism
  • 🛠️Reference
    • API Reference
    • App SDK Reference
    • Contracts
      • Aleph Zero
      • Arbitrum
      • Base
      • BNB Smart Chain
      • Blast
      • Ethereum
      • Linea
      • Ink
      • Lens
      • Lisk
      • Mode
      • Optimism
      • Polygon
      • Redstone
      • Scroll
      • Soneium
      • Unichain
      • World Chain
      • zkSync
      • Zora
    • Selected Contract Functions
    • Supported Chains
    • Fees in the System
    • Actors in the System
    • Security Model and Verification
      • Disputing Root Bundles
      • Validating Root Bundles
    • Tracking Events
  • 🔁Relayers
    • Running a Relayer
    • Relayer Nomination
  • 📚Resources
    • Release Notes
    • Developer Support
    • Bug Bounty
    • Audits
Powered by GitBook
On this page
  • Pre-Requisites
  • Initialize the SDK
  • Retrieve a Quote
  • Execute a Quote
  • Tracking Deposits
  1. 🔗Use Cases
  2. Instant Bridging in your Application

Bridge Integration Guide

PreviousInstant Bridging in your ApplicationNextMultichain Bridge UI Guide

Last updated 2 months ago

LogoLogo

Products

  • Across Bridge
  • Across+
  • Across Settlement

Socials

  • Discord
  • Twitter
  • Medium
  • Forum

Resources

  • Blog
  • Across Brand Assets
  • Github

Routes

  • Bridge to Unichain
  • Bridge to Arbitrum
  • Bridge to Optimism
  • Bridge to Linea
  • Bridge to Polygon
  • Bridge to Base
  • Bridge to World Chain
  • Bridge to zkSync

This is a step-by-step framework agnostic guide to help you integrate Across Protocol into your applications using @across-protocol/app-sdk.

The App-SDK makes it extremely easy to add a bridge experience to any app (wallet, DEX, staking, lending/borrowing, etc). It abstracts away a lot of nuances about fetching bridge quotes and executing deposits by simply following a 2 step process:

  1. Request quotes using getQuote()

  2. Initiate deposit using executeQuote()

Along with the above mentioned functions, there are several utilities that we can use to ensure that the entire process of integrating a bridge takes less than an hour. Let's dive in!

Before starting the integration, please ensure that you fill this form and get your integrator ID. This will allow us to support you better as you progress towards the production-release and accelerate co-marketing efforts from our side.

Once you fill out the form, we’ll get in touch and provide your integrator ID. In the meantime, you can proceed with the integration by following this guide and simply add the integrator ID once it’s shared with you.


Pre-Requisites

  1. A frontend framework of your choice (Next.js, Vue, Svelte, Vanilla JS/TS, etc).

  2. Basic understanding of intents architecture in Across.

  3. A web3 wallet kit of your choice.


Initialize the SDK

Please ensure that you have followed all the necessary steps to setup your development environment. This may include - but is not limited to - setting up the frontend framework and adding a wallet kit along with any CSS frameworks.

We recommend Wagmi along with a wallet of your choice.

  1. We will kickoff by initializing the @across-protocol/app-sdk. To do so, install the SDK:

pnpm i @across-protocol/app-sdk viem
npm i @across-protocol/app-sdk viem
yarn add @across-protocol/app-sdk viem
bun add @across-protocol/app-sdk viem

We will be using some utilities provided by Viem to ensure a quick integration.

  1. Now, set up the AcrossClient , add in your integrator ID and configure the chains you want to support.

import { createAcrossClient } from "@across-protocol/app-sdk";
import { mainnet, optimism, arbitrum } from "viem/chains";

const client = createAcrossClient({
  integratorId: "your-integrator-ID", 
  chains: [mainnet, optimism, arbitrum],
});

In case you don't have an integrator ID, please fill this form.

Depending on your state management approach, there are multiple ways to integrate the AcrossClient. The key is to ensure that the AcrossClient wraps the body of your app correctly.

You can achieve this by setting up a React Context or by leveraging sophisticated solutions like Tanstack Query for better efficiency and robustness.


Retrieve a Quote

A Quote refers to the user’s expressed intent for a crosschain transaction. Rather than detailing the exact execution path, the user defines the desired outcome, all encapsulated in a standardized order structure. Here are the parameters you must define while retrieving a quote:

  • originChainId: chainId where the user's deposit is originating

  • destinationChainId: chainId where the user intends to receive their funds

  • inputToken: address of the token that the user is depositing on the origin chain

  • outputToken : address of the token that the user intends to receive on the destination chain

  • inputAmount: the raw amount the user is transferring. By raw amount, this means it should be represented exactly as it is in Solidity, meaning 1 USDC would be 1e6 or 1 ETH would be 1e18.

Here is an example:

import { parseEther } from "viem";

// WETH from Arbitrum -> Optimism
const quote = await client.getQuote({
  originChainId: arbitrum.id,
  destinationChainId: optimism.id,
  inputToken: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // WETH arb
  outputToken: "0x4200000000000000000000000000000000000006", // WETH opt
  inputAmount: parseEther("1"),
});

When executed successfully, the output will be something like this:

{
  "deposit": {
    "destinationChainId": 10,
    "destinationSpokePoolAddress": "0x6f26Bf09B1C792e3228e5467807a900A503c0281",
    "exclusiveRelayer": "0x15652636f3898F550b257B89926d5566821c32E1",
    "exclusivityDeadline": 9,
    "fillDeadline": 1742312575,
    "inputAmount": "1000000000000000000n",
    "inputToken": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
    "message": "0x",
    "originChainId": 42161,
    "outputAmount": "999873046291859064n",
    "outputToken": "0x4200000000000000000000000000000000000006",
    "quoteTimestamp": 1742300663,
    "recipient": null,
    "spokePoolAddress": "0xe35e9842fceaCA96570B734083f4a58e8F7C5f2A",
  },
  "estimatedFillTimeSec": 2,
  "fees": {
    "lpFee": {
      "pct": "48018079727047n",
      "total": "48018079727047n"
    },
    "relayerCapitalFee": {
      "pct": "78750000000001n",
      "total": "78750000000001n"
    },
    "relayerGasFee": {
      "pct": "185628413888n",
      "total": "185628413888n"
    },
    "totalRelayFee": {
      "pct": "126953708140936n",
      "total": "126953708140936n"
    }
  },
  "isAmountTooLow": false,
  "limits": {
    "maxDeposit": "1218075461911387487542n",
    "maxDepositInstant": "240880099587949741088n",
    "maxDepositShortDelay": "1218075461911387487542n",
    "minDeposit": "263464345370141n",
    "recommendedDepositInstant": "240880099587949741088n"
  }
}

Important fields to note here are:

  1. deposit.outputAmount : This is the amount of outputToken the user will receive after deducting the totalRelayFee from the inputAmount .

  2. estimatedFillTimeSec : This is the estimated time for the deposit to be filled and user to receive funds on the destination chain.

  3. fees.totalRelayFee : This is the total fees (lpFee + capitalFee + destinationGasFee) that the user will be charged for conducting the bridge transaction.

  4. isAmountTooLow : This is a warning for when the inputAmount is too low for any relayer to fill. You can use this to show an error on the UI and request user to increase inputAmount.

  5. limits : To make the UX seamless, we present you with minimum and maximum deposit limits of the system so you can help the user achieve maximum efficiency with their fill times on Across.

Get Supported Chains Easily

The Across App-SDK comes with the getSupportedChains() function that allows developers to get all the necessary data about the origin and destination chain in less than a second.

With this, you can build dropdown menus and simply pass the information fetched by getSupportedChains() to build a complete user experience in seconds with minimal effort.

To fetch supported chains, simply call the function with the SDK initialized like we had discussed above:

const fetchChains = async () => {
  const chains = await client.getSupportedChains({});
  console.log("supported chains are: ", chains);
  return chains;  
};

fetchChains();

You can use the same function to get the details of a singular chain as well:

const fetchChain = async (chainId: number) => {
  const chains = await client.getSupportedChains({chainId:chainId});
  console.log("supported chains are: ", chains);
  return chain;  
};

fetchChain(chainId)

Upon running the above command with chainId = 1 , You will see inputTokens and outputTokens supported by Across on Ethereum. Along with this, there will be several other details that will allow you to build a seamless UX:

{
  "chainId": 1,
  "explorerUrl": "https://etherscan.io",
  "inputTokens": [{...}, {...}, {...}],
  "logoUrl": "https://raw.githubusercontent.com/across-protocol/frontend/master/scripts/chain-configs/mainnet/assets/logo.svg",
  "name": "Ethereum",
  "outputTokens": [{...}, {...}, {...}],
  "publicRpcUrl": "https://mainnet.gateway.tenderly.co",
  "spokePool": "0x5c7BCd6E7De5423a257D81B442095A1a6ced35C5",
  "spokePoolBlock": 17117454
}

inputTokens and outputTokens are array of objects. These objects contain details like address, decimal, logoUrl, name and symbol related to all supported tokens.


Execute a Quote

Once the quote is received, you will need the following parameters to execute it successfully:

  1. deposit : This is the deposit to execute. You can get this from the return value of the useQuote()function that we discussed above.

  2. walletClient : This is the wallet client to use for the deposit. We recommend using wagmi for this.

  3. onProgress : This is a handler for the execution progress.

Quote execution has 3 steps:

  1. Approve: Approving the SpokePool contract to transfer the specified amount of tokens from your account.

  2. Deposit: Depositing the input token on the origin chain.

  3. Fill: Waiting for the deposit to be filled on the destination chain.

using the onProgress handler you can track which step the current quote execution is on and debug if needed.

Here is how you can call the executeQuote() function in the Across App-SDK:

import { useWalletClient } from "wagmi";

const wallet = useWalletClient();

await client.executeQuote({
  walletClient: wallet,
  deposit: quote.deposit, // returned by `getQuote`
  onProgress: (progress) => {
    if (progress.step === "approve" && progress.status === "txSuccess") {
      // if approving an ERC20, you have access to the approval receipt
      const { txReceipt } = progress;
    }
    if (progress.step === "deposit" && progress.status === "txSuccess") {
      // once deposit is successful you have access to depositId and the           receipt
      const { depositId, txReceipt } = progress;
    }
    if (progress.step === "fill" && progress.status === "txSuccess") {
      // if the fill is successful, you have access the following data
      const { fillTxTimestamp, txReceipt, actionSuccess } = progress;
      // actionSuccess is a boolean flag, telling us if your cross chain messages were successful
    }
  },
});

Upon successful execution, you will be able to see funds on the destinationChain in the requested outputToken .

Congratulations! You have now successfully integrated Across into your application.

You can find a complete App-SDK integration here for your reference.


Tracking Deposits

To track the lifecycle of a deposit you can use the app.across.to/api/deposit/status endpoint with the following parameters:

  • originChainId: chainId where the deposit originated from

  • depositId: The deposit id that is emitted from the DepositV3 function call as a V3FundsDeposited event

The recommended solution for tracking all Across deposits originating from your integration, for a single user, is to store the user's depositId and originChainId from each transaction originating from your app, and then get the status of each via the above endpoint.

In case you face any errors or need support, please feel free to reach out to us.