Conversation
A Yearn timelock batch adding two strategies (add_strategy +
update_max_debt_for_strategy on two WETH vaults) produced a summary that
could not name either vault, called a Spark looper an Aave looper, hedged
that max_debt had "units unconfirmed", said the add_to_queue flag could
not be interpreted, and reported the debt-setting calls as unverified.
- Resolve EIP-1167 clones in ERC20 metadata. Every Yearn V3 vault is a
clone, so vaults never got symbol/decimals and the model had nothing to
size amounts with.
- Read name() with symbol/decimals and lead address labels with it when
the explorer label is only the contract type ("Yearn V3 Vault",
"LSTAaveLooper").
- Match ABI parameter names on the full signature when a function is
overloaded. A Vyper default argument compiles to one entry per arity,
and the first-entry lookup left add_to_queue unnamed.
- Extract Vyper function signatures and docstrings (CRLF-safe, preferring
decorated definitions over interface stubs) for source context.
- Add a Yearn V3 protocol-context adapter keyed on call shape and
apiVersion(). It states max_debt and related values in the vault asset
against totalAssets, the default queue, peer caps and the deposit
limit, and says what add_to_queue does given use_default_queue.
- When the bundle simulation is unavailable, name the earlier same-target
call in an independent revert's diagnostic.
- Tell the model that Protocol Context units are verified, and forbid
unsupported "audited"/"low-fee" claims.
- Number timelock batch calls from 1 in Telegram so they match the AI
report's call flow.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
When the Tenderly bundle request failed, batch calls fell back to independent simulations against current state. Out of batch order a dependent call reverts falsely (update_max_debt_for_strategy before its add_strategy), and the Yearn timelock alert reported exactly those false reverts. A batch is now only ever simulated as one ordered bundle from the executor. If the bundle is unavailable, no call is simulated: the call flow says so and the prompt tells the model not to infer an outcome. Calls after a bundle revert are marked "not reached" rather than re-simulated. Tenderly's error payload is logged when a bundle returns no results, so the next fallback is diagnosable. This replaces the same-target dependency hint added earlier in this branch, which only existed to explain fallback reverts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
spalen0
marked this pull request as ready for review
September 25, 2026 19:00
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.
Problem
The Yearn timelock alert for
0x2714…d788(add_strategy+update_max_debt_for_strategyon two WETH vaults) produced this summary:What went wrong, and why:
fetch_erc20_metadataonly resolved EIP-1967/getter proxies, so clones got no symbol/decimals/name.LSTAaveLooper. The on-chainname()was never read.max_debtis always in the vault asset, but nothing said so. Related Tokens listed several candidates (shares,asset(),accountant()).add_strategyis overloaded (a Vyper default arg). The param-name lookup took the first ABI entry,add_strategy(address), soadd_to_queuewas unnamed. Vyper source context was also never extracted (the regex only knew Solidityfunction).update_max_debt_for_strategyran before itsadd_strategyand reverted. The same bundle succeeds locally for all four calls.Changes
utils/proxy.py:minimal_proxy_implementation()decodes EIP-1167 clone bytecode.utils/erc20_metadata.py: resolves clones before the selector check. It also readsname()in the same batch when the bytecode dispatches it, and retries without it on failure (bytes32 names such as MKR).utils/llm/ai_explainer.py:wstETH/WETH Spark Looper (ysWETH, 18 dec) — LSTAaveLooper.Batch simulation: unavailableand the prompt tells the model not to infer an outcome. Calls after a bundle revert are markednot reached.utils/tenderly/simulation.py: logs Tenderly's error payload when a bundle returns no results.utils/source_context.py:defextraction covers decorators, multi-line signatures with defaults, and docstrings. It normalizes CRLF (Etherscan serves the V3 vault source with CRLF) and prefers decorated definitions overinterfacestubs.utils/llm/yearn_v3_context.py(new adapter, registered inprotocol_context.py):apiVersion()3.x rather than protocol name, so it also covers third-party curators and Safe alerts.use_default_queue, and the default queue with debt/caps, plus each named strategy's registration, asset, size and whether it is itself an allocator vault.protocols/timelock/timelock_alerts.py: batch calls are numbered from 1.utils/llm/README.md(new 5f-2 section, source-context/metadata notes) andprotocols/timelock/README.md.Result on the same transaction (local end-to-end run)
The gist also gains a deterministic Protocol Context section with each vault's queue, debts and caps. The corrected report for the original alert is published as revision 2 of https://gist.wavey.info/E1U8blubtKZMJ82Px2vxfP3B.
Not addressed / worth knowing
mainevery 10 minutes, so it had the bundle code. The same bundle succeeds locally, andfetch_jsonalready retries 429/5xx/timeouts. Check the runner logs around 2026-09-25 18:06 UTC forBundle simulation unavailable,Tenderly bundle simulation returned, or an HTTP 4xx onsimulate-bundle. With this PR, a repeat shows "unavailable" instead of false reverts.≈ 500 WETH) are not added formax_debt. The normalized values appear in the Protocol Context section instead.Testing
uv run pytest tests/: 1432 passed. New tests cover clone resolution,name()read and fallback, EIP-1167 decoding, overload disambiguation, Vyper extraction (CRLF, multi-line signature, interface stub), the label format, bundle-only simulation (unavailable / not reached, no single-call fallback), Telegram numbering, and the adapter (proposal lines, rendering, protocol-agnostic grouping, non-V3/non-vault skip, read path).uv run ruff format .anduv run ruff check .are clean. mypy is clean on the new module.explain_batch_transactionrun with the configured LLM.🤖 Generated with Claude Code