Skip to content

Embed per-chain exposure in ledger account status - #13

Merged
szhygulin merged 1 commit into
mainfrom
feat/ledger-account-chains
Apr 14, 2026
Merged

Embed per-chain exposure in ledger account status#13
szhygulin merged 1 commit into
mainfrom
feat/ledger-account-chains

Conversation

@szhygulin

Copy link
Copy Markdown
Collaborator

Summary

  • WalletConnect advertises accounts as eip155:<chainId>:<address>, so one address exposed on Ethereum + Arbitrum + Polygon surfaces as three raw entries. get_ledger_status was passing them through unchanged, which looked like duplicates to agents (e.g. "5 entries with 1/4 and 2/5 as duplicates") and forced a manual "same address on different chains" explanation.
  • getConnectedAccounts() now deduplicates by address — callers doing membership checks (pre-sign guard, default-from resolution) keep working but no longer see redundant entries.
  • Adds getConnectedAccountsDetailed() returning { address, chainIds, chains } per unique address, preserving first-seen order.
  • SessionStatus gains accountDetails: SessionAccount[] with the per-address chain breakdown. Each entry maps chainIds to the server's SupportedChain names (ethereum / arbitrum / polygon); unsupported chainIds are preserved in chainIds[] with an empty chains[] slot.
  • accounts stays as a deduped flat list for backward compat with agents that already parse it.

Test plan

  • npm run build — clean
  • npm test — 209 tests pass
  • Manual: call get_ledger_status with a Ledger session exposing the same address on multiple chains; verify accounts is deduped and accountDetails shows each address with its chains array populated.
  • Manual: send a transaction and confirm the pre-sign check still passes (it consumes getConnectedAccounts() which now returns deduped addresses).

🤖 Generated with Claude Code

WalletConnect advertises accounts as `eip155:<chainId>:<address>`, so an
address exposed on Ethereum + Arbitrum + Polygon surfaces as three raw
entries. The flat `accounts` array in get_ledger_status was passing them
through unchanged, which looked like duplicates to agents and forced a
"these two are the same address on different chains" explanation.

- getConnectedAccounts now deduplicates by address (callers that only
  need a membership check, like the pre-sign guard, keep working).
- Adds getConnectedAccountsDetailed returning {address, chainIds,
  chains} per unique address, preserving first-seen order.
- SessionStatus gains `accountDetails: SessionAccount[]` with the
  per-address chain breakdown. `accounts` stays as a deduped flat list
  for backward compat with agents that already parse it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@szhygulin
szhygulin merged commit 2746218 into main Apr 14, 2026
2 checks passed
@szhygulin
szhygulin deleted the feat/ledger-account-chains branch April 25, 2026 02:15
szhygulin added a commit that referenced this pull request Apr 28, 2026
fix(skill-pin): bump to v6 for Inv #13 multi-step BTC flows (closes #463)
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>
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