feat(vault): NAV circuit breaker and guardian pause (Section 4)#15
Merged
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
pause; the ownerunpauses after review.whenNotPausedgates the value-moving entry points (settle, both request lanes, sync deposit and redeem) andisValidSignature, 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.settle. It compares the current share price (convertToAssets(1e18)) to the last-settled reference and, on a move beyondmaxNavDeviationBps, auto-pauses for guardian review before any pending membership change can autonomously execute. The reference updates each settle and resets onunpauseso a resolved deviation cannot immediately re-trip.Two design decisions worth a look
paused = true; revert, but a revert rolls back the pause, so the two are contradictory in one transaction. The correct pattern (the one BoringVault'sAccountantWithRateProvidersuses) 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.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 fmtclean, no regression.