Skip to content

refactor(interface): rename to AggregatorV2V3Interface + add getRoundData#222

Open
hardyjosh wants to merge 1 commit into
chore/remove-multipythfrom
fix/aggregator-v2-v3-interface
Open

refactor(interface): rename to AggregatorV2V3Interface + add getRoundData#222
hardyjosh wants to merge 1 commit into
chore/remove-multipythfrom
fix/aggregator-v2-v3-interface

Conversation

@hardyjosh

Copy link
Copy Markdown
Collaborator

The local oracle interface was named AggregatorV3Interface but its surface
is actually V2+V3: it includes the deprecated V2 latestAnswer() (needed for
Aave V3 and Compound V3 compatibility) and omitted V3's getRoundData(uint80).
Two integrator-facing footguns followed:

  1. Anyone importing the canonical Chainlink AggregatorV3Interface and
    casting a PassthroughProtocolAdapter instance to it would compile against
    a five-method surface including getRoundData and runtime-revert when
    that selector was called.
  2. The interface name suggested "V3" semantics while the contents matched
    Chainlink's own AggregatorV2V3Interface. New code reaching for the
    "real" V3 surface would silently lose latestAnswer without warning.

Renames the type to AggregatorV2V3Interface (matching Chainlink's own
hybrid type), moves the file to src/interface/IAggregatorV2V3.sol, and
adds full NatSpec including an explicit deprecation note on latestAnswer.
Adds the missing canonical getRoundData(uint80) method.

Implementations:

  • BasePythOracleAdapter.getRoundData reverts with
    HistoricalRoundDataUnsupported(uint80). Pyth does not expose per-round
    history through this interface; callers needing point-in-time data should
    use Pyth's getPriceAtPublishTime or an indexer directly. The error
    selector is explicit so integrators can detect it on the wire.
  • PassthroughProtocolAdapter.getRoundData forwards to the underlying
    oracle unchanged — a future Chainlink-backed oracle returns real
    historical data; a Pyth-backed oracle bubbles up the revert.

Tests:

  • testPassthroughGetRoundData — happy path through Passthrough with a
    mocked underlying oracle returning real round data.
  • testPassthroughGetRoundDataRevertSelectorPropagates — confirms the
    Pyth-backed revert selector + payload reach the integrator unchanged.
  • testGetRoundDataAlwaysReverts — fuzzes the round ID into the
    BasePythOracleAdapter revert path.

Does not touch the in-adapter latestAnswer() consumption (Morpho still
calls latestAnswer without a staleness check — that's a separate Pass 5
finding tracked at MEDIUM and out of this PR's scope).

Found by /audit Pass 1 (Security), Pass 3 (Documentation), Pass 4 (Code
Quality), and Pass 5 (Correctness / Intent Verification) — A14p5-1
"Interface name asserts V3, contents are V2+V3 hybrid".

Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3924e4ba-2e3a-4285-aede-5a7f8254ae83

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/aggregator-v2-v3-interface

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

This was referenced May 11, 2026
@hardyjosh hardyjosh force-pushed the fix/aggregator-v2-v3-interface branch from 4bf3a79 to c8842ed Compare May 11, 2026 20:42
@hardyjosh hardyjosh changed the base branch from fix/auto-pause-sentinel-correctness to graphite-base/222 May 11, 2026 21:22
@hardyjosh hardyjosh force-pushed the graphite-base/222 branch from 7c1d3ea to b0f9ab3 Compare May 11, 2026 21:22
@hardyjosh hardyjosh force-pushed the fix/aggregator-v2-v3-interface branch from c8842ed to 34429b9 Compare May 11, 2026 21:22
@hardyjosh hardyjosh changed the base branch from graphite-base/222 to docs/readme-integrator-notes May 11, 2026 21:22
@hardyjosh hardyjosh force-pushed the fix/aggregator-v2-v3-interface branch from 34429b9 to 7e45f29 Compare May 11, 2026 21:52
@hardyjosh hardyjosh force-pushed the docs/readme-integrator-notes branch from b0f9ab3 to 34be518 Compare May 11, 2026 21:52
@hardyjosh hardyjosh force-pushed the fix/aggregator-v2-v3-interface branch from 7e45f29 to 35029d5 Compare May 11, 2026 22:09
@hardyjosh hardyjosh force-pushed the docs/readme-integrator-notes branch from 34be518 to cfbe962 Compare May 11, 2026 22:27
@hardyjosh hardyjosh force-pushed the fix/aggregator-v2-v3-interface branch from 35029d5 to 5599f88 Compare May 11, 2026 22:27
@hardyjosh hardyjosh changed the base branch from docs/readme-integrator-notes to graphite-base/222 May 26, 2026 17:34
@hardyjosh hardyjosh force-pushed the graphite-base/222 branch from cfbe962 to 4f54fad Compare May 26, 2026 17:35
@hardyjosh hardyjosh force-pushed the fix/aggregator-v2-v3-interface branch from 5599f88 to 653f532 Compare May 26, 2026 17:35
@hardyjosh hardyjosh changed the base branch from graphite-base/222 to chore/remove-multipyth May 26, 2026 17:35
@hardyjosh hardyjosh force-pushed the fix/aggregator-v2-v3-interface branch from 653f532 to 336605a Compare May 26, 2026 17:38
@hardyjosh hardyjosh force-pushed the chore/remove-multipyth branch 2 times, most recently from b928009 to a9a35d3 Compare May 26, 2026 18:15
@hardyjosh hardyjosh force-pushed the fix/aggregator-v2-v3-interface branch from 336605a to 6d85151 Compare May 26, 2026 18:15
…Data

The local oracle interface was named `AggregatorV3Interface` but its surface
is actually V2+V3: it includes the deprecated V2 `latestAnswer()` (needed for
Aave V3 and Compound V3 compatibility) and omitted V3's `getRoundData(uint80)`.
Two integrator-facing footguns followed:

1. Anyone importing the canonical Chainlink `AggregatorV3Interface` and
   casting a `PassthroughProtocolAdapter` instance to it would compile against
   a five-method surface including `getRoundData` and runtime-revert when
   that selector was called.
2. The interface name suggested "V3" semantics while the contents matched
   Chainlink's own `AggregatorV2V3Interface`. New code reaching for the
   "real" V3 surface would silently lose `latestAnswer` without warning.

Renames the type to `AggregatorV2V3Interface` (matching Chainlink's own
hybrid type), moves the file to `src/interface/IAggregatorV2V3.sol`, and
adds full NatSpec including an explicit deprecation note on `latestAnswer`.
Adds the missing canonical `getRoundData(uint80)` method.

Implementations:
- `BasePythOracleAdapter.getRoundData` reverts with
  `HistoricalRoundDataUnsupported(uint80)`. Pyth does not expose per-round
  history through this interface; callers needing point-in-time data should
  use Pyth's `getPriceAtPublishTime` or an indexer directly. The error
  selector is explicit so integrators can detect it on the wire.
- `PassthroughProtocolAdapter.getRoundData` forwards to the underlying
  oracle unchanged — a future Chainlink-backed oracle returns real
  historical data; a Pyth-backed oracle bubbles up the revert.

Tests:
- `testPassthroughGetRoundData` — happy path through Passthrough with a
  mocked underlying oracle returning real round data.
- `testPassthroughGetRoundDataRevertSelectorPropagates` — confirms the
  Pyth-backed revert selector + payload reach the integrator unchanged.
- `testGetRoundDataAlwaysReverts` — fuzzes the round ID into the
  `BasePythOracleAdapter` revert path.

Hardens `ProdFork.t.sol` at the same time:
- Replaces silent `return;` in fork-availability modifiers with
  `vm.skip(true)` so missing fork env or unset prod addresses surface as
  SKIPPED in CI output instead of false-green vacuously-passing tests
  (audit #213).
- Removes the duplicate `LibProdOracles.ORACLE_REGISTRY` constant and
  imports the canonical `LibProdDeploy.ORACLE_REGISTRY` so a future
  prod-address rotation only requires one edit (audit #208).

Does not touch the in-adapter `latestAnswer()` consumption (Morpho still
calls `latestAnswer` without a staleness check — that's a separate Pass 5
finding tracked at MEDIUM and out of this PR's scope).

Found by /audit Pass 1 (Security), Pass 3 (Documentation), Pass 4 (Code
Quality), and Pass 5 (Correctness / Intent Verification) — A14p5-1
"Interface name asserts V3, contents are V2+V3 hybrid".

Closes #208, #213.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@hardyjosh hardyjosh force-pushed the chore/remove-multipyth branch from a9a35d3 to 48b9306 Compare May 31, 2026 11:48
@hardyjosh hardyjosh force-pushed the fix/aggregator-v2-v3-interface branch from 6d85151 to 2134c50 Compare May 31, 2026 11:48
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