How to Write a Token Price Oracle Smart Contract

how-to-write-a-token-price-oracle-smart-contract

Introduction

In the rapidly evolving DeFi ecosystem, accurate and reliable token price data is fundamental for various applications like lending protocols, automated trading systems, and yield farming strategies. Token price oracles serve as the critical infrastructure that enables these protocols to access real-time price information directly from the blockchain.

This article shows you how to build a robust token price oracle smart contract that leverages Uniswap V2’s liquidity pools to derive token prices. By understanding and implementing this oracle system, developers can create DeFi applications that make informed decisions based on current market conditions.

Key benefits of building a token price oracle:

  • Real-time price discovery from decentralized exchanges
  • On-chain price verification
  • Integration capability with any DeFi protocol
  • Support for multiple tokens through batch queries
  • Cost-effective price fetching through view functions
  • Throughout this guide, we’ll explore a practical implementation that showcases:

How to interact with Uniswap V2 contracts

  • Methods for accurate price calculations
  • Handling different token decimals
  • Batch processing for multiple tokens
  • Event emission for price tracking

Okay, now let’s dive into the technical implementation and understand how each component works together to create a reliable price oracle system.

How to Implement Price Oracle Smart Contract Step by Step

We are going to use Uniswap V2’s liquidity pools to fetch token prices.

This approach offers several advantages:

  • Decentralization: Uniswap V2 is a decentralized exchange, ensuring that price data is not controlled by a single entity.
  • Liquidity: Uniswap V2 pools provide liquidity for various tokens, allowing for a wide range of price data.
  • Flexibility: Uniswap V2 supports multiple tokens, enabling the oracle to fetch prices for multiple tokens simultaneously.

We need to import required interfaces like IUniswapV2Pair, IUniswapV2Factory, and IERC20.
And define the contract’s state variables, events.

Key functions

1. Getting Pair Address

function getPairAddress(address tokenA, address tokenB) public view returns (address) {
    return IUniswapV2Factory(UNISWAP_V2_FACTORY_ADDRESS).getPair(tokenA, tokenB);
}

This function retrieves the liquidity pair address for any token pair from Uniswap V2.

2. Token Price Calculation

    function getTokenPrice(address token) public returns (uint256 tokenPrice, uint256 ethPrice, string memory symbol) {
        address pairAddress = getPairAddress(token, WETH);
        require(pairAddress != address(0), "Pair not found");

        IUniswapV2Pair pairContract = IUniswapV2Pair(pairAddress);

        (uint112 reserve0, uint112 reserve1, ) = pairContract.getReserves();
        address token0 = pairContract.token0();

        uint8 tokenDecimals = IERC20(token).decimals();
        symbol = IERC20(token).symbol();

        if (token0 == WETH) {
            ethPrice = (reserve0 * (10 ** tokenDecimals)) / reserve1;
            tokenPrice = (reserve1 * (10 ** 18)) / reserve0;
        } else {
            ethPrice = (reserve1 * (10 ** tokenDecimals)) / reserve0;
            tokenPrice = (reserve0 * (10 ** 18)) / reserve1;
        }

        emit PriceUpdated(token, tokenPrice, ethPrice, symbol);
    }

First we get the pair address for the token pair.
And if the pair is not found, it throws an error.

The price calculation logic handles two scenarios:

  • If WETH is token0:
if (token0 == WETH) {
    ethPrice = (reserve0 * (10 ** tokenDecimals)) / reserve1;
    tokenPrice = (reserve1 * (10 ** 18)) / reserve0;
}
  • If WETH is token1:
else {
    ethPrice = (reserve1 * (10 ** tokenDecimals)) / reserve0;
    tokenPrice = (reserve0 * (10 ** 18)) / reserve1;
}

3. Batch Price Fetching

function getMultipleTokenPrices(address[] calldata tokens) external returns (
    uint256[] memory tokenPrices,
    uint256[] memory ethPrices,
    string[] memory symbols
)

This function efficiently fetches prices for multiple tokens in a single transaction.

Usage Example

We can use this price oracle contract in other smart contract and also frontend for getting real time token price.

Usage in other solidity Smart contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import "./UniswapV2PriceOracle.sol";

contract PriceConsumer {
    UniswapV2PriceOracle public oracle;

    constructor(address _oracle) {
        oracle = UniswapV2PriceOracle(_oracle);
    }

    function getTokenPriceInETH(address token, uint256 amount) external view returns (uint256) {
        return oracle.getTokenValueInETH(token, amount);
    }

    function checkPriceImpact(address token, uint256 amount) external view returns (uint256) {
        return oracle.getPriceImpact(token, amount);
    }
}

Frontend Implementation with ethers.js

import { ethers } from 'ethers';
import { useState, useEffect } from 'react';

const PriceTracker = () => {
    const [price, setPrice] = useState('0');
    const [priceImpact, setPriceImpact] = useState('0');

    const ORACLE_ADDRESS = "YOUR_ORACLE_ADDRESS";
    const TOKEN_ADDRESS = "YOUR_TOKEN_ADDRESS";

    useEffect(() => {
        const provider = new ethers.providers.Web3Provider(window.ethereum);
        const oracle = new ethers.Contract(ORACLE_ADDRESS, oracleABI, provider);

        const fetchPrices = async () => {
            const amount = ethers.utils.parseEther("1");

            // Get token price
            const tokenPrice = await oracle.getTokenValueInETH(
                TOKEN_ADDRESS, 
                amount
            );
            setPrice(ethers.utils.formatEther(tokenPrice));

            // Get price impact
            const impact = await oracle.getPriceImpact(
                TOKEN_ADDRESS,
                amount
            );
            setPriceImpact(ethers.utils.formatUnits(impact, 2));
        };

        fetchPrices();

        // Listen for price updates
oracle.on("PriceUpdated", (token, ethPrice, tokenPrice, symbol) => {
    if(token === TOKEN_ADDRESS) {
        setPrice(ethers.utils.formatEther(ethPrice));
        setTokenPrice(ethers.utils.formatEther(tokenPrice));
        setSymbol(symbol);
    } else {
        // Track other token updates
        setOtherTokenPrices(prev => ({
            ...prev,
            [token]: {
                ethPrice: ethers.utils.formatEther(ethPrice),
                tokenPrice: ethers.utils.formatEther(tokenPrice),
                symbol
            }
        }));

        // Optionally notify about other token updates
        console.log(`Price updated for ${symbol}: ${ethers.utils.formatEther(ethPrice)} ETH`);
    }
});

        return () => {
            oracle.removeAllListeners();
        };
    }, []);

    return (
        

Token Price: {price} ETH

Price Impact: {priceImpact}%

); }; export default PriceTracker;

This implementation provides a reliable way to fetch token prices directly from Uniswap V2 liquidity pools, making it suitable for various DeFi applications requiring on-chain price data

Conclusion

The implementation of a token price oracle smart contract demonstrates the power and flexibility of on-chain price discovery through Uniswap V2 liquidity pools.

Future Possibilities

  • Integration with additional DEX protocols
  • Implementation of time-weighted average prices (TWAP)
  • Price feed aggregation from multiple sources
  • Enhanced security features and price manipulation protection

This oracle implementation serves as a robust starting point for developers building DeFi applications that require reliable, on-chain price data. By understanding and implementing these concepts, developers can create more sophisticated and reliable DeFi protocols.

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post
python-development-in-vscode-using-devcontainer

Python Development in VSCode Using Devcontainer

Next Post
sunday-rewind:-how-to-encourage-clicks-without-shady-tricks-by-paul-boag

Sunday Rewind: How to encourage clicks without shady tricks by Paul Boag

Related Posts
c++-指向類別成員的指位器的實作細節

C++ 指向類別成員的指位器的實作細節

C++ 可以定義指向成員函式的指位器, 不過因為成員函式可能是虛擬函式, 如何能夠透過指向成員函式的指位器達到呼叫正確的成員函式呢?本來就來簡單探究。(本文均以 g++ 為例, 並且只探討單純的單一繼承)。 指向非虛擬函式的指位器 首先來看個簡單的範例, 建立指向非虛擬函式的指位器: #include using namespace std; class A { public:…
Read More