@agentlayer.tech/wallet 0.1.28 → 0.1.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/.openclaw/extensions/agent-wallet/README.md +5 -7
  2. package/.openclaw/extensions/agent-wallet/dist/index.js +35 -360
  3. package/.openclaw/extensions/agent-wallet/index.ts +35 -360
  4. package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +2 -45
  5. package/.openclaw/extensions/agent-wallet/package.json +1 -1
  6. package/.openclaw/extensions/agent-wallet/skills/wallet-operator/SKILL.md +1 -3
  7. package/CHANGELOG.md +73 -0
  8. package/README.md +4 -0
  9. package/agent-wallet/.env.example +0 -12
  10. package/agent-wallet/README.md +18 -57
  11. package/agent-wallet/agent_wallet/bootstrap.py +28 -12
  12. package/agent-wallet/agent_wallet/btc_user_wallets.py +33 -7
  13. package/agent-wallet/agent_wallet/config.py +110 -29
  14. package/agent-wallet/agent_wallet/evm_user_wallets.py +4 -14
  15. package/agent-wallet/agent_wallet/openclaw_adapter.py +29 -687
  16. package/agent-wallet/agent_wallet/openclaw_cli.py +0 -7
  17. package/agent-wallet/agent_wallet/openclaw_runtime.py +3 -12
  18. package/agent-wallet/agent_wallet/providers/evm_portfolio.py +18 -42
  19. package/agent-wallet/agent_wallet/providers/jupiter.py +1 -307
  20. package/agent-wallet/agent_wallet/providers/kamino.py +21 -4
  21. package/agent-wallet/agent_wallet/providers/solana_rpc.py +0 -23
  22. package/agent-wallet/agent_wallet/providers/wdk_btc_local.py +31 -3
  23. package/agent-wallet/agent_wallet/providers/wdk_evm_local.py +37 -3
  24. package/agent-wallet/agent_wallet/providers/x402.py +4 -9
  25. package/agent-wallet/agent_wallet/transaction_policy.py +0 -262
  26. package/agent-wallet/agent_wallet/user_wallets.py +4 -3
  27. package/agent-wallet/agent_wallet/wallet_layer/base.py +3 -103
  28. package/agent-wallet/agent_wallet/wallet_layer/factory.py +8 -5
  29. package/agent-wallet/agent_wallet/wallet_layer/solana.py +453 -1177
  30. package/agent-wallet/agent_wallet/wallet_layer/wdk_btc.py +2 -8
  31. package/agent-wallet/agent_wallet/wallet_layer/wdk_evm.py +2 -12
  32. package/agent-wallet/examples/openclaw_runtime_onboarding.py +1 -1
  33. package/agent-wallet/examples/openclaw_user_wallet_example.py +1 -1
  34. package/agent-wallet/openclaw.plugin.json +1 -5
  35. package/agent-wallet/pyproject.toml +2 -1
  36. package/agent-wallet/scripts/bootstrap_openclaw_btc.py +3 -5
  37. package/agent-wallet/scripts/bootstrap_openclaw_evm.py +2 -12
  38. package/agent-wallet/scripts/build_release_bundle.py +1 -0
  39. package/agent-wallet/scripts/flash-sdk-bridge/bridge.mjs +1 -4
  40. package/agent-wallet/scripts/install_agent_wallet.py +114 -6
  41. package/agent-wallet/scripts/install_openclaw_local_config.py +10 -10
  42. package/agent-wallet/scripts/manage_openclaw_btc_wallet.py +2 -4
  43. package/agent-wallet/scripts/manage_openclaw_evm_wallet.py +2 -15
  44. package/agent-wallet/scripts/reveal_btc_seed.sh +7 -16
  45. package/agent-wallet/scripts/setup_btc_wallet.sh +7 -16
  46. package/agent-wallet/scripts/setup_evm_wallet.sh +1 -11
  47. package/agent-wallet/scripts/switch_openclaw_wallet_network.py +4 -1
  48. package/agent-wallet/skills/wallet-operator/SKILL.md +1 -6
  49. package/bin/openclaw-agent-wallet.mjs +356 -0
  50. package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +20 -0
  51. package/claude-code/plugins/agent-wallet/.mcp.json +14 -0
  52. package/claude-code/plugins/agent-wallet/README.md +65 -0
  53. package/claude-code/plugins/agent-wallet/scripts/run_mcp.sh +39 -0
  54. package/claude-code/plugins/agent-wallet/skills/wallet-operator/SKILL.md +18 -0
  55. package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +38 -0
  56. package/codex/plugins/agent-wallet/.mcp.json +15 -0
  57. package/codex/plugins/agent-wallet/README.md +39 -0
  58. package/codex/plugins/agent-wallet/scripts/run_mcp.sh +21 -0
  59. package/codex/plugins/agent-wallet/server.py +961 -0
  60. package/codex/plugins/agent-wallet/skills/wallet-operator/SKILL.md +18 -0
  61. package/hermes/plugins/agent_wallet/schemas.py +2 -2
  62. package/hermes/plugins/agent_wallet/tools.py +18 -4
  63. package/package.json +6 -1
  64. package/setup.sh +2 -0
  65. package/wdk-btc-wallet/src/local_vault.js +45 -68
  66. package/wdk-btc-wallet/src/server.js +1 -0
  67. package/wdk-evm-wallet/README.md +4 -3
  68. package/wdk-evm-wallet/src/config.js +15 -0
  69. package/wdk-evm-wallet/src/local_vault.js +45 -68
  70. package/wdk-evm-wallet/src/server.js +1 -0
  71. package/agent-wallet/agent_wallet/providers/houdini.py +0 -539
package/CHANGELOG.md CHANGED
@@ -2,6 +2,79 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## v0.1.32 - 2026-06-01
6
+
7
+ - Fixed a `SyntaxError` in `codex/plugins/agent-wallet/server.py` introduced by
8
+ the Houdini private-swap removal in v0.1.31: the deletion left the orphaned
9
+ `_cache_pending_private_swap_order` signature fused onto the body of
10
+ `_normalize_wallet_backend`, leaving an unclosed parenthesis. The Codex and
11
+ Claude Code plugins share this `server.py`, so the broken file prevented the
12
+ agent-wallet MCP server from starting in both runtimes (JSON-RPC `-32000` /
13
+ "failed to reconnect"). Restored the `_normalize_wallet_backend` definition;
14
+ the server now starts and completes the MCP `initialize` handshake.
15
+ - Fixed Claude Code agent-wallet MCP failing to start (`-32000` / "failed to
16
+ reconnect"). When Claude Code copies the plugin into its plugin cache, the
17
+ launcher's relative `../../codex/...` and local `server.py` paths no longer
18
+ resolve, so it reported "server.py not found". `run_mcp.sh` now falls back to
19
+ the codex `server.py` inside the installed runtime package
20
+ (`~/.openclaw/agent-wallet-runtime/current/codex/plugins/agent-wallet`), which
21
+ is always present after install. Codex was unaffected (its launcher is
22
+ self-contained).
23
+
24
+ ## v0.1.31 - 2026-05-31
25
+
26
+ - Hardened the WDK EVM/BTC local vaults with a decrypt-on-demand key model: the
27
+ decrypted seed is no longer held in process memory between requests. Each
28
+ signing request decrypts the seed just-in-time from the sealed password and
29
+ zeroizes the key/plaintext buffers afterward; `unlock`/`lock` are now
30
+ deprecated no-ops. The at-rest format is unchanged, so existing wallets keep
31
+ working without migration.
32
+ - EVM wallets are now provisioned automatically at install, alongside Solana.
33
+ Every install creates both wallets; `--backend` only selects the active one.
34
+ EVM provisioning auto-generates and seals the vault password and binds both
35
+ base and ethereum. It is best-effort: an install-time failure does not abort
36
+ the install, and the wallet is created lazily on first EVM use instead.
37
+ - Fixed the local config installer to validate EVM networks with the EVM
38
+ normalizer (previously an EVM backend was rejected as a Solana network).
39
+
40
+ ## v0.1.30 - 2026-05-30
41
+
42
+ - Added a Claude Code plugin bridge under `claude-code/plugins/agent-wallet` so
43
+ the existing local wallet runtime can be used directly inside Claude Code
44
+ without creating a new wallet.
45
+ - The Claude Code bridge connects to the current `~/.openclaw` runtime and
46
+ reuses the same Solana, Bitcoin, and EVM wallet surface already used by
47
+ OpenClaw, Hermes, and Codex.
48
+ - Added `wallet claude-code install --yes`, which symlinks the bundled Claude
49
+ Code plugin into `~/.claude/plugins/agent-wallet` and attempts to register it
50
+ via the Claude Code CLI.
51
+ - Fixed the `smoke_install_from_github` test, which was failing because
52
+ `setup.sh` checks for the Codex plugin manifest but the test bundle never
53
+ created that path.
54
+ - Added the Claude Code plugin manifest check to `setup.sh` so bundle integrity
55
+ is validated for both the Codex and Claude Code plugin surfaces.
56
+
57
+ ## v0.1.29 - 2026-05-29
58
+
59
+ - Added a new `Codex` plugin bridge under `codex/plugins/agent-wallet` so the
60
+ existing local wallet runtime can be used directly inside Codex without
61
+ creating a second wallet.
62
+ - Kept the Codex bridge non-custodial and additive: it reuses the current
63
+ `agent-wallet` runtime, existing wallets, and the current tool surface
64
+ instead of replacing OpenClaw or Hermes.
65
+ - Added `wallet codex install --yes`, which links the bundled Codex plugin into
66
+ the standard local plugin marketplace path and can ask Codex to install the
67
+ plugin from that local marketplace.
68
+ - Added `get_kamino_open_positions`, which aggregates the wallet's Kamino
69
+ positions across markets with loan details, reserve APYs, and rewards.
70
+ - Removed Solana devnet/testnet support from the wallet runtime, host bridges,
71
+ and local helper scripts so the supported Solana surface is now mainnet-only.
72
+ - Removed EVM testnet support from the wallet runtime and host bridges so the
73
+ supported EVM surface is now `ethereum` and `base` mainnet only.
74
+ - Removed Bitcoin `testnet` and `regtest` support from the wallet runtime,
75
+ host bridges, and local helper scripts so the supported BTC surface is now
76
+ `bitcoin` mainnet only.
77
+
5
78
  ## v0.1.28 - 2026-05-28
6
79
 
7
80
  - Simplified `x402_pay_request` into a single-shot paid execution flow while
package/README.md CHANGED
@@ -16,6 +16,7 @@ For Hermes:
16
16
  npx @agentlayer.tech/wallet install --yes && npx @agentlayer.tech/wallet hermes install --yes
17
17
  ```
18
18
 
19
+
19
20
  AgentLayer is a beta local-first wallet and finance stack for agents.
20
21
 
21
22
  The repository includes:
@@ -23,6 +24,7 @@ The repository includes:
23
24
  - `agent-wallet/` - the main wallet backend for AgentLayer
24
25
  - `.openclaw/` - the local AgentLayer bridge layer for the OpenClaw wallet integration
25
26
  - `hermes/` - optional Hermes Agent plugin bridge for the same wallet backend
27
+ - `codex/` - optional Codex plugin bridge for the same wallet backend
26
28
  - `wdk-btc-wallet/` - the local Bitcoin wallet service
27
29
  - `wdk-evm-wallet/` - the local EVM wallet service
28
30
  - `provider-gateway/` - shared provider access for Solana RPC, Bags, and related finance reads
@@ -84,6 +86,7 @@ Useful npm CLI commands:
84
86
  wallet status
85
87
  wallet doctor
86
88
  wallet hermes install --yes
89
+ wallet codex install --yes
87
90
  wallet update --yes
88
91
  wallet update --yes --dry-run
89
92
  wallet rollback
@@ -200,6 +203,7 @@ Kamino integration gives the wallet a structured Solana lending surface:
200
203
  - `get_kamino_lend_market_reserves` - inspect reserve metrics for one Kamino market.
201
204
  - `get_kamino_lend_user_obligations` - inspect the wallet's obligations inside a Kamino market.
202
205
  - `get_kamino_lend_user_rewards` - fetch the wallet's Kamino rewards summary.
206
+ - `get_kamino_open_positions` - aggregate all open Kamino positions across markets with loan details, reserve APYs, and rewards.
203
207
  - `kamino_lend_deposit` - preview, prepare, or execute a lending deposit.
204
208
  - `kamino_lend_withdraw` - preview, prepare, or execute a lending withdrawal.
205
209
  - `kamino_lend_borrow` - preview, prepare, or execute a borrow.
@@ -44,7 +44,6 @@ JUPITER_API_BASE_URL=https://lite-api.jup.ag/swap/v1
44
44
  JUPITER_ULTRA_API_BASE_URL=https://lite-api.jup.ag/ultra/v1
45
45
  JUPITER_PRICE_API_BASE_URL=https://lite-api.jup.ag/price/v3
46
46
  JUPITER_PORTFOLIO_API_BASE_URL=https://api.jup.ag/portfolio/v1
47
- JUPITER_LEND_API_BASE_URL=https://api.jup.ag/lend/v1
48
47
  JUPITER_API_KEY=
49
48
 
50
49
  # LI.FI cross-chain routing. API key is optional for basic read-only quote/status calls.
@@ -54,17 +53,6 @@ LIFI_API_KEY=
54
53
  LIFI_INTEGRATOR=openclaw
55
54
  LIFI_DEFAULT_DENY_BRIDGES=mayan
56
55
 
57
- # Houdini Partner API for private Solana payouts.
58
- # HOUDINI_USER_IP is required because Houdini rejects requests without compliance headers.
59
- # If PROVIDER_GATEWAY_URL points at a gateway with Houdini enabled,
60
- # the wallet runtime can omit these partner secrets and route through the gateway instead.
61
- HOUDINI_API_BASE_URL=https://api-partner.houdiniswap.com/v2
62
- HOUDINI_API_KEY=
63
- HOUDINI_API_SECRET=
64
- HOUDINI_USER_IP=
65
- HOUDINI_USER_AGENT=AgentLayer/0.1.12
66
- HOUDINI_USER_TIMEZONE=UTC
67
-
68
56
  # Flash Trade perps
69
57
  FLASH_API_BASE_URL=
70
58
  FLASH_SDK_BRIDGE_COMMAND=
@@ -26,7 +26,7 @@ It provides:
26
26
  - runtime instructions for safe wallet usage
27
27
  - a single `invoke()` method for safe dispatch
28
28
  - OpenClaw-style plugin manifest and skill bundle
29
- - explicit network-aware results so the host and agent can see `devnet` vs `mainnet`
29
+ - explicit network-aware results so the host and agent can see the active chain network
30
30
 
31
31
  ## Hermes integration
32
32
 
@@ -89,17 +89,11 @@ Current safe tools:
89
89
  - `stake_sol_native`
90
90
  - `transfer_spl_token`
91
91
  - `swap_solana_tokens` - Solana Jupiter swaps; prefer `intent_preview` -> chat confirmation -> `intent_execute` so execution refreshes the quote inside approved limits.
92
- - `swap_solana_privately` - Houdini-backed private Solana payout flow for same-token `SOL->SOL` or `USDC->USDC` transfers to a destination wallet.
93
- - `get_solana_private_swap_status`
94
- - `get_jupiter_earn_tokens`
95
- - `get_jupiter_earn_positions`
96
- - `get_jupiter_earn_earnings`
97
92
  - `get_kamino_lend_markets`
98
93
  - `get_kamino_lend_market_reserves`
99
94
  - `get_kamino_lend_user_obligations`
100
95
  - `get_kamino_lend_user_rewards`
101
- - `jupiter_earn_deposit`
102
- - `jupiter_earn_withdraw`
96
+ - `get_kamino_open_positions`
103
97
  - `kamino_lend_deposit`
104
98
  - `kamino_lend_withdraw`
105
99
  - `kamino_lend_borrow`
@@ -107,7 +101,6 @@ Current safe tools:
107
101
  - `close_empty_token_accounts`
108
102
  - `deactivate_solana_stake`
109
103
  - `withdraw_solana_stake`
110
- - `request_devnet_airdrop`
111
104
  - `x402_search_services`
112
105
  - `x402_get_service_details`
113
106
  - `x402_preview_request`
@@ -122,8 +115,6 @@ Temporarily disabled but kept in the codebase for later re-enable:
122
115
  The signing tool still requires explicit `user_confirmed=true`.
123
116
  Transfer, native staking, swap, and Aave position-management tools support `preview`, `prepare`, and `execute` modes. The safe operational path is still preview-first. `prepare` now returns an execution plan only and never exposes signed transaction bytes to the agent. `execute` works only when the backend has a signer and `sign_only=false`.
124
117
 
125
- Exception: `swap_solana_privately` is intentionally optimized for `preview -> execute`. Hosts should not insert a separate `prepare` step for Houdini private payouts because it adds no execution value and only burns additional provider quota.
126
-
127
118
  Policy defaults:
128
119
 
129
120
  - read-only tools are always allowed
@@ -190,42 +181,16 @@ For production `mainnet`, prefer a dedicated RPC instead of the public Solana en
190
181
 
191
182
  - `SOLANA_RPC_URL` for one primary endpoint
192
183
  - `SOLANA_RPC_URLS` as a comma-separated ordered failover list
193
- - or just `ALCHEMY_API_KEY` / `HELIUS_API_KEY`, which auto-derive a primary Solana RPC for `mainnet` or `devnet`
184
+ - or just `ALCHEMY_API_KEY` / `HELIUS_API_KEY`, which auto-derive a primary Solana RPC for `mainnet`
194
185
 
195
186
  Production recommendation: treat RPC as deployment-owned config, not wallet logic. Runtime env wins over `openclaw.json` plugin config, so keep `Alchemy/Helius/QuickNode` endpoints in deployment secrets or service env and use plugin `rpcUrl` / `rpcUrls` only as local fallback.
196
187
 
197
- For Houdini-backed private Solana payouts, also provide:
198
-
199
- - `HOUDINI_API_KEY`
200
- - `HOUDINI_API_SECRET`
201
- - `HOUDINI_USER_IP`
202
- - optional `HOUDINI_USER_AGENT`
203
- - optional `HOUDINI_USER_TIMEZONE`
204
-
205
- The current MVP intentionally keeps the scope narrow:
206
-
207
- - supported private routes are same-token Solana payouts only
208
- - `SOL -> SOL`
209
- - `USDC -> USDC`
210
- - execution binds to the approved Houdini `quoteId`, creates a single private exchange, and sends the exact Solana deposit locally from the wallet
211
-
212
- This is a private payout flow expressed in Houdini's swap terminology. Cross-token private swaps can be added later without changing the OpenClaw/Hermes approval model.
213
-
214
- For production, the cleaner setup is to place Houdini partner secrets on `provider-gateway` and let `agent-wallet` consume the narrow gateway endpoints through `PROVIDER_GATEWAY_URL` and optional `PROVIDER_GATEWAY_BEARER_TOKEN`. In that mode:
215
-
216
- - the gateway owns `HOUDINI_API_KEY` / `HOUDINI_API_SECRET`
217
- - the gateway derives the authoritative user IP from ingress
218
- - `agent-wallet` still performs preview/prepare/execute, local transaction verification, signing, and broadcast
219
- - direct Houdini env vars can be omitted from the wallet runtime
220
-
221
188
  For OpenClaw onboarding, `agent-wallet` now ships with a hosted default provider gateway:
222
189
 
223
190
  - `https://agent-layer-production.up.railway.app`
224
191
 
225
192
  So users do not need to enter `PROVIDER_GATEWAY_URL` manually for the default Bags launch/fees flows or shared mainnet RPC path. You only need to set `PROVIDER_GATEWAY_URL` yourself if you want to override that hosted default with your own deployment.
226
193
 
227
- That same provider gateway path can now also cover Jupiter Earn reads and transaction-building. Ordinary Jupiter swap routing remains direct.
228
-
229
194
  For a self-hosted install where each operator brings their own RPC key, a minimal Solana setup can be just:
230
195
 
231
196
  ```bash
@@ -277,9 +242,7 @@ export AGENT_WALLET_BOOT_KEY='paste-generated-secret-here'
277
242
  In that mode, `agent-wallet` will auto-resolve:
278
243
 
279
244
  - `mainnet` -> `https://solana-mainnet.g.alchemy.com/v2/<ALCHEMY_API_KEY>`
280
- - `devnet` -> `https://solana-devnet.g.alchemy.com/v2/<ALCHEMY_API_KEY>`
281
245
  - `mainnet` -> `https://mainnet.helius-rpc.com/?api-key=<HELIUS_API_KEY>`
282
- - `devnet` -> `https://devnet.helius-rpc.com/?api-key=<HELIUS_API_KEY>`
283
246
 
284
247
  and still append the official Solana endpoint as fallback.
285
248
 
@@ -317,7 +280,7 @@ printf '%s\n' 'your-local-btc-password' | \
317
280
  python -m agent_wallet.openclaw_cli btc-wallet-create \
318
281
  --user-id alice@example.com \
319
282
  --password-stdin \
320
- --config-json '{"backend":"wdk_btc_local","network":"testnet","wdkBtcServiceUrl":"http://127.0.0.1:8080"}'
283
+ --config-json '{"backend":"wdk_btc_local","network":"bitcoin","wdkBtcServiceUrl":"http://127.0.0.1:8080"}'
321
284
  ```
322
285
 
323
286
  If you want a friendlier host-shell flow, use:
@@ -325,7 +288,7 @@ If you want a friendlier host-shell flow, use:
325
288
  ```bash
326
289
  python scripts/manage_openclaw_btc_wallet.py setup \
327
290
  --user-id alice@example.com \
328
- --network testnet \
291
+ --network bitcoin \
329
292
  --service-url http://127.0.0.1:8080
330
293
  ```
331
294
 
@@ -340,11 +303,11 @@ If you want a true one-command OpenClaw bootstrap, use:
340
303
  ```bash
341
304
  python agent-wallet/scripts/bootstrap_openclaw_btc.py \
342
305
  --user-id alice@example.com \
343
- --network testnet \
306
+ --network bitcoin \
344
307
  --service-url http://127.0.0.1:8080
345
308
  ```
346
309
 
347
- For BTC mainnet, use the same bootstrap with `--network mainnet`. The script normalizes that to `bitcoin` in plugin config automatically.
310
+ You can also pass `--network mainnet`; the bootstrap normalizes that alias to `bitcoin`.
348
311
 
349
312
  For the simplest host-side UX, use the shell wrapper instead:
350
313
 
@@ -356,7 +319,7 @@ That is the intended "agent/host installs, user only enters password" entrypoint
356
319
 
357
320
  - the script wraps the full BTC bootstrap
358
321
  - it auto-starts the local `wdk-btc-wallet` service for localhost URLs if needed
359
- - it asks for `user-id` and shows a small `mainnet / testnet / regtest` menu if you did not pass them as args or env
322
+ - it asks for `user-id` and defaults BTC network to `mainnet` if you did not pass it explicitly
360
323
  - it prompts for the BTC wallet password interactively unless you explicitly pass `--password-stdin`
361
324
  - it prefers `/tmp/agent-wallet-venv/bin/python`, then `agent-wallet/.venv/bin/python`, and only then falls back to system `python3`
362
325
  - it creates or unlocks the BTC wallet binding and patches local OpenClaw config in one pass
@@ -407,7 +370,7 @@ For the local EVM backend (`backend=wdk_evm_local`), the lifecycle mirrors the B
407
370
  - `agent-wallet` talks to it through a local bearer token loaded from `~/.openclaw/wdk-evm-wallet/local-auth-token`
408
371
  - `agent-wallet` stores only a per-user EVM wallet binding under `~/.openclaw/users/<normalized-user-id>/wallets/evm-<network>-agent.json`
409
372
  - the runtime can auto-create missing EVM bindings or auto-unlock the local vault during ordinary OpenClaw switching/tool calls when `sealed_keys.json` contains `wdk_evm_wallet_password`
410
- - supported EVM networks are `ethereum`, `sepolia`, `base`, and `base-sepolia`
373
+ - supported EVM networks are `ethereum` and `base`
411
374
  - OpenClaw-facing EVM tools accept an optional per-call `network` override for `ethereum` or `base`, so the agent can switch between the two mainnet EVM paths without editing host config
412
375
  - EVM `get_wallet_balance` now returns an enriched portfolio-style payload with native balance, discovered ERC-20 balances, and USD values when token discovery and pricing are available
413
376
  - if a requested EVM network binding is missing, `agent-wallet` auto-binds it from the same local wallet when there is exactly one reusable EVM wallet for that user or when `wdkEvmWalletId` is provided explicitly
@@ -430,7 +393,7 @@ That wrapper:
430
393
  - defaults to `http://127.0.0.1:8081`
431
394
  - can auto-start `wdk-evm-wallet/run-local.sh` if the local service is not already healthy
432
395
  - creates or unlocks the local EVM wallet binding
433
- - also binds the paired EVM network by default: `ethereum <-> base`, `sepolia <-> base-sepolia`
396
+ - also binds the paired EVM network by default: `ethereum <-> base`
434
397
  - stores the entered EVM vault password into `sealed_keys.json` when `AGENT_WALLET_BOOT_KEY` is available, so later OpenClaw wallet switching can auto-raise the EVM backend without another password prompt
435
398
  - patches OpenClaw config to `backend=wdk_evm_local`
436
399
 
@@ -441,7 +404,7 @@ printf '%s\n' 'your-local-evm-password' | \
441
404
  python -m agent_wallet.openclaw_cli evm-wallet-create \
442
405
  --user-id alice@example.com \
443
406
  --password-stdin \
444
- --config-json '{"backend":"wdk_evm_local","network":"sepolia","wdkEvmServiceUrl":"http://127.0.0.1:8081"}'
407
+ --config-json '{"backend":"wdk_evm_local","network":"ethereum","wdkEvmServiceUrl":"http://127.0.0.1:8081"}'
445
408
  ```
446
409
 
447
410
  After that, `onboard` and `invoke` can use the bound EVM wallet by `user_id` without manually passing `wdkEvmWalletId` every time.
@@ -500,14 +463,13 @@ This keeps wallet creation and custody in the host/runtime layer while the agent
500
463
 
501
464
  The wallet backend is already network-scoped:
502
465
 
503
- - `devnet` and `mainnet` use different wallet files
504
- - per-user wallets are stored as `solana-<network>-agent.json`
505
- - switching networks does not mix balances or stake accounts across clusters
466
+ - Solana stays on `mainnet`
467
+ - per-user Solana wallets continue to use `solana-mainnet-agent.json`
468
+ - switching BTC or EVM networks does not mix balances across chains
506
469
 
507
470
  For a local OpenClaw install, use:
508
471
 
509
472
  ```bash
510
- python agent-wallet/scripts/switch_openclaw_wallet_network.py --network devnet
511
473
  python agent-wallet/scripts/switch_openclaw_wallet_network.py --network mainnet
512
474
  ```
513
475
 
@@ -570,7 +532,7 @@ Operational notes:
570
532
 
571
533
  - this path uses Solana RPC and the Stake Program directly, without third-party DeFi APIs
572
534
  - stake creation allocates a new stake account controlled by the connected wallet as staker and withdrawer
573
- - preview and prepare were live-checked on devnet against a real wallet context
535
+ - preview and prepare were live-checked against a real wallet context
574
536
 
575
537
  ## Official OpenClaw plugin
576
538
 
@@ -612,13 +574,13 @@ Public-safe helper scripts are available in `agent-wallet/scripts/`:
612
574
  Both scripts now use generic defaults instead of hardcoded local usernames or paths. Sensitive secrets must be supplied via protected environment variables, not config JSON or CLI arguments.
613
575
  When `~/.openclaw/agent-wallet-runtime/current` exists, the config installer now prefers that trusted runtime path over a workspace checkout for the plugin manifest, package root, and Python bridge launcher.
614
576
 
615
- Recommended devnet setup:
577
+ Recommended Solana mainnet setup:
616
578
 
617
579
  ```bash
618
580
  AGENT_WALLET_BACKEND=solana_local
619
581
  AGENT_WALLET_BOOT_KEY=change-this-in-production
620
- SOLANA_NETWORK=devnet
621
- SOLANA_RPC_URLS=https://api.devnet.solana.com
582
+ SOLANA_NETWORK=mainnet
583
+ SOLANA_RPC_URLS=https://api.mainnet-beta.solana.com
622
584
  SOLANA_AUTO_CREATE_WALLET=true
623
585
  AGENT_WALLET_SIGN_ONLY=false
624
586
  ```
@@ -641,4 +603,3 @@ The package now supports:
641
603
  - Jupiter-based swap preview and execution on mainnet
642
604
  - compact swap `fee_summary` in preview/prepare/execute, including known network fees and route fee bps when Jupiter provides them
643
605
  - zero-balance token account cleanup
644
- - devnet/testnet faucet airdrop
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import json
6
6
  from pathlib import Path
7
7
 
8
- from agent_wallet.config import refuse_mainnet_wallet_recreation
8
+ from agent_wallet.config import normalize_solana_network, refuse_mainnet_wallet_recreation
9
9
  from agent_wallet.file_ops import atomic_write_text
10
10
  from agent_wallet.wallet_layer.base import WalletBackendError
11
11
  from agent_wallet.wallet_layer.base58 import b58encode
@@ -79,11 +79,12 @@ def load_wallet_pin(path: Path) -> dict[str, str] | None:
79
79
 
80
80
  def write_wallet_pin(path: Path, *, address: str, network: str) -> dict[str, str]:
81
81
  """Persist the expected wallet address for later mismatch checks."""
82
+ normalized_network = normalize_solana_network(network)
82
83
  payload = {
83
84
  "kind": WALLET_ADDRESS_PIN_KIND,
84
85
  "version": WALLET_ADDRESS_PIN_VERSION,
85
86
  "address": address,
86
- "network": network.strip().lower(),
87
+ "network": normalized_network,
87
88
  "wallet_file": path.name,
88
89
  }
89
90
  pin_path = resolve_wallet_pin_path(path)
@@ -97,7 +98,7 @@ def write_wallet_pin(path: Path, *, address: str, network: str) -> dict[str, str
97
98
 
98
99
  def ensure_wallet_pin(path: Path, *, address: str, network: str) -> dict[str, str]:
99
100
  """Ensure the wallet pin exists and matches the expected address."""
100
- expected_network = network.strip().lower()
101
+ expected_network = normalize_solana_network(network)
101
102
  existing = load_wallet_pin(path)
102
103
  if existing is None:
103
104
  return write_wallet_pin(path, address=address, network=expected_network)
@@ -114,7 +115,7 @@ def ensure_wallet_pin(path: Path, *, address: str, network: str) -> dict[str, st
114
115
 
115
116
  def refuse_recreation_if_pinned(path: Path, *, network: str) -> None:
116
117
  """Refuse to recreate a wallet when a mainnet address is already pinned."""
117
- expected_network = network.strip().lower()
118
+ expected_network = normalize_solana_network(network)
118
119
  if expected_network != "mainnet" or not refuse_mainnet_wallet_recreation():
119
120
  return
120
121
  existing = load_wallet_pin(path)
@@ -128,7 +129,11 @@ def refuse_recreation_if_pinned(path: Path, *, network: str) -> None:
128
129
 
129
130
  def ensure_solana_wallet_ready() -> dict[str, str] | None:
130
131
  """Ensure that a Solana wallet exists when auto-create is enabled."""
131
- from agent_wallet.config import default_solana_wallet_path, resolve_solana_private_key, settings
132
+ from agent_wallet.config import (
133
+ default_solana_wallet_path,
134
+ resolve_solana_private_key,
135
+ settings,
136
+ )
132
137
 
133
138
  if settings.agent_wallet_backend.strip().lower() not in {"solana", "solana_local", "solana-local"}:
134
139
  return None
@@ -136,8 +141,13 @@ def ensure_solana_wallet_ready() -> dict[str, str] | None:
136
141
  if resolve_solana_private_key():
137
142
  return {"address": "", "path": ""}
138
143
 
144
+ normalized_network = normalize_solana_network(settings.solana_network)
139
145
  configured_path = settings.solana_agent_keypair_path.strip()
140
- path = Path(configured_path).expanduser() if configured_path else default_solana_wallet_path(settings.solana_network)
146
+ path = (
147
+ Path(configured_path).expanduser()
148
+ if configured_path
149
+ else default_solana_wallet_path(normalized_network)
150
+ )
141
151
 
142
152
  if path.exists():
143
153
  return {"address": "", "path": str(path)}
@@ -145,9 +155,9 @@ def ensure_solana_wallet_ready() -> dict[str, str] | None:
145
155
  if not settings.solana_auto_create_wallet:
146
156
  return None
147
157
 
148
- refuse_recreation_if_pinned(path, network=settings.solana_network)
158
+ refuse_recreation_if_pinned(path, network=normalized_network)
149
159
  created = create_solana_wallet_file(path)
150
- write_wallet_pin(path, address=created["address"], network=settings.solana_network)
160
+ write_wallet_pin(path, address=created["address"], network=normalized_network)
151
161
  return created
152
162
 
153
163
 
@@ -155,22 +165,28 @@ def describe_bootstrap() -> dict[str, str | bool]:
155
165
  """Return the effective bootstrap configuration for installer/runtime usage."""
156
166
  from agent_wallet.config import (
157
167
  default_solana_wallet_path,
168
+ normalize_solana_network,
158
169
  resolve_solana_rpc_url,
159
170
  resolve_runtime_solana_rpc_urls,
160
171
  settings,
161
172
  )
162
173
 
174
+ normalized_network = normalize_solana_network(settings.solana_network)
163
175
  configured_path = settings.solana_agent_keypair_path.strip()
164
- path = Path(configured_path).expanduser() if configured_path else default_solana_wallet_path(settings.solana_network)
176
+ path = (
177
+ Path(configured_path).expanduser()
178
+ if configured_path
179
+ else default_solana_wallet_path(normalized_network)
180
+ )
165
181
  rpc_urls = resolve_runtime_solana_rpc_urls(
166
- settings.solana_network,
182
+ normalized_network,
167
183
  settings.solana_rpc_url,
168
184
  settings.solana_rpc_urls,
169
185
  )
170
186
  return {
171
187
  "backend": settings.agent_wallet_backend,
172
- "network": settings.solana_network,
173
- "rpc_url": rpc_urls[0] if rpc_urls else resolve_solana_rpc_url(settings.solana_network, ""),
188
+ "network": normalized_network,
189
+ "rpc_url": rpc_urls[0] if rpc_urls else resolve_solana_rpc_url(normalized_network, ""),
174
190
  "rpc_urls": rpc_urls,
175
191
  "auto_create_wallet": settings.solana_auto_create_wallet,
176
192
  "keypair_path": str(path),
@@ -6,19 +6,41 @@ import json
6
6
  from pathlib import Path
7
7
  from typing import Any
8
8
 
9
- from agent_wallet.config import resolve_openclaw_home, settings
9
+ from agent_wallet.config import (
10
+ normalize_btc_network,
11
+ resolve_boot_key,
12
+ resolve_openclaw_home,
13
+ settings,
14
+ )
10
15
  from agent_wallet.providers.wdk_btc_local import WdkBtcLocalClient
11
16
  from agent_wallet.user_wallets import normalize_user_id
12
17
  from agent_wallet.wallet_layer.base import WalletBackendError
13
18
 
14
19
 
15
20
  def _normalize_btc_network(value: str | None) -> str:
16
- network = str(value or "").strip().lower()
17
- aliases = {"mainnet": "bitcoin"}
18
- network = aliases.get(network, network)
19
- if network not in {"bitcoin", "testnet", "regtest"}:
20
- return "bitcoin"
21
- return network
21
+ return normalize_btc_network(value)
22
+
23
+
24
+ def _maybe_store_btc_wallet_password(password: str) -> bool:
25
+ """Seal the BTC vault password so signing can decrypt-on-demand without re-entry.
26
+
27
+ Mirrors the EVM flow: once sealed under the boot key, the local BTC client
28
+ injects it automatically on each signing request.
29
+ """
30
+ value = str(password or "").strip()
31
+ if not value:
32
+ return False
33
+ boot_key = resolve_boot_key()
34
+ if not boot_key:
35
+ return False
36
+ from agent_wallet.sealed_keys import resolve_sealed_keys_path, seal_keys, unseal_keys
37
+
38
+ sealed_path = resolve_sealed_keys_path()
39
+ existing = unseal_keys(boot_key) if sealed_path.exists() else {}
40
+ if existing.get("wdk_btc_wallet_password") == value:
41
+ return False
42
+ seal_keys(boot_key, {**existing, "wdk_btc_wallet_password": value})
43
+ return True
22
44
 
23
45
 
24
46
  def _resolve_service_url(service_url: str | None = None) -> str:
@@ -77,6 +99,7 @@ def create_user_btc_wallet(
77
99
  "walletId": created["walletId"],
78
100
  "accountIndex": effective_account_index,
79
101
  "network": effective_network,
102
+ "password": password,
80
103
  },
81
104
  )
82
105
  binding = {
@@ -92,6 +115,7 @@ def create_user_btc_wallet(
92
115
  "updated_at": created.get("updatedAt"),
93
116
  }
94
117
  _write_wallet_binding(resolve_user_btc_wallet_path(user_id, effective_network), binding)
118
+ _maybe_store_btc_wallet_password(password)
95
119
  return {
96
120
  **binding,
97
121
  "unlocked": bool(created.get("unlocked", True)),
@@ -128,6 +152,7 @@ def import_user_btc_wallet(
128
152
  "walletId": created["walletId"],
129
153
  "accountIndex": effective_account_index,
130
154
  "network": effective_network,
155
+ "password": password,
131
156
  },
132
157
  )
133
158
  binding = {
@@ -167,6 +192,7 @@ def unlock_user_btc_wallet(
167
192
  "timeoutSeconds": 0,
168
193
  },
169
194
  )
195
+ _maybe_store_btc_wallet_password(password)
170
196
  return {
171
197
  **binding,
172
198
  "unlocked": bool(payload.get("unlocked", True)),