Free and simple real-time multichain token pricing

Fetching blockchain prices can be challenging, time consuming, or expensive using 3rd party services. On-chain oracles can make this process simple for fetching realtime token price data accross several chains for free.

Lets get started!

First we start with defining the price oracle contracts which we can use to fetch realtime prices. 1Inch has a set of these oracles which aggregate across mutliple DEXs to calculate the most accurate prices. https://github.com/1inch/spot-price-aggregator

Defining these contract address constants as a mapping allows us to dynamically grab the correct contract address based on the chainId. Defined here are contract addresses on each respective EVM chain for 1Inch token oracle addresses.

const ONE_INCH_ORACLE_ADDRESS_MAP: { [key: number]: string } = {
  [ChainId.ETHEREUM]: '0x07d91f5fb9bf7798734c3f606db065549f6893bb',
  [ChainId.BNB]: '0xfbd61b037c325b959c0f6a7e69d8f37770c2c550',
  [ChainId.POLYGON]: '0x7f069df72b7a39bce9806e3afaf579e54d8cf2b9',
  [ChainId.OPTIMISM]: '0x11dee30e710b8d4a8630392781cc3c0046365d4c',
  [ChainId.ARBITRUM]: '0x735247fb0a604c0adc6cab38ace16d0dba31295f',
  [ChainId.AVALANCHE]: '0xbd0c7aaf0bf082712ebe919a9dd94b2d978f79a9',
  [ChainId.GNOSIS]: '0x142db045195cecabe415161e1df1cf0337a4d02e',
  [ChainId.FANTOM]: '0xe8e598a1041b6fdb13999d275a202847d9b654ca',
}

Second, we define the ETH oracle addresses necessary for converting the exchange rate to USD fiat since the previous contracts return a rate relative the ETH prices. https://data.chain.link/ethereum/mainnet/crypto-usd/eth-usd

const CHAINLINK_ETH_ORACLE: { [key: number]: string } = {
  [ChainId.ETHEREUM]: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419',
  [ChainId.BNB]: '0x9ef1b8c0e4f7dc8bf5719ea496883dc6401d5b2e',
  [ChainId.POLYGON]: '0xf9680d99d6c9589e2a93a78a04a279e509205945',
  [ChainId.OPTIMISM]: '0x13e3ee699d1909e989722e753853ae30b17e08c5',
  [ChainId.ARBITRUM]: '0x639fe6ab55c921f74e7fac1ee960c0b6293ba612',
  [ChainId.AVALANCHE]: '0x976b3d034e162d8bd72d6b9c989d545b839003b0',
  [ChainId.FANTOM]: '0x11ddd3d147e5b83d01cee7070027092397d63658',
}

Next we add our imports. Here we utilize a few different libraries. First is ethers which allows us to easily interact with on-chain contracts and the second is bignumber.js which is used to help convert amounts from atomic to a display value.

We also define the necessary ABIs required for the 1Inch and Chainlink oracle contracts. Which can be extracted from the verified contracts on block explorers.

1Inch Oracle ABI

Chainlink Oracle ABI

import { ethers } from 'ethers'
import BigNumber from 'bignumber.js'

import oneInchOracleAbi from './oneInchOracle/abi.json'
import chainLinkEthOracleAbi from './chainLinkEthOracle/abi.json'

Starting with creating a provider using ethers to interact with the blockchain. Any EVM compatible RPC provider can be used here.

const rpcUrl = 'https://cloudflare-eth.com'
const provider = new ethers.providers.JsonRpcProvider(rpcUrl)

Lets then based upon a given chainId we assign the return address to a new constant and create the contract object using the provider above to interact with the contract easily.

const oneInchOracleAddress = ONE_INCH_ORACLE_ADDRESS_MAP[chainId]
const chainLinkEthOracleAddress = CHAINLINK_ETH_ORACLE[chainId]

const oneInchOracleContract = new ethers.Contract(oneInchOracleAddress, oneInchOracleAbi, provider)
const chainLinkEthOracleContract = new ethers.Contract(chainLinkEthOracleAddress, chainLinkEthOracleAbi, provider)

The next step is to use the 1inch oracle contract method getRateToEth using a given tokenAddress to fetch the token rate relative to the chains native token. The tokenAddress for the token you want to price can be found by searching for the token contract on the chains block explorer (https://etherscan.io). Once we have the rate relative to the native token then we just need to convert the rate to USD which can be done by using the Chainlink oracle contract method latestAnswer which returns the current native token price.

const oneInchOracleResponse = await oneInchOracleContract.getRateToEth(tokenAddress, true)
const ethPriceRaw = (await chainLinkEthOracleContract.latestAnswer()).toString()
const ethPrice = getDisplayAmountFromAtomicAmount(ethPriceRaw, 8)

Using the 1inch oracle token price response and the ethPrice from above we can convert to a USD price for our our given token.

const numerator = new BigNumber(10 ** 18)
const denominator = new BigNumber(10 ** 18) // eth decimals
const priceEthRaw = new BigNumber(oneInchOracleResponse.toString()).times(numerator).dividedBy(denominator).toString()
const priceRaw = new BigNumber(priceEthRaw).times(ethPrice).toString()
const price = getDisplayAmountFromAtomicAmount(priceRaw, 18)

Here is all is wrapped in a reusable function named getSpotPrice that takes a chainId and tokenAddress which allows for dynamically fetching token spot prices accross several chains for any token.

import { ethers } from 'ethers'
import BigNumber from 'bignumber.js'

import oneInchOracleAbi from './oneInchOracle/abi.json'
import chainLinkEthOracleAbi from './chainLinkEthOracle/abi.json'

const ONE_INCH_ORACLE_ADDRESS_MAP: { [key: number]: string } = {
  [ChainId.ETHEREUM]: '0x07d91f5fb9bf7798734c3f606db065549f6893bb',
  [ChainId.BNB]: '0xfbd61b037c325b959c0f6a7e69d8f37770c2c550',
  [ChainId.POLYGON]: '0x7f069df72b7a39bce9806e3afaf579e54d8cf2b9',
  [ChainId.OPTIMISM]: '0x11dee30e710b8d4a8630392781cc3c0046365d4c',
  [ChainId.ARBITRUM]: '0x735247fb0a604c0adc6cab38ace16d0dba31295f',
  [ChainId.AVALANCHE]: '0xbd0c7aaf0bf082712ebe919a9dd94b2d978f79a9',
  [ChainId.GNOSIS]: '0x142db045195cecabe415161e1df1cf0337a4d02e',
  [ChainId.FANTOM]: '0xe8e598a1041b6fdb13999d275a202847d9b654ca',
}

const CHAINLINK_ETH_ORACLE: { [key: number]: string } = {
  [ChainId.ETHEREUM]: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419',
  [ChainId.BNB]: '0x9ef1b8c0e4f7dc8bf5719ea496883dc6401d5b2e',
  [ChainId.POLYGON]: '0xf9680d99d6c9589e2a93a78a04a279e509205945',
  [ChainId.OPTIMISM]: '0x13e3ee699d1909e989722e753853ae30b17e08c5',
  [ChainId.ARBITRUM]: '0x639fe6ab55c921f74e7fac1ee960c0b6293ba612',
  [ChainId.AVALANCHE]: '0x976b3d034e162d8bd72d6b9c989d545b839003b0',
  [ChainId.FANTOM]: '0x11ddd3d147e5b83d01cee7070027092397d63658',
}

const rpcUrl = 'https://cloudflare-eth.com'
const provider = new ethers.providers.JsonRpcProvider(rpcUrl)

const getSpotPrice = async (chainId: number, tokenAddress: string): { price: number; ethPrice: number } => {
  const oneInchOracleAddress = ONE_INCH_ORACLE_ADDRESS_MAP[chainId]
  const chainLinkEthOracleAddress = CHAINLINK_ETH_ORACLE[chainId]

  const oneInchOracleContract = new ethers.Contract(oneInchOracleAddress, oneInchOracleAbi, provider)
  const chainLinkEthOracleContract = new ethers.Contract(chainLinkEthOracleAddress, chainLinkEthOracleAbi, provider)

  const oneInchOracleResponse = await oneInchOracleContract.getRateToEth(tokenAddress, true)
  const ethPriceRaw = (await chainLinkEthOracleContract.latestAnswer()).toString()
  const ethPrice = getDisplayAmountFromAtomicAmount(ethPriceRaw, 8)

  const numerator = new BigNumber(10 ** 18)
  const denominator = new BigNumber(10 ** 18) // eth decimals
  const priceEthRaw = new BigNumber(oneInchOracleResponse.toString()).times(numerator).dividedBy(denominator).toString()
  const priceRaw = new BigNumber(priceEthRaw).times(ethPrice).toString()
  const price = getDisplayAmountFromAtomicAmount(priceRaw, 18)

  return { price, ethPrice }
}

Here is an example using the above function to find the price of UNI (Uniswap) token on Ethereum. Thats it, a very simple and free method of getting real-time token prices on several networks!

const chainId = 1
const tokenAddress = '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984'

const { price, ethPrice } = await getSpotPrice(chainId, tokenAddress)

// {
//   price: 6.09,
//   ethPrice: 1,408.09
// }