Repository navigation
feat(llm): Yearn auction and nested-vault context, verified bodies, leaner prompts - #427
Merged
Merged
Conversation
A Safe batch that lowered both Grove compounders' auction floor and ran report() was explained as leaving the auction's pricing untouched, as kicking a 284k GROVE lot, and without saying what the 880 USDS of profit was. In fact setMinimumAuctionPrice also sets Auction.minimumPrice, the auction held 698k GROVE unsold from an expired lot (so the lot was 983k), and the USDS was proceeds of a lot that had sold out. - source_context: show short function bodies next to the natspec, and stop a /** */ block from swallowing every earlier function in the file - ai_explainer: name the emitter of each simulated event; the prompt trusts a shown body over stale natspec; repeated per-contract explanations are written once - yearn_auction_context: new adapter for strategies with auction() and for Auction calls: pricing, pending lots, kick size and floor proceeds, floor vs DeFiLlama market price, idle proceeds; setters are taken from the strategy's verified body, never from the name alone - yearn_strategy_context: the report() transfer note is said once - Safe reports carry the Safe, nonce and safeTxHash (linked) in the header Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… vaults Strategist Safe nonce 3373 moved 270 WETH of yvWETH-2 out of yvWETH-1 into the Spark looper and yvWETH-1's 270 WETH out of the looper back into Yearn OG WETH, all through DebtAllocator.update_debt. The V3 adapter keyed on calls made on the vault, so the report had no vault context, and it called the move a shift "from indirect to direct exposure" — yvWETH-2's looper exposure went from 70.53 to 294.60 WETH, all of it outside the default withdrawal queue. - DebtAllocator.update_debt(vault, strategy, target) is read as the vault's update_debt, and the allocator's maxDebtUpdateLoss is stated - every vault is walked in one global batch order; a deposit into or a redemption from a vault the batch also manages is applied to that vault (idle, then default queue in order, capped by what each strategy can return), so its later calls start from the right state - a deposit into a strategy outside the default queue is flagged - a vault holding another vault gets look-through exposure before → after Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oves An outside review of Strategist Safe nonce 3373 rated it MEDIUM: moving 270 WETH of yvWETH-2 into a strategy outside its default queue cut what ordinary withdrawals can reach from 571.29 to 301.29 WETH (38.2% → 20.1%), and the DebtAllocator has no config for that pair, so automation ignores it. Our report said LOW and mentioned neither. - exit liquidity (idle + what each queue strategy can return now) before → after whenever a vault's debt moves - each allocator-moved pair's getStrategyConfig against the debt ratio the batch leaves, using totalAssets after any redemption - a deposit's redeemable amount is credited back in the batch model - the prompt rates a sharp exit-liquidity cut, or funds reachable only by manual update_debt, at least MEDIUM Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VaultBatchState.receive() raised the first queue strategy's current_debt when an inner vault auto-allocates a parent vault's deposit, but left max_withdraw and max_deposit at their pre-batch values, unlike _deposit(). The inner vault's exit liquidity then capped that strategy at the stale limit, and a later redemption in the same batch reported part of itself as untraceable. Both paths now book the deposit through StrategyDebt.credit(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Source context - select the overload the call decodes to (parameter types against the ABI signature) for the snippet and the body, and dedupe contexts by signature; omit the body when no definition or several differ — the Auction's take(address) body was shown for take(address,uint256,address,bytes) Yearn V3 batch model - an inner vault's auto_allocate runs _update_debt(first queue strategy, max) with every deposit limit and existing idle, instead of crediting the whole deposit to the strategy - a parent's redemption limit on an inner vault is refreshed from the inner vault's running exit liquidity before each call - look-through exposure keeps a held vault's idle and its holdings outside the default queue and the batch as explicit buckets, with a note, instead of dropping them Yearn Auction - lots carry through the batch: liveness is recomputed from Auction._price under the running settings (a lower floor can revive a decayed lot, and a setter while a lot is live reverts on 1.0.5), setMinAmountToSell gates later reports, sweeps empty the lot, kicks reset it - kick timing uses the settings in force at that call - setAuction routes the strategy's later calls to the new auction - direct Auction calls take their subject from the first argument (disable(address,uint256) appends an index) - idle want is stated without asserting it is sale proceeds Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
spalen0
marked this pull request as ready for review
October 19, 2026 19:01
…ering - V3: refreshing a parent's redemption limit from the inner vault's liquidity discarded owner-specific limits (a withdraw_limit_module, or an observed maxRedeem of 0). A pre-batch limit that debt and liquidity do not explain, or any limit on a vault with a module, is kept as a cap. - Auction: a strategy's held and claimable balances carry across auction contexts in call order, so kickAuction → setAuction → kickAuction no longer sells the same tokens twice. - Auction: disable deletes the lot's state as on-chain, so a live lot stops blocking setters and a later kick reverts (not enabled); enable of an enabled token reverts. - Auction: out-of-range setter values on 1.0.5 are described as reverting and leave settings unchanged; timing math guards zero/100% decay. - protocol_context: an adapter that fails while rendering is skipped like one that fails while resolving, instead of escaping the alert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The auction-replacement fix carried held and claimable balances but not the strategy's kick thresholds, so a setMinAmountToSell before setAuction was lost and the new auction gated its kicks on the pre-batch threshold. minAmountToSell belongs to the strategy, so it now carries with the balances. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… caps - Auction: a strategy's held, claimable and minAmountToSell are one shared state per token across its auctions, and every auction call is replayed in transaction order (walk_auctions). Replaying each auction's calls as a block gave A → B → A's rewards to A instead of B. - V3: only a pre-batch redemption limit below min(position, liquidity) is an owner restriction. A withdraw_limit_module alone no longer creates a cap — an empty position reads 0 under any module, and after depositing the parent could not redeem. A deposit into a vault with a real restriction drops the cap and states the limit as not modelled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ark forecasts - Yearn V3 and Auction semantics are stated once per Protocol Context section instead of once per vault or auction; each keeps its own numbers. - Simulation results drop Transfer events when token transfers are listed (they restate them), leaving the ten-event budget for DebtUpdated, UpdatedMinimumPrice and the like. - The Protocol Context preamble and the system prompt separate on-chain observations (verified) from the adapters' forecasts (amounts moved, lots, after-batch liquidity, exposure, ratios): where the simulation differs, the simulation wins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sale loss Stating "the gap is the most a sale can give away" once at the top of the auction context tipped a routine floor change (Safe nonce 3372, pinned to its alert block) to MEDIUM in 3 of 4 samples, against 1 of 4 before. The mechanics now say how a Dutch auction clears: takers buy near market as the price falls, the floor only matters for an unbid lot, and a floor below market is what lets lots clear. Back to 1 of 4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Providers record each call's LLMUsage (input incl. cached, cached input, cache writes, output, latency): Venice reports prefix-cache hits in prompt_tokens_details.cached_tokens, Anthropic reports reads and writes separately and they are folded into the same fields. The explainer keeps a StageUsage per call on Explanation.stages — summary, critique-N (pass/revised, with the revised TLDR) and detail — and logs one line per alert, so cost, cache hits and critique rounds can be measured on real alerts instead of estimated from prompt sizes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rendered sizes, before → after: - Permission grants: scope and members once per (target, allowlist), then ordered changes. A 10-removal batch: prompt 9,164 → 2,501 chars, report 17,304 → 3,468. - Pendle upgrades: integration reference, proxy state and each implementation's source once; every upgrade call keeps its own line and now its initialization payload (coalescing keys on it). Two-entry fixture: 7,119 → 3,820. - Infinifi farms: minimum-output semantics once, a compact row per pair. Pair row 999 → 555; three pairs 2,999 → 2,313 (one pair +202). - Yearn strategies: settings shown only when the calls change or depend on them; role explanations shared once. Configuration fixture 2,168 → 2,040; two report() calls 1,780 → 1,213. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… critiques apart - V3: once a deposit invalidates a vault's owner-specific withdraw limit on an inner vault, a later redemption from it was still credited in full, and every later call built on that guess (a 10 WETH limit forecast as a 250 WETH redemption). The redemption is now described as "would withdraw up to X" and not applied; both vaults' later calls and their after-batch liquidity, exposure and ratios are marked not forecast. - A blank critique response keeps the draft as before but is recorded as `empty`, not `pass`, so the stage telemetry tells a missing review from an explicit PASS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Safe, nonce and safeTxHash rows only repeated what the Telegram alert already carries (Safe address, Safe URL, nonce) and added plumbing (report_metadata, ReportContext.metadata) used by nothing else. They never reached the LLM prompt and made no Safe API calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a frozen vault moves - Auction: a setter that Auction 1.0.5 rejects while a lot is live was described as reverting but still written into the running settings, so later kicks used a floor or liveness that never landed. It now leaves the settings unchanged and runs no revival check. - V3: after an unknowable redemption freezes a vault, its later update_debt into another vault the batch reports left that vault's liquidity, exposure and ratios forecast without the move. That vault is now frozen too, with a line saying the amount is not forecast. - Pendle: upgrades are rendered per proxy, each with its own owner, current implementation and source. Only the published PendleSwap proxy resolves today, so this guards an assumption rather than a reachable case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Why
Two Strategist Safe alerts were checked against outside reviews and on-chain state:
setMinimumAuctionPriceleft the auction's pricing untouched; it setsAuction.minimumPrice;Both gists were corrected by hand, pinned to their alert blocks, and keep their original LOW tag to match the Telegram alerts. Both transactions have since executed. 3372's actual lots (1,176,467 and 162,021 GROVE) match the model: the leftover plus rewards claimed by execution time.
What changed
Evidence the LLM sees
utils/source_context.py):ai_explainer.py):Transferevents that restate the listed token transfers are dropped.Yearn Auction adapter (new,
yearn_auction_context.py)Runs for strategies that have
auction()and for direct Auction calls.minAmountToSell;report()only "kicks" when the verified harvest shows it.setAuctionA → B → A). It also covers:disable/enable, sweeps, and theminAmountToSellgate;disable(address,uint256)read correctly.Yearn V3 adapter (
yearn_v3_context.py,yearn_v3_batch.py)DebtAllocator.update_debt(vault, strategy, target)is read as the vault's own call, with the allocator'smaxDebtUpdateLoss.Nested vaults: every vault is walked in one global batch order, and flows are applied to the inner vault too:
auto_allocatewithin the vault's deposit limits;A parent's redemption limit is refreshed from the inner vault's liquidity, keeping owner-specific restrictions. When a deposit makes that limit unknowable, the model stops forecasting from that point instead of guessing.
New lines:
The prompt rates a sharp exit-liquidity cut, or funds that only a manual
update_debtcan reach, at least MEDIUM.Smaller prompts
Facts shared across calls are stated once per batch: V3 and auction mechanics, the
report()transfer note, and role explanations. Rendered sizes, before → after:report()Telemetry
LLMUsage). The explainer keeps oneStageUsageper stage (summary,critique-N=pass/revised/empty,detail) and logs one line per alert.resolve_protocol_contextskips an adapter that fails while rendering, as it already did for one that fails while resolving.Verification
AuctionKickedlot within 0.04%, and on 3373 the yvWETH-1 queue walk matched the simulation (OG WETH 659.20 → 389.20 → 659.20).uv run pytest tests/passes; the 4TestSafeApiQuotatests fail only locally, when a realSAFE_API_KEYis in.env, and the same onmain.prek run --all-filespasses.Not in this PR
🤖 Generated with Claude Code