Skip to content

fix(M4/L5/L8): oracle robustness - #18

Merged
jayeshy14 merged 1 commit into
mainfrom
fix/oracle-robustness
Jul 20, 2026
Merged

fix(M4/L5/L8): oracle robustness#18
jayeshy14 merged 1 commit into
mainfrom
fix/oracle-robustness

Conversation

@jayeshy14

Copy link
Copy Markdown
Owner

Category PR grouping the audit's oracle-robustness findings. Branched off main, targets main.

M4 — prolonged-staleness circuit breaker

A prolonged Chainlink outage freezes the risky-leg mark, so the normal CPPI trigger (which reads that mark) is blind to a real decline and never fires. OracleHub.prolongedStale() reports when the feed has been stale beyond a configurable window since the last good snapshot; CPPIVault.deRiskUnderProlongedStaleness() is then permissionless and fully de-risks into the safe leg at the degraded bound (H6). Over-conservative but floor-safe; a later rebalance re-risks once the feed recovers. Reverts to zero risky then no-ops, so it isn't spammable.

L5 — gate settlement on health

settleEpoch now calls _requireOracleHealthy(), so an epoch's NAV is never crystallized at a stale or depegged price. (Claims read the frozen epochNavPerShare, so they stay open per the audit.)

L8 — USDC/USD conflation

OracleHub takes an optional USDC/USD feed via setUsdcFeed. When USDC depegs beyond maxUsdcDepegBps (or its feed is stale), healthy() flips false so deposits and settlement pause until the peg returns. address(0) disables the check (par assumed). This closes the ETH/USD-used-as-ETH/USDC gap at the settlement/gating layer without threading a conversion through every swap bound.

Tests

7 regressions: prolonged-stale full de-risk (permissionless), not-allowed-when-not-prolonged, settleEpoch blocked when unhealthy, USDC depeg / stale-feed / within-tolerance / no-feed, and the prolonged-stale view timing. Suite 127 -> 134. No fork surface changed (ExecutionModule/PT adapter untouched).

Groups the audit's oracle-robustness findings:

- M4: a prolonged Chainlink outage freezes the risky-leg mark, so the
  normal CPPI trigger goes blind to a real decline. OracleHub gains
  prolongedStale() (feed stale beyond a configurable window since the
  last good snapshot); the vault gains a permissionless
  deRiskUnderProlongedStaleness() that fully de-risks into the safe leg
  at the degraded bound. Over-conservative but floor-safe; a later
  rebalance re-risks once the feed recovers.
- L5: settleEpoch now requires oracle health, so value is never
  crystallized at a stale/degraded price.
- L8: OracleHub takes an optional USDC/USD feed; when USDC depegs
  beyond a tolerance (or its feed is stale), healthy() flips false so
  deposits and settlement pause until the peg returns. address(0)
  disables the check (par assumed). Addresses the ETH/USD-as-ETH/USDC
  conflation at the settlement/gating layer.

IOracleHealth extended with prolongedStale(); constructor unchanged
(USDC feed and windows are owner setters). 7 regression tests;
127 -> 134.
@jayeshy14
jayeshy14 merged commit fc27e8c into main Jul 20, 2026
3 checks passed
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