Skip to main content

Overview

The Price Oracle system provides accurate token pricing for collateral valuation, health factor calculations, and liquidation decisions. Gearbox uses a dual-feed architecture with main and reserve price feeds for enhanced security.

IPriceOracleV3 Interface

interface IPriceOracleV3 {
    /// @notice Get price of token in USD (scaled to token decimals)
    function getPrice(address token) external view returns (uint256);
    
    /// @notice Get safe price (minimum of main and reserve feeds)
    function getSafePrice(address token) external view returns (uint256);
    
    /// @notice Get reserve price feed price
    function getReservePrice(address token) external view returns (uint256);
    
    /// @notice Convert token amount to USD
    function convertToUSD(uint256 amount, address token) 
        external view returns (uint256);
    
    /// @notice Convert USD amount to token
    function convertFromUSD(uint256 amount, address token) 
        external view returns (uint256);
    
    /// @notice Convert between tokens
    function convert(uint256 amount, address tokenFrom, address tokenTo) 
        external view returns (uint256);
    
    /// @notice Safe conversion to USD (uses safe price)
    function safeConvertToUSD(uint256 amount, address token) 
        external view returns (uint256);
}

Price Feed Configuration

Price Feed Parameters

struct PriceFeedParams {
    address priceFeed;        // Price feed contract address
    uint32 stalenessPeriod;   // Max age of price data in seconds
    bool skipCheck;           // Whether to skip staleness check
    uint8 tokenDecimals;      // Token decimals for price scaling
}

Querying Price Feed Info

ICreditManagerV3 manager = ICreditManagerV3(creditManagerAddress);
IPriceOracleV3 oracle = IPriceOracleV3(manager.priceOracle());

// Get price feed for a token
address priceFeed = oracle.priceFeeds(token);

// Get complete price feed parameters
PriceFeedParams memory params = oracle.priceFeedParams(token);

// Get reserve price feed
address reserveFeed = oracle.reservePriceFeeds(token);
PriceFeedParams memory reserveParams = oracle.reservePriceFeedParams(token);

Price Queries

Basic Price Retrieval

IPriceOracleV3 oracle = IPriceOracleV3(oracleAddress);

// Get current price (USD per token, scaled by token decimals)
uint256 price = oracle.getPrice(token);

// Example: USDC with 6 decimals
// price = 1_000_000 means 1 USDC = 1 USD

// Example: WETH with 18 decimals
// price = 2000_000000000000000000 means 1 WETH = 2000 USD

Safe Prices

Safe prices use the minimum of main and reserve feeds:
// Get safe price (minimum of both feeds)
uint256 safePrice = oracle.getSafePrice(token);

// Get reserve price directly
uint256 reservePrice = oracle.getReservePrice(token);

// Safe price is used during:
// - Withdrawals
// - Operations with forbidden tokens
// - Liquidation scenarios
Safe pricing prevents manipulation by using the more conservative price estimate from dual feeds.

Price Conversions

Token to USD

// Convert token amount to USD value
uint256 amount = 1000 * 10**18; // 1000 tokens (18 decimals)
uint256 usdValue = oracle.convertToUSD(amount, token);

// Safe conversion (uses safe price)
uint256 safeUsdValue = oracle.safeConvertToUSD(amount, token);

USD to Token

// Convert USD value to token amount
uint256 usdAmount = 5000 * 10**18; // $5000 USD
uint256 tokenAmount = oracle.convertFromUSD(usdAmount, token);

Token to Token

// Convert between two tokens
uint256 wethAmount = 10 * 10**18; // 10 WETH
uint256 usdcAmount = oracle.convert(
    wethAmount,
    weth,
    usdc
);

// This performs:
// 1. WETH -> USD
// 2. USD -> USDC

Price Updates

On-Demand Price Updates

For protocols with push-based oracles (like Pyth), update prices before operations:
struct PriceUpdate {
    bytes32 priceId;    // Price feed identifier
    bytes updateData;   // Signed price data
}

MultiCall[] memory calls = new MultiCall[](2);

// 1. Update prices (must be first call)
PriceUpdate[] memory updates = new PriceUpdate[](1);
updates[0] = PriceUpdate({
    priceId: pythPriceId,
    updateData: signedPriceData
});

calls[0] = MultiCall({
    target: address(facade),
    callData: abi.encodeCall(
        ICreditFacadeV3Multicall.onDemandPriceUpdates,
        (updates)
    )
});

// 2. Execute operation with fresh prices
calls[1] = MultiCall({
    target: address(facade),
    callData: operationCallData
});

facade.multicall(creditAccount, calls);
onDemandPriceUpdates must be the first call in the multicall array or it will revert.

Collateral Valuation

Health Factor Calculation

The oracle is used to calculate account health:
// Health Factor = (Total Weighted Value) / (Total Debt)
// Both values calculated using oracle prices

ICreditManagerV3 manager = ICreditManagerV3(creditManagerAddress);

CollateralDebtData memory data = manager.calcDebtAndCollateral(
    creditAccount,
    CollateralCalcTask.DEBT_COLLATERAL
);

// Values in USD (oracle-priced)
uint256 totalValueUSD = data.totalValueUSD;      // Sum of all tokens
uint256 twvUSD = data.twvUSD;                    // LT-weighted value
uint256 totalDebtUSD = data.totalDebtUSD;        // Total debt

// Health factor in basis points
uint256 healthFactor = (twvUSD * 10000) / totalDebtUSD;

Liquidation Thresholds

Each token has a liquidation threshold (LT) that weights its value:
// Get token's liquidation threshold
uint16 lt = manager.liquidationThresholds(token);

// LT in basis points (e.g., 8500 = 85%)
// Weighted value = balance * price * (LT / 10000)

// Example:
// Token balance: 100
// Token price: $10
// LT: 8500 (85%)
// Weighted value: 100 * $10 * 0.85 = $850

Safe Price Scenarios

The protocol uses safe prices in specific situations:

Withdrawal Operations

// When withdrawing collateral, safe prices are used
// This prevents manipulation where user withdraws at inflated price

MultiCall[] memory calls = new MultiCall[](1);

calls[0] = MultiCall({
    target: address(facade),
    callData: abi.encodeCall(
        ICreditFacadeV3Multicall.withdrawCollateral,
        (token, amount, recipient)
    )
});

// Collateral check uses safe prices
facade.multicall(creditAccount, calls);

Forbidden Tokens

// If account has forbidden tokens enabled,
// all collateral checks use safe prices

uint256 forbiddenMask = facade.forbiddenTokenMask();
uint256 enabledMask = manager.enabledTokensMaskOf(creditAccount);

if (enabledMask & forbiddenMask != 0) {
    // Safe prices will be used for this account
}

Price Feed Types

Gearbox supports multiple oracle types: Standard pull-based oracles:
// Chainlink feeds update automatically
// Protocol checks staleness period

PriceFeedParams memory params = oracle.priceFeedParams(token);
require(!params.skipCheck, "Staleness check enabled");

// Price must be updated within stalenessPeriod

Pyth Network

Push-based oracles requiring updates:
// Must call onDemandPriceUpdates before operations
MultiCall[] memory calls = new MultiCall[](2);

calls[0] = MultiCall({
    target: address(facade),
    callData: abi.encodeCall(
        ICreditFacadeV3Multicall.onDemandPriceUpdates,
        (pythUpdates)
    )
});

calls[1] = MultiCall({
    target: address(facade),
    callData: operationCallData
});

Custom Price Feeds

Gearbox can integrate custom oracle logic:
PriceFeedParams memory params = oracle.priceFeedParams(token);

if (params.skipCheck) {
    // Custom feed implements its own safety checks
    // No staleness validation by protocol
}

Complete Example

Integrating oracle prices into your application:
contract OracleIntegration {
    ICreditManagerV3 public immutable creditManager;
    IPriceOracleV3 public immutable oracle;
    
    constructor(address _creditManager) {
        creditManager = ICreditManagerV3(_creditManager);
        oracle = IPriceOracleV3(creditManager.priceOracle());
    }
    
    function getAccountHealth(address creditAccount) 
        external view returns (
            uint256 totalValueUSD,
            uint256 totalDebtUSD,
            uint256 healthFactor
        ) 
    {
        CollateralDebtData memory data = creditManager.calcDebtAndCollateral(
            creditAccount,
            CollateralCalcTask.DEBT_COLLATERAL
        );
        
        totalValueUSD = data.twvUSD;
        totalDebtUSD = data.totalDebtUSD;
        
        if (totalDebtUSD > 0) {
            healthFactor = (totalValueUSD * 10000) / totalDebtUSD;
        } else {
            healthFactor = type(uint256).max;
        }
    }
    
    function getTokenValue(
        address token,
        uint256 amount,
        bool useSafePrice
    ) external view returns (uint256 usdValue) {
        if (useSafePrice) {
            usdValue = oracle.safeConvertToUSD(amount, token);
        } else {
            usdValue = oracle.convertToUSD(amount, token);
        }
    }
    
    function canWithdraw(
        address creditAccount,
        address token,
        uint256 amount
    ) external view returns (bool) {
        // Get current health
        CollateralDebtData memory data = creditManager.calcDebtAndCollateral(
            creditAccount,
            CollateralCalcTask.DEBT_COLLATERAL
        );
        
        if (data.totalDebtUSD == 0) return true;
        
        // Calculate value being withdrawn (using safe price)
        uint256 withdrawValueUSD = oracle.safeConvertToUSD(amount, token);
        
        // Get token's LT
        uint16 lt = creditManager.liquidationThresholds(token);
        uint256 weightedWithdrawValue = withdrawValueUSD * lt / 10000;
        
        // New TWV after withdrawal
        uint256 newTwvUSD = data.twvUSD - weightedWithdrawValue;
        
        // Check if health factor stays above 100%
        uint256 newHealthFactor = (newTwvUSD * 10000) / data.totalDebtUSD;
        return newHealthFactor >= 10000;
    }
}

Best Practices

Always use safe prices

For user-facing withdrawals, use safe prices to prevent manipulation

Check staleness

Verify price feed staleness periods match your risk tolerance

Update push oracles

Always call onDemandPriceUpdates for Pyth and similar feeds

Monitor health factor

Track account health using oracle prices to avoid liquidation

Next Steps

Integration Guide

Complete integration walkthrough

Managing Debt

How debt affects health factor

Build docs developers (and LLMs) love