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.
exactInputPossible values: Required amount of output token in smallest unit.
1000000Address of the input token on the origin chain.
0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85Address of the output token on the destination chain.
0x82aF49447D8a07e3bd95BD0d56f35241523fBab1Chain ID of the origin chain.
1Chain ID of the destination chain.
232Address of the depositor initiating the swap.
0xDEPOSITOR_ADDRESSAddress of the account receiving the output token.
0xRECIPIENT_ADDRESS2-byte hex-string that identifies the integrator. E.g., "0xdead".
0xdeadAddress to receive refunds. Defaults to depositor if not provided.
0xDEPOSITOR_ADDRESSSpecifies whether refund should be sent on the origin chain. Defaults to true.
trueSlippage tolerance percentage (e.g., 1 for 1%, 0.5 for 0.5%).
1Swap approval data returned successfully.
Bad request due to invalid input parameter.
GET /api/swap/approval?tradeType=exactInput&amount=1000000&inputToken=0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85&outputToken=0x82aF49447D8a07e3bd95BD0d56f35241523fBab1&originChainId=1&destinationChainId=232&depositor=0xDEPOSITOR_ADDRESS&recipient=0xRECIPIENT_ADDRESS 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 acrossSwapApprovalconst wallet = new Wallet(PRIVATE_KEY).connect(
new ethers.providers.JsonRpcProvider(RPC_URL)
);
const { data } = await axios.get(
`https://preview.across.to/api/swap/approval`,
{
params: {
tradeType: "minOutput",
// required min. output amount of `outputToken` in smallest unit
amount: ethers.utils.parseUnits("0.001", 18).toString(),
// Eth on Ethereum Mainnet
inputToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
// Ethereum Mainnet
originChainId: 1,
// WETH on Lens Chain
outputToken: "0xE5ecd226b3032910CEaa43ba92EE8232f8237553",
// Lens Chain
destinationChainId: 232,
depositor: wallet.address,
},
}
);
// If the depositor address has insufficient allowance, the response
// will contain the field `approvalTxns`. This field is a list of
// approve-transactions that are required before a cross-swap can be
// facilitated.
if (data.approvalTxns) {
// This field is a list because in rare cases (e.g. USDT), updating an
// already-set allowance requires 2 steps:
// 1. Set allowance to 0
// 2. Set allowance to new value
for (const approvalTxn of data.approvalTxns) {
const tx = await signer.sendTransaction({
to: approvalTxn.to,
data: approvalTxn.data,
});
console.log(`Approval tx hash:`, tx.hash);
await tx.wait();
}
}
// Sign cross-swap tx
const tx = await wallet.sendTransaction({
to: data.swapTx.to,
data: data.swapTx.data,
value: data.swapTx.value,
});
console.log("Cross-swap tx hash: ", tx.hash);
await tx.wait();Important fields to note here are:
tradeType: Defines the type of trade for eg -minOutput.amount: Specifies amount ofinputTokenin 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 ofinputTokenin 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