fix(L3): cap composition slippage + freshness gate (L7 analyzed) - #19
Merged
Conversation
- L3: rebalanceComposition clamps the caller-supplied maxSlippageBps to a 5% ceiling so a keeper can no longer drive minOut toward zero, and the whole function is gated on wstethBuyAllowed() so neither branch trims at a mispriced/stale mark (the buy branch already required it; the sell branch did not). Composition maintenance pauses during a depeg/outage and the keeper retries when healthy. - L7: assessed and documented as already-mitigated. For an exact-input single-hop swap the oracle-anchored amountOutMinimum already bounds the output (hence extractable sandwich value) to the slippage bound; a sqrtPriceLimitX96 would only add partial-fill semantics without tightening that bound, so it is intentionally omitted. Comment on _swap records the reasoning. 2 regression tests (slippage clamp misses on both tiers; sell branch no-ops during depeg). 134 -> 136; 9 fork tests green.
This was referenced Jul 20, 2026
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.
Category PR for the audit's execution MEV/slippage bounds (L3, L7). Branched off main, targets main.
L3 — rebalanceComposition
maxSlippageBpsis clamped to a 5% ceiling (MAX_COMPOSITION_SLIPPAGE_BPS), so a keeper can no longer driveminOuttoward zero.wstethBuyAllowed(): the buy branch already required it; the sell branch did not and could trim at a mispriced/stale mark. Composition maintenance now pauses during a depeg or feed outage; the keeper retries when healthy.L7 — analyzed, already mitigated (no code change beyond documentation)
The audit suggested a
sqrtPriceLimitX96pool-impact cap on the emergency swap. On analysis, for an exact-input single-hop swap the oracle-anchoredamountOutMinimumalready bounds the output, and therefore the extractable sandwich value, to the slippage bound: the swap delivers >= minOut or reverts. A price limit would only introduce exact-input partial-fill semantics (leftovertokenInto account for) without tightening that bound, and a mis-set limit could brick swaps. So it is intentionally omitted; the_swapcomment records the reasoning. (Chunked/partial-fill execution for large deltas remains the deferred L6 item.)Tests
2 regressions: 100%-slippage ask still misses
minOuton both tiers (clamp works); sell branch no-ops during a depeg. Suite 134 -> 136; 9/9 fork tests green.