@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.
- package/.openclaw/extensions/agent-wallet/README.md +5 -7
- package/.openclaw/extensions/agent-wallet/dist/index.js +35 -360
- package/.openclaw/extensions/agent-wallet/index.ts +35 -360
- package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +2 -45
- package/.openclaw/extensions/agent-wallet/package.json +1 -1
- package/.openclaw/extensions/agent-wallet/skills/wallet-operator/SKILL.md +1 -3
- package/CHANGELOG.md +73 -0
- package/README.md +4 -0
- package/agent-wallet/.env.example +0 -12
- package/agent-wallet/README.md +18 -57
- package/agent-wallet/agent_wallet/bootstrap.py +28 -12
- package/agent-wallet/agent_wallet/btc_user_wallets.py +33 -7
- package/agent-wallet/agent_wallet/config.py +110 -29
- package/agent-wallet/agent_wallet/evm_user_wallets.py +4 -14
- package/agent-wallet/agent_wallet/openclaw_adapter.py +29 -687
- package/agent-wallet/agent_wallet/openclaw_cli.py +0 -7
- package/agent-wallet/agent_wallet/openclaw_runtime.py +3 -12
- package/agent-wallet/agent_wallet/providers/evm_portfolio.py +18 -42
- package/agent-wallet/agent_wallet/providers/jupiter.py +1 -307
- package/agent-wallet/agent_wallet/providers/kamino.py +21 -4
- package/agent-wallet/agent_wallet/providers/solana_rpc.py +0 -23
- package/agent-wallet/agent_wallet/providers/wdk_btc_local.py +31 -3
- package/agent-wallet/agent_wallet/providers/wdk_evm_local.py +37 -3
- package/agent-wallet/agent_wallet/providers/x402.py +4 -9
- package/agent-wallet/agent_wallet/transaction_policy.py +0 -262
- package/agent-wallet/agent_wallet/user_wallets.py +4 -3
- package/agent-wallet/agent_wallet/wallet_layer/base.py +3 -103
- package/agent-wallet/agent_wallet/wallet_layer/factory.py +8 -5
- package/agent-wallet/agent_wallet/wallet_layer/solana.py +453 -1177
- package/agent-wallet/agent_wallet/wallet_layer/wdk_btc.py +2 -8
- package/agent-wallet/agent_wallet/wallet_layer/wdk_evm.py +2 -12
- package/agent-wallet/examples/openclaw_runtime_onboarding.py +1 -1
- package/agent-wallet/examples/openclaw_user_wallet_example.py +1 -1
- package/agent-wallet/openclaw.plugin.json +1 -5
- package/agent-wallet/pyproject.toml +2 -1
- package/agent-wallet/scripts/bootstrap_openclaw_btc.py +3 -5
- package/agent-wallet/scripts/bootstrap_openclaw_evm.py +2 -12
- package/agent-wallet/scripts/build_release_bundle.py +1 -0
- package/agent-wallet/scripts/flash-sdk-bridge/bridge.mjs +1 -4
- package/agent-wallet/scripts/install_agent_wallet.py +114 -6
- package/agent-wallet/scripts/install_openclaw_local_config.py +10 -10
- package/agent-wallet/scripts/manage_openclaw_btc_wallet.py +2 -4
- package/agent-wallet/scripts/manage_openclaw_evm_wallet.py +2 -15
- package/agent-wallet/scripts/reveal_btc_seed.sh +7 -16
- package/agent-wallet/scripts/setup_btc_wallet.sh +7 -16
- package/agent-wallet/scripts/setup_evm_wallet.sh +1 -11
- package/agent-wallet/scripts/switch_openclaw_wallet_network.py +4 -1
- package/agent-wallet/skills/wallet-operator/SKILL.md +1 -6
- package/bin/openclaw-agent-wallet.mjs +356 -0
- package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +20 -0
- package/claude-code/plugins/agent-wallet/.mcp.json +14 -0
- package/claude-code/plugins/agent-wallet/README.md +65 -0
- package/claude-code/plugins/agent-wallet/scripts/run_mcp.sh +39 -0
- package/claude-code/plugins/agent-wallet/skills/wallet-operator/SKILL.md +18 -0
- package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +38 -0
- package/codex/plugins/agent-wallet/.mcp.json +15 -0
- package/codex/plugins/agent-wallet/README.md +39 -0
- package/codex/plugins/agent-wallet/scripts/run_mcp.sh +21 -0
- package/codex/plugins/agent-wallet/server.py +961 -0
- package/codex/plugins/agent-wallet/skills/wallet-operator/SKILL.md +18 -0
- package/hermes/plugins/agent_wallet/schemas.py +2 -2
- package/hermes/plugins/agent_wallet/tools.py +18 -4
- package/package.json +6 -1
- package/setup.sh +2 -0
- package/wdk-btc-wallet/src/local_vault.js +45 -68
- package/wdk-btc-wallet/src/server.js +1 -0
- package/wdk-evm-wallet/README.md +4 -3
- package/wdk-evm-wallet/src/config.js +15 -0
- package/wdk-evm-wallet/src/local_vault.js +45 -68
- package/wdk-evm-wallet/src/server.js +1 -0
- 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=
|
package/agent-wallet/README.md
CHANGED
|
@@ -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
|
|
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
|
-
- `
|
|
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`
|
|
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":"
|
|
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
|
|
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
|
|
306
|
+
--network bitcoin \
|
|
344
307
|
--service-url http://127.0.0.1:8080
|
|
345
308
|
```
|
|
346
309
|
|
|
347
|
-
|
|
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
|
|
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
|
|
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
|
|
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":"
|
|
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
|
-
-
|
|
504
|
-
- per-user wallets
|
|
505
|
-
- switching networks does not mix balances
|
|
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
|
|
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
|
|
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=
|
|
621
|
-
SOLANA_RPC_URLS=https://api.
|
|
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":
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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=
|
|
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=
|
|
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 =
|
|
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
|
-
|
|
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":
|
|
173
|
-
"rpc_url": rpc_urls[0] if rpc_urls else resolve_solana_rpc_url(
|
|
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
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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)),
|