Skip to content

LibUsdOracle: Division by zero in encodeType 0x02 path when Uniswap V3 getTwap returns zero #1184

@exTypen

Description

@exTypen

Summary

LibUsdOracle.getTokenPriceFromExternal() has a missing zero-check in the encodeType == 0x02 (Uniswap V3 oracle) branch. When LibUniswapOracle.getTwap() returns 0 due to a failed observe() call, the return value is used as a divisor at lines 156-158, causing a division-by-zero revert. This prevents graceful oracle failure handling that the rest of the codebase expects and implements.

Severity: Medium

No whitelisted token currently uses encodeType 0x02. All existing oracle configurations use encodeType 0x01 (direct Chainlink: WETH, WBTC, USDC, USDT) or encodeType 0x00 (LSDChainlinkOracle: WSTETH, weETH). The bug would only become reachable if a new token were whitelisted with the Uniswap V3 oracle path via governance in the future.

Root Cause

LibUniswapOracle.getTwap() explicitly returns 0 on failure:

// LibUniswapOracle.sol line 38-39
(bool success, int24 tick) = consult(pool, lookback);
if (!success) return 0;

consult() catches observe() reverts silently:

// LibUniswapOracle.sol lines 81-93
try IUniswapV3Pool(pool).observe(secondsAgos) returns (...) {
    success = true;
} catch {}

The calling code in LibUsdOracle.getTokenPriceFromExternal() does not check for zero before dividing:

// LibUsdOracle.sol lines 126-134
tokenPrice = LibUniswapOracle.getTwap(...);
// No zero-check here

// LibUsdOracle.sol lines 155-158
if (isMillion) {
    tokenPrice = (1e12 * (10 ** tokenDecimals)) / tokenPrice; // division by zero
} else {
    tokenPrice = (1e6 * (10 ** tokenDecimals)) / tokenPrice;  // division by zero
}

The rest of the codebase handles oracle-returns-zero gracefully (LibGauge.sol line 105, LibWell.sol line 165, LibEvaluate.sol line 225), confirming this is the expected failure mode. The 0x02 path breaks that contract.

Impact

If a token using encodeType 0x02 were whitelisted and its Uniswap V3 pool's observe() failed (insufficient observations, uninitialized pool, etc.), the revert would propagate up through getUsdTokenPrice() and block Sunrise from completing.

The encodeType 0x01 (Chainlink) path is not affected as LibChainlinkOracle.getTokenPrice() handles failures internally and returns 0 without reverting.

Affected Code

Source

Immunefi report #70553 (confirmed, Medium severity, 1000$).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions