Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions packages/evm-wallet-experiment/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# @ocap/evm-wallet-experiment

A capability-driven EVM wallet implemented as an OCAP kernel subcluster. It uses the [MetaMask Delegation Framework (Gator)](https://github.com/MetaMask/delegation-framework) for delegated transaction authority via ERC-4337 UserOperations. The wallet subcluster isolates key management, Ethereum RPC communication, and delegation lifecycle into separate vats, enforcing the principle of least authority across the entire signing pipeline.
A capability-driven EVM wallet implemented as an OCAP kernel subcluster. It uses the [MetaMask Delegation Framework (Gator)](https://github.com/MetaMask/delegation-framework) for delegated transaction authority. **Hybrid** smart accounts submit ERC-4337 UserOperations through a bundler; **stateless EIP-7702** home accounts (mnemonic path) redeem delegations with normal EIP-1559 transactions via your JSON-RPC provider (e.g. Infura), without a bundler. The wallet subcluster isolates key management, Ethereum RPC communication, and delegation lifecycle into separate vats, enforcing the principle of least authority across the entire signing pipeline.

For a deeper explanation of the components and data flow, see [How It Works](./docs/how-it-works.md). For deploying the wallet on a home device + VPS with OpenClaw, see the [Setup Guide](./docs/setup-guide.md).

## Security model and known limitations

- **Peer signing has no interactive approval for message/typed-data requests.** Transaction signing over peer requests is now disabled and peer-connected wallets must use delegation redemption for sends, but message and typed-data peer signing still execute immediately without an approval prompt.
- **`revokeDelegation()` requires a bundler.** Revocation submits an on-chain `disableDelegation` UserOp to the DelegationManager contract. The bundler and (optionally) paymaster must be configured. If the UserOp fails, the local delegation status is not changed.
- **`revokeDelegation()` and hybrid redemption require a bundler.** Hybrid accounts submit on-chain `disableDelegation` / redemption via ERC-4337 UserOps; configure a bundler (and optional paymaster). **Stateless 7702** accounts use a direct EIP-1559 transaction instead; only the JSON-RPC provider must be configured. If the on-chain transaction fails, the local delegation status is not changed.
- **Mnemonic encryption is optional.** The keyring vat can encrypt the mnemonic at rest using AES-256-GCM with a PBKDF2-derived key. Pass a `password` and `salt` to `initializeKeyring()` to enable encryption. Without a password, the mnemonic is stored in plaintext. When encrypted, the keyring starts in a locked state on daemon restart and must be unlocked with `unlockKeyring(password)` before signing operations work.
- **Throwaway keyring needs secure entropy.** `initializeKeyring({ type: 'throwaway' })` requires either `crypto.getRandomValues` in the runtime or caller-provided entropy via `{ type: 'throwaway', entropy: '0x...' }`. Under SES lockdown (where `crypto` is unavailable inside vat compartments), the caller must generate 32 bytes of entropy externally and pass it in.

Expand Down Expand Up @@ -748,6 +748,15 @@ PIMLICO_API_KEY=xxx SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/xxx \

Full on-chain test on Sepolia testnet. Creates a Hybrid smart account (standalone single-device test), creates and signs a delegation, redeems it by submitting an ERC-4337 UserOp to the Pimlico bundler with paymaster gas sponsorship, and waits for on-chain inclusion. Skips automatically if `PIMLICO_API_KEY` and `SEPOLIA_RPC_URL` are not set.

### Sepolia E2E — 7702 direct (no Pimlico)

```bash
SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/xxx TEST_MNEMONIC="..." \
yarn workspace @ocap/evm-wallet-experiment test:node:sepolia-7702-direct
```

On-chain flow using `implementation: 'stateless7702'`: EIP-7702 upgrade tx, delegation creation, redemption via `eth_sendRawTransaction` through your RPC only (no bundler). Skips if `SEPOLIA_RPC_URL` is unset. See [7702 direct verification](./docs/7702-direct-tx-verification.md).

### Peer wallet Sepolia E2E (41 assertions, requires API keys)

```bash
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Verifying EIP-7702 direct (Infura) delegation calls

Before relying on the coordinator’s **self-call + SDK `callData`** path for `stateless7702` accounts, confirm on your target chain (e.g. Sepolia) that the delegated implementation accepts direct EOA invocation — not only calls routed through the ERC-4337 EntryPoint.

## What to simulate

Use `eth_call` with:

- `from`: the upgraded EOA address (same as the smart account address)
- `to`: the same address (self-call)
- `data`: the hex produced by `buildSdkRedeemCallData` / `buildSdkBatchRedeemCallData` for a **minimal** redemption you care about (or a static call that matches production encoding)

If the call reverts with an “only EntryPoint” (or similar) error, do not ship the direct path against that implementation without contract/SDK changes.

## Example (cast)

Replace placeholders with real values from your environment and a real `callData` from the wallet or a unit test fixture:

```bash
cast call --rpc-url "$SEPOLIA_RPC_URL" \
--from "$EOA" \
"$EOA" \
"$CALLDATA"
```

A successful static call strongly suggests the direct transaction path is viable. A revert requires investigation against the [delegation-framework EIP-7702 docs](https://github.com/MetaMask/delegation-framework/blob/main/documents/EIP7702DeleGator.md).

## Automated check on Sepolia

Run the optional E2E (funded mnemonic, no Pimlico):

```bash
SEPOLIA_RPC_URL=https://... TEST_MNEMONIC="..." \
yarn workspace @ocap/evm-wallet-experiment test:node:sepolia-7702-direct
```

See [run-sepolia-7702-direct-e2e.mjs](../test/e2e/run-sepolia-7702-direct-e2e.mjs).
16 changes: 11 additions & 5 deletions packages/evm-wallet-experiment/docs/how-it-works.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,25 @@ The MetaMask SDK must connect **before** SES lockdown (which freezes built-in pr

> **Note:** MetaMask Mobile requires `EIP712Domain` to be explicitly listed in the `types` field of `eth_signTypedData_v4` requests. Without it, MetaMask computes an empty domain separator, producing invalid signatures. The `makeProviderSigner` adapter handles this automatically.

### Smart Accounts (ERC-4337)
### Smart Accounts (ERC-4337 and EIP-7702)

Both devices create **DeleGator smart accounts** via MetaMask's Delegation Framework. In mnemonic mode, the home device uses **EIP-7702** to promote the EOA into a smart account (same address, no funding needed). In interactive mode, the home device uses a **Hybrid** smart account (different address, auto-funded from the EOA). The away device always uses a **Hybrid** counterfactual smart account (deploys on first UserOp). These are ERC-4337 smart contract wallets that support:
Both devices create **DeleGator smart accounts** via MetaMask's Delegation Framework. In mnemonic mode, the home device uses **EIP-7702** to promote the EOA into a smart account (same address, no separate contract deployment). In interactive mode, the home device uses a **Hybrid** smart account (different address, counterfactual until the first UserOp). The away device always uses **Hybrid** (deploys on first UserOp).

**Submission path:**

- **Hybrid** — redemption, batch execution, and revocation go through **ERC-4337 UserOperations** (bundler + optional paymaster). Gas can be sponsored so the agent does not need ETH.
- **Stateless 7702 (home / mnemonic)** — the same SDK-encoded `execute` calldata is sent as a **normal EIP-1559 transaction** (self-call on the upgraded EOA) via your configured JSON-RPC provider (e.g. Infura). No bundler is required for redemption or revocation; the EOA pays gas.

All modes support:

- **UserOperations** — transactions submitted through a bundler instead of directly
- **Delegations** — signed permission slips that authorize another account to act on behalf of the smart account
- **Caveat enforcers** — on-chain contracts that restrict what a delegation can do

The Pimlico bundler handles UserOp submission, gas estimation, and optional paymaster sponsorship (so the agent doesn't need ETH for gas).
When a bundler is configured for Hybrid, the Pimlico client handles UserOp submission, gas estimation, and optional paymaster sponsorship.

#### Batch execution

The coordinator supports `sendBatchTransaction`, which combines multiple transactions into a single UserOp using `DeleGatorCore.executeWithMode` with `BatchDefault` mode. This is used when an operation requires multiple on-chain steps — for example, a token swap that needs an ERC-20 approval followed by the swap trade. Instead of submitting two separate UserOps, batch execution packs both into one atomic operation: either both succeed or both revert. The single-delegation redemption path remains available for standalone transactions.
The coordinator supports `sendBatchTransaction`, which combines multiple transactions into a single atomic execution using `DeleGatorCore.executeWithMode` with `BatchDefault` mode — either as one UserOp (Hybrid + bundler) or one direct EIP-1559 tx (stateless 7702). This is used when an operation requires multiple on-chain steps — for example, a token swap that needs an ERC-20 approval followed by the swap trade. The single-delegation redemption path remains available for standalone transactions.

### Delegations and Caveats

Expand Down
2 changes: 1 addition & 1 deletion packages/evm-wallet-experiment/docs/setup-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ During delegation setup, the home script prompts for two optional spending limit

Both limits are enforced on-chain by caveat enforcers in the DeleGator framework. The agent cannot bypass them. Press Enter at either prompt to skip that limit.

The `--pimlico-key` configures the Pimlico bundler for ERC-4337 UserOp submission with paymaster sponsorship. Without it, smart account deployment and on-chain delegation redemption will not work.
The `--pimlico-key` configures the Pimlico bundler for ERC-4337 UserOp submission with paymaster sponsorship. It is **required** for the away device (Hybrid smart account) and for any **Hybrid** home setup. For a **mnemonic home wallet using stateless EIP-7702** (`implementation: 'stateless7702'`), delegation redemption uses your normal RPC only — Pimlico is optional on the home device in that configuration.

All scripts also accept `--chain <name>` (e.g. `--chain base`, `--chain ethereum`) or `--chain-id <number>` (default: Sepolia 11155111), `--quic-port` (default: 4002), and `--no-build`. For chains not supported by Infura (e.g. BNB Smart Chain), pass `--rpc-url` instead of `--infura-key`. Run with `--help` for details.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Use the **wallet tools** for any Ethereum balance, send, or sign request. Do not

- **wallet_accounts** — List wallet accounts. Returns cached home accounts if the home device is offline.
- **wallet_balance** — Get ETH balance for an address. Use `wallet_accounts` first to find the right address.
- **wallet_send** — Send ETH to an address. Fully autonomous — uses delegation redemption via the bundler, no home device needed.
- **wallet_send** — Send ETH to an address. Fully autonomous — uses delegation redemption (bundler for Hybrid accounts, direct RPC for stateless EIP-7702 home accounts), no home device needed when the away wallet is configured.
- **wallet_token_resolve** — Resolve a token symbol or name (e.g. "USDC") to its contract address on the current chain. Not available for testnets.
- **wallet_token_balance** — Get ERC-20 token balance. Accepts a contract address or symbol (e.g. "USDC"). Returns human-readable amount with symbol.
- **wallet_token_send** — Send ERC-20 tokens to an address. Accepts a contract address or symbol. Automatically converts decimal amounts using the token's decimals.
Expand Down
1 change: 1 addition & 0 deletions packages/evm-wallet-experiment/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"test:node:peer": "yarn build && node --conditions development test/integration/run-peer-wallet.mjs",
"test:node:daemon": "yarn build && node --conditions development test/integration/run-daemon-wallet.mjs",
"test:node:sepolia": "yarn build && node --conditions development test/e2e/run-sepolia-e2e.mjs",
"test:node:sepolia-7702-direct": "yarn build && node --conditions development test/e2e/run-sepolia-7702-direct-e2e.mjs",
"test:node:peer-e2e": "yarn build && node --conditions development test/e2e/run-peer-e2e.mjs",
"test:node:spending-limits": "yarn build && node --conditions development test/e2e/run-spending-limits-e2e.mjs"
},
Expand Down
Loading
Loading