Skip to content
Merged
Show file tree
Hide file tree
Changes from 175 commits
Commits
Show all changes
185 commits
Select commit Hold shift + click to select a range
c870ec1
feat(eth-wallet): add @ocap/eth-wallet package — capability-driven Et…
sirtimid Feb 17, 2026
bc79b0e
feat(eth-wallet): add peer wallet integration tests over QUIC
sirtimid Feb 17, 2026
ae12541
feat(eth-wallet): add delegation matching, UserOp pipeline, bundler, …
sirtimid Feb 17, 2026
7dc334e
feat(eth-wallet): wire coordinator to UserOp pipeline, add external s…
sirtimid Feb 17, 2026
9edd152
add lavamoat exceptions
sirtimid Feb 17, 2026
02dd8d5
feat(eth-wallet): add EntryPoint nonce, gas estimation, delegation va…
sirtimid Feb 17, 2026
fd9f49d
feat(eth-wallet): fix UserOp signing, add signature verification, gas…
sirtimid Feb 17, 2026
f128f7d
feat(eth-wallet): add input validation, delegation match diagnostics,…
sirtimid Feb 18, 2026
b9d2637
feat(eth-wallet): integrate @metamask/smart-accounts-kit SDK
sirtimid Feb 19, 2026
1967e3d
feat(eth-wallet): add paymaster, factory fields, signing fix, E2E tes…
sirtimid Feb 19, 2026
36c8995
feat(eth-wallet): fix SES compatibility, add Node.js integration tests
sirtimid Feb 23, 2026
05ccc85
feat(eth-wallet): add daemon integration test
sirtimid Feb 23, 2026
55b849e
docs(eth-wallet): document testing tiers in README
sirtimid Feb 23, 2026
efe7e6d
feat(eth-wallet): add Sepolia E2E test, document in README
sirtimid Feb 23, 2026
b347a65
fix(eth-wallet): replace viem clients with raw fetch for SES compat, …
sirtimid Feb 23, 2026
9fe2262
fix(ocap-kernel): enqueue async vat syscalls immediately when outside…
sirtimid Feb 23, 2026
a8799ca
fix(eth-wallet): wrap redeemDelegations in DeleGatorCore.execute for …
sirtimid Feb 23, 2026
1c83ded
fix(eth-wallet): use EIP-712 typed data for UserOp signing, fix Deleg…
sirtimid Feb 23, 2026
8673609
feat(eth-wallet): complete Sepolia E2E — 13/13 on-chain assertions pass
sirtimid Feb 23, 2026
e256663
feat(eth-wallet): fix OpenClaw plugin, add setup guide
sirtimid Feb 23, 2026
1434fb3
fix(eth-wallet): convert OpenClaw plugin back to TypeScript
sirtimid Feb 23, 2026
5994993
feat(eth-wallet): add automated setup scripts for home and away devices
sirtimid Feb 23, 2026
e5240fc
docs(eth-wallet): improve setup guide with API key instructions, TOC,…
sirtimid Feb 23, 2026
9bfb5e1
fix script paths
sirtimid Feb 23, 2026
5149257
fix(eth-wallet): harden error handling, add retry logic, expand test …
sirtimid Feb 23, 2026
d6eb6c2
fix(eth-wallet): decode plugin responses and infer sender for wallet_…
sirtimid Feb 23, 2026
911abd7
docs(eth-wallet): add separate OpenClaw plugin install section
sirtimid Feb 23, 2026
9fba8c7
fix(eth-wallet): initialize child kill before spying in plugin tests
sirtimid Feb 23, 2026
011ba5e
feat(ocap-kernel, eth-wallet): expose initRemoteComms and registerLoc…
sirtimid Feb 24, 2026
1f368dd
fix(eth-wallet): harden scripts, error handling, and SES compat for V…
sirtimid Feb 24, 2026
bc901bc
fix(eth-wallet): harden for VPS deployment — SES guards, QUIC port, s…
sirtimid Feb 24, 2026
d8bdc37
fix(cli): add missing node shebang to CLI entry point
sirtimid Feb 24, 2026
8a867ac
fix(eth-wallet): invoke ocap CLI via node explicitly in setup scripts
sirtimid Feb 24, 2026
b75cd89
fix(eth-wallet): add public IP detection and daemon stop hint to setu…
sirtimid Feb 24, 2026
7c815a8
fix(eth-wallet): add log tail hint to setup script output
sirtimid Feb 24, 2026
caa7eaf
fix(cli, eth-wallet): add --timeout flag to daemon exec for long-runn…
sirtimid Feb 24, 2026
0fe26f1
fix(eth-wallet): fix OpenClaw plugin setup docs and support .mjs CLI …
sirtimid Feb 24, 2026
185f6fc
docs(eth-wallet): remove duplicate OpenClaw plugin section from setup…
sirtimid Feb 24, 2026
94c092f
fix(ocap-kernel, eth-wallet): fix trailing comma in OCAP URLs and imp…
sirtimid Feb 24, 2026
a380f64
feat(eth-wallet): add --relay flag to setup scripts, fix OCAP URL tra…
sirtimid Feb 24, 2026
d02d3ec
docs(eth-wallet): update setup guide with relay and direct connection…
sirtimid Feb 24, 2026
329c4ef
docs(eth-wallet): use yarn ocap relay in setup guide
sirtimid Feb 24, 2026
cf31a8b
fix(eth-wallet): use repo-root paths in docs and fix remote comms log…
sirtimid Feb 24, 2026
614f09e
fix(eth-wallet): remove duplicate raw output at end of setup-home.sh
sirtimid Feb 24, 2026
3a7dddf
docs(eth-wallet): clarify why npm install is needed for OpenClaw plugin
sirtimid Feb 24, 2026
873e255
docs(eth-wallet): fix relay address to use WebSocket port 9001
sirtimid Feb 24, 2026
289ef64
fix(eth-wallet): use yarn ocap in daemon stop hint
sirtimid Feb 24, 2026
1a6caab
docs(eth-wallet): use yarn ocap daemon throughout setup guide
sirtimid Feb 24, 2026
de420bc
fix(eth-wallet): reformat daemon hints in setup script output
sirtimid Feb 24, 2026
011ba62
docs(eth-wallet): add note about purging state when switching relay/d…
sirtimid Feb 24, 2026
28bf853
fix(eth-wallet): add ready-to-run delegation command to away script o…
sirtimid Feb 24, 2026
793fcbc
docs(eth-wallet): add delegation step to quick start section
sirtimid Feb 24, 2026
634d82f
fix(eth-wallet): address audit findings in setup scripts and docs
sirtimid Feb 24, 2026
5f2a3af
fix(eth-wallet): fix gas fee fallback test mock to match actual error…
sirtimid Feb 24, 2026
62aab6a
test(eth-wallet): add full peer wallet E2E test against Sepolia
sirtimid Feb 24, 2026
0bb330f
docs(eth-wallet): document ports, firewall, and setup for peer E2E test
sirtimid Feb 24, 2026
803c687
fix(eth-wallet): remove typebox dependency from openclaw plugin and f…
sirtimid Feb 25, 2026
b77f15f
fix(eth-wallet): auto-detect CLI path in openclaw plugin
sirtimid Feb 25, 2026
e9a1c6e
docs(eth-wallet): add systemd service setup for relay
sirtimid Feb 25, 2026
fb9ed9e
docs(eth-wallet): remove incorrect tools.allow step from plugin setup
sirtimid Feb 25, 2026
78d93e1
docs(eth-wallet): add Telegram channel setup link to prerequisites
sirtimid Feb 25, 2026
c687c3a
docs(eth-wallet): add missing tools.allow step to plugin setup
sirtimid Feb 25, 2026
d8d0a2e
fix(eth-wallet): correct CLI relative path in openclaw plugin
sirtimid Feb 25, 2026
5fa3537
feat(eth-wallet): include peer wallet accounts in getAccounts
sirtimid Feb 25, 2026
0d046a5
fix(eth-wallet): hide throwaway key from public wallet API
sirtimid Feb 25, 2026
989c332
fix(eth-wallet): fix integration tests and exclude from default test run
sirtimid Feb 25, 2026
a5fa701
docs(eth-wallet): clarify delegation transfer as a 3-step process
sirtimid Feb 25, 2026
a3993e3
fix(eth-wallet): hide internal fields from wallet_capabilities output
sirtimid Feb 25, 2026
9dfd169
feat(eth-wallet): add createSmartAccount to away setup script
sirtimid Feb 25, 2026
e5cfde0
feat(eth-wallet): interactive delegation transfer in away setup script
sirtimid Feb 25, 2026
1bca5ac
docs(eth-wallet): add OpenClaw plugin install commands to away script…
sirtimid Feb 25, 2026
300b412
fix(eth-wallet): include cd to repo root in plugin install output
sirtimid Feb 25, 2026
ddafd64
fix(eth-wallet): handle multi-line delegation JSON paste in away script
sirtimid Feb 25, 2026
507d1b2
fix(eth-wallet): use cat for multi-line delegation paste (Ctrl+D to s…
sirtimid Feb 25, 2026
0d8e3ef
fix(eth-wallet): make delegation command copy-pasteable in away script
sirtimid Feb 25, 2026
f0a122c
feat(eth-wallet): interactive delegation creation in home setup script
sirtimid Feb 25, 2026
86ff150
docs(eth-wallet): rewrite quick start as interactive 3-step handshake
sirtimid Feb 25, 2026
9f30cb0
feat(eth-wallet): add createSmartAccount to home setup script
sirtimid Feb 25, 2026
e723f32
feat(eth-wallet): auto-fund home smart account from EOA in setup script
sirtimid Feb 25, 2026
a8ac5ae
docs(eth-wallet): update delegation section for automated script flow
sirtimid Feb 25, 2026
0d60c9d
fix(eth-wallet): skip signature verification in receiveDelegation
sirtimid Feb 25, 2026
22f3d13
fix(eth-wallet): estimate gas fields for direct (non-delegation) sends
sirtimid Feb 25, 2026
93d7aa1
fix(eth-wallet): detect funding errors in home setup script
sirtimid Feb 25, 2026
b36595f
fix(eth-wallet): use gasLimit field name for gas estimation
sirtimid Feb 25, 2026
83ba780
feat(eth-wallet): deploy home smart account on-chain in setup script
sirtimid Feb 25, 2026
d1df197
feat(eth-wallet): add ETH spending limits to delegation setup
sirtimid Feb 25, 2026
31ce972
feat(eth-wallet): add EIP-7702 stateless smart account support
sirtimid Feb 26, 2026
0f0e19d
fix(eth-wallet): fix EIP-7702 authorization tx signing and reliability
sirtimid Feb 26, 2026
d42d590
test(eth-wallet): fill EIP-7702 test coverage gaps
sirtimid Feb 26, 2026
38bac7f
fix(eth-wallet): harden signing, remove dead code, require secure ent…
sirtimid Feb 26, 2026
9cc228f
chore(eth-wallet): align test scripts with monorepo CI conventions
sirtimid Feb 26, 2026
b6acb82
fix(eth-wallet): increase setup-home smart account timeout to 120s
sirtimid Feb 26, 2026
1a72989
docs(eth-wallet): add disclaimer for experimental status
sirtimid Feb 26, 2026
f590c89
fix(eth-wallet): address review findings — signing, entropy, caveat m…
sirtimid Feb 26, 2026
fe5c57b
docs(eth-wallet): fix stale assertion counts and test descriptions
sirtimid Feb 26, 2026
ab7570f
fix(eth-wallet): fix EIP-7702 confirmation polling and away script
sirtimid Feb 27, 2026
8c21d90
feat(eth-wallet): auto-install OpenClaw plugin in away setup script
sirtimid Feb 27, 2026
44b462c
docs(eth-wallet): add purge command to setup script output
sirtimid Feb 27, 2026
2fb449d
fix(eth-wallet): use Hybrid smart account for away device
sirtimid Feb 27, 2026
1d2b19c
fix(eth-wallet): pass allowedWsHosts for relay in setup scripts
sirtimid Feb 27, 2026
8456b4c
fix(eth-wallet): switch home device to Hybrid smart account
sirtimid Feb 27, 2026
2d72fa4
docs(eth-wallet): update smart account docs for Hybrid default
sirtimid Feb 27, 2026
357641f
fix(eth-wallet): auto-fund home smart account from EOA
sirtimid Feb 27, 2026
d2b76b9
feat(eth-wallet): resolve UserOp hashes and show correct explorer URLs
sirtimid Feb 27, 2026
ccb21ee
feat(eth-wallet): output full setup-away.sh command from home script
sirtimid Feb 27, 2026
499e95f
docs(eth-wallet): update setup guide for Hybrid smart accounts
sirtimid Feb 27, 2026
fbd4e3b
feat(eth-wallet): surface detailed delegation match failures in errors
sirtimid Mar 1, 2026
d97b981
fix(eth-wallet): detect CapData error encoding in OpenClaw plugin
sirtimid Mar 1, 2026
0b7e96d
fix(eth-wallet): auto-top-up smart account when balance drops below t…
sirtimid Mar 1, 2026
5714cec
fix(eth-wallet): pass correct nonce to EIP-7702 authorization signing
sirtimid Mar 3, 2026
daaf2cc
feat(eth-wallet): switch home device to EIP-7702 stateless smart account
sirtimid Mar 3, 2026
880dd2c
fix(ocap-kernel): restore init-remote-comms with allowedWsHosts support
sirtimid Mar 3, 2026
4e23bc0
docs(eth-wallet): update setup guide for Hybrid smart accounts
sirtimid Mar 3, 2026
d70a107
fix(eth-wallet): accept decimal ETH amounts in OpenClaw wallet_send
sirtimid Mar 3, 2026
f5708ba
feat(eth-wallet): add update-limits.sh for changing delegation spendi…
sirtimid Mar 3, 2026
e41416d
fix(eth-wallet): note that update-limits resets cumulative spending c…
sirtimid Mar 3, 2026
efb39e8
fix(eth-wallet): output full setup-away.sh command from home script
sirtimid Mar 3, 2026
16291c7
fix(eth-wallet): only show active issued delegations in update-limits
sirtimid Mar 3, 2026
a0ca0b0
fix(eth-wallet): replace illegal return with process.exit in update-l…
sirtimid Mar 3, 2026
bb81786
fix(eth-wallet): fix stdout/stderr redirect in update-limits delegati…
sirtimid Mar 3, 2026
bdf5b45
fix(eth-wallet): pass parsed delegation object in receiveDelegation c…
sirtimid Mar 3, 2026
805410a
docs(eth-wallet): document update-limits.sh for changing spending limits
sirtimid Mar 3, 2026
a471505
fix(eth-wallet): revoke old delegations before creating new one in up…
sirtimid Mar 3, 2026
536aed3
docs(eth-wallet): reflect revocation step in update-limits docs
sirtimid Mar 3, 2026
da1bf15
docs(eth-wallet): rewrite overview as general explainer, delete demo …
sirtimid Mar 3, 2026
b711fe2
docs(eth-wallet): rename demo-overview.md to how-it-works.md
sirtimid Mar 3, 2026
43c5db3
feat(eth-wallet): add interactive MetaMask approval mode
sirtimid Mar 4, 2026
5f6c02c
fix(eth-wallet): signingMode should prioritize peer wallet over local…
sirtimid Mar 4, 2026
49a4016
fix(eth-wallet): deploy hybrid smart account before creating delegations
sirtimid Mar 4, 2026
08d542b
fix(eth-wallet): wait for smart account deployment before creating de…
sirtimid Mar 5, 2026
badf23e
fix(eth-wallet): add deployment diagnostics and pre-delegation deploy…
sirtimid Mar 5, 2026
386d74f
fix(eth-wallet): inject EIP712Domain types for MetaMask Mobile signing
sirtimid Mar 6, 2026
572448f
fix(eth-wallet): update diagnostic messages in interactive setup
sirtimid Mar 6, 2026
fa7f2dc
feat(eth-wallet): add delegation details and autonomy level to capabi…
sirtimid Mar 6, 2026
8e03d35
feat(eth-wallet): add offline autonomy with peer account caching
sirtimid Mar 6, 2026
2953e7a
docs(eth-wallet): document offline autonomy for VPS mode
sirtimid Mar 6, 2026
a97ce10
docs(eth-wallet): fix MetaMask approval description in interactive mode
sirtimid Mar 9, 2026
7004c20
feat(eth-wallet): push delegations to away device over QUIC/CapTP
sirtimid Mar 9, 2026
7aed25e
fix(eth-wallet): address code review issues for delegation push feature
sirtimid Mar 9, 2026
1ad72e1
fix(eth-wallet): add delegation push to setup-home.sh
sirtimid Mar 9, 2026
a963288
feat(eth-wallet): auto-exchange delegate address over QUIC/CapTP
sirtimid Mar 9, 2026
b4238fa
fix(eth-wallet): poll for delegate address in setup-home.sh
sirtimid Mar 9, 2026
afd6dc2
feat(eth-wallet): allow manual override during polling waits
sirtimid Mar 9, 2026
802896b
chore(eth-wallet): increase polling timeout from 120s to 10 minutes
sirtimid Mar 9, 2026
3037c4d
docs(eth-wallet): update docs for zero-copy-paste setup flow
sirtimid Mar 9, 2026
8145624
feat(eth-wallet): on-chain delegation revocation via DelegationManager
sirtimid Mar 9, 2026
058b7d4
refactor(eth-wallet): extract shared UserOp pipeline and propagate re…
sirtimid Mar 9, 2026
adb2d2d
fix(eth-wallet): detect signing mode from on-chain code for UserOp su…
sirtimid Mar 9, 2026
1d40b58
fix(eth-wallet): use EIP-712 typed data signing for stateless7702 Use…
sirtimid Mar 10, 2026
14ea606
fix(eth-wallet): address code review findings from subagent analysis
sirtimid Mar 10, 2026
7734828
docs(eth-wallet): fix stale explanation for interactive mode smart ac…
sirtimid Mar 10, 2026
a74b9a1
fix(eth-wallet): revert grep to loose #error match and bump gas buffe…
sirtimid Mar 10, 2026
ff67da0
feat(eth-wallet): use pimlico_getUserOperationGasPrice for bundler ga…
sirtimid Mar 10, 2026
987c905
fix(eth-wallet): update Jiffyscan URLs to use query param instead of …
sirtimid Mar 10, 2026
48e3553
fix(eth-wallet): use Blockscout for UserOp explorer links
sirtimid Mar 10, 2026
29cacf7
fix(eth-wallet): remove misleading QUIC references from script messages
sirtimid Mar 10, 2026
9fbaebf
feat(eth-wallet): add optional password-based mnemonic encryption
sirtimid Mar 10, 2026
83bdf89
feat(eth-wallet): improve gas estimation with dynamic estimates and s…
sirtimid Mar 10, 2026
346e8b0
feat(eth-wallet): add @metamask/logger to vats, replace console.warn
sirtimid Mar 10, 2026
c6761b5
feat(eth-wallet): add ERC-20 support, token symbol resolution, and pl…
sirtimid Mar 10, 2026
e37fd5c
feat(eth-wallet): improve signTypedData routing, plugin robustness, a…
sirtimid Mar 11, 2026
f06357f
fix package cli path
sirtimid Mar 12, 2026
38efbe3
fix(eth-wallet): verify relay reservation and ensure circuit addresse…
sirtimid Mar 12, 2026
8269d44
fix(eth-wallet): update openclaw plugin CLI path from cli to kernel-cli
sirtimid Mar 12, 2026
5477810
fix(eth-wallet): increase UserOp receipt polling timeout to 45s
sirtimid Mar 12, 2026
0f12661
docs(eth-wallet): add ERC-20 token tools to documentation
sirtimid Mar 12, 2026
68aacb7
feat(eth-wallet): add swap support, batch execution, and comprehensiv…
sirtimid Mar 12, 2026
90d5711
feat(eth-wallet): add multi-chain support with chain-aware scripts an…
sirtimid Mar 12, 2026
0398333
refactor(evm-wallet): rename eth-wallet to evm-wallet-experiment
sirtimid Mar 12, 2026
a0d9ed9
fix(evm-wallet): use universal enforcer addresses for Sepolia
sirtimid Mar 12, 2026
b638501
fix(evm-wallet): include --chain-id and --rpc-url in away device command
sirtimid Mar 16, 2026
382fdeb
fix(evm-wallet): replace URLSearchParams with manual query encoding i…
sirtimid Mar 16, 2026
9640993
docs(evm-wallet): fix stale enforcer claim and update test count in R…
sirtimid Mar 16, 2026
c9c6587
fix(evm-wallet): update stale workspace references after rebase
sirtimid Mar 16, 2026
bba2fcd
fix(evm-wallet): add block explorer URLs for all supported chains
sirtimid Mar 16, 2026
1404c8a
fix(evm-wallet): use nullish coalescing per lint rules in coordinator…
sirtimid Mar 16, 2026
7bd7d7f
fix(evm-wallet): update capabilities test expectations with new fields
sirtimid Mar 16, 2026
fd48ccf
fix(evm-wallet): address code review findings
sirtimid Mar 16, 2026
3ea6eac
fix(evm-wallet): skip openclaw plugin integration test when CLI binar…
sirtimid Mar 16, 2026
452c2b3
fix(evm-wallet): add missing RPC method stubs to openclaw plugin test
sirtimid Mar 16, 2026
e4c453c
fix(evm-wallet): update wallet_send test to handle formatted tx result
sirtimid Mar 16, 2026
5874155
fix(evm-wallet): address code review findings for plugin and scripts
sirtimid Mar 16, 2026
15b6c22
fix(evm-wallet): make native token resolution chain-aware in swaps
sirtimid Mar 16, 2026
9de6d78
fix(evm-wallet): fix funding precision and support MATIC alias on Pol…
sirtimid Mar 16, 2026
28230aa
fix(evm-wallet): fail swap when chain ID is unknown instead of defaul…
sirtimid Mar 16, 2026
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
3 changes: 3 additions & 0 deletions .github/workflows/lint-build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ jobs:
'@metamask/kernel-node-runtime',
'@ocap/extension',
'@ocap/omnium-gatherum',
'@ocap/evm-wallet-experiment',
]
include:
- package: '@metamask/kernel-node-runtime'
Expand All @@ -158,6 +159,8 @@ jobs:
directory: extension
- package: '@ocap/omnium-gatherum'
directory: omnium-gatherum
- package: '@ocap/evm-wallet-experiment'
directory: evm-wallet-experiment
steps:
- name: Checkout and setup environment
uses: MetaMask/action-checkout-and-setup@v3
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@
"@metamask/kernel-cli>@metamask/kernel-node-runtime>@metamask/kernel-store>better-sqlite3": false,
"@metamask/kernel-cli>@metamask/kernel-node-runtime>@metamask/streams": false,
"@metamask/kernel-cli>@metamask/kernel-shims>@libp2p/webrtc>@ipshipyard/node-datachannel": false,
"@metamask/kernel-cli>@metamask/kernel-utils": false
"@metamask/kernel-cli>@metamask/kernel-utils": false,
"vitest>jsdom>ws>bufferutil": false,
"vitest>jsdom>ws>utf-8-validate": false
}
},
"resolutions": {
Expand Down
818 changes: 818 additions & 0 deletions packages/evm-wallet-experiment/README.md

Large diffs are not rendered by default.

253 changes: 253 additions & 0 deletions packages/evm-wallet-experiment/docs/how-it-works.md

Large diffs are not rendered by default.

737 changes: 737 additions & 0 deletions packages/evm-wallet-experiment/docs/setup-guide.md

Large diffs are not rendered by default.

200 changes: 200 additions & 0 deletions packages/evm-wallet-experiment/openclaw-plugin/daemon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/**
* Daemon communication layer for the OpenClaw wallet plugin.
*
* Spawns `ocap daemon exec` commands and decodes Endo CapData responses.
*/
import { spawn } from 'node:child_process';

type ExecResult = { stdout: string; stderr: string; code: number | null };
type CapDataLike = { body: string; slots: unknown[] };

export type WalletCallOptions = {
cliPath: string;
walletKref: string;
method: string;
args: unknown[];
timeoutMs: number;
};

/**
* Bound wallet caller — pre-configured with CLI path, kref, and timeout.
*/
export type WalletCaller = (
method: string,
args: unknown[],
timeoutMs?: number,
) => Promise<unknown>;

/**
* Create a bound wallet caller from plugin config.
*
* @param options - Wallet connection options.
* @param options.cliPath - Path to the ocap CLI.
* @param options.walletKref - Kernel reference for the wallet coordinator.
* @param options.timeoutMs - Default timeout in ms.
* @returns A bound caller function.
*/
export function makeWalletCaller(options: {
cliPath: string;
walletKref: string;
timeoutMs: number;
}): WalletCaller {
const { cliPath, walletKref, timeoutMs } = options;
return async (method, args, overrideTimeout) =>
callWallet({
cliPath,
walletKref,
method,
args,
timeoutMs: overrideTimeout ?? timeoutMs,
});
}

/**
* Check if a value looks like Endo CapData.
*
* @param value - The parsed JSON value.
* @returns True when value has CapData shape.
*/
function isCapDataLike(value: unknown): value is CapDataLike {
if (typeof value !== 'object' || value === null) {
return false;
}
if (!('body' in value) || !('slots' in value)) {
return false;
}
const { body } = value as { body?: unknown };
const { slots } = value as { slots?: unknown };
return typeof body === 'string' && Array.isArray(slots);
}

/**
* Decode daemon JSON output, unwrapping Endo CapData.
*
* @param raw - Raw stdout from `ocap daemon exec`.
* @param method - Wallet method name (for better errors).
* @returns The decoded value.
*/
function decodeCapData(raw: string, method: string): unknown {
let parsed: unknown;
try {
parsed = JSON.parse(raw);
} catch {
throw new Error(`Wallet ${method} returned non-JSON output`);
}

if (!isCapDataLike(parsed)) {
return parsed;
}

if (!parsed.body.startsWith('#')) {
throw new Error(`Wallet ${method} returned invalid CapData body`);
}

const bodyContent = parsed.body.slice(1);

// Handle error bodies from vat exceptions (e.g. "#error:message")
if (bodyContent.startsWith('error:')) {
throw new Error(`Wallet ${method} vat error: ${bodyContent.slice(6)}`);
}

let decoded: unknown;
try {
decoded = JSON.parse(bodyContent);
} catch {
throw new Error(`Wallet ${method} returned undecodable CapData body`);
}

// Handle Endo CapData error encoding: #{"#error": "message", ...}
if (decoded !== null && typeof decoded === 'object' && '#error' in decoded) {
const errorMsg = (decoded as Record<string, unknown>)['#error'];
throw new Error(
`Wallet ${method} failed: ${typeof errorMsg === 'string' ? errorMsg : JSON.stringify(errorMsg)}`,
);
}

return decoded;
}

/**
* Run an `ocap daemon exec` command and return its output.
*
* @param options - Execution options.
* @param options.cliPath - Path to the ocap CLI.
* @param options.method - The daemon RPC method.
* @param options.params - The method parameters.
* @param options.timeoutMs - Timeout in ms.
* @returns The command result.
*/
async function runDaemonExec(options: {
cliPath: string;
method: string;
params: unknown;
timeoutMs: number;
}): Promise<ExecResult> {
const { cliPath, method, params, timeoutMs } = options;
const daemonArgs = ['daemon', 'exec', method, JSON.stringify(params)];

// If cliPath points to a .mjs file, invoke it via node.
const command = cliPath.endsWith('.mjs') ? 'node' : cliPath;
const args = cliPath.endsWith('.mjs') ? [cliPath, ...daemonArgs] : daemonArgs;

return new Promise((resolve, reject) => {
const child = spawn(command, args, {
stdio: ['ignore', 'pipe', 'pipe'],
});

let stdout = '';
let stderr = '';

child.stdout?.setEncoding('utf8');
child.stderr?.setEncoding('utf8');
child.stdout?.on('data', (chunk: string) => {
stdout += chunk;
});
child.stderr?.on('data', (chunk: string) => {
stderr += chunk;
});

const timer = setTimeout(() => {
try {
child.kill('SIGKILL');
} finally {
reject(new Error(`ocap daemon exec timed out after ${timeoutMs}ms`));
}
}, timeoutMs);

child.once('error', (error: Error) => {
clearTimeout(timer);
reject(error);
});

child.once('exit', (code) => {
clearTimeout(timer);
resolve({ stdout, stderr, code: code ?? null });
});
});
}

/**
* Call a wallet coordinator method via the OCAP daemon.
*
* @param options - Call options.
* @returns The decoded response value.
*/
async function callWallet(options: WalletCallOptions): Promise<unknown> {
const { cliPath, walletKref, method, args, timeoutMs } = options;
const result = await runDaemonExec({
cliPath,
method: 'queueMessage',
params: [walletKref, method, args],
timeoutMs,
});

if (result.code !== 0) {
const detail = result.stderr.trim() || result.stdout.trim();
throw new Error(`Wallet ${method} failed (exit ${result.code}): ${detail}`);
}

return decodeCapData(result.stdout.trim(), method);
}
55 changes: 55 additions & 0 deletions packages/evm-wallet-experiment/openclaw-plugin/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* OpenClaw wallet plugin: registers tools that forward to the OCAP daemon.
*
* The OCAP daemon runs the evm-wallet subcluster. This plugin sends JSON-RPC
* messages to the daemon over its Unix socket, routing wallet operations
* through the kernel's capability system. The AI agent never touches keys.
*
* Enable tools via agents.list[].tools.allow: ["wallet_balance", "wallet_send"]
* or allow all with ["wallet"].
*
* Config (optional, in openclaw plugin settings):
* ocapCliPath - Absolute path to the `ocap` CLI (auto-detected from monorepo)
* walletKref - The kernel reference for the wallet coordinator (default: "ko4")
*/
import { resolve as resolvePath, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

import { makeWalletCaller } from './daemon.ts';
import { registerEthTools } from './tools/eth.ts';
import { registerMiscTools } from './tools/misc.ts';
import { registerSwapTools } from './tools/swap.ts';
import { registerTokenTools } from './tools/token.ts';
import type { OpenClawPluginApi } from './types.ts';

const pluginDir = dirname(fileURLToPath(import.meta.url));
const DEFAULT_CLI = resolvePath(pluginDir, '../../kernel-cli/dist/app.mjs');
const DEFAULT_TIMEOUT_MS = 60_000;

/**
* Register the wallet plugin tools.
*
* @param api - The OpenClaw plugin API.
*/
export default function register(api: OpenClawPluginApi): void {
const { pluginConfig } = api;
const cliPath =
typeof pluginConfig?.ocapCliPath === 'string'
? pluginConfig.ocapCliPath.trim()
: DEFAULT_CLI;
const walletKref =
typeof pluginConfig?.walletKref === 'string'
? pluginConfig.walletKref.trim()
: 'ko4';
const timeoutMs =
typeof pluginConfig?.timeoutMs === 'number'
? pluginConfig.timeoutMs
: DEFAULT_TIMEOUT_MS;

const wallet = makeWalletCaller({ cliPath, walletKref, timeoutMs });

registerEthTools(api, wallet);
registerTokenTools(api, wallet);
registerSwapTools(api, wallet);
registerMiscTools(api, wallet);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"id": "wallet",
"name": "Wallet (OCAP)",
"description": "Ethereum wallet tools backed by the OCAP kernel daemon. The AI agent can check balances (ETH and ERC-20 tokens), send transactions (native and token transfers), resolve token symbols to addresses, and sign messages without accessing private keys.",
"configSchema": {
"type": "object",
"properties": {
"ocapCliPath": {
"type": "string",
"description": "Absolute path to the ocap CLI entry point (.mjs file or binary). Default: 'ocap' from PATH."
},
"walletKref": {
"type": "string",
"description": "Kernel reference of the wallet coordinator (e.g. 'ko4'). Found via `ocap daemon exec getStatus`."
},
"timeoutMs": {
"type": "number",
"description": "Timeout for daemon calls in ms (default: 60000)"
}
}
},
"uiHints": {
"walletKref": {
"label": "Wallet Coordinator KRef",
"placeholder": "ko4"
},
"ocapCliPath": {
"label": "OCAP CLI Path",
"placeholder": "~/ocap-kernel/packages/kernel-cli/dist/app.mjs"
},
"timeoutMs": {
"label": "Timeout (ms)",
"placeholder": "60000"
}
}
}
14 changes: 14 additions & 0 deletions packages/evm-wallet-experiment/openclaw-plugin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "@openclaw/wallet",
"version": "0.0.1",
"description": "Wallet plugin: tools that call the away kernel CLI (balance, send, sign).",
"type": "module",
"devDependencies": {
"openclaw": "workspace:*"
},
"openclaw": {
"extensions": [
"./index.ts"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
name: wallet
description: Use the wallet tools for all balance, send, and sign operations. Supports both ETH and ERC-20 tokens. The away wallet operates autonomously after setup — the home device does not need to be online.
metadata: { 'openclaw': { 'emoji': '👛', 'requires': { 'bins': ['wallet'] } } }
---

# Wallet (ocap)

Use the **wallet tools** for any Ethereum balance, send, or sign request. Do not use exec or other CLIs for wallet operations.

## Tools

- **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_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.
- **wallet_token_info** — Get ERC-20 token metadata (name, symbol, decimals). Accepts a contract address or symbol.
- **wallet_swap_quote** — Get a token swap quote without executing. Shows expected output amount, aggregator, and gas estimate. Accepts contract addresses or token symbols.
- **wallet_swap** — Execute a token swap. Handles ERC-20 approval and swap in sequence. Accepts contract addresses or token symbols.
- **wallet_sign** — Sign a message or typed data with the local key. The signature is valid for EIP-1271 verification against the smart account address.
- **wallet_capabilities** — Check what the wallet can do (local keys, peer wallet, delegations, bundler, cached accounts, autonomy level).

## Autonomy

After setup, the away wallet is **fully autonomous** for sending ETH, sending ERC-20 tokens, and signing messages. The home device only needs to be online for signing as the home EOA address specifically (which is rarely needed). Check `wallet_capabilities` — if `autonomy` contains "offline-capable", the wallet works independently.

## ERC-20 Token Operations

All token tools accept either a **contract address** (0x...) or a **token symbol** (e.g. "USDC", "DAI"). When a symbol is given, the tool resolves it to the contract address on the current chain via the MetaMask Token API. This works on mainnet and major L2s but **not on testnets** — for testnets, provide the contract address directly.

To send tokens, use `wallet_token_send` with the token (address or symbol), recipient, and amount in human-readable decimals (e.g., "100.5" for 100.5 USDC). The tool automatically queries the token's decimals and converts to raw units.

To check a token balance, use `wallet_token_balance` with the token (address or symbol).

If you need to discover a token's address, use `wallet_token_resolve` to search by name or symbol. If you need to check which token a contract is, use `wallet_token_info` to get its name, symbol, and decimals.

## Token Swaps

Use `wallet_swap_quote` to preview a swap before executing, and `wallet_swap` to execute. Both accept token symbols (e.g. "ETH", "USDC") or contract addresses. The swap uses MetaSwap (MetaMask's aggregator) to find the best rate across multiple DEXs.

- Default slippage is 1%. Adjust with the `slippage` parameter (0.1–50%).
- For ERC-20 source tokens, approval is handled automatically if needed.
- When a smart account is configured, the approve and swap are batched atomically in a single UserOp — no separate approval transaction.
- Native ETH is represented as "ETH" or the zero address.
- Swaps are not available on all testnets — MetaSwap may not support them.

## Rules

- Use only the wallet tools for wallet operations.
- Do not use exec to run cast, ethers, or other scripts for sending ETH/tokens or signing.
- If a send or sign returns pending or waiting for approval, tell the user and do not retry with a different method.
- Never prompt the user to type "yes" or "allow" in chat for approval; approval happens in the user's wallet or home UI.
Loading
Loading