Skip to main content

Integrate THORSwap API to your wallet using SwapKit SDK - WIP

Learn how to integrate the THORSwap API into your wallet application. This app aggregates the liquidity across blockchains supported by THORChain, provides the best price to the user and then execute swaps.

info

This tutorial will use the SwapKit SDK for wallet management, building and broadcasting transactions.

If your project is using core blockchain libraries bitcoinjs-lib, Cosmos Client and web3.js, then check out the advanced tutorial.


In this tutorial, we will learn how to use the THORSwap quote endpoint, which allows users to fetch available quotes across a selection of top providers, and then execute a trade using the data in the response.

Prerequisites

To prepare for the rest of this tutorial, you need to have:

  • npm (npx) version
  • node version
  • private key of a wallet with some funds

Here is the github repo if you ever need help.

There are 3 main parts to executing a swap using the THORSwap API:

  • ⚪ Fetch a quote from the API
  • ⚪ Set a token allowance (EVM only)
  • ⚪ Perform the swap

This is our gameplan, so now let's get started!

Part 1. Fetch Quote

The THORSwap API will return an array of quotes for a given trade, ordered by the best return amount. The function below will pick the best route from the API response. For more detail on the endpoint check out the API docs.

// helpers.ts

// Function get a quote using /quote and returns the best route. We will pass in a specified senderAddress & recipientAddress, but you might want to use the connected wallet's address in your project.
const fetchBestQuote = async ({
amount,
fromAsset,
toAsset,
senderAddress,
recipientAddress,
provider,
}) => {
try {
const THORSWAP_QUOTE_BASE_URL =
'https://api.thorswap.net/aggregator/tokens/quote'
const thorswapApiUrl = new URL(THORSWAP_QUOTE_BASE_URL)
thorswapApiUrl.searchParams.append('sellAsset', fromAsset)
thorswapApiUrl.searchParams.append('buyAsset', toAsset)
thorswapApiUrl.searchParams.append('sellAmount', amount)
thorswapApiUrl.searchParams.append('senderAddress', senderAddress)
thorswapApiUrl.searchParams.append('recipientAddress', recipientAddress)
thorswapApiUrl.searchParams.append('providers', provider)
thorswapApiUrl.searchParams.append('providers', 'THORCHAIN')
const response = await fetch(thorswapApiUrl.toString())
const data = await response.json()

const bestRoute = data.routes[0]
return bestRoute
} catch (error) {
console.error(error)
}
}

Within the response there is a transaction object that we will be using to make our swap. As THORChain supports multiple blockchains, the THORSwap API will return a transaction object that can be used to execute the swap on the blockchain of the sellAsset. For example, if the sellAsset is ETH.CRV, the transaction object will be an Ethereum transaction object. If the sellAsset is BTC.BTC, the transaction object will be a hex string of a Bitcoin transaction.

Part 1a. Inspecting the transaction

The transaction in the response will differ depending on the scenario of the swap. Here is what to expect, depending on the sellAsset and buyAsset:

ScenarioSellAssetBuyAssetTransactionexample
EVM OnlyETH.CRVETH.UNIEthereum Transaction object
SwapInETH.CRVBTC.BTCEthereum Transaction object
SwapOutBTC.BTC / LTC.LTC / DOGE.DOGE / BCH.BCHETH.CRVHex string of a PSBT transaction
SwapOutGAIA.ATOM / THOR.RUNE / BNB.BNBETH.CRVThe TxBody of a Cosmos TxBody
THORChain onlyBTC.BTC / LTC.LTC / DOGE.DOGE / BCH.BCHTHOR.RUNEThe TxBody of a Cosmos TxBody
THORChain onlyGAIA.ATOM / THOR.RUNE / BNB.BNBBTC.BTCThe TxBody of a Cosmos TxBody

The transaction objects differ depending on the SellAsset were created with the low-level libraries of each blockchain in mind. For example, the Ethereum transaction object can be broadcasted using either the ethers.js or web3.js libraries. The Bitcoin transaction object is created using the bitcoinjs-lib library. The Cosmos transaction object is created using the Cosmos Client library.

Examples for handling the different transaction objects will be shown in Part 3.

Step 1 done!

  • ✅ Fetch a quote from the API
  • ⚪ Set a token allowance (EVM only)
  • ⚪ Perform the swap

Part 2. Set a Token Allowance (EVM only)

A token allowance is required if you want a third-party to move funds on your behalf. In short, you are allowing them to move your tokens.

In our case, we would like the THORSwap Token Transfer Proxy smart contract to trade our ERC20 tokens for us, so we will need to approve an allowance (a specified amount) for the THORSwap Token Transfer Proxy smart contract to move our tokens on our behalf.

The requirements to do this:

  • Connect with the ERC20 token's approve() method using Web3js or Ethers.js.
  • Set the approval amount to maxApproval
  • Use approve() to give our allowanceTarget an allowance for a max amount

Part 3. Perform the Swap

As outlined in Step 1, the THORSwap API will return a transaction object that can be used to execute the swap on the blockchain of the sellAsset. For example, if the sellAsset is ETH.CRV, the transaction object will be an Ethereum transaction object. If the sellAsset is BTC.BTC, the transaction object will be a hex string of a Bitcoin transaction.

Therefore, the code executing the swap will differ depending on our sell asset. We will take a look at 3 examples with a different sellAsset:

  • ETH.CRV -> BTC.BTC (SwapIn)
  • BTC.BTC -> ETH.CRV (SwapOut)
  • GAIA.ATOM -> THOR.RUNE (THORChain only)

The sellAsset blockchains are: Ethereum, Bitcoin, and Cosmos respectively. These 3 cases will cover the different type of transaction object in the quote response, and can be adjusted slightly to work for similar chains. I.e BTC case can be adjusted for: BCH, DOGE, and LTC. And the Cosmos case can be adjusted for: BNB and RUNE.

Part 3a. Perform a SwapIn (ETH.CRV -> BTC.BTC)

As mentioned in 'Inspecting the Transaction', the transaction object returned by the THORSwap API for this scenario is an Ethereum transaction object. We will use the web3js library to sign and broadcast the transaction from our wallet.

First, a quick look at the transaction object:

// Example transaction object
{
"to": "0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852",
"value": "0x0",
"gas": "0x5208",
"gasPrice": "0x4a817c800",
"nonce": "0x1",
"data": "0x7f4e6d1d0000000000000000000000000000000000000000000000000000000000000001",
"chainId": 1
}

The THORSwap API includes suggested values for: gas (the gas limit) & gasPrice. We can use these values, or we can use our own values. The gas limit is the maximum amount of gas we are willing to pay for the transaction. The gas price is the amount of gas we are willing to pay per unit of gas. The gas price is usually denominated in Wei.

Now we will use web3js to sign and broadcast the transaction. We will use the web3.eth.accounts.signTransaction method to sign the transaction, and the web3.eth.sendSignedTransaction method to broadcast the transaction.

import Web3 from 'web3'

const web3 = new Web3(
new Web3.providers.HttpProvider(
`https://mainnet.infura.io/v3/${process.env.INFURA_PROJECT_ID}`,
),
)

const ethPrivateKey = '...'

const privateKeyBuffer = Buffer.from(ethPrivateKey, 'hex')
const tx = new Transaction(rawTx)
const signedTx = tx.sign(privateKeyBuffer)
const serializedTx = signedTx.serialize()

const sentTx = await web3.eth.sendSignedTransaction(
'0x' + serializedTx.toString('hex'),
)

Part 3b. Perform a SwapOut (BTC.BTC -> ETH.CRV)

Part 3c. Perform a THORChain only Swap (GAIA.ATOM -> THOR.RUNE)

Part 3d. Perform a Swap (non-EVM)