You could read this guide to understand the flow of integrating Rango-SDK. If you prefer to dive directly into the code and explore it there, you can use the links below.
Install TS SDK
If you decide not to use our TypeScript SDK and prefer integration in other programming languages, feel free to skip this step.
To integrate Rango SDK inside your dApp or wallet, you need to install rango-sdk using npm or yarn.
npm install --save rango-sdk
# or
yarn add rango-sdk
Then you need to instantiate RangoClient and use it in the next steps.
import { RangoClient } from "rango-sdk"
const rango = new RangoClient(RANGO_API_KEY)
Get Tokens & Blockchains Data
To get the list of available blockchains, tokens, and protocols (dex or bridge) supported by Rango, you could use the getAllMetadata method like this:
const meta = await rango.getAllMetadata()
Routing
Get All Routes
Using information retrieved from the meta, you could implement your own SwapBox including your blockchain and token selector. The next step is to show the preview of the best route possible when the user selects the source and the destination tokens.
The blockchain and symbol names must be exactly what is fetched from Rango's Meta API.
You could call this method periodically to get the updated route before the user confirms the route.
Confirm Route
Using the getAllRoutes method, you obtain a list of available routes. Once the user selects and confirms a route, you need to call the confirm method to notify Rango that the user has chosen this route for the next step and retrieve the final, updated route to display to the user.
To confirm the route, you must provide the selected route's request ID, the wallet addresses for all blockchains in the route, and optionally, a custom destination if the user wishes to send funds to a different wallet than the one specified for the related blockchain (in selectedWallets).
// user selects one of the routes
const selectedRoute = routingResponse.results[0]
const confirmResponse = await rango.confirmRoute({
requestId: selectedRoute.requestId,
selectedWallets: {
'BSC': '0xeae6d42093eae057e770010ffd6f4445f7956613',
'AVAX_CCHAIN': '0xeae6d42093eae057e770010ffd6f4445f7956613'
},
destination: '0x6f33bb1763eebead07cf8815a62fcd7b30311fa3'
})
At this stage, it is also possible to verify if the user has sufficient balance and fees for each step of the route in advance based on confirm response validation field. Also, you can compare the confirmed route with the route's output amount and alert the user if there is a significant difference.
// check if the route was okay
if (!confirmResponse.result || confirmResponse.error) {
// there was a problem in confirming the route
} else {
// everything was okay
// you could compare confirmed route with the route output amount
// and warn the user if there is noticable difference
// e.g. check if confirmed route output is 2% less than previous one
const confirmedOutput = new BigNumber(routingResponse.results[selected]?.outputAmount)
const finalOutput = new BigNumber(confirmResponse.result?.outputAmount)
if (finalOutput.lt(confirmedOutput.multipliedBy(new BigNumber(0.98))) {
// get double confirmation from the user
} else {
// proceed to executing the route
}
}
Route Execution
For every steps of the selected route, we need to repeat the next steps:
Creating Transaction
If the source blockchain for this step requires approval, such as EVM-based blockchains, Starknet, or Tron, and the user lacks sufficient approval, the Rango API will return an approval transaction in the response. The user must sign this transaction, and in the next createTransaction call for this step, the Rango API will provide the main transaction. Therefore, the createTransaction API response could either be an approval transaction (isApproval=true) or the main transaction (isApproval=false).
If you are verifying the balance and fee amount on your client side or you've already checked them in route confirmation step, it is advisable to set the balance and fee parameters to false to prevent duplicate checks or potential errors during validation.
Tracking Swap Status
After signing the transaction by the user and receiving transaction hash, you could periodically call Rango check-status API to track the transaction status. In Rango, each swap step could have 3 different states: running, failed and success. You only need to keep checking the status until you find out whether the transaction failed or succeeded.
const state = await rango.checkStatus({
requestId: confirmedRoute.requestId,
step: 1, // related step
txId: '0xfa88b705a5b4049adac7caff50c887d9600ef023ef1a937f8f8b6f44e90042b5'
})
if (response.status) {
// show latest status of the swap to the user
if (response.status === TransactionStatus.SUCCESS) {
// swap suceeded
} else if (response.status === TransactionStatus.FAILED) {
// swap failed
} else {
// swap is still running
// we need to call check-status method again after a timeout (10s)
}
}
Complete Code Flow
Node.JS Example
// run `node --import=tsx index.ts` in the terminal
import { CreateTransactionRequest, RangoClient, TransactionStatus, TransactionType } from "rango-sdk";
import { findToken } from '../shared/utils/meta.js'
import { TransactionRequest, ethers } from "ethers";
import { setTimeout } from 'timers/promises'
// setup wallet & RPC provider
// please change rpc provider url if you want to test another chain rather than BSC
const privateKey = 'YOUR_PRIVATE_KEY';
const wallet = new ethers.Wallet(privateKey);
const rpcProvider = new ethers.JsonRpcProvider('https://bsc-dataseed1.defibit.io');
const walletWithProvider = wallet.connect(rpcProvider);
const waleltAddress = walletWithProvider.address
// initiate sdk using your api key
const API_KEY = "c6381a79-2817-4602-83bf-6a641a409e32"
const rango = new RangoClient(API_KEY)
// get blockchains and tokens meta data
const meta = await rango.getAllMetadata()
// some example tokens for test purpose
const sourceBlockchain = "BSC"
const sourceTokenAddress = "0x55d398326f99059ff775485246999027b3197955"
const targetBlockchain = "BSC"
const targetTokenAddress = null
const amount = "0.001"
// find selected tokens in meta.tokens
const sourceToken = findToken(meta.tokens, sourceBlockchain, sourceTokenAddress)
const targetToken = findToken(meta.tokens, targetBlockchain, targetTokenAddress)
// get route
const routingRequest = {
from: sourceToken,
to: targetToken,
amount,
slippage: '1.0',
}
const routingResponse = await rango.getAllRoutes(routingRequest)
if (routingResponse.results.length === 0) {
throw new Error(`There was no route! ${routingResponse.error}`)
}
// confirm one of the routes
const selectedRoute = routingResponse.results[0]
const selectedWallets = selectedRoute.swaps
.flatMap(swap => [swap.from.blockchain, swap.to.blockchain])
.filter((blockchain, index, self) => self.indexOf(blockchain) === index)
.map(blockchain => ({ [blockchain]: waleltAddress }))
.reduce((acc, obj) => {
return { ...acc, ...obj };
}, {});
const confirmResponse = await rango.confirmRoute({
requestId: selectedRoute.requestId,
selectedWallets,
})
const confirmedRoute = confirmResponse.result
if (!confirmedRoute) {
throw new Error(`Error in confirming route, ${confirmResponse.error}`)
}
let step = 1
const swapSteps = confirmedRoute.result?.swaps || []
for (const swap of swapSteps) {
const request: CreateTransactionRequest = {
requestId: confirmedRoute.requestId,
step: step,
userSettings: {
slippage: '1.0',
infiniteApprove: false
},
validations: {
approve: true,
balance: false,
fee: false,
}
}
let createTransactionResponse = await rango.createTransaction(request)
let tx = createTransactionResponse.transaction
if (!tx) {
throw new Error(`Error creating the transaction ${createTransactionResponse.error}`)
}
if (tx.type === TransactionType.EVM) {
if (tx.isApprovalTx) {
// sign the approve transaction
const approveTransaction: TransactionRequest = {
from: tx.from,
to: tx.to,
data: tx.data,
value: tx.value,
maxFeePerGas: tx.maxFeePerGas,
maxPriorityFeePerGas: tx.maxPriorityFeePerGas,
gasPrice: tx.gasPrice,
gasLimit: tx.gasLimit,
}
const { hash } = await walletWithProvider.sendTransaction(approveTransaction);
// wait for approval
while (true) {
await setTimeout(5_000)
const { isApproved, currentApprovedAmount, requiredApprovedAmount, txStatus } = await rango.checkApproval(confirmedRoute.requestId, hash)
if (isApproved)
break
else if (txStatus === TransactionStatus.FAILED)
throw new Error('Approve transaction failed in blockchain')
else if (txStatus === TransactionStatus.SUCCESS)
throw new Error(`Insufficient approve, current amount: ${currentApprovedAmount}, required amount: ${requiredApprovedAmount}`)
}
// create the main transaction if previous one was approval transaction
createTransactionResponse = await rango.createTransaction(request)
tx = createTransactionResponse.transaction
if (!tx || tx.type !== TransactionType.EVM) {
throw new Error(`Error creating the transaction ${createTransactionResponse.error}`)
}
}
// sign the main transaction
const mainTransaction: TransactionRequest = {
from: tx.from,
to: tx.to,
data: tx.data,
value: tx.value,
maxFeePerGas: tx.maxFeePerGas,
maxPriorityFeePerGas: tx.maxPriorityFeePerGas,
gasPrice: tx.gasPrice,
gasLimit: tx.gasLimit,
}
const { hash } = await walletWithProvider.sendTransaction(mainTransaction);
// track swap status
while (true) {
await setTimeout(10_000)
const state = await rango.checkStatus({
requestId: confirmedRoute.requestId,
step,
txId: hash
})
const status = state.status
if (status === TransactionStatus.SUCCESS) {
// we could proceed with the next step of the route
step += 1;
break
} else if (status === TransactionStatus.FAILED) {
throw new Error(`Swap failed on step ${step}`)
}
}
}
}