@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
@@ -49,18 +49,11 @@ class Settings(BaseSettings):
49
49
  jupiter_ultra_api_base_url: str = "https://lite-api.jup.ag/ultra/v1"
50
50
  jupiter_price_api_base_url: str = "https://lite-api.jup.ag/price/v3"
51
51
  jupiter_portfolio_api_base_url: str = "https://api.jup.ag/portfolio/v1"
52
- jupiter_lend_api_base_url: str = "https://api.jup.ag/lend/v1"
53
52
  jupiter_api_key: str = ""
54
53
  lifi_api_base_url: str = "https://li.quest/v1"
55
54
  lifi_api_key: str = ""
56
55
  lifi_integrator: str = "openclaw"
57
56
  lifi_default_deny_bridges: str = "mayan"
58
- houdini_api_base_url: str = "https://api-partner.houdiniswap.com/v2"
59
- houdini_api_key: str = ""
60
- houdini_api_secret: str = ""
61
- houdini_user_ip: str = ""
62
- houdini_user_agent: str = "AgentLayer/0.1.12"
63
- houdini_user_timezone: str = "UTC"
64
57
  flash_api_base_url: str = ""
65
58
  flash_sdk_bridge_command: str = ""
66
59
  flash_sdk_bridge_mode: str = "mock"
@@ -82,6 +75,79 @@ class Settings(BaseSettings):
82
75
  settings = Settings()
83
76
 
84
77
 
78
+ def normalize_solana_network(network: str | None) -> str:
79
+ """Canonicalize supported Solana network names and reject test clusters."""
80
+ normalized = str(network or "").strip().lower() or "mainnet"
81
+ aliases = {
82
+ "mainnet-beta": "mainnet",
83
+ }
84
+ normalized = aliases.get(normalized, normalized)
85
+ if normalized in {"devnet", "testnet"}:
86
+ from agent_wallet.wallet_layer.base import WalletBackendError
87
+
88
+ raise WalletBackendError(
89
+ "Solana devnet/testnet are no longer supported by agent-wallet. "
90
+ "Use mainnet or remove the Solana network override."
91
+ )
92
+ if normalized != "mainnet":
93
+ from agent_wallet.wallet_layer.base import WalletBackendError
94
+
95
+ raise WalletBackendError(
96
+ f"Unsupported Solana network: {normalized}. Only mainnet is supported."
97
+ )
98
+ return "mainnet"
99
+
100
+
101
+ def normalize_evm_network(network: str | None) -> str:
102
+ """Canonicalize supported EVM network names and reject testnets."""
103
+ normalized = str(network or "").strip().lower() or "ethereum"
104
+ aliases = {
105
+ "mainnet": "ethereum",
106
+ "eth": "ethereum",
107
+ "eth-mainnet": "ethereum",
108
+ "base-mainnet": "base",
109
+ }
110
+ normalized = aliases.get(normalized, normalized)
111
+ if normalized in {"sepolia", "base-sepolia", "base_sepolia"}:
112
+ from agent_wallet.wallet_layer.base import WalletBackendError
113
+
114
+ raise WalletBackendError(
115
+ "EVM testnets are no longer supported by agent-wallet. Use ethereum or base."
116
+ )
117
+ if normalized not in {"ethereum", "base"}:
118
+ from agent_wallet.wallet_layer.base import WalletBackendError
119
+
120
+ raise WalletBackendError(
121
+ f"Unsupported EVM network: {normalized}. Use ethereum or base."
122
+ )
123
+ return normalized
124
+
125
+
126
+ def normalize_btc_network(network: str | None) -> str:
127
+ """Canonicalize supported BTC network names and reject non-mainnet chains."""
128
+ normalized = str(network or "").strip().lower() or "bitcoin"
129
+ aliases = {
130
+ "mainnet": "bitcoin",
131
+ "btc": "bitcoin",
132
+ "bitcoin-mainnet": "bitcoin",
133
+ "bitcoin_mainnet": "bitcoin",
134
+ }
135
+ normalized = aliases.get(normalized, normalized)
136
+ if normalized in {"testnet", "regtest"}:
137
+ from agent_wallet.wallet_layer.base import WalletBackendError
138
+
139
+ raise WalletBackendError(
140
+ "Bitcoin testnet/regtest are no longer supported by agent-wallet. Use bitcoin."
141
+ )
142
+ if normalized != "bitcoin":
143
+ from agent_wallet.wallet_layer.base import WalletBackendError
144
+
145
+ raise WalletBackendError(
146
+ f"Unsupported Bitcoin network: {normalized}. Only bitcoin is supported."
147
+ )
148
+ return "bitcoin"
149
+
150
+
85
151
  def _normalize_provider_mode(value: str | None) -> str:
86
152
  mode = (value or "").strip().lower()
87
153
  if not mode:
@@ -129,20 +195,20 @@ def resolve_openclaw_home() -> Path:
129
195
 
130
196
  def default_solana_wallet_path(network: str) -> Path:
131
197
  """Return the default keypair path for a Solana wallet."""
132
- return resolve_openclaw_home() / "wallets" / f"solana-{network}-agent.json"
198
+ normalized_network = normalize_solana_network(network)
199
+ return resolve_openclaw_home() / "wallets" / f"solana-{normalized_network}-agent.json"
133
200
 
134
201
 
135
202
  def resolve_solana_rpc_url(network: str, configured: str) -> str:
136
203
  """Resolve the effective Solana RPC URL from network + optional override."""
204
+ normalized_network = normalize_solana_network(network)
137
205
  if configured.strip():
138
206
  return configured.strip()
139
207
 
140
208
  mapping = {
141
209
  "mainnet": "https://api.mainnet-beta.solana.com",
142
- "devnet": "https://api.devnet.solana.com",
143
- "testnet": "https://api.testnet.solana.com",
144
210
  }
145
- return mapping.get(network.strip().lower(), mapping["mainnet"])
211
+ return mapping.get(normalized_network, mapping["mainnet"])
146
212
 
147
213
 
148
214
  def resolve_solana_rpc_urls(
@@ -151,17 +217,18 @@ def resolve_solana_rpc_urls(
151
217
  configured_list: str = "",
152
218
  ) -> list[str]:
153
219
  """Resolve the ordered list of Solana RPC URLs to try."""
220
+ normalized_network = normalize_solana_network(network)
154
221
  candidates: list[str] = []
155
222
  for raw in (configured_list or "").split(","):
156
223
  value = raw.strip()
157
224
  if value and value not in candidates:
158
225
  candidates.append(value)
159
226
 
160
- primary = resolve_solana_rpc_url(network, configured)
227
+ primary = resolve_solana_rpc_url(normalized_network, configured)
161
228
  if primary and primary not in candidates:
162
229
  candidates.insert(0, primary)
163
230
 
164
- official = resolve_solana_rpc_url(network, "")
231
+ official = resolve_solana_rpc_url(normalized_network, "")
165
232
  if official and official not in candidates:
166
233
  candidates.append(official)
167
234
 
@@ -169,7 +236,8 @@ def resolve_solana_rpc_urls(
169
236
 
170
237
 
171
238
  def _build_provider_gateway_rpc_url(base_url: str, provider: str, network: str) -> str:
172
- return f"gateway::{provider}::{network.strip().lower()}::{base_url.rstrip('/')}/v1/rpc"
239
+ normalized_network = normalize_solana_network(network)
240
+ return f"gateway::{provider}::{normalized_network}::{base_url.rstrip('/')}/v1/rpc"
173
241
 
174
242
 
175
243
  def resolve_runtime_solana_rpc_config(
@@ -185,6 +253,7 @@ def resolve_runtime_solana_rpc_config(
185
253
  3. shared proxy gateway
186
254
  4. public official fallback
187
255
  """
256
+ normalized_network = normalize_solana_network(network)
188
257
  mode = _normalize_provider_mode(
189
258
  os.getenv("SOLANA_RPC_PROVIDER_MODE", settings.solana_rpc_provider_mode)
190
259
  )
@@ -200,10 +269,10 @@ def resolve_runtime_solana_rpc_config(
200
269
  "mode": "user_direct",
201
270
  "provider": "custom",
202
271
  "transport": "direct",
203
- "rpc_urls": resolve_solana_rpc_urls(network, env_primary, env_list),
272
+ "rpc_urls": resolve_solana_rpc_urls(normalized_network, env_primary, env_list),
204
273
  }
205
274
  if env_list:
206
- official = resolve_solana_rpc_url(network, "")
275
+ official = resolve_solana_rpc_url(normalized_network, "")
207
276
  candidates = [item.strip() for item in env_list.split(",") if item.strip()]
208
277
  if official and official not in candidates:
209
278
  candidates.append(official)
@@ -218,16 +287,15 @@ def resolve_runtime_solana_rpc_config(
218
287
  if alchemy_key:
219
288
  alchemy_base_by_network = {
220
289
  "mainnet": "https://solana-mainnet.g.alchemy.com/v2",
221
- "devnet": "https://solana-devnet.g.alchemy.com/v2",
222
290
  }
223
- alchemy_base = alchemy_base_by_network.get(network.strip().lower())
291
+ alchemy_base = alchemy_base_by_network.get(normalized_network)
224
292
  if alchemy_base:
225
293
  return {
226
294
  "mode": "user_direct",
227
295
  "provider": "alchemy",
228
296
  "transport": "direct",
229
297
  "rpc_urls": resolve_solana_rpc_urls(
230
- network,
298
+ normalized_network,
231
299
  f"{alchemy_base}/{alchemy_key}",
232
300
  "",
233
301
  ),
@@ -237,35 +305,40 @@ def resolve_runtime_solana_rpc_config(
237
305
  if helius_key:
238
306
  helius_base_by_network = {
239
307
  "mainnet": "https://mainnet.helius-rpc.com/",
240
- "devnet": "https://devnet.helius-rpc.com/",
241
308
  }
242
- helius_base = helius_base_by_network.get(network.strip().lower())
309
+ helius_base = helius_base_by_network.get(normalized_network)
243
310
  if helius_base:
244
311
  return {
245
312
  "mode": "user_direct",
246
313
  "provider": "helius",
247
314
  "transport": "direct",
248
315
  "rpc_urls": resolve_solana_rpc_urls(
249
- network,
316
+ normalized_network,
250
317
  f"{helius_base}?api-key={helius_key}",
251
318
  "",
252
319
  ),
253
320
  }
254
321
 
255
- if network.strip().lower() == "mainnet" and (mode == "shared_proxy" or (mode == "auto" and gateway_url)):
322
+ if normalized_network == "mainnet" and (mode == "shared_proxy" or (mode == "auto" and gateway_url)):
256
323
  if gateway_url:
257
324
  return {
258
325
  "mode": "shared_proxy",
259
326
  "provider": gateway_provider,
260
327
  "transport": "proxy",
261
- "rpc_urls": [_build_provider_gateway_rpc_url(gateway_url, gateway_provider, network)],
328
+ "rpc_urls": [
329
+ _build_provider_gateway_rpc_url(
330
+ gateway_url,
331
+ gateway_provider,
332
+ normalized_network,
333
+ )
334
+ ],
262
335
  }
263
336
 
264
337
  return {
265
338
  "mode": "public_fallback",
266
339
  "provider": "official",
267
340
  "transport": "direct",
268
- "rpc_urls": resolve_solana_rpc_urls(network, configured, configured_list),
341
+ "rpc_urls": resolve_solana_rpc_urls(normalized_network, configured, configured_list),
269
342
  }
270
343
 
271
344
 
@@ -289,10 +362,7 @@ def resolve_runtime_solana_swap_config(network: str) -> dict[str, str]:
289
362
  requested = _normalize_swap_provider(
290
363
  os.getenv("SOLANA_SWAP_PROVIDER", settings.solana_swap_provider)
291
364
  )
292
- normalized_network = network.strip().lower()
293
-
294
- if normalized_network != "mainnet":
295
- return {"provider": "jupiter", "transport": "direct"}
365
+ normalize_solana_network(network)
296
366
 
297
367
  if requested == "jupiter":
298
368
  return {"provider": "jupiter", "transport": "direct"}
@@ -380,6 +450,17 @@ def resolve_evm_wallet_password() -> str:
380
450
  )
381
451
 
382
452
 
453
+ def resolve_btc_wallet_password() -> str:
454
+ """Resolve the local BTC vault password from env or the sealed secret store."""
455
+ direct = os.getenv("WDK_BTC_WALLET_PASSWORD", "").strip()
456
+ if direct:
457
+ return direct
458
+ return _resolve_sealed_secret(
459
+ "wdk_btc_wallet_password",
460
+ "btc_wallet_password",
461
+ )
462
+
463
+
383
464
  def use_encrypted_user_wallets() -> bool:
384
465
  """Per-user wallet files are always encrypted in the hardened runtime."""
385
466
  return True
@@ -14,6 +14,7 @@ from urllib.parse import urlparse
14
14
  from urllib.request import urlopen
15
15
 
16
16
  from agent_wallet.config import (
17
+ normalize_evm_network,
17
18
  resolve_boot_key,
18
19
  resolve_evm_wallet_password,
19
20
  resolve_openclaw_home,
@@ -27,18 +28,7 @@ LOCAL_WDK_EVM_HOSTS = {"127.0.0.1", "localhost", "::1"}
27
28
 
28
29
 
29
30
  def _normalize_evm_network(value: str | None) -> str:
30
- network = str(value or "").strip().lower()
31
- aliases = {
32
- "mainnet": "ethereum",
33
- "eth": "ethereum",
34
- "eth-mainnet": "ethereum",
35
- "base-mainnet": "base",
36
- "base_sepolia": "base-sepolia",
37
- }
38
- network = aliases.get(network, network)
39
- if network not in {"ethereum", "sepolia", "base", "base-sepolia"}:
40
- return "ethereum"
41
- return network
31
+ return normalize_evm_network(value)
42
32
 
43
33
 
44
34
  def _resolve_service_url(service_url: str | None = None) -> str:
@@ -52,8 +42,6 @@ def _paired_network(network: str) -> str | None:
52
42
  mapping = {
53
43
  "ethereum": "base",
54
44
  "base": "ethereum",
55
- "sepolia": "base-sepolia",
56
- "base-sepolia": "sepolia",
57
45
  }
58
46
  return mapping.get(_normalize_evm_network(network))
59
47
 
@@ -541,6 +529,7 @@ def create_user_evm_wallet(
541
529
  "walletId": created["walletId"],
542
530
  "accountIndex": effective_account_index,
543
531
  "network": effective_network,
532
+ "password": password,
544
533
  },
545
534
  )
546
535
  binding = {
@@ -593,6 +582,7 @@ def import_user_evm_wallet(
593
582
  "walletId": created["walletId"],
594
583
  "accountIndex": effective_account_index,
595
584
  "network": effective_network,
585
+ "password": password,
596
586
  },
597
587
  )
598
588
  binding = {