feat: add evm-wallet-experiment package#877
Conversation
…hereum wallet subcluster Implements Phase 1 of the eth-wallet subcluster with: - Keyring management (SRP mnemonic + throwaway keys, HD derivation, signing) - EIP-7710 delegation framework (create, sign, receive, revoke, match) - Coordinator vat orchestrating signing strategy resolution (delegation → local → peer) - Provider vat wrapping JSON-RPC communication - Cluster config using bundleSpec with `ocap bundle` build step - 109 unit tests, all lint-clean Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add end-to-end integration tests validating two wallet subclusters communicating via QUIC direct transport: peer connection via OCAP URL, remote message/transaction signing, no-authority rejection, and capabilities reporting. Key fixes discovered during integration testing: - Fix exo self-reference: use stored variable instead of `this` - Fix delegation-vat parameters when undefined (no parameters configured) - Fix signMessage peer forwarding with optional account field - Add TextEncoder/TextDecoder to VatSupervisor allowed globals - Add globals config to wallet vat cluster config - Specify individual vat files in build script to exclude test files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…and MetaMask SDK signer - Fix delegation matching to use proper ABI decoding instead of substring matching - Add valueLte and timestamp caveat checks to delegationMatchesAction - Make contract addresses configurable via ChainContracts registry - Add UserOperation type (ERC-4337 v0.7) and Execution type - Add userop.ts: delegation chain encoding, UserOp building, hash computation - Add bundler.ts: submitUserOp, estimateGas, waitForUserOp polling - Add metamask-signer.ts: MetaMask SDK signing adapter with provider abstraction - Add @metamask/sdk dependency - Add chainId option to WalletClusterConfigOptions and caveat helpers Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…igner support Connect the coordinator vat to the ERC-4337 UserOp pipeline for delegation redemption, add external signer (MetaMask) integration, and extend the provider vat with bundler RPC methods. - Add submitUserOp/estimateUserOpGas to provider vat via viem http transport - Add ExternalSignerFacet type and connectExternalSigner method - Add configureBundler method with ENTRY_POINT_V07 default - Add redeemDelegation method (full UserOp build → sign → submit flow) - Extract resolveTypedDataSigning/resolveMessageSigning helpers - Update all signing paths with keyring → external → peer priority - Update createDelegation to work with external signer when no keyring - Merge and deduplicate accounts from local + external signers - Add hasExternalSigner/hasBundlerConfig to WalletCapabilities Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lidation, harden, and README Wire the full ERC-4337 UserOp pipeline: EntryPoint nonce lookup, bundler gas estimation, delegation status validation, explicit delegation chains, and sendTransaction delegation path. Add harden() to all lib modules and constants for SES compatibility, fix Date.now() in delegation matching, add HTTP status check in bundler, restore derived accounts on keyring resuscitation, add connectMetaMaskSigner test, and validate external signer interface. Comprehensive README documenting architecture, home/away kernel setup, signing strategy resolution, and delegation flow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… estimation, and caveat handling - Fix EIP-191 bug: add signHash for raw ECDSA UserOp hash signing - Add delegation signature verification in receiveDelegation (EIP-712) - Add erc20TransferAmount and limitedCalls caveat matching - Add chain ID filtering in findDelegationForAction - Add getGasFees provider method with EIP-1559 heuristic - Make gas params optional in redeemDelegation/sendTransaction - Add waitForUserOpReceipt polling on coordinator - Make forceReset configurable in cluster config - Export generateMnemonicPhrase from package index Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… and makeChainConfig factory Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace manual UserOp/bundler/encoding pipeline with the SDK as the single source of truth for contract addresses, delegation encoding, and smart account management. - Add `lib/sdk.ts` adapter wrapping all SDK imports (type mapping, environment resolution, delegation encoding, execution, smart account) - Wire `constants.ts` to resolve addresses from SDK environment first, falling back to manual registry then placeholders - Delegate `userop.ts` encoding to SDK (`encodeSdkDelegations`, `buildSdkRedeemCallData`) - Add `lib/bundler-client.ts` using viem `createClient + bundlerActions`; wire `provider-vat.ts` to use it instead of raw `http()` transports - Add `SmartAccountConfig` type with counterfactual address derivation via SDK's `getCounterfactualAccountData` - Coordinator uses smart account address as sender/delegator when configured; signing still done by underlying EOA key - Deprecate `lib/bundler.ts` (retained for backward compat) - Add Sepolia (`SEPOLIA_CHAIN_ID`) and Pimlico constants Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…t, remove dead code - Add Pimlico paymaster sponsorship via `pm_sponsorUserOperation` RPC: `bundler-client.ts` gets `sponsorUserOperation()`, `provider-vat.ts` gets `sponsorUserOp()`, coordinator uses it when `usePaymaster` is set - Add factory/factoryData fields to SmartAccountConfig and buildDelegationUserOp for first-time smart account deployment; coordinator marks account as deployed after first successful UserOp - Fix critical signing bug: submitDelegationUserOp now resolves the EOA owner address for signing when a smart account is configured, instead of passing the smart account address to the keyring - Wire coordinator.configureBundler() to call E(providerVat).configureBundler() for bundler client reuse - Decouple constants.ts from sdk.ts to prevent SDK from being bundled into non-coordinator vats (SES lockdown compatibility) - Revert userop.ts to manual ABI encoding (no SDK import) for same reason — SDK only enters coordinator-vat bundle now - Remove dead code: fromSdkDelegation, resolveCaveatType, createHybridSmartAccount and their unused imports - Add Sepolia E2E integration test (skips without env vars) - Update README for SDK, Sepolia, Pimlico, smart accounts, paymaster Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix vat code to run under real SES lockdown and add plain Node.js integration tests that bypass vitest SES issues. SES fixes: - Add TextEncoder/TextDecoder globals to coordinator vat config - Add keccak256-counter fallback for generateSalt() (crypto unavailable) - Add keccak256-counter fallback for throwaway key generation - Add harden() to all delegation-vat return values and baggage persists Tests: - run-wallet.mjs: single-kernel test (34 assertions) — keyring init, signing, delegation lifecycle, throwaway keys, capabilities - run-peer-wallet.mjs: two-kernel QUIC test (29 assertions) — peer connection via OCAP URL, remote signing forwarded over CapTP, delegation transfer, signature parity verification Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test the wallet subcluster through the daemon JSON-RPC socket, verifying the full agent-to-wallet communication path: daemon → kernel → vats. Covers: daemon getStatus, launchSubcluster via RPC, keyring init via queueMessage, signing (message/tx/delegation), capabilities, error propagation, and subcluster termination — 23 assertions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a Testing section describing all four test tiers: unit tests (275), single-kernel integration (34), peer wallet over QUIC (29), and daemon integration (23) — with commands and what each tier verifies. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add run-sepolia-e2e.mjs — a plain Node.js test that exercises the full on-chain flow on Sepolia: smart account creation, delegation, UserOp submission via Pimlico paymaster, and on-chain receipt verification. Skips automatically when PIMLICO_API_KEY / SEPOLIA_RPC_URL are not set. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…add Sepolia E2E viem's createClient/createPublicClient uses Math.random() which is blocked by SES lockdown. Replace with raw fetch-based JSON-RPC in both provider.ts and bundler-client.ts. This also halves the provider bundle size (800KB → 287KB). Additional SES fixes: - Add harden() to coordinator-vat for bundlerConfig, smartAccountConfig, and getCapabilities return values - Add harden() to provider-vat for baggage persists - Replace URL constructor validation with regex (URL unavailable in SES) - Add platformConfig.fetch with allowedHosts to provider vat config Sepolia E2E: - Add run-sepolia-e2e.mjs (skips if PIMLICO_API_KEY/SEPOLIA_RPC_URL unset) - Remove old vitest-based sepolia-e2e.test.ts (blocked by SES/vitest) - Test passes through smart account creation + delegation signing - UserOp on-chain submission requires further async pipeline work Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… a crank When a vat's async operation (e.g. fetch) completes between cranks, the resulting syscall.resolve was buffered with immediate=false. Since no crank was active to flush the buffer, notifications were never delivered and the crank loop never restarted — causing the kernel to hang. Fix: check kernelStore.isInCrank() in VatSyscall handlers. During a crank, continue buffering (immediate=false). Outside a crank, enqueue immediately (immediate=true) so the run queue wakes up. This unblocks any vat method that chains multiple E() calls with real async I/O (e.g. eth-wallet's redeemDelegation pipeline which chains getGasFees → getEntryPointNonce → sponsorUserOp → signHash → submitUserOp). Also update eth-wallet README to reflect vitest integration tests now passing, and fix Sepolia E2E delegation to use EOA as delegate. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…UserOps The UserOp callData must call the smart account's execute() function, which routes to the DelegationManager, not call redeemDelegations directly. Use DeleGatorCore.encode.execute() from the SDK to wrap the redeemDelegations callData properly. Also: - Add Date to coordinator globals (SDK code uses Date.now at import) - Add dummy 65-byte signature for paymaster sponsorship simulation - Fix delegation delegate to match smart account (msg.sender) - Add sponsorshipPolicyId to Sepolia test bundler config - buildSdkRedeemCallData now requires chainId parameter Sepolia E2E progress: 11/13 pass. Remaining issue is a Gator contract-level error (0x155ff427) during delegation signature verification in the redemption flow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ationManager address HybridDeleGator validates UserOp signatures as EIP-712 typed data over PackedUserOperation (not raw ECDSA over the standard UserOp hash). Add prepareUserOpTypedData() to sdk.ts and use signTypedData instead of signHash in the coordinator's submitDelegationUserOp pipeline. Also pass the real DelegationManager address (from SDK environment resolution) to the delegation vat so EIP-712 verifyingContract matches the on-chain contract. Previously used a zero-address placeholder, causing InvalidERC1271Signature errors. Sepolia E2E: 11/13 pass. Remaining AA24 signature error likely due to packed UserOp format mismatch — needs further debugging of the EIP-712 domain/message encoding against HybridDeleGator's on-chain validation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix the remaining issues blocking on-chain delegation redemption: - Use 'HybridDeleGator' as EIP-712 domain name (not 'DelegationManager') - Add 10% gas fee buffer to meet Pimlico's bundler minimums - Use unique deploy salt per test run to avoid AA10 (sender already constructed) errors - Poll for UserOp receipt from test script (setTimeout unavailable under SES in vats) Full on-chain flow verified on Sepolia: 1. Smart account deployed via CREATE2 2. Delegation created and EIP-712 signed 3. UserOp submitted to Pimlico bundler with paymaster sponsorship 4. UserOp included on-chain Tx: 0xe56bf8d5dfcc2377edd69f8f4a4ce3bfdcfd9279f165091c3afdb06acc6d6768 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix the OpenClaw plugin to use the actual OCAP daemon interface: - Call `ocap daemon exec queueMessage` instead of a non-existent `wallet` binary - Route all tools through the wallet coordinator via its kref - Add wallet_capabilities and wallet_accounts tools - Update plugin manifest with correct configSchema and uiHints - Convert to plain JS (.mjs) for standalone deployment Add docs/setup-guide.md with step-by-step instructions for: - Building and starting the home daemon with wallet subcluster - Setting up the away VPS with throwaway key + peer connection - Delegating authority from home to away - Installing and configuring the OpenClaw wallet plugin Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OpenClaw expects TypeScript plugins. Add a local tsconfig.json for the plugin directory and restore proper TypeScript types. Add plugin loading instructions to the setup guide. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
… and CLI examples Co-authored-by: Cursor <cursoragent@cursor.com>
…coverage Co-authored-by: Cursor <cursoragent@cursor.com>
…send Decode daemon CapData for OpenClaw wallet tool outputs and infer a default sender before sendTransaction. Add unit and real daemon-backed integration tests for plugin behavior, plus a dedicated test:integration:plugin script. Co-authored-by: Cursor <cursoragent@cursor.com>
Clarify that setup-away.sh does not install or configure the OpenClaw wallet plugin, and add a dedicated step-by-step section for plugin install, config, and tool allowlist. Co-authored-by: Cursor <cursoragent@cursor.com>
Define the mock child process kill method before applying vi.spyOn so wallet plugin unit tests no longer throw on missing properties. Co-authored-by: Cursor <cursoragent@cursor.com>
…ationHints as RPC methods Add kernel-control RPC handlers so the daemon CLI can initialize the libp2p networking stack and register peer location hints. Update the eth-wallet setup scripts to call these new methods (QUIC transport) and pass listen addresses between home and away devices. Update the setup guide docs to reflect the complete connection flow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@SocketSecurity ignore-all |
… vat Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Validate all batch actions against delegation, not just the first - Fix tsconfig include to cover tools/ subdirectory - Default forceReset to false to preserve wallet state across runs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…y missing The ocap CLI binary may not be available in all CI environments. Use describe.skipIf to gracefully skip instead of failing the suite. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The mock RPC server needs to handle eth_getBlockByNumber, eth_estimateGas, eth_getTransactionCount, eth_chainId, and eth_maxPriorityFeePerGas for viem to build transactions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The wallet_send tool now returns formatted output (UserOp hash, explorer link) instead of a raw hex hash. Update assertion accordingly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Validate decimal places in encodeEthToTerms instead of silent truncation - Add try-catch for getTokenMetadata in swap token resolution - Recognize native tokens (BNB, MATIC/POL) across supported chains - Reject zero-value hex amounts in wallet_send Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NATIVE_TOKEN_SYMBOLS replaced with NATIVE_TOKEN_BY_CHAIN map so that BNB/MATIC/POL only resolve to the zero address on their home chains, preventing incorrect native token resolution on other networks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ygon - Use BigInt constants for smart account funding thresholds to avoid floating-point precision issues that could produce negative fund amounts - Accept both "MATIC" and "POL" as native token on Polygon (chain 137) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ting to mainnet Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
|
I would call it Otherwise, what kind of review do you want? I have made a couple branches off of various stages of this work so having a trackable target sounds nice. |
Why use an adjective instead of a noun here though? Sounds a bit odd as a package suffix to me. I ask just an approval to merge since this is a self-contained experimental package and it doesn't touch anything else. It's been manually tested end-to-end and has 500+ unit/integration tests. As we discussed, just getting it into main so we can iterate from there. |
|
For reasons I know not, |
…n setup scripts
Both setup scripts configured the provider vat with `platformConfig: { fetch:
{ allowedHosts } }` and no fetch-family globals. The kernel's
`platformConfigStruct` (`kernel-platforms/src/capabilities/index.ts`) only
accepts `fs?: FsConfig`, so the `fetch` field was either rejected by
`isVatConfig` or silently dropped — leaving the provider vat unable to reach
any RPC. The correct form (already used in `cluster-config.ts`, the Docker e2e
helper, and the setup-guide config blocks) is:
globals: [..., 'fetch', 'Request', 'Headers', 'Response'],
network: { allowedHosts: hosts },
Pre-existing since the package's original introduction in PR #877. Also
extends `test/setup-scripts.test.ts` with per-role provider-vat assertions so
this exact class of drift (missing fetch globals, invalid `platformConfig`)
cannot silently regress.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Summary
Adds
@ocap/evm-wallet-experiment— a capability-driven EVM wallet implemented as an OCAP kernel subcluster. Uses the MetaMask Delegation Framework for delegated transaction authority via ERC-4337 UserOperations.Features
Core wallet
Delegation framework
ValueLteEnforcer) and cumulative (NativeTokenTransferAmountEnforcer)DelegationManager.disableDelegationSmart accounts
Multi-chain support
--chain <name>(e.g.--chain base) and--rpc-urlfor custom providersToken swaps
ERC-20 tokens
Peer wallet (home ↔ away)
OpenClaw agent plugin
wallet_balance,wallet_send,wallet_token_balance,wallet_token_send,wallet_token_info,wallet_token_resolve,wallet_swap,wallet_swap_quote,wallet_sign,wallet_accounts,wallet_capabilitiesSetup & operations
update-limits.shfor changing delegation spending limits (revoke + re-create)Testing
Documentation
Test plan
yarn workspace @ocap/evm-wallet-experiment test:dev:quiet)yarn workspace @ocap/evm-wallet-experiment lint:fix)yarn workspace @ocap/evm-wallet-experiment build)🤖 Generated with Claude Code
Note
High Risk
Large new package introduces key management, signing, delegated transaction execution, and network/bundler interactions; these security- and funds-adjacent paths increase the risk of subtle authorization or RPC misuse bugs. CI is updated to run the new package’s E2E suite, but the surface area added is still substantial.
Overview
Adds a new
@ocap/evm-wallet-experimentworkspace package implementing an experimental capability-based EVM wallet subcluster, plus extensive docs and setup scripts (including an interactive MetaMask-driven home flow).Introduces an OpenClaw plugin (
openclaw-plugin/) that exposes wallet tools by shelling out toocap daemon execand decoding CapData, including token symbol resolution via MetaMask’s Token API and helper logic for formatting/awaiting transaction and UserOp results.Updates CI to include the new workspace in the E2E matrix and adjusts root
lavamoat.allowScriptsto suppress nativewsoptional deps (bufferutil,utf-8-validate) used viavitest/jsdom.Written by Cursor Bugbot for commit 28230aa. This will update automatically on new commits. Configure here.