Repository navigation
Conversation
… funds V1 submitMarketRemoval is now CRITICAL when the vault still supplies the market (any amount), with the amount at risk and its share of totalAssets, and re-alerted once within 24h of removableAt. Empty-market removals stay MEDIUM. The V1 timelock alert now shows current -> new and executable time. V2 removeAdapter of an adapter holding assets, and decreaseTimelock on removeAdapter while adapters hold assets, are CRITICAL; other timelock decreases and abdicate are HIGH. removeAdapter labels name the adapter again. New adapter_timelocks_v2 reads MorphoMarketV1AdapterV2's own timelock on-chain (not in the Morpho API): executableAt(burnShares(id)) for every held market, plus a HIGH alert when timelock(burnShares) drops. Loss findings are sent as their own "POTENTIAL VAULT LOSS" message on the morpho channel. Adds Robinhood's official public RPC as a fallback. Refs #434 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Noncanonical calldata: executableAt is keyed by exact calldata and Solidity ignores trailing bytes, so a padded burnShares submission evaded the canonical-key check (reproduced on a mainnet fork: padded submit pending, canonical key 0, padded call burned the shares). Pending burns are now also discovered from the adapter's Submit(burnShares) logs, scanned incrementally from its creation block with a per-adapter cursor; every discovered calldata is kept verbatim and re-read until it resolves. Canonical keys stay as a fast path, and a failed or incomplete scan fails the run after alerting. - Valuation: burned-market amounts use stored supplyShares and Morpho Blue market totals instead of expectedSupplyAssets, which goes through the IRM and may revert; criticality follows shares. - V1: totalAssets() is best effort, so a reverting withdraw-queue IRM only drops the "% of vault" figure instead of aborting governance checks. - V2 governance query: page size 100 (the added fields pushed first: 200 past Morpho's 1M complexity limit), passed as a variable and guarded by a test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
getLogs history scans are too expensive for our RPC budget. The adapter check now uses batched eth_call only: - pending burns: canonical executableAt(burnShares(id)) per held market; - executed burns: adapter supplyShares[id] always equals its Morpho Blue position (verified on all 50 live positions), and burnShares zeroes only the former, so any gap is a written-off position -> CRITICAL. This catches burns submitted with padded calldata once they execute (fork-tested); markets are remembered after leaving marketIds. Known limitation, documented: a padded-calldata burn is not visible while pending without Submit events. Removes the log cursor, chunk config, and the adapter creationBlockNumber query field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop the share-parity check (extra Morpho position/market reads per market and a remembered-markets cache). Non-canonical burnShares submissions need the adapter's Submit events, which the Morpho API does not expose; that gap is now a documented TODO. Valuation calls run only when a canonical burn is pending. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Could you add a zero-to-positive shares transition test for the same pending burn? At I reproduced that sequence with the extracted function and mocked cache, clock and delivery; a clean-cache positive-share control sends CRITICAL. I have not run the full suite or a live-chain test. The existing tests cover zero and positive shares separately; the transition would catch this gap. |
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.
Closes #434
What
Curator actions that can write off vault funds now send a CRITICAL alert for any non-zero amount. Each goes out as its own
🚨 POTENTIAL VAULT LOSSmessage on themorphochannel, and is re-alerted once within 24h of its execution time.submitMarketRemovalconfig.removableAt+ Morpho Blueposition/marketremoveAdapter(adapter)pendingConfigs+ adapterassetsdecreaseTimelock(removeAdapter, …)pendingConfigsburnShares(marketId)executableAt(burnShares(id))Other changes:
Timelock decrease: 3d → 1d, with the execution time and a countdown. It used to say just "Timelock is changing".decreaseTimelockcalls andabdicateare now HIGH (they were MEDIUM).removeAdapter(...)names the adapter again.timelock(burnShares)sends HIGH. The first run seeds the cache silently.How
burnSharesis detected (batchedeth_callonly)The Morpho GraphQL API has no adapter-level timelock data, so each
MorphoMarketV1AdapterV2is read on-chain:executableAt(burnShares(id))for every held market → CRITICAL with the amount at risk, plus a reminder within 24h. A drop intimelock(burnShares)→ HIGH. Valuation calls run only when a burn is pending.TODO: a burn submitted with non-canonical calldata (canonical + trailing bytes) is not detected. Solidity ignores the extra bytes, but
executableAtis keyed by the exact calldata, so the key can't be derived. Catching it needs the adapter'sSubmitevents, which the Morpho API does not expose. It can be added once an indexer provides them.Amounts come from stored shares and Morpho Blue market totals, never the IRM (it can revert), and criticality follows shares. On V1,
totalAssets()is best effort, so a broken IRM only drops the "% of vault" figure.Robinhood had no RPC in the env or in
PUBLIC_RPC_URLS, so I added its official public RPC (rpc.mainnet.chain.robinhood.com, listed on chainid.network) as a fallback.The added GraphQL fields pushed
first: 200past Morpho's 1M complexity limit (1,151,000). The page size is now 100 (575,500), passed as a variable, and a test checks that the configured vaults fit.Testing
ruff,ty, and the full test suite pass (1945 tests). New tests cover severity classification, standalone delivery, reminder-once logic, timelock decrease, and adapterexecutableAtreads. The fake client in the adapter tests rejects.call()outside a batch context; that is the bug the first live run caught.eth_blockNumberandeth_call. No pending loss operations exist today. The Katana Yearn OG USDCweETH/vbUSDCremoval (no supply) correctly came out as MEDIUM. Every adapter returnstimelock(burnShares)of 3d or 7d.Rollout notes
Removals and
removeAdapteroperations that were already pending (and already alerted at MEDIUM) before this deploys won't trigger a new first alert. If they still have funds at risk, they'll get the CRITICAL reminder within 24h of execution.🤖 Generated with Claude Code