Crosschain Swap
The Across /swap/approval
API enables developers to prepare and execute crosschain swaps through a single streamlined interface. It abstracts the complexity of bridging + swapping into a developer-friendly flow that can be integrated into any stack.
The response includes:
Allowance state: current and required allowance values.
Balance state: current and expected balance after the swap.
Executable approval transaction: calldata to set the required allowance if insufficient.
Executable swap transaction: ready-to-submit transaction for the crosschain swap.
Swap quote: details of the swap execution (input, output, fees, route).
This ensures developers can both validate prerequisites (balances, approvals) and directly execute crosschain swaps with minimal custom logic.
Crosschain Swap Flow
Fetch Chains and Token
Fetch all the available chains and tokens using GET /swap/chains and GET /swap/tokens and select your tradeType
, originChain
, destinationChain
, inputToken
and outputToken
Execute Crosschan Swap
Now that we have crosschain intent filled out, make sure you define the amount
of inputToken
you want to use here. Call the GET /swap/approval endpoint. This endpoint will return a ready transaction and you just need to sign and execute the transaction using your wallet.
Now let's dive deeper and code the whole flow out!
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.
Basic Execution
Let us now focus on conducting a crosschain swap with the following parameters:
tradeType
: Defines the type of trade for eg -minOutput
.amount
: Specifies amount ofinputToken
in the smallest unit (wei for ETH or 6 decimals for USDC).inputToken
: Address of the token being swapped.originChainId
: Chain ID of the source/origin chain.outputToken
: Address of the token to be received after the swap.destinationChainId
: Chain ID of the destination chain where the output token will be delivered.depositor
: The address of the sender (usually the wallet initiating the swap).
In this specific example, we will be swapping 1 USDC on Optimism with a related amount of ETH on Arbitrum.
Run the following script to conduct the crosschain swap:
import { createWalletClient, http, parseUnits } from 'viem'
import { privateKeyToAccount } from 'viem/accounts';
import { optimism, arbitrum } from 'viem/chains'
import axios from 'axios'
import dotenv from 'dotenv'
dotenv.config()
async function acrossSwapApproval() {
const PRIVATE_KEY = process.env.PRIVATE_KEY
const account = privateKeyToAccount(PRIVATE_KEY)
const client = createWalletClient({
account,
chain: optimism,
transport: http()
})
const { data } = await axios.get('https://app.across.to/api/swap/approval', {
params: {
tradeType: 'minOutput',
amount: parseUnits('1',6).toString(),
inputToken: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85',
originChainId: 10,
outputToken: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
destinationChainId: 42161,
depositor: account.address,
}
})
// Handle token approvals if required
if (data.approvalTxns) {
for (const approvalTxn of data.approvalTxns) {
const tx = await client.sendTransaction({
to: approvalTxn.to,
data: approvalTxn.data,
})
console.log('Approval tx hash:', tx)
}
}
// Execute swap transaction
const tx = await client.sendTransaction({
to: data.swapTx.to,
data: data.swapTx.data,
value: data.swapTx.value ? BigInt(data.swapTx.value) : undefined,
})
console.log('Crosschain swap tx hash:', tx)
}
acrossSwapApproval()
Upon successful execution of the /swap/approval
endpoint, the return data
should be something like this:
{
"checks": {
"allowance": {
"token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"spender": "0x5c7BCd6E7De5423a257D81B442095A1a6ced35C5",
"actual": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"expected": "1065159719994"
},
"balance": {
"token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"actual": "0",
"expected": "1065159719994"
}
},
"steps": {
"bridge": {
"inputAmount": "1065159719994",
"outputAmount": "940201830",
"tokenIn": {
"decimals": 18,
"symbol": "ETH",
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"name": "Ether",
"chainId": 1
},
"tokenOut": {
"decimals": 18,
"symbol": "ETH",
"address": "0xE5ecd226b3032910CEaa43ba92EE8232f8237553",
"name": "Ether",
"chainId": 232
},
"fees": {
"totalRelay": {
"pct": "999117313760226216",
"total": "1064219518164"
},
"relayerCapital": {
"pct": "99999954936899",
"total": "106515923"
},
"relayerGas": {
"pct": "999017313805289317",
"total": "1064113002241"
},
"lp": {
"pct": "0",
"total": "0"
}
}
}
},
"refundToken": {
"decimals": 18,
"symbol": "WETH",
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"name": "Ether",
"chainId": 1
},
"inputAmount": "1065159719994",
"expectedOutputAmount": "940201830",
"minOutputAmount": "940201830",
"expectedFillTime": 8,
"swapTx": {
"simulationSuccess": false,
"chainId": 1,
"to": "0x5c7BCd6E7De5423a257D81B442095A1a6ced35C5",
"data": "0x7b939232000000000000000000000000a4d353bbc130cbef1811f27ac70989f9d568ceab000000000000000000000000c5939f59b3c9662377dda53a08d5085b2d52b719000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000e5ecd226b3032910ceaa43ba92ee8232f8237553000000000000000000000000000000000000000000000000000000f800777c3a00000000000000000000000000000000000000000000000000000000380a576600000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067f4f4530000000000000000000000000000000000000000000000000000000067f5494300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000c5939f59b3c9662377dda53a08d5085b2d52b719000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044ef8738d3000000000000000000000000e5ecd226b3032910ceaa43ba92ee8232f8237553000000000000000000000000a4d353bbc130cbef1811f27ac70989f9d568ceab00000000000000000000000000000000000000000000000000000000",
"maxFeePerGas": "1213349849",
"maxPriorityFeePerGas": "500000000"
}
}
With the above response from the /swap/approval
endpoint, you can proceed to sign transaction on the wallet client.
Sample Routes using /swap/approval
Endpoint
/swap/approval
EndpointThis section is aimed at displaying how some routes can be formed to help developers build crosschain swap interfaces quickly.
1. Swap USDC on Ethereum Mainnet to ETH on Arbitrum
import axios from 'axios'
import { parseUnits } from 'viem'
async function fetchSwapDetails() {
const { data } = await axios.get('https://app.across.to/api/swap/approval', {
params: {
tradeType: 'minOutput',
amount: parseUnits("1",6).toString(),
inputToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // USDC on Ethereum
originChainId: 1,
outputToken: '0xA4d353BBc130cbeF1811f27ac70989F9d568CeAB', // ETH on Arbitrum
destinationChainId: 42161,
depositor: '<address-of-the-sender>'
}
})
// console.log(data) [if needed]
return data
}
Key Parameters:
tradeType
: Defines the type of trade for eg -minOutput
.amount
: Specifies amount ofinputToken
in the smalled unit (wei for ETH or 6 decimals for USDC).inputToken
: Address of the token being swapped.originChainId
: Chain ID of the source/origin chain.outputToken
: Address of the token to be received after the swap.destinationChainId
: Chain ID of the destination chain where the output token will be delivered.depositor
: The address of the sender (usually the wallet initiating the swap).
Same parameters will be followed in all other examples in this section.
2. Swap ETH from Ethereum Mainnet to GHO on Lens Chain
import axios from 'axios'
import { parseUnits } from 'viem'
async function fetchSwapDetails() {
const { data } = await axios.get('https://app.across.to/api/swap/approval', {
params: {
tradeType: 'minOutput',
amount: parseUnits("0.001",18).toString(),
inputToken: '0x1ff1dC3cB9eeDbC6Eb2d99C03b30A05cA625fB5a', // ETH on Ethereum
originChainId: 1,
outputToken: '0x6bDc36E20D267Ff0dd6097799f82e78907105e2F', // GHO on Lens Chain
destinationChainId: 232,
depositor: '<address-of-the-sender>'
}
})
// console.log(data) [if needed]
return data
}
3. Swap USDC on Ethereum Mainnet to GHO on Lens Chain
import axios from 'axios'
import { parseUnits } from 'viem'
async function fetchSwapDetails() {
const { data } = await axios.get('https://app.across.to/api/swap/approval', {
params: {
tradeType: 'minOutput',
amount: parseUnits("1",6).toString(),
inputToken: '0x1ff1dC3cB9eeDbC6Eb2d99C03b30A05cA625fB5a', // USDC on Ethereum
originChainId: 1,
outputToken: '0x6bDc36E20D267Ff0dd6097799f82e78907105e2F', // GHO on Lens Chain
destinationChainId: 232,
depositor: '<address-of-the-depositor>'
}
})
// console.log(data) [if needed]
return data
}
Tracking Deposits
To track the lifecycle of a deposit you can use the GET /deposit/status endpoint with the following parameters:
originChainId
: chainId where the deposit originated from. Use this in conjunction with thedepositId
parameter.depositId
: The deposit id that is emitted from theDepositV3
function call as aV3FundsDeposited
event. Use this in conjunction with theoriginChainId
parameter.depositTxHash
: The deposit transaction hash that is emitted from the DepositV3 function call as a V3FundsDeposited event. If you are using this, you do not need the above parameters.
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
or the depositTxHash
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.
Last updated