Skip to content

fix(morpho): require 8-decimal upstream oracle (audit #187)#224

Open
hardyjosh wants to merge 2 commits into
fix/aggregator-v2-v3-interfacefrom
fix/audit-morpho-decimals-check
Open

fix(morpho): require 8-decimal upstream oracle (audit #187)#224
hardyjosh wants to merge 2 commits into
fix/aggregator-v2-v3-interfacefrom
fix/audit-morpho-decimals-check

Conversation

@hardyjosh

Copy link
Copy Markdown
Collaborator

MorphoProtocolAdapter.price() unconditionally scales latestAnswer() * 1e28, which is correct iff the upstream oracle reports 8 decimals.
OracleRegistry.setOracle() doesn't constrain decimals — a registry
swap to an 18-decimal feed (e.g. a future Chainlink integration) would
silently mis-price Morpho collateral by 10^10×. There is no observable
revert; Morpho would just see a wildly wrong number.

Adds:

  • EXPECTED_ORACLE_DECIMALS constant (= 8) derived from the 36 - 8
    scaling exponent.
  • UnexpectedOracleDecimals(uint8 actual, uint8 expected) error.
  • A decimals() check at the top of price(), BEFORE latestAnswer()
    is read, so a misconfigured oracle fails loudly before the wrong
    number ever enters the computation.

Tests:

  • testPriceRevertsOnNonEightDecimals — fuzz over every uint8 != 8 and
    assert the new revert fires with the right payload.
  • testPriceDecimalsCheckRunsBeforeAnswer — mocks latestAnswer to
    revert; confirms the decimals check fires first.
  • All four existing positive tests updated to mock oracle.decimals()
    to 8 (default-zero would now hit the new revert).
  • AutoPausePropagation tests continue to pass — they use the real
    PythOracleAdapter which hardcodes decimals = 8.

Closes #187.

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: ac2c26de-1cbb-4d34-a7f2-0eeb4bc0730f

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/audit-morpho-decimals-check

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.

@hardyjosh hardyjosh force-pushed the docs/audit-followups-sweep branch from 50d26bd to 8d871ae Compare May 11, 2026 20:42
@hardyjosh hardyjosh force-pushed the fix/audit-morpho-decimals-check branch 2 times, most recently from e479257 to dc9ec25 Compare May 11, 2026 21:22
@hardyjosh hardyjosh force-pushed the docs/audit-followups-sweep branch 2 times, most recently from a311916 to f505724 Compare May 11, 2026 21:52
@hardyjosh hardyjosh force-pushed the fix/audit-morpho-decimals-check branch 2 times, most recently from 7e068f3 to 44edffa Compare May 11, 2026 22:09
@hardyjosh hardyjosh force-pushed the docs/audit-followups-sweep branch from f505724 to 25d31e3 Compare May 11, 2026 22:10
@hardyjosh hardyjosh force-pushed the fix/audit-morpho-decimals-check branch from 44edffa to 9bd9d66 Compare May 11, 2026 22:27
@hardyjosh hardyjosh force-pushed the fix/audit-morpho-decimals-check branch from 9bd9d66 to 9b397c2 Compare May 26, 2026 17:35
@hardyjosh hardyjosh force-pushed the docs/audit-followups-sweep branch from 427065b to a153778 Compare May 26, 2026 17:35
@hardyjosh hardyjosh changed the base branch from docs/audit-followups-sweep to graphite-base/224 May 26, 2026 17:38
@hardyjosh hardyjosh force-pushed the fix/audit-morpho-decimals-check branch from 9b397c2 to d2838f5 Compare May 26, 2026 17:38
@hardyjosh hardyjosh force-pushed the graphite-base/224 branch from a153778 to 336605a Compare May 26, 2026 17:39
@hardyjosh hardyjosh changed the base branch from graphite-base/224 to fix/aggregator-v2-v3-interface May 26, 2026 17:39
@hardyjosh hardyjosh force-pushed the fix/audit-morpho-decimals-check branch from d2838f5 to f3e41d2 Compare May 26, 2026 18:15
Jai and others added 2 commits May 31, 2026 11:46
…md sync

Bundles the documentation-only changes for the audit MEDIUMs whose right
answer was "tell the truth in the spec/docs" rather than "change the code".

- #27 SPEC.md: add MultiPythOracleAdapter, BasePythOracleAdapter,
  MultiPythOracleAdapterBeaconSetDeployer, MultiOracleUnifiedDeployer to
  §3, §6, §9, §10, §11; add IAggregatorV2V3 and LibCorporateActionsPause
  to §11. Repo tree now matches src/.
- #28 CLAUDE.md: architecture summary names BasePythOracleAdapter,
  MultiPythOracleAdapter, the auto-pause mechanism, and the
  OraclePausedManual / OraclePausedCorporateAction selectors; repo tree
  reflects src/abstract/, src/interface/IAggregatorV2V3.sol,
  src/lib/LibCorporateActionsPause.sol, and the Multi-* deployers;
  dependencies list adds st0x.deploy.
- #29 SPEC.md: single-feed PythOracleAdapter is fully immutable
  post-init (no priceId / maxAge setters); MultiPythOracleAdapter has
  admin-controlled setFeeds and setMaxAge. §3 diagram and §13
  governance updated to match implementation.
- #164 SPEC.md §9: newOracleRegistry() correctly described as no-args
  with msg.sender becoming admin; rationale added (eliminates an
  attacker-controlled admin slot in a permissionless factory).
- #199 LibCorporateActionsPause.sol: @dev note on `inPauseWindow`
  documenting that the upper-bound predicates are enforced by the
  upstream CompletionFilter, not locally; future vault regressions
  surface here as fail-open; mitigation via upstream conformance test
  tracked at #216.
- #209 OracleUnifiedDeployer.sol and MultiOracleUnifiedDeployer.sol:
  contract-level @dev note documenting that sub-deployer addresses
  (LibProdDeploy.*) are inlined at compile time; redeploying any
  sub-deployer requires redeploying this contract too.
- #210 AGENTS.md Deployment: pipeline-drift-risk callout describing the
  manual log-parse → operator-PR pipeline and the typo / wrong-slot /
  untimely-PR risks; references #210 and the related #220 networks.json
  duplication.
- #212 IAggregatorV2V3.sol: SYNC NOTE comment pointing to
  smartcontractkit/chainlink:contracts/src/v0.8/shared/interfaces/AggregatorV2V3Interface.sol,
  last synced 2026-05-11, and noting the missing drift-detection test
  tracked at #212.
- #220 subgraph/README.md (new): documents the by-hand duplication
  between subgraph/networks.json and src/lib/LibProdDeploy.sol, and
  tracks the gen-pipeline overhaul at #220.

No executable code changed. nix CI green:
- rainix-sol-test, rainix-sol-static, rainix-sol-legal all pass.
- LibCorporateActionsPauseTest still 17/17 (NatSpec-only edit).

Closes #27, #28, #29, #164, #199, #209, #210, #212, #220.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
MorphoProtocolAdapter.price() unconditionally scales `latestAnswer() *
1e28`, which is correct iff the upstream oracle reports 8 decimals.
`OracleRegistry.setOracle()` doesn't constrain decimals — a registry
swap to an 18-decimal feed (e.g. a future Chainlink integration) would
silently mis-price Morpho collateral by 10^10×. There is no observable
revert; Morpho would just see a wildly wrong number.

Adds:
- `EXPECTED_ORACLE_DECIMALS` constant (= 8) derived from the `36 - 8`
  scaling exponent.
- `UnexpectedOracleDecimals(uint8 actual, uint8 expected)` error.
- A `decimals()` check at the top of `price()`, BEFORE `latestAnswer()`
  is read, so a misconfigured oracle fails loudly before the wrong
  number ever enters the computation.

Tests:
- testPriceRevertsOnNonEightDecimals — fuzz over every uint8 != 8 and
  assert the new revert fires with the right payload.
- testPriceDecimalsCheckRunsBeforeAnswer — mocks `latestAnswer` to
  revert; confirms the decimals check fires first.
- All four existing positive tests updated to mock `oracle.decimals()`
  to 8 (default-zero would now hit the new revert).
- AutoPausePropagation tests continue to pass — they use the real
  PythOracleAdapter which hardcodes `decimals = 8`.

Closes #187.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@hardyjosh hardyjosh force-pushed the fix/audit-morpho-decimals-check branch from f3e41d2 to dd82097 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