Skip to content

Auto-discover Morpho Blue positions via event-log scan - #12

Merged
szhygulin merged 1 commit into
mainfrom
feat/morpho-discovery
Apr 14, 2026
Merged

Auto-discover Morpho Blue positions via event-log scan#12
szhygulin merged 1 commit into
mainfrom
feat/morpho-discovery

Conversation

@szhygulin

Copy link
Copy Markdown
Collaborator

Summary

  • Morpho Blue has no on-chain enumeration of a user's markets, so get_morpho_positions and get_portfolio_summary previously skipped Morpho unless the caller supplied marketIds — meaning any agent asking "what are my DeFi positions?" silently missed Morpho exposure.
  • Adds discoverMorphoMarketIds: paginated eth_getLogs scan of Supply / Borrow / SupplyCollateral events filtered by the indexed onBehalf topic, from the chain's deploy block to head in 10k-block chunks (override via MORPHO_DISCOVERY_CHUNK for premium RPCs).
  • get_morpho_positions: marketIds is now optional; falls back to discovery. Explicit marketIds remain a fast path.
  • Portfolio aggregator now fans out Morpho per-chain, merges positions into the lending bucket, and contributes to lendingNetUsd / perChain / totalUsd. Coverage flips from hardcoded covered: false to live errored/covered status.
  • MorphoLendingPosition added to LendingPositionUnion.

Tradeoffs

  • Cold get_portfolio_summary calls get slower: ~40 sequential 10k-block chunks × 3 parallel event calls on Ethereum mainnet. Each chunk is capped so it works on Alchemy/Infura free tier. A TTL cache is the natural next step if this becomes painful in practice.

Test plan

  • npm run build — clean
  • npm test — 209 tests pass (no new tests added; discovery relies on live RPC and isn't covered by the existing unit-test fixtures)
  • Manual: call get_morpho_positions with a wallet known to have Morpho exposure and no marketIds — confirm positions are returned
  • Manual: call get_portfolio_summary for the same wallet — confirm Morpho appears in breakdown.lending and contributes to totalUsd
  • Manual: call with a wallet that has zero Morpho exposure — confirm empty result and coverage.morpho = { covered: true }

🤖 Generated with Claude Code

Morpho Blue has no on-chain enumeration of a user's markets, so
get_morpho_positions and get_portfolio_summary previously skipped Morpho
unless the caller supplied marketIds — meaning any agent asking "what
are my DeFi positions?" silently missed Morpho exposure.

Adds discoverMorphoMarketIds: paginated eth_getLogs scan of Supply,
Borrow, and SupplyCollateral events filtered by the indexed onBehalf
topic, from the chain's deploy block to head in 10k-block chunks
(override via MORPHO_DISCOVERY_CHUNK for premium RPC endpoints).

- get_morpho_positions: marketIds is now optional; falls back to
  discovery. Explicit marketIds remain a fast path.
- Portfolio aggregator: fans out Morpho per-chain, merges positions
  into the lending bucket, and contributes to lendingNetUsd /
  perChain / totalUsd. Coverage flips from hardcoded covered:false
  to live errored/covered status.
- MorphoLendingPosition added to LendingPositionUnion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@szhygulin
szhygulin merged commit d384dc6 into main Apr 14, 2026
2 checks passed
@szhygulin
szhygulin deleted the feat/morpho-discovery branch April 25, 2026 02:15
szhygulin added a commit that referenced this pull request Apr 27, 2026
Two updates to SECURITY.md tied to recent integrity-check work:

1. Preflight-skill row's "Honest limits" column now narrates the
   silent-skip-on-mismatch failure mode that was open prior to
   skill v0.4.1: the skill's prose instructed comparison but didn't
   mandate it, the agent could skip silently, and we observed exactly
   that in production during the v4-typo pin bump. skill PR #12
   (closes skill #10) added Step 0 — mandatory integrity self-check —
   that halts the flow on divergence with explicit alarm prose.
2. Updated the parallel paragraph (~line 107) that mentioned
   `PreToolUse` hook as the only roadmap option: now also references
   vaultpilot-mcp #379 (server-side enforcement) as the
   equally-valid parallel path, plus calls out skill v0.4.1's Step 0
   as the agent-side fix that's already shipped.

Cross-references the now-merged skill PR #12, the closed skill #10,
and the open vaultpilot-mcp #379 server-side companion. Pure docs.
szhygulin added a commit that referenced this pull request Apr 28, 2026
…501)

Inv #12.5 (skill-side, issue #501) marks a curated list of op classes
where the second-LLM check is a precondition of `confirmed: true`,
not opt-in. The skill-side rule lives in vaultpilot-skill v9+; this
PR is the MCP-side scaffold so future hard-trigger op producers can
flip a flag at build time and have the existing verification block
surface a mandatory ⚠ line.

What ships:

- New `secondLlmRequired?: boolean` on `UnsignedTx`. Default absent
  / false. Documentation comment lists today's hard-trigger op
  classes (all currently deferred or not built):
    - EIP-7702 setCode (#481)
    - Permit2 batch grants (#453)
    - Opaque-facet bridges (#451)
    - Approval-management N-candidate selection (Inv #13)
    - Safe enableModule / setGuard / threshold changes
- `renderVerificationBlock` reads the flag and emits a single
  ⚠ line below the hash:
    ⚠ SECOND-LLM CHECK REQUIRED — call get_verification_artifact(handle)
    and relay the pasteableBlock to the user BEFORE 'send' (Inv #12.5
    hard-trigger op).
- 3 tests pinning the behavior: omitted when absent/false, emitted
  when true, composes cleanly with recipient + tokenClass warnings.

What does NOT ship:

- No producer wires the flag yet (every hard-trigger op class is
  itself deferred). The flag is dormant scaffold.
- No enforcement layer below the rendered ⚠. The MCP can't tell
  whether the agent actually ran the second-LLM check — same self-
  attestation gap as `userDecision: "send"`.

Trust note (in the field doc):
The flag is a workflow signal, not a cryptographic primitive.
Closing the agent-side honesty gap requires infrastructure that
doesn't exist today (provider-signed LLM responses, TEE attestation,
or zkML proofs of NN inference). Discussed in the PR thread for
this issue.

Coordinated with skill v9 release (lifts §16 unconditional 7702
refusal + adds Inv #12.5 hard-trigger language); MCP-side
`EXPECTED_SKILL_SHA256` bump happens in the skill-coordination PR,
not this one.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
szhygulin added a commit that referenced this pull request Apr 28, 2026
feat(security): UnsignedTx.secondLlmRequired scaffold for Inv #12.5 (#501)
szhygulin added a commit that referenced this pull request Aug 6, 2026
Deletes the Inv #12.5 hard-trigger flag `secondLlmRequired` on
`UnsignedTx` and the `⚠ SECOND-LLM CHECK REQUIRED` line the
verification renderer emitted for it. No op class makes the check a
precondition of 'send'; docs restate Inv #12.5 as a recommendation.

The check needs the user to physically paste into a second provider's
session, so a rule that blocks 'send' until it happens blocks the user,
not an attacker — a rogue agent self-attests the verdict either way
(the `userDecision: "send"` self-attestation gap). The flag also never
had a producer: no `prepare_*` path set it, so nothing on this server
changes behavior. The load-bearing gate lives in the skill, filed at
szhygulin/vaultpilot-security-skill#51.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant