refactor(evm-wallet-experiment): split coordinator-vat into home/away#939
Open
refactor(evm-wallet-experiment): split coordinator-vat into home/away#939
Conversation
d7ab81d to
1ea6fef
Compare
Contributor
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1ea6fef to
5485331
Compare
sirtimid
reviewed
Apr 21, 2026
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4d3f87e. Configure here.
…n and slim method-catalog Remove CaveatSpec, CaveatSpecStruct, BigIntStruct, and the old DelegationGrant type (which carried raw caveat bytes on the away side). Replace with TransferNativeGrant | TransferFungibleGrant — a discriminated union where each variant carries pre-decoded semantic fields (to, maxAmount, token) alongside the signed Delegation. This means the away side never needs to decode caveat bytes; the home encodes once when building the grant. Slim method-catalog to only the two methods this refactor introduces: transferNative and transferFungible. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Vat that builds and stores delegation grants on the home side. Exposes: - buildTransferNativeGrant: valueLte + allowedTargets caveats (conditional) - buildTransferFungibleGrant: allowedTargets(token) + allowedMethods(ERC20 transfer selector) always; erc20TransferAmount + allowedCalldata(to) conditionally - storeGrant / removeGrant / listGrants: persisted in baggage Grants are returned unsigned (status: 'unsigned'); signing is done by the home coordinator before storeGrant is called. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Simple away-side store for DelegationGrant values received from the home coordinator. Grants are keyed by delegation.id and persisted in baggage. Exposes receiveGrant / removeGrant / listGrants; used by away-coordinator when rebuilding the away sheaf. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tor-vat Home coordinator keeps all shared infrastructure (signing, tx submission, ERC-20, swap, smart account, OcapURL) and adds delegation management via the new delegator-vat plus a homeSection exo. New additions: - buildTransferNativeGrant / buildTransferFungibleGrant: sign + store via delegator-vat - signDelegationInGrant: resolves DelegationManager from chain contracts, signs via keyring or external signer, finalizes delegation - listGrants / revokeGrant: list and on-chain disable grants - getHomeSection(): returns the pre-built homeSection exo - homeSection: local exo with transferNative/transferFungible that submit direct transactions; throws after 2 uses per method (demo limit) Removes: all away-side peer protocol (connectToPeer, registerAwayWallet, handleSigningRequest, handleRedemptionRequest) and the old delegation routing in sendTransaction. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
makeDelegationTwin({ grant, redeemFn }) builds a discoverable exo that
enforces the grant's constraints locally before submitting an Execution:
- transferNative: validates recipient (eq constraint or any) and amount
(lte constraint or any), using M.interface guards
- transferFungible: validates token (must match), recipient (optional eq),
and tracks cumulative spend with reserve-before-await rollback on error;
concurrent calls cannot together exceed the budget
DelegationSection is a discriminated union carrying method and (for
transferFungible) token — used by away-coordinator routing to filter
matching sections and propagate constraint errors correctly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…er-relay support Away coordinator manages the wallet API on the agent (away) side. Shares infrastructure with home (provider, smart account, tx submission) and routes semantic calls through delegation twins before falling back to the home section. Key design: - receiveDelegation(grant): stores in redeemer-vat, rebuilds delegation sections from all stored grants via makeDelegationTwin - transferNative / transferFungible: filter delegation sections by method and token; if any matched sections exist, try them and propagate errors (constraint violations are not swallowed); fall back to homeSection only if no sections match - makeRedeemFn: submits delegation UserOp locally when bundler/smart account is available; relays to homeCoordRef.redeemDelegation() in peer-relay mode (away has no bundler; home's smart account is delegate) - connectToPeer(ocapUrl): redeems URL, fetches homeSection, rebuilds routing - initializeKeyring / unlockKeyring / isKeyringLocked, signMessage, signTypedData, listGrants, refreshPeerAccounts, sendDelegateAddressToPeer: peer setup and key management Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ion-grant Delete the monolithic coordinator-vat (3275 lines mixing home and away concerns) and the old delegation-vat, now superseded by the home/away split and the new delegator-vat/redeemer-vat pair. Remove delegation-grant.ts, which built grants from raw CaveatSpec lists; that role now belongs to delegator-vat, which encodes caveats and returns pre-decoded TransferNativeGrant / TransferFungibleGrant values. Update package.json build script: coordinator-vat → home-coordinator + away-coordinator. Remove delegation-grant exports from index.ts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tor API Replace old coordinator API references (createDelegation, provisionTwin, listDelegations, pushDelegationToAway) with the new semantic grant methods (buildTransferNativeGrant, receiveDelegation, listGrants, transferNative). - wallet-setup.ts: launchWalletSubcluster now accepts role:'home'|'away' and points to home-coordinator.bundle/away-coordinator.bundle with the appropriate delegator/redeemer auxiliary vat - setup-wallets.ts: replace createDelegation+provisionTwin with buildTransferNativeGrant+receiveDelegation - docker-e2e.test.ts: delegation redemption suite uses new grant API; DelegationGrant type replaces old Delegation type; transferNative replaces sendTransaction for delegated ETH sends - run-delegation-twin-e2e.mjs: rewrites twin test using buildTransferFungibleGrant+receiveDelegation+transferFungible - docker-exec.ts: callVat now passes --raw to the CLI and decodes smallcaps CapData inline, fixing BigInt values arriving as '1000000000000000000n' strings from prettifySmallcaps Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…unters Normalize token to lowercase in makeDelegationTwin and the transferFungible routing filter so checksummed vs. lowercase addresses always match. Introduce delegationTwinMap in rebuildRouting so existing twins are reused instead of recreated, preserving in-memory spent counters across receiveDelegation calls. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…cker-exec Replace the manual typeof/in/Array.isArray guard with an is() check against a CapDataStruct, per review feedback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… integration test Same pattern as the docker-exec fix: replace manual typeof/Array.isArray guard with assert() against CapDataStruct. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move applyGasBuffer, validateGasEstimate, and validateTokenCallResult out of both coordinator vats into src/lib/tx-utils.ts. Both vats now import from the shared module. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ation twin The twin's M.eq guard is built with a lowercased token. Passing a checksummed address from the outer transferFungible call caused the guard to reject it. Compute tokenLower once and use it for both the section filter and the exo call. Adds a regression test that constructs a twin with a checksummed token and asserts section.token is normalized. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Updates all seven node test scripts and three source files to work with
the home/away coordinator split introduced in this branch.
Source changes:
- types.ts: add totalLimit field to TransferNativeGrant
- delegator-vat.ts: support totalLimit (NativeTokenTransferAmount caveat)
in buildTransferNativeGrant
- home-coordinator.ts: thread totalLimit through to delegator vat
- away-coordinator.ts: forward getAccounts, signMessage, and signTypedData
to homeCoordRef when a peer wallet is connected
Test script changes:
- Replace createDelegation with buildTransferNativeGrant/buildTransferFungibleGrant
- Update redeemDelegation calls from { delegationId } to { delegation }
- Replace listDelegations with listGrants; use grant.delegation.* for assertions
- Rewrite callExpectError to use try/catch (kernel.queueMessage throws on
rejection rather than returning error capdata)
- Add role: 'away' to walletConfig2 in peer tests so away-coordinator is loaded
- spending-limits: add EOA → smart-account funding step so on-chain ETH
transfer executions succeed; fix TOTAL_LIMIT constant so remaining budget
fits within the per-tx limit for the ceiling-exhaustion test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove HOME_SECTION_LIMIT demo counter from homeSection exo (rate-limiting belongs on-chain via limitedCalls caveat, not in the vat) - Add superstruct validation (DelegationGrantStruct) on baggage restore in delegator-vat and redeemer-vat instead of unsafe `as` casts - Tighten delegation-twin guard/type mismatch: M.bigint() guard now matches `amount: bigint` param type; remove redundant BigInt coercions - Add home-coordinator.test.ts covering configureBundler URL/chainId validation and revokeGrant error paths - Add peer-wallet integration test: delegation relay (away → home) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rdinator CLI callers (Docker e2e, kernel-cli) serialize bigints as numeric strings in JSON. The away coordinator is the external boundary — coerce amount to BigInt() in transferNative/transferFungible before forwarding to delegation twins whose M.bigint() / M.lte(bigint) guards now strictly require bigints. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e twin The transferFungible twin correctly tracked budget via spent/max with pre-reserve + rollback, but transferNative only checked maxAmount (per-call) and silently ignored totalLimit (cumulative cap). Add the same pattern: - cumulativeMax = totalLimit ?? 2^256-1 - Reserve before await, roll back on redeemFn failure Retain the per-call maxAmount body check for test environments where interface guards are mocked out. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
62111cc to
9c824f4
Compare
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.

Splits the monolithic 3,275-line
coordinator-vatinto four focused components — home coordinator, away coordinator, delegator vat, and redeemer vat — and replaces raw caveat bytes with a discriminated union of decoded semantic grant types.Architecture before:
Architecture after:
Note
High Risk
High risk due to a large refactor of wallet orchestration and delegation redemption paths, including new coordinator vats and updated grant/twin execution semantics that affect transaction submission and budget enforcement.
Overview
Splits the monolithic coordinator into role-specific vats. The build now bundles
home-coordinatorandaway-coordinator(plusdelegator-vat/redeemer-vat), andmakeWalletClusterConfiggains aroleoption that switches the coordinator bundle and includes eitherdelegator(home) orredeemer(away); tests are updated to assert role-specific vat composition and bundle names.Reworks delegation grants and twin execution around semantic transfer methods.
DelegationGrantis replaced with a discriminated union (transferNative,transferFungible),METHOD_CATALOGis simplified to those two schemas, andmakeDelegationTwinnow builds aDelegationSectionthat enforces per-grant local guards and spend tracking for these methods.Removes legacy grant builder API.
lib/delegation-grant.tsand its tests are deleted and related exports are removed fromindex.ts; a newtx-utils.tsis added for gas buffering and RPC result validation, and the newaway-coordinatorvat is introduced to manage away-side routing, grant reception, and redemption submission.Reviewed by Cursor Bugbot for commit 9c824f4. Bugbot is set up for automated code reviews on this repo. Configure here.