Skip to content

Latest commit

 

History

History
85 lines (59 loc) · 3.4 KB

File metadata and controls

85 lines (59 loc) · 3.4 KB

Recumbent Daisy Salmon

Medium

Unchecked oracle token price timestamp will lead to the old price been accept

Summary

All oracle token price timestamp not check it will cause the price is old than the latest price when the oracle is not update on time

Root Cause

API3

In API3Oracle.sol:79 the code read from the api3 oracle, but not check the return timestamp, if the oracle doesn't update on time. It will lead to an old price. In the function's @notice it says it will get the latest price from the API3 proxy but not check the timestamp may break it

// API3 returns prices with scaled up by 1e18 base
// https://docs.api3.org/dapps/integration/contract-integration.html#using-value
(int224 price,) = api3Proxy.read(); // <-- Here

Pyth

Also in PythOracle.sol:98 pyth oracle implement, the code use getPriceUnsafe which may return a very old price. The pyth doucment point out this and recommand to use the getPrice function and check the timestamp and confidence (pyth returns the confidence of the price, it should alse be checked): image The following code use getPriceUnsafe:

    function _getLatestPrice(address token) internal view returns (uint256, uint256) {
        // Return 0 if price feed id is not set, reverts are handled by caller
        if (priceFeedIds[token] == bytes32(0)) return (0, 0);

        bytes32 priceFeedId = priceFeedIds[token];
        PythStructs.Price memory pythPrice = pyth.getPriceUnsafe(priceFeedId); // <-- Here

        uint256 price = uint256(uint64(pythPrice.price));
        uint256 expo = uint256(uint32(-pythPrice.expo));

        return (price, expo);
    }

Bond

The oracle's timestamp not validate in Bond oracle. The Bond returns a ReferenceData structure and inside it has a update time: image In BandOracle.sol:85 the code should check the update timestamp to avoid the too old price

    function _getLatestPrice(address token) internal view returns (uint256) {
        // Return 0 if underlying symbol is not set, reverts are handled by caller
        if (bytes(tokenToBandSymbol[token]).length == 0) return 0;

        IStdReference.ReferenceData memory data = bandReference.getReferenceData(tokenToBandSymbol[token], QUOTE_SYMBOL); // <-- Here

        return data.rate;
    }

Internal Pre-conditions

No response

External Pre-conditions

No response

Attack Path

No response

Impact

No response

PoC

No response

Mitigation

The code can use the oracle's timestamp to do more check, if the timestamp is much older than now, maybe revert the transaction. The API3 oracle be done use following code to check the timestamp, the pyth and Bond oracle are the same issue.

(int224 price,uint256 timestamp) = api3Proxy.read();
if(block.timestamp - 24 * 60 * 60 > timestamp) { // maybe 24 hours
     revert("stale price, more than 24 hours old");
}