DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Incorrect Stale Price Check Leads to Premature Price Expiry

Summary

The _check function in the KeeperProxy contract contains a price validation mechanism that compares the updatedAt timestamp of a Chainlink price feed against block.timestamp - maxTimeWindow[token]. However, the condition require(updatedAt > block.timestamp - maxTimeWindow[token], "stale price feed") causes prices to expire earlier than expected. Specifically, when updatedAt == block.timestamp - maxTimeWindow[token], the function incorrectly considers the price stale and reverts.

Vulnerability Details

The require condition in _check is:

function _check(address token, uint256 price) internal view {
// https://github.com/code-423n4/2021-06-tracer-findings/issues/145
(, int chainLinkPrice, , uint256 updatedAt, ) = AggregatorV2V3Interface(dataFeed[token]).latestRoundData();
>> require(updatedAt > block.timestamp - maxTimeWindow[token], "stale price feed");
uint256 decimals = 30 - IERC20Meta(token).decimals();
price = price / 10 ** (decimals - 8); // Chainlink price decimals is always 8.
require(
_absDiff(price, chainLinkPrice.toUint256()) * BPS / chainLinkPrice.toUint256() < priceDiffThreshold[token],
"price offset too big"
);
}

This logic causes the function to revert even when updatedAt is exactly block.timestamp - maxTimeWindow[token].

Impact

The price should only be considered stale if updatedAt < block.timestamp - maxTimeWindow[token] but with the current check the price is considered stale when updatedAt <= block.timestamp - maxTimeWindow[token]. This causes unnecessary reverts, rejecting valid transactions that should have been processed threfore leading to operational inefficiencies, preventing execution of actions in run, runNextAction, and other functions relying on _check function.

Tools Used

Manual Review

Recommendations

Modify the require statement in _check as follows

function _check(address token, uint256 price) internal view {
// https://github.com/code-423n4/2021-06-tracer-findings/issues/145
(, int chainLinkPrice, , uint256 updatedAt, ) = AggregatorV2V3Interface(dataFeed[token]).latestRoundData();
- require(updatedAt > block.timestamp - maxTimeWindow[token], "stale price feed");
+ require(updatedAt >= block.timestamp - maxTimeWindow[token], "stale price feed");
uint256 decimals = 30 - IERC20Meta(token).decimals();
price = price / 10 ** (decimals - 8); // Chainlink price decimals is always 8.
require(
_absDiff(price, chainLinkPrice.toUint256()) * BPS / chainLinkPrice.toUint256() < priceDiffThreshold[token],
"price offset too big"
);
}

This change ensures that prices remain valid until updatedAt is strictly less than the threshold, preventing premature expiration.

Updates

Lead Judging Commences

n0kto Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.