@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
|
@@ -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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
316
|
+
normalized_network,
|
|
250
317
|
f"{helius_base}?api-key={helius_key}",
|
|
251
318
|
"",
|
|
252
319
|
),
|
|
253
320
|
}
|
|
254
321
|
|
|
255
|
-
if
|
|
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": [
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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 = {
|