fix(L6/I2/I3): emergency-bound escape hatch, oracle prepare, param scale - #22
Merged
Merged
Conversation
L6 (emergency de-risk reverts under thin/attacker-thinned liquidity): the permissionless de-risk swaps once at 98.5% of oracle-fair with no admin knob; a >1.5% pool dislocation reverts both fee tiers. Add a guardian/owner-settable emergencySlippageBps that widens ONLY the healthy-oracle emergency bound, capped within [EMERGENCY_SLIPPAGE_BPS, EMERGENCY_DEGRADED_SLIPPAGE_BPS] and resettable to the tight default (0). The scheduled and degraded bounds are untouched, and the widening is a deliberate trade of more single-swap sandwich exposure (L7) for guaranteed execution during a declared dislocation. Correct the overstated "atomic" invariant comment. Full chunked/partial-fill execution is deliberately not added: rebalance() is re-callable every block and self-heals as arbitrage re-aligns the pool, so it is disproportionate to a self-healing Low. I2 (dead oracle cardinality self-heal): _bindMarket issued increaseObservationsCardinalityNext then reverted OracleNotReady in the same tx on a cold oracle, rolling the bump back so it never persisted. Add a standalone, non-reverting prepareMarket(market) to warm a market's TWAP window ahead of the roll; clarify the _bindMarket comment. I3 (hardcoded USDC_SCALE = 1e12): the vault, safe leg and PT adapter all parameterize assetDecimals but ExecutionModule baked in 1e12, a latent redeployment footgun for any non-6-decimal asset. Derive assetScale and the dust floor from an assetDecimals constructor arg (public immutables). Updated all six construction sites (6 for USDC). Tests (+8, 149 total): three prepareMarket cases with minimal Pendle mocks (persists bump / bind rolls it back / owner-only); scale+dust-floor derivation for 6/8/18 decimals and >18 revert; guardian-widened bound clears the thin- liquidity de-risk that reverts at 150bps, reset restores the tight bound, plus setter access/bounds.
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.
Batch PR F of the CPPI vault audit remediation — the remaining low/informational tail (follows A–E: #17 revert-resilience, #18 oracle-robustness, #19 execution-mev, #20 floor-correctness, #21 request-hygiene). Closes L6, I2, I3.
L6 — emergency de-risk reverts under thin / attacker-thinned liquidity (Low)
The permissionless emergency de-risk does one
exactInputSingleat 98.5% of oracle-fair with no admin knob. If a required sell moves both the 500 and 3000 pools past 1.5%, both tiers revert andrebalance()reverts.Fix: guardian/owner-settable
emergencySlippageBpsthat widens only the healthy-oracle emergency bound, hard-capped within[EMERGENCY_SLIPPAGE_BPS (150), EMERGENCY_DEGRADED_SLIPPAGE_BPS (1000)]and resettable to the tight default (0). The scheduled and degraded bounds are untouched. Widening is a deliberate trade of more single-swap sandwich exposure (L7) for guaranteed execution during a declared dislocation. The overstated "atomic" invariant comment is corrected.Deliberately not done: full chunked/partial-fill execution.
rebalance()is re-callable every block and self-heals as arbitrage re-aligns the pool, so chunking is disproportionate to a self-healing Low and carries its own leftover-token/accounting risk.I2 — dead oracle cardinality self-heal (Informational)
_bindMarketissuedincreaseObservationsCardinalityNextthen revertedOracleNotReadyin the same tx on a cold oracle, rolling the bump back so it never persisted.Fix: standalone, non-reverting
prepareMarket(market)(owner-only; the underlying market call is itself permissionless) to warm a market's TWAP window ahead of the roll._bindMarketcomment clarified.I3 — hardcoded
USDC_SCALE = 1e12(Informational)The vault, safe leg and PT adapter all parameterize
assetDecimals, butExecutionModulebaked in1e12— a latent footgun for any non-6-decimal redeployment.Fix: derive
assetScaleand the dust floor from anassetDecimalsconstructor arg (public immutables). All six construction sites updated (6for USDC).Tests (+8, 149 total)
prepareMarketpersists the cardinality bump / the_bindMarketpath rolls it back on a cold oracle / owner-only — with minimal Pendle mocks (test/PendlePTAdapterPrepare.t.sol, the first non-fork adapter coverage).assetScale/dustFloorderivation for 6/8/18 decimals and a>18revert.test_h6_tightBoundStillProtectsWhenHealthyis the paired revert case); reset restores the tight bound; setter access + range bounds.Full suite green.