SOR API Guide

This guide will help you get started with the Odos API endpoints for generating a quote (/sor/quote/v2) and assembling a transaction (/sor/assemble).

This guide is meant to be the simplest implementation possible for getting and executing Odos quotes via the API. For advanced usage and detailed explanation of each parameter and feature, please explore other areas of the documentation such as our detailed interactive documentation.

Anatomy of a Swap

This diagram shows the flow of a swap transaction using the Odos API. The following steps will show how to do each part of this diagram.

%%{init:{"sequence":{"showSequenceNumbers":true}}}%% sequenceDiagram participant U as User participant Q as Quote Endpoint participant A as Assemble Endpoint participant W as Wallet U->>Q: Request Quote activate Q Q->>U: Receive Quote deactivate Q break U-->U: Determine if quote should be executed end U->>A: Request assembled transaction using pathId from quote activate A A->>U: Receive assembled transaction from quote deactivate A U->>W: Sign and send transaction

Setup

Firstly, you’ll need to setup your project. This involves importing the required libraries for making HTTP requests.

Recommended HTTP libraries (examples below):

  • Python: requests (Requires installation from here)
  • Javascript: fetch (natively supported in most environments)

Step 1: Generate a Quote

The first step in using the Odos SOR API is generating a quote. This can be achieved by sending a POST request to the /sor/quote/v2 endpoint. It is recommended to start with the example request body and customize as necessary.

Required Quote Request Parameters:

  • chainId: the ID of the chain you are requesting the quote on
  • inputTokens: a list of token addresses and input amounts, currently anywhere from 1-6 input assets at once. Input amounts should be strings representing fixed precision integer amounts relative to each asset’s decimals
  • outputTokens: a list of token addresses and output proportions, currently anywhere from 1-6 output assets at once. The proportions should always total up to 1
  • slippageLimitPercent: the slippage tolerance for the quote, meaning the maximum percentage of the quote the user is willing to lose to slippage, where 1 represents 1% (99% of the original quoted output)
  • userAddr: the checksummed address of the user who will eventually assemble and execute the quote (required to get a pathId in the response for assembly)

Recommended Optional Quote Request Parameters:

  • referralCode: a referral code for tracking your projects activity using the Odos API and enabling partner relationships based on usage, learn more here. Defaults to 0 for unregistered activity.
  • disableRFQs: Whether or not to disable RFQ liquidity sources, which have third-party API dependencies and quote expirations, both of which can cause an increase in failed quotes and TXN reverts. Defaults to true to maximize reliability, but can be set to false in order to access these additional liquidity sources.
  • compact: a flag for enabling compact call data for quote assembly, setting to true is recommended, learn more here. Defaults to true.

For path route visualization parameters, refer to the Path Viz Quick Start Guide.

import json

import requests

quote_url = "https://api.odos.xyz/sor/quote/v2"

quote_request_body = {
    "chainId": 1, # Replace with desired chainId
    "inputTokens": [
        {
            "tokenAddress": "0x...", # checksummed input token address
            "amount": "100000", # input amount as a string in fixed integer precision
        }
    ],
    "outputTokens": [
        {
            "tokenAddress": "0x...", # checksummed output token address
            "proportion": 1
        }
    ],
    "slippageLimitPercent": 0.3, # set your slippage limit percentage (1 = 1%)
    "userAddr": "0x...", # checksummed user address
    "referralCode": 0, # referral code (recommended)
    "disableRFQs": True,
    "compact": True,
}

response = requests.post(
  quote_url,
  headers={"Content-Type": "application/json"},
  json=quote_request_body
)

if response.status_code == 200:
  quote = response.json()
  # handle quote response data
else:
  print(f"Error in Quote: {response.json()}")
  # handle quote failure cases
const quoteUrl = 'https://api.odos.xyz/sor/quote/v2';

const quoteRequestBody = {
  chainId: 1, // Replace with desired chainId
  inputTokens: [
    {
        tokenAddress: '0x...', // checksummed input token address
        amount: '100000', // input amount as a string in fixed integer precision
    }
  ],
  outputTokens: [
    {
        tokenAddress: '0x...', // checksummed output token address
        proportion: 1
    }
  ],
  userAddr: '0x...', // checksummed user address
  slippageLimitPercent: 0.3, // set your slippage limit percentage (1 = 1%),
  referralCode: 0, // referral code (recommended)
  disableRFQs: true,
  compact: true,
};

const response = await fetch(
  quoteUrl,
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(quoteRequestBody),
  });

if (response.status === 200) {
  const quote = await response.json();
  // handle quote response data
} else {
  console.error('Error in Quote:', response);
  // handle quote failure cases
}

Replace the placeholder values in the request body with actual values for your desired quote. The userAddr should be the checksummed address of the user who may eventually assemble and execute the transaction.

You’ll receive a response with the quote details. The pathId response field will be necessary for assembling your transactions in the next step. Paths are valid for 60 seconds after the quote is received, after which a new quote will have to be requested.

Step 2: Assemble the Transaction

In order to execute an Odos quote on chain, the transaction must be assembled. The Odos API will completely assemble the transaction for you, manual assembly is discouraged and not supported. Once you have the pathId from the quote, you can send a POST request to the /sor/assemble endpoint to get the on-chain transaction data.

assemble_url = "https://api.odos.xyz/sor/assemble"

assemble_request_body = {
    "userAddr": "0x...", # the checksummed address used to generate the quote
    "pathId": quote["pathId"], # Replace with the pathId from quote response in step 1
    "simulate": False, # this can be set to true if the user isn't doing their own estimate gas call for the transaction
}

response = requests.post(
  assemble_url,
  headers={"Content-Type": "application/json"},
  json=assemble_request_body
)

if response.status_code == 200:
  assembled_transaction = response.json()
  # handle Transaction Assembly response data
else:
  print(f"Error in Transaction Assembly: {response.json()}")
  # handle Transaction Assembly failure cases
const assembleUrl = 'https://api.odos.xyz/sor/assemble';

const assembleRequestBody = {
  userAddr: '0x...', // the checksummed address used to generate the quote
  pathId: quote.pathId, // Replace with the pathId from quote response in step 1
  simulate: true, // this can be set to true if the user isn't doing their own estimate gas call for the transaction
};

const response = await fetch(
  assembleUrl,
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(assembleRequestBody),
  });

if (response.status === 200) {
  const assembledTransaction = await response.json();
  // handle Transaction Assembly response data
} else {
  console.error('Error in Transaction Assembly:', response);
  // handle quote failure cases
}

Replace the placeholder values in the request body with actual values. The userAddr should be the checksummed address of the user who requested the quote and will be executing the transaction.

The response will contain all of the necessary parameters for transaction execution in the transaction field which can be used to sign and execute the swap on-chain.

Step 3: Execute the Transaction

Once you have the transaction details from the /sor/assemble response, you can use the transaction object to sign and send directly with an EOA wallet or conduct a low level call from a smart contract with the transaction.data field. Reminder that modifying transaction.data or assembling any Odos router function call from scratch is not officially supported and 100% at your own risk.

Recommended Web3 libraries (examples below):

from web3 import Web3

# 1. create web3 provider
w3 = Web3(Web3.HTTPProvider("node http endpoint for your network"))

# 2. Extract transaction object from assemble API response
transaction = assembled_transaction["transaction"]

# 3. Sign tx with a private key
pk = "Your EOA private key"

# web3py requires the value to be an integer
transaction["value"] = int(transaction["value"])

signed_tx = w3.eth.account.sign_transaction(transaction, pk)

# 4. Send the signed transaction
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
const Web3 = require('web3');

// 1. create web3 provider
const web3 = new Web3('node http endpoint for your network or wallet provider');

// 2. Extract transaction object from assemble API response
const transaction = assembledTransaction.transaction;

let txHash;
// 3a. Sign transaction with a web3 provider / wallet
txHash = await web3.eth.accounts.signTransaction(transaction);

// 3b. sign transaction with private key
const pk = 'Your EOA private key';
const signedTx = web3.eth.accounts.signTransaction(transaction, pk);
txHash = await web3.eth.send_raw_transaction(signedTx.rawTransaction);