code-423n4 / 2023-07-tapioca-findings

13 stars 9 forks source link

Seer contract returns incorrect rate in peekSpot() function #1183

Closed code423n4 closed 1 year ago

code423n4 commented 1 year ago

Lines of code

https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/main/contracts/oracle/Seer.sol#L75

Vulnerability details

Description

The Seer contract incorrectly returns the rate of the requested asset / pair / pool in the peekSpot() function. The function is supposed to return the current spot exchange rate without any state changes. However, the current implementation of the function returns the rate that was last read from the oracle. This means that the rate returned by peekSpot() may be outdated.

The peekSpot() function in the Seer contract is implemented as follows:

function peekSpot( bytes calldata ) external view virtual returns (uint256 rate) { (, uint256 high) = _readAll(inBase); return high; }

The _readAll() function is used to read the latest price information from the oracle. The function returns a tuple of two values: the first value is a boolean indicating whether the price information was successfully read, and the second value is the price information itself.

The peekSpot() function simply returns the second value returned by the _readAll() function. This means that the peekSpot() function will always return the rate that was last read from the oracle, even if the rate has changed since then.

Impact

This bug can have a significant impact on users of the Seer contract. If the rate returned by peekSpot() is outdated, then users may make decisions based on incorrect information. This could lead to financial losses for users.

Proof of Concept

pragma solidity ^0.8.18;

import "./Seer.sol";

contract SeerTest { Seer seer;

constructor(address seerAddress) {
    seer = Seer(seerAddress);
}

function test() public {
    uint256 rate1 = seer.peekSpot();
    uint256 rate2 = seer.get();

    if (rate1 != rate2) {
        // Bug found!
    }
}

}

This will deploy a SeerTest contract and call the test() function. The test() function will call the peekSpot() function and the get() function. If the bug is present, then the peekSpot() function will return a different rate than the get() function.

Tools Used

Remix IDE

Recommended Mitigation Steps

The current implementation of the peekSpot() function can be fixed by changing the function to read the current spot exchange rate from the oracle instead of the rate that was last read. This can be done by modifying the peekSpot() function as follows:

function peekSpot( bytes calldata ) external view virtual returns (uint256 rate) { rate = _readCurrentRate(); return rate; }

The _readCurrentRate() function is a new function that would be added to the Seer contract. The _readCurrentRate() function would be responsible for reading the current spot exchange rate from the oracle.

Assessed type

Other

c4-pre-sort commented 1 year ago

minhquanym marked the issue as low quality report

minhquanym commented 1 year ago

Spam

c4-judge commented 1 year ago

dmvt marked the issue as unsatisfactory: Insufficient quality