Skip to content

feat(evm-wallet-experiment): Add delegator exos#882

Draft
grypez wants to merge 17 commits intomainfrom
grypez/gator-exo
Draft

feat(evm-wallet-experiment): Add delegator exos#882
grypez wants to merge 17 commits intomainfrom
grypez/gator-exo

Conversation

@grypez
Copy link
Copy Markdown
Contributor

@grypez grypez commented Mar 19, 2026

WIP

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 19, 2026

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 78.19%
⬇️ -0.40%
8741 / 11179
🔵 Statements 77.98%
⬇️ -0.42%
8879 / 11385
🔵 Functions 75.85%
⬇️ -0.28%
2042 / 2692
🔵 Branches 75.64%
⬇️ -0.63%
3759 / 4969
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/evm-wallet-experiment/src/constants.ts 91.42%
⬇️ -5.13%
83.33%
⬇️ -6.67%
60%
⬇️ -15.00%
94.11%
⬇️ -5.89%
3, 195, 208
packages/evm-wallet-experiment/src/index.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/evm-wallet-experiment/src/types.ts 88.88%
⬇️ -2.42%
0%
🟰 ±0%
25%
⬇️ -8.33%
88.88%
⬇️ -2.42%
28, 37, 332
packages/evm-wallet-experiment/src/lib/caveats.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/evm-wallet-experiment/src/lib/delegation-grant.ts 77.41% 71.42% 71.42% 80% 24-33, 99, 165-177, 274-280
packages/evm-wallet-experiment/src/lib/delegation-twin.ts 87.71% 83.33% 85.71% 87.71% 68, 92, 100, 142, 166-168
packages/evm-wallet-experiment/src/lib/delegation.ts 90.66%
⬇️ -1.40%
88.46%
⬇️ -3.54%
90.9%
🟰 ±0%
91.54%
⬇️ -1.90%
20, 81-87, 266-271, 311-315
packages/evm-wallet-experiment/src/lib/erc20.ts 98.14%
⬆️ +0.03%
91.66%
🟰 ±0%
94.44%
🟰 ±0%
100%
🟰 ±0%
9
packages/evm-wallet-experiment/src/lib/method-catalog.ts 90% 50% 75% 100% 11
packages/evm-wallet-experiment/src/vats/coordinator-vat.ts 82.25%
⬇️ -5.85%
75%
⬇️ -4.42%
86.86%
⬇️ -6.61%
82.31%
⬇️ -5.74%
61, 85-87, 125-170, 367, 451, 483-485, 512-515, 543, 599-601, 690-692, 712, 716, 909, 912, 948-950, 958, 973-977, 1140-1142, 1196-1198, 1204, 1219-1221, 1301, 1325-1328, 1337-1339, 1372-1375, 1394-1396, 1424-1427, 1439-1441, 1506, 1530, 1537, 1544, 1608-1623, 1643-1645, 1774, 1809-1812, 1818-1822, 1897, 1949-1950, 1986, 2091, 2167, 2203-2205, 2219-2221, 2251-2254, 2271, 2284-2434, 2450, 2556-2562, 2651, 2725, 2743, 2828, 2845-2848, 2893-2896, 2901, 2915, 2965-2966, 3008, 3016, 3019-3021, 3037, 3057, 3154-3155, 3169-3172, 3207, 3216
packages/evm-wallet-experiment/src/vats/delegation-vat.ts 75%
⬇️ -17.59%
77.77%
⬇️ -4.58%
75%
⬇️ -17.30%
76.11%
⬇️ -18.22%
27, 118, 176, 201, 211-237
packages/ocap-kernel/src/rpc/kernel-control/queue-message.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
Generated in workflow #4261 for commit 9d0cbd8 by the Vitest Coverage Report Action

@grypez grypez force-pushed the grypez/gator-exo branch from 6823dad to 42376f0 Compare April 1, 2026 13:23
@grypez grypez force-pushed the grypez/gator-exo branch from 42376f0 to 7626556 Compare April 8, 2026 20:04
grypez and others added 17 commits April 9, 2026 11:27
…handler

Vat rejections propagate through the kernel as CapData objects. The
queueMessage RPC handler was passing these through undeserialized,
causing callers to receive a generic 'Internal error' instead of the
original rejection message.

Apply the same isCapData/kunser pattern already used in SubclusterManager
to deserialize CapData rejections into Errors before rethrowing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…JSON requests

JSON.stringify drops BigInt fields silently; replace with a replacer
that coerces BigInt to string so numeric values survive the wire.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…llowedMethods, and ERC20TransferAmount

These enforcers expect tightly-packed bytes, not ABI head/tail
encoding. Switch encodeAllowedTargets/encodeAllowedMethods to
encodePacked and encodeErc20TransferAmount likewise. Update
explainDelegationMatch to decode packed terms the same way, and
update the caveat encoding tests to assert packed layout directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…and enforcer key map

Add registerChainContracts() / customChainContracts so devnet chains
(e.g. Anvil at 31337) can be registered at runtime without touching the
static CHAIN_CONTRACTS table. Add ENFORCER_CONTRACT_KEY_MAP to translate
PascalCase enforcer keys in contracts.json to camelCase CaveatType names.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Represent delegations as discoverable exo objects ("twins") so an agent
can call E(twin).transfer(to, amount) instead of manually building
Execution structs. Adds a method catalog, grant builder, twin factory
with cumulative spend tracking, and wires them into the delegation and
coordinator vats.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…wins

Build typed method guards from METHOD_CATALOG entries and pass them to
makeDiscoverableExo, enabling arg-count/type validation at the exo boundary.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… generation

generateSalt accepts an optional entropy hex string that is mixed into
the counter hash when crypto.getRandomValues is unavailable. Thread
the same entropy option through makeDelegation and all three
buildDelegationGrant overloads (transfer, approve, call) so callers
can supply per-run entropy to avoid salt collisions across fresh vat
instances.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…win method args

Add valueLte to CaveatSpecStruct so call twins can carry a per-call
ETH value limit that is checked locally before submission. In
makeDelegationTwin, normalize args[1] to BigInt on entry (required
when values arrive as hex strings over the daemon JSON-RPC boundary),
apply the valueLte check, and pass normalizedArgs to buildExecution.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… caveat

Integrate with the delegation framework's AllowedCalldataEnforcer to pin
the first argument (recipient/spender) of transfer/approve at both the
on-chain enforcer level and the local exo interface guard.

- Add `allowedCalldata` to CaveatTypeValues and CaveatSpec
- Add `encodeAllowedCalldata` helper and deployed enforcer address
- Wire optional `recipient`/`spender` through grant builders
- Twin derives address restriction from caveatSpecs, not a standalone field

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Accepts a full DelegationGrant and persists only the delegation to
baggage. Used by coordinator-vat's provisionTwin, where redeemFn/readFn
closures must stay in the coordinator scope and cannot cross the CapTP
vat boundary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ransaction

- setup-wallets: provision delegation twin via provisionTwin (not
  receiveDelegation) so the away coordinator holds an active twin
  with a cumulativeSpend caveat spec (1000 ETH, native token)
- coordinator-vat: store twins created by provisionTwin in a local
  coordinatorTwins map keyed by delegation ID
- coordinator-vat: sendTransaction routes through the twin when one
  is registered for the matched delegation, so local caveat checks
  (e.g. cumulativeSpend budget) fire before the UserOp hits the chain

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Script invoked by the docker e2e suite to test local cumulativeSpend
enforcement and chain-side rejection of an expired timestamp caveat.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Runs run-delegation-twin-e2e.mjs inside the away container as part of
the Docker E2E suite. Covers local cumulativeSpend enforcement and
chain-side rejection of an expired timestamp delegation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-20 grant builders

- Export FIRST_ARG_OFFSET from erc20.ts; remove local copies in
  delegation-grant.ts and delegation-twin.ts
- Extract buildErc20Grant helper from the near-identical
  buildTransferGrant/buildApproveGrant (~90 lines removed)
- Strengthen delegation twin method guards: M.any() -> M.string() for
  Hex returns, M.bigint() for getBalance
- Clarify cast: as keyof typeof METHOD_CATALOG -> as CatalogMethodName

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Explains the grant → twin → discoverable capability layering:
- Delegation grants as serializable describable delegations (redeemable
  bytestring + readable caveat specs)
- Delegation twins as local capabilities that mirror on-chain stateful
  caveats (latently; on-chain is authoritative)
- M.* patterns as the mechanism for discoverability and pre-validation

Reorganizes the enforcer mapping table around this model and preserves
the existing M.*/Gator overlap reference content.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… message directly

With the queueMessage RPC fix in @MetaMask/ocap-kernel, vat rejections now
surface as response.error with the actual message rather than a generic
'Internal error'. Update callVatExpectError in both the docker e2e helper
and the integration test runner to return response.error.message directly
and throw on unexpected success, replacing the dead response.result.body path.
@grypez grypez force-pushed the grypez/gator-exo branch from 977cffe to 9d0cbd8 Compare April 9, 2026 17:26
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