Lens Crosschain Swap Guide
This is a comprehensive walkthrough for developers looking to implement crosschain token swaps using the Across /swap/approval
API on the Lens chain.
Across /swap/approval
API provides a streamlined interface for developers to build crosschain swapping applications that easily fit in any development environment and tech stacks.
The ideal Across Crosschain Swap API approach involves setting an allowance and executing the transaction directly via the signer. This is a gasful flow.
The Cross-swap API is currently in beta. We appreciate your participation and kindly request you to report any unexpected behaviors or API response anomalies.
Let's get started!
Understanding the /swap/approval
Endpoint
/swap/approval
EndpointIn the /swap/approval
endpoint crosschain swap flow, consumers of the API need to make sure that the depositor has granted sufficient allowance to the respective contract of the Across Protocol.
The API returns data that can be used to:
check current and expected allowance
check current and expected balance
executable approval transaction(s)
executable swap transaction
swap quote
API Endpoint
GET https://app.across.to/api/swap/approval
Basic Execution
Returns data required to execute a cross-chain swap.
If the input token requires approval, approvalTxns
will be included in the response.
Type of trade. Use minOutput
, exactInput
or exactOutput
.
exactInput
Possible values: Required amount of output token in smallest unit.
1000000
Address of the input token on the origin chain.
0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85
Address of the output token on the destination chain.
0x82aF49447D8a07e3bd95BD0d56f35241523fBab1
Chain ID of the origin chain.
1
Chain ID of the destination chain.
232
Address of the depositor initiating the swap.
0xDEPOSITOR_ADDRESS
Address of the account receiving the output token.
0xRECIPIENT_ADDRESS
2-byte hex-string that identifies the integrator. E.g., "0xdead".
0xdead
Address to receive refunds. Defaults to depositor if not provided.
0xDEPOSITOR_ADDRESS
Specifies whether refund should be sent on the origin chain. Defaults to true.
true
Slippage tolerance percentage (e.g., 1 for 1%, 0.5 for 0.5%).
1
Swap approval data returned successfully.
Bad request due to invalid input parameter.
GET /api/swap/approval HTTP/1.1
Host: app.across.to
Accept: */*
{
"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": "0x7b939232000000000000000000000000a4d353bbc130cbef1811f27ac70989f9d568ceab...",
"maxFeePerGas": "1213349849",
"maxPriorityFeePerGas": "500000000"
}
}
Quickstart Code Snippets
import { createWalletClient, http, privateKeyToAccount, parseUnits } from 'viem'
import { optimism } from 'viem/chains'
import axios from 'axios'
async function acrossSwapApproval() {
const PRIVATE_KEY = '0x...' as `0x${string}`
const RPC_URL = 'https://rpc.lens.xyz'
const account = privateKeyToAccount(PRIVATE_KEY)
const client = createWalletClient({
account,
chain: lens,
transport: http(RPC_URL)
})
const { data } = await axios.get('https://app.across.to/api/swap/approval', {
params: {
tradeType: 'minOutput',
amount: parseUnits('0.001',18).toString(),
inputToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // ETH on Ethereum Mainnet
originChainId: 1,
outputToken: '0xE5ecd226b3032910CEaa43ba92EE8232f8237553', // WETH on Lens Chain
destinationChainId: 232,
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)
await client.waitForTransactionReceipt({ 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('Cross-swap tx hash:', tx)
await client.waitForTransactionReceipt({ hash: tx })
}
export default acrossSwapApproval
Important fields to note here are:
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 (232 for Lens Chain mainnet).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).
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.
Lens Use Cases using /swap/approval
Endpoint
/swap/approval
Endpoint1. Bridge ETH on Ethereum Mainnet to ETH 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: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // ETH on Ethereum
originChainId: 1,
outputToken: '0xE5ecd226b3032910CEaa43ba92EE8232f8237553', // WETH on Lens Chain
destinationChainId: 232,
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 (232 for lens chain mainnet).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).
2. Bridge GHO 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', // WETH on Lens Chain
destinationChainId: 232,
depositor: '<address-of-the-sender>'
}
})
// console.log(data) [if needed]
return data
}
Key Parameters:
tradeType
: Use"minOutput"
to ensure a minimum amount after the swap.inputToken
: Address of ETH or USDC on the origin chain. (Note: USDC contains 6 decimal places)originChainId
: Chain ID of the origin chain.outputToken
: Corresponding token address on Lens.destinationChainId
: Chain ID of Lens.
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("0.001",18).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
}
Key Parameters:
inputToken
: Address of USDC on Ethereum Mainnet.outputToken
: Address of GHO on Lens.
With this, you have now completely understood the core principles of using the /swap/approval
endpoint and walked through certain examples that enable you to swap and bridge tokens to build custom use cases on Lens Chain.
If you have any doubts, please feel free to reach out to us here.
Last updated