Open code423n4 opened 1 year ago
GalloDaSballo marked the issue as primary issue
GainsGoblin marked the issue as sponsor acknowledged
We don't want a trader's trade to revert just because the chainlink feed is a round behind.
The Warden has pointed out to a possible risk related to the price oracle returning stale data.
Alternatively to checking for latest round, a check for updatedAt
to not be too far in the past should also help mitigate the risk of offering an incorrect price which can lead to value extraction or unintended behaviour.
Because of the risk, I do agree with Medium Severity
GalloDaSballo marked the issue as selected for report
GainsGoblin marked the issue as sponsor confirmed
Lines of code
https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/utils/TradingLibrary.sol#L91-L122
Vulnerability details
Impact
As mentioned by https://docs.tigris.trade/protocol/oracle, "Prices provided by the oracle network are also compared to Chainlink's public price feeds for additional security. If prices have more than a 2% difference the transaction is reverted." The Chainlink price verification logic in the following
TradingLibrary.verifyPrice
function serves this purpose. However, besides thatIPrice(_chainlinkFeed).latestAnswer()
uses Chainlink's deprecatedlatestAnswer
function, this function also does not guarantee that the price returned by the Chainlink price feed is not stale. WhenassetChainlinkPriceInt != 0
istrue
, it is still possible thatassetChainlinkPriceInt
is stale in which the Chainlink price verification would compare the off-chain price against a stale price returned by the Chainlink price feed. For a off-chain price that has more than a 2% difference when comparing to a more current price returned by the Chainlink price feed, this off-chain price can be incorrectly considered to have less than a 2% difference when comparing to a stale price returned by the Chainlink price feed. As a result, a trading transaction that should revert can go through, which makes the price verification much less secure.https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/utils/TradingLibrary.sol#L91-L122
Based on https://docs.chain.link/docs/historical-price-data, the followings can be done to avoid using a stale price returned by the Chainlink price feed.
latestRoundData
function can be used instead of the deprecatedlatestAnswer
function.roundId
andansweredInRound
are also returned. "You can checkansweredInRound
against the currentroundId
. IfansweredInRound
is less thanroundId
, the answer is being carried over. IfansweredInRound
is equal toroundId
, then the answer is fresh."Proof of Concept
The following steps can occur for the described scenario.
Trading.initiateMarketOrder
function, which eventually calls theTradingLibrary.verifyPrice
function, to initiate a market order.TradingLibrary.verifyPrice
function is called, the off-chain price is compared to the price returned by the Chainlink price feed for the position asset.Trading.initiateMarketOrder
transaction goes through. However, this transaction should revert because the off-chain price has more than a 2% difference if comparing to a more current price returned by the Chainlink price feed.Tools Used
VSCode
Recommended Mitigation Steps
https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/utils/TradingLibrary.sol#L113 can be updated to the following code.