Skip to content

fix: reject a zero-scaled feed price and staleness-check the composite USD leg#206

Closed
nikkaroraa wants to merge 1 commit into
feat/lendfrom
nik/feed-staleness-fix
Closed

fix: reject a zero-scaled feed price and staleness-check the composite USD leg#206
nikkaroraa wants to merge 1 commit into
feat/lendfrom
nik/feed-staleness-fix

Conversation

@nikkaroraa

@nikkaroraa nikkaroraa commented Jul 19, 2026

Copy link
Copy Markdown

Addresses two Bugbot findings on the Aave v4 price feeds (raised on #199). Both are behavior-preserving hardening; also pushed to feat/lend-price-feeds so #199/master stays in sync.

Context

Neither issue was introduced by the recent BaseAaveV4PriceFeed extraction — both existed verbatim in the original feeds. The extraction just centralized the logic into _composeUsd, which is now the single place to fix them. The production listing script (AddSummerLendCollateral) already wires the safe pattern; the unsafe wiring only lived in the fork tests.

Fixes

  • Stale USD leg (High): the composite only staleness-checked the rate leg; the USD leg's freshness was trusted. _composeUsd can't check the underlying's age (the IAaveV4PriceFeed interface has no timestamp), so the invariant is now documented in NatSpec — the underlying must be a staleness-checking feed, never a raw aggregator. The fork tests were rewired to wrap ETH/USD in a ChainlinkPriceFeed (the pattern prod already uses), and a new test_reverts_whenUnderlyingStale proves a stale underlying makes the composite revert StalePrice.
  • Zero scaled price (Medium): a positive rate could floor to zero after decimal scaling and be returned as a valid 0. _composeUsd now reverts InvalidPrice on a zero result (matching PriceProviderV2). New test_latestAnswer_revertsWhenScaledPriceFloorsToZero covers it.

Testing

forge build clean, forge fmt/forge lint clean. All 33 feed tests pass (test/price-provider/{Chainlink,VedaAccountant,Pyth}PriceFeed.t.sol), including the 3 new ones.

Follow-up (not in this PR)

scripts/aave-v4/DeployAaveV4TestInstance.s.sol (a dev test-instance deployer, not prod) still wires a raw ETH/USD aggregator as the underlying. Worth aligning to the wrapped pattern for consistency, but it's dev-only and doesn't affect deployed feeds.


Note

Medium Risk
Changes oracle collateral pricing invariants (no zero prices, composite staleness via wiring); behavior-preserving hardening but affects lending risk if misconfigured underlying feeds remain in production.

Overview
Hardens Aave v4 composite price feeds in BaseAaveV4PriceFeed._composeUsd so collateral prices cannot be reported as 0 after decimal scaling, and documents how the USD leg must be wired for freshness.

_composeUsd now uses a single path through stable snapping, then reverts InvalidPrice if the result is zero (e.g. a positive rate that floors away in integer division). NatSpec also states that underlyingUsdFeed must be a staleness-checking IAaveV4PriceFeed, not a raw Chainlink aggregator, because the base trusts latestAnswer() and does not re-check timestamps on that leg.

Fork tests for Chainlink and VedaAccountant composites were updated to wrap ETH/USD in a ChainlinkPriceFeed (matching production wiring), with new test_reverts_whenUnderlyingStale cases. Pyth adds test_latestAnswer_revertsWhenScaledPriceFloorsToZero for the zero-after-scaling case.

Reviewed by Cursor Bugbot for commit 454f0c0. Bugbot is set up for automated code reviews on this repo. Configure here.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@nikkaroraa nikkaroraa closed this Jul 19, 2026
@nikkaroraa
nikkaroraa deleted the nik/feed-staleness-fix branch July 19, 2026 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant