Skip to content

feat(vault): NAV circuit breaker and guardian pause (Section 4)#15

Merged
jayeshy14 merged 1 commit into
feat/safety-quarantinefrom
feat/safety-circuit-breaker
Jun 28, 2026
Merged

feat(vault): NAV circuit breaker and guardian pause (Section 4)#15
jayeshy14 merged 1 commit into
feat/safety-quarantinefrom
feat/safety-circuit-breaker

Conversation

@jayeshy14

Copy link
Copy Markdown
Owner

Second slice of the Section 4 safety layer, stacked on #14. Where Slice 1 (quarantine) handles the routine single-feed case by degrading NAV so buffer redemptions survive, this slice is the systemic backstop: a collective mispricing that passes every per-feed staleness check, for example a compromised constituent token or a correlated oracle failure.

What changed

  • Guardian role and pause state. The guardian (or owner) can pause; the owner unpauses after review. whenNotPaused gates the value-moving entry points (settle, both request lanes, sync deposit and redeem) and isValidSignature, so CoW orders stop validating during a halt. Already-settled claims (_claimDeposit/_claimRedeem) stay open, since their value was fixed at a prior validated settlement and blocking them would trap funds. Pause stops execution, not clocks: timelocks keep elapsing.
  • NAV-per-share circuit breaker at settle. It compares the current share price (convertToAssets(1e18)) to the last-settled reference and, on a move beyond maxNavDeviationBps, auto-pauses for guardian review before any pending membership change can autonomously execute. The reference updates each settle and resets on unpause so a resolved deviation cannot immediately re-trip.

Two design decisions worth a look

  • Pause-and-return, not revert. The breaker originally did paused = true; revert, but a revert rolls back the pause, so the two are contradictory in one transaction. The correct pattern (the one BoringVault's AccountantWithRateProviders uses) is to set paused and return early, so the pause persists and settlement does not proceed. The tests assert exactly this: the tripped settle succeeds as a transaction but leaves the vault paused.
  • The default band is 50%, not 25%. A 25% default false-tripped on a legitimate 40% NAV move and would train operators to reflexively unpause, defeating the backstop. 50% catches the implausible (a constituent doubling, a 2x or 10x compromise) while tolerating even severe real volatility. Owner-tunable per index.

Composition with quarantine

Quarantine (Slice 1) runs when not paused and degrades-but-continues on a single stale feed; the breaker is the systemic path where everything halts. Per Section 17.2, on a correlated mass failure the breaker trips first and pauses before any rapid autonomous removal can run.

Tests

10 new tests in test/IndexVaultCircuitBreaker.t.sol: guardian pause and owner unpause, role gating, paused blocks entry/exit/settle/order-validation while allowing settled claims, the breaker trips on an implausible jump (auto-pause, no revert) and not within band, unpause resets the reference, and param validation. 159 total, all green, forge fmt clean, no regression.

The per-feed staleness checks catch a single bad feed; this is the backstop
for a collective mispricing that passes every per-feed check (a compromised
constituent token, a correlated oracle failure).

- Guardian role and pause state: the guardian or owner can pause, the owner
  unpauses after review. whenNotPaused gates the value-moving entry points
  (settle, both request lanes, sync deposit and redeem) and isValidSignature,
  while already-settled claims stay open since their value was fixed at a prior
  validated settlement. Pause stops execution, not clocks.
- NAV-per-share circuit breaker at settle: compares the share price
  (convertToAssets(1e18)) to the last-settled reference and, on a move beyond
  maxNavDeviationBps, auto-pauses before any pending membership change can run.
  The reference resets on unpause so a resolved deviation cannot re-trip.

Two decisions: the breaker pauses and returns rather than reverting, because a
revert would roll back the pause (the pattern BoringVault's Accountant uses);
and the default band is 50%, sized to catch an implausible jump (a constituent
doubling) while tolerating severe real volatility so it does not false-trip and
train operators to reflexively unpause. 10 new tests, 159 total green.
@jayeshy14 jayeshy14 merged commit 8b798a5 into feat/safety-quarantine Jun 28, 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