如何编写代币价格预言机智能合约

2025-06-07

如何编写代币价格预言机智能合约

介绍

在快速发展的 DeFi 生态系统中,准确可靠的代币价格数据对于借贷协议、自动交易系统和收益挖矿策略等各种应用至关重要。代币价格预言机是关键的基础设施,使这些协议能够直接从区块链获取实时价格信息。

本文将向您展示如何构建一个强大的代币价格预言机智能合约,该合约利用 Uniswap V2 的流动性池来获取代币价格。通过理解和实现该预言机系统,开发者可以创建能够根据当前市场状况做出明智决策的 DeFi 应用。

构建代币价格预言机的主要好处:

  • 去中心化交易所的实时价格发现
  • 链上价格验证
  • 与任何 DeFi 协议的集成能力
  • 通过批量查询支持多个 token
  • 通过视图函数获取具有成本效益的价格
  • 在本指南中,我们将探索一个实际的实现,展示以下内容:

如何与 Uniswap V2 合约交互

  • 准确价格计算方法
  • 处理不同的令牌小数
  • 多个 token 的批处理
  • 价格跟踪的事件发射

好的,现在让我们深入研究技术实现并了解每个组件如何协同工作以创建可靠的价格预言系统。

如何逐步实现价格预言机智能合约

我们将使用 Uniswap V2 的流动性池来获取代币价格。

这种方法有几个优点:

  • 去中心化:Uniswap V2 是一个去中心化的交易所,确保价格数据不受单一实体控制。
  • 流动性:Uniswap V2 池为各种代币提供流动性,允许提供广泛的价格数据。
  • 灵活性:Uniswap V2 支持多种代币,使预言机能够同时获取多种代币的价格。

我们需要导入所需的接口,如IUniswapV2PairIUniswapV2FactoryIERC20
并定义合约的状态变量,事件。

主要功能

1. 获取配对地址

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

此函数从 Uniswap V2 检索任何代币对的流动性对地址。

2. 代币价格计算

    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);
    }
Enter fullscreen mode Exit fullscreen mode

首先,我们获取代币对的地址。
如果找不到该地址,则会抛出错误。

价格计算逻辑处理两种情况:

  • 如果 WETH 是 token0:
if (token0 == WETH) {
    ethPrice = (reserve0 * (10 ** tokenDecimals)) / reserve1;
    tokenPrice = (reserve1 * (10 ** 18)) / reserve0;
}
Enter fullscreen mode Exit fullscreen mode
  • 如果 WETH 是 token1:
else {
    ethPrice = (reserve1 * (10 ** tokenDecimals)) / reserve0;
    tokenPrice = (reserve0 * (10 ** 18)) / reserve1;
}
Enter fullscreen mode Exit fullscreen mode

3.批量取价

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

此功能可在单笔交易中高效获取多个代币的价格。

使用示例

我们可以在其他智能合约中使用此价格预言合约,也可以在前端获取实时代币价格。

在其他 Solidity 智能合约中的使用

// 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

使用 ethers.js 实现前端

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

const PriceTracker = () => {
    const [price, setPrice] = useState<string>('0');
    const [priceImpact, setPriceImpact] = useState<string>('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 (
        <div>
            <h2>Token Price: {price} ETH</h2>
            <h3>Price Impact: {priceImpact}%</h3>
        </div>
    );
};

export default PriceTracker;
Enter fullscreen mode Exit fullscreen mode

此实现提供了一种直接从 Uniswap V2 流动性池获取代币价格的可靠方法,使其适用于需要链上价格数据的各种 DeFi 应用

结论

代币价格预言机智能合约的实施展示了通过 Uniswap V2 流动性池进行链上价格发现的强大功能和灵活性。

未来的可能性

  • 与其他 DEX 协议集成
  • 实施时间加权平均价格(TWAP)
  • 来自多个来源的价格信息汇总
  • 增强的安全功能和价格操纵保护

对于需要可靠链上价格数据的 DeFi 应用开发者来说,此预言机的实现是一个坚实的起点。通过理解和实现这些概念,开发者可以创建更复杂、更可靠的 DeFi 协议。

文章来源:https://dev.to/marksantiago02/how-to-write-a-token-price-oracle-smart-contract-1oj
PREV
如何使用 Solidity 和 Hardhat 编写 ERC20 代币预售智能合约
NEXT
完美是无用的