@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
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Any
6
6
 
7
+ from agent_wallet.config import normalize_btc_network
7
8
  from agent_wallet.providers.wdk_btc_local import WdkBtcLocalClient
8
9
  from agent_wallet.wallet_layer.base import AgentWalletBackend, WalletBackendError, WalletCapabilities
9
10
 
@@ -13,14 +14,7 @@ def _sats_to_btc(value: Any) -> float:
13
14
 
14
15
 
15
16
  def _normalize_btc_network(value: str | None) -> str:
16
- network = str(value or "").strip().lower()
17
- aliases = {
18
- "mainnet": "bitcoin",
19
- }
20
- network = aliases.get(network, network)
21
- if network not in {"bitcoin", "testnet", "regtest"}:
22
- return "bitcoin"
23
- return network
17
+ return normalize_btc_network(value)
24
18
 
25
19
 
26
20
  class WdkBtcLocalWalletBackend(AgentWalletBackend):
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
6
6
  from typing import Any
7
7
 
8
+ from agent_wallet.config import normalize_evm_network
8
9
  from agent_wallet.providers.evm_portfolio import build_portfolio_snapshot
9
10
  from agent_wallet.providers import lifi
10
11
  from agent_wallet.providers.wdk_evm_local import WdkEvmLocalClient
@@ -12,18 +13,7 @@ from agent_wallet.wallet_layer.base import AgentWalletBackend, WalletBackendErro
12
13
 
13
14
 
14
15
  def _normalize_evm_network(value: str | None) -> str:
15
- network = str(value or "").strip().lower()
16
- aliases = {
17
- "mainnet": "ethereum",
18
- "eth": "ethereum",
19
- "eth-mainnet": "ethereum",
20
- "base-mainnet": "base",
21
- "base_sepolia": "base-sepolia",
22
- }
23
- network = aliases.get(network, network)
24
- if network not in {"ethereum", "sepolia", "base", "base-sepolia"}:
25
- return "ethereum"
26
- return network
16
+ return normalize_evm_network(value)
27
17
 
28
18
 
29
19
  def _lifi_chain_id_for_evm_network(network: str) -> str:
@@ -13,7 +13,7 @@ def main() -> None:
13
13
  context = onboard_openclaw_user_wallet(
14
14
  user_id,
15
15
  sign_only=False,
16
- network="devnet",
16
+ network="mainnet",
17
17
  )
18
18
 
19
19
  print("Session metadata:")
@@ -16,7 +16,7 @@ async def main() -> None:
16
16
  backend = create_wallet_backend_for_user(
17
17
  user_id,
18
18
  sign_only=False,
19
- network="devnet",
19
+ network="mainnet",
20
20
  )
21
21
  adapter = OpenClawWalletAdapter(backend)
22
22
 
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "network": {
41
41
  "type": "string",
42
- "description": "Backend network selector. Solana uses mainnet/devnet/testnet. BTC uses bitcoin/testnet/regtest. EVM uses ethereum/sepolia/base/base-sepolia."
42
+ "description": "Backend network selector. Solana uses mainnet. BTC uses bitcoin. EVM uses ethereum/base."
43
43
  },
44
44
  "wdkBtcServiceUrl": {
45
45
  "type": "string",
@@ -117,10 +117,6 @@
117
117
  "type": "string",
118
118
  "description": "Optional Jupiter Portfolio API base URL for Jupiter-specific positions and staking data."
119
119
  },
120
- "jupiterLendBaseUrl": {
121
- "type": "string",
122
- "description": "Optional Jupiter Lend API base URL for Earn read and deposit/withdraw flows."
123
- },
124
120
  "jupiterApiKey": {
125
121
  "type": "string",
126
122
  "description": "Optional Jupiter API key if your deployment uses a keyed endpoint."
@@ -4,10 +4,11 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "openclaw-agent-wallet"
7
- version = "0.1.28"
7
+ version = "0.1.32"
8
8
  description = "Plugin-friendly wallet backend for OpenClaw agents"
9
9
  requires-python = ">=3.10"
10
10
  dependencies = [
11
+ "fastmcp>=2.0.0",
11
12
  "httpx>=0.27.0",
12
13
  "pydantic>=2.0.0",
13
14
  "pydantic-settings>=2.0.0",
@@ -17,6 +17,7 @@ from pathlib import Path
17
17
  sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
18
18
 
19
19
  from agent_wallet.file_ops import atomic_write_text, chmod_if_exists
20
+ from agent_wallet.config import normalize_btc_network
20
21
 
21
22
 
22
23
  def _default_config_path() -> Path:
@@ -44,10 +45,7 @@ def _script_path(name: str) -> Path:
44
45
 
45
46
 
46
47
  def _normalize_network(value: str) -> str:
47
- network = str(value or "").strip().lower()
48
- if network == "mainnet":
49
- return "bitcoin"
50
- return network or "bitcoin"
48
+ return normalize_btc_network(value)
51
49
 
52
50
 
53
51
  def build_parser() -> argparse.ArgumentParser:
@@ -55,7 +53,7 @@ def build_parser() -> argparse.ArgumentParser:
55
53
  parser.add_argument("--config-path", default=str(_default_config_path()))
56
54
  parser.add_argument("--plugin-id", default="agent-wallet")
57
55
  parser.add_argument("--user-id", default=_default_user_id())
58
- parser.add_argument("--network", default="testnet")
56
+ parser.add_argument("--network", default="bitcoin")
59
57
  parser.add_argument("--service-url", default="http://127.0.0.1:8080")
60
58
  parser.add_argument("--wdk-wallet-root", default=str(_repo_root() / "wdk-btc-wallet"))
61
59
  parser.add_argument("--label", default="Agent BTC Wallet")
@@ -17,6 +17,7 @@ from urllib.request import urlopen
17
17
  sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
18
18
 
19
19
  from agent_wallet.file_ops import atomic_write_text, chmod_if_exists
20
+ from agent_wallet.config import normalize_evm_network
20
21
 
21
22
 
22
23
  def _default_config_path() -> Path:
@@ -44,18 +45,7 @@ def _script_path(name: str) -> Path:
44
45
 
45
46
 
46
47
  def _normalize_network(value: str) -> str:
47
- network = str(value or "").strip().lower()
48
- aliases = {
49
- "mainnet": "ethereum",
50
- "eth": "ethereum",
51
- "eth-mainnet": "ethereum",
52
- "base-mainnet": "base",
53
- "base_sepolia": "base-sepolia",
54
- }
55
- network = aliases.get(network, network)
56
- if network not in {"ethereum", "sepolia", "base", "base-sepolia"}:
57
- return "ethereum"
58
- return network
48
+ return normalize_evm_network(value)
59
49
 
60
50
 
61
51
  def build_parser() -> argparse.ArgumentParser:
@@ -24,6 +24,7 @@ INCLUDED_ROOT_FILES = [
24
24
  "setup.sh",
25
25
  ]
26
26
  INCLUDED_TOP_LEVEL_DIRS = [
27
+ "codex",
27
28
  ".openclaw",
28
29
  "agent-a2a-gateway",
29
30
  "agent-wallet",
@@ -264,16 +264,13 @@ function createReadOnlyWallet(web3, owner) {
264
264
  }
265
265
 
266
266
  function resolveClusterName(network) {
267
- return network === "mainnet" ? "mainnet-beta" : network;
267
+ return "mainnet-beta";
268
268
  }
269
269
 
270
270
  function defaultRpcUrlForNetwork(network) {
271
271
  if (network === "mainnet") {
272
272
  return "https://api.mainnet-beta.solana.com";
273
273
  }
274
- if (network === "devnet") {
275
- return "https://api.devnet.solana.com";
276
- }
277
274
  return "";
278
275
  }
279
276
 
@@ -25,6 +25,7 @@ INCLUDED_RUNTIME_ROOT_FILES = [
25
25
  "setup.sh",
26
26
  ]
27
27
  INCLUDED_RUNTIME_TOP_LEVEL_DIRS = [
28
+ "codex",
28
29
  ".openclaw",
29
30
  "agent-wallet",
30
31
  "agent-a2a-gateway",
@@ -181,6 +182,7 @@ def build_parser() -> argparse.ArgumentParser:
181
182
  parser.add_argument("--extension-path", default=str(_extension_path()))
182
183
  parser.add_argument("--wdk-btc-root", default=str(_default_wdk_btc_root()))
183
184
  parser.add_argument("--wdk-evm-root", default=str(_default_wdk_evm_root()))
185
+ parser.add_argument("--wdk-evm-service-url", default=EVM_DEFAULT_SERVICE_URL)
184
186
  parser.add_argument("--runtime-root", default=str(_default_runtime_root()))
185
187
  parser.add_argument("--npm-bin", default=_default_npm_bin())
186
188
  parser.add_argument("--plugin-id", default="agent-wallet")
@@ -664,6 +666,9 @@ def _build_next_steps(
664
666
  command.extend(["--extension-path", str(effective_extension_path)])
665
667
  command.extend(["--package-root", str(effective_package_root)])
666
668
  command.extend(["--python-bin", str(python_bin)])
669
+ if _is_evm_backend(args.backend):
670
+ service_url = str(getattr(args, "wdk_evm_service_url", "") or EVM_DEFAULT_SERVICE_URL).strip()
671
+ command.extend(["--wdk-evm-service-url", service_url or EVM_DEFAULT_SERVICE_URL])
667
672
  return command
668
673
 
669
674
 
@@ -671,18 +676,51 @@ def _is_solana_backend(backend: str) -> bool:
671
676
  return backend.strip().lower() in {"solana", "solana_local", "solana-local"}
672
677
 
673
678
 
679
+ def _is_evm_backend(backend: str) -> bool:
680
+ return backend.strip().lower() in {
681
+ "wdk_evm_local",
682
+ "wdk-evm-local",
683
+ "evm_local",
684
+ "evm-local",
685
+ }
686
+
687
+
688
+ EVM_DEFAULT_SERVICE_URL = "http://127.0.0.1:8081"
689
+
690
+
691
+ def _build_evm_onboard_config(args: argparse.Namespace) -> dict[str, object]:
692
+ # The EVM wallet is provisioned on every install (best-effort). Seed creation
693
+ # with a valid EVM network; ensure_user_evm_wallet_ready binds BOTH base and
694
+ # ethereum (one address), so the active --network only matters when EVM is the
695
+ # active backend.
696
+ network = args.network.strip().lower() if _is_evm_backend(args.backend) else "base"
697
+ if network not in {"base", "ethereum"}:
698
+ network = "base"
699
+ service_url = str(getattr(args, "wdk_evm_service_url", "") or EVM_DEFAULT_SERVICE_URL).strip()
700
+ return {
701
+ "backend": "wdk_evm_local",
702
+ "network": network,
703
+ "signOnly": bool(args.sign_only),
704
+ "wdkEvmServiceUrl": service_url or EVM_DEFAULT_SERVICE_URL,
705
+ }
706
+
707
+
674
708
  def _build_solana_onboard_config(args: argparse.Namespace) -> dict[str, object]:
709
+ # Solana is provisioned on every install (both wallets are created), so force
710
+ # the Solana backend/network here -- this must work even when the active
711
+ # backend chosen by the user is EVM or BTC.
712
+ solana_active = _is_solana_backend(args.backend)
675
713
  config: dict[str, object] = {
676
- "backend": args.backend,
677
- "network": args.network,
714
+ "backend": "solana_local",
715
+ "network": "mainnet",
678
716
  "signOnly": bool(args.sign_only),
679
717
  "encryptUserWallets": True,
680
718
  "migratePlaintextUserWallets": True,
681
719
  "refuseMainnetWalletRecreation": True,
682
720
  }
683
- if args.rpc_url.strip():
721
+ if solana_active and args.rpc_url.strip():
684
722
  config["rpcUrl"] = args.rpc_url.strip()
685
- if args.rpc_urls.strip():
723
+ if solana_active and args.rpc_urls.strip():
686
724
  config["rpcUrls"] = [item.strip() for item in args.rpc_urls.split(",") if item.strip()]
687
725
  return config
688
726
 
@@ -707,8 +745,6 @@ def _bootstrap_solana_wallet(
707
745
  package_root: Path,
708
746
  args: argparse.Namespace,
709
747
  ) -> dict[str, object] | None:
710
- if not _is_solana_backend(args.backend):
711
- return None
712
748
  result = subprocess.run(
713
749
  [
714
750
  str(python_bin),
@@ -740,6 +776,60 @@ def _bootstrap_solana_wallet(
740
776
  }
741
777
 
742
778
 
779
+ def _bootstrap_evm_wallet(
780
+ python_bin: Path,
781
+ package_root: Path,
782
+ args: argparse.Namespace,
783
+ wdk_evm_root: Path,
784
+ ) -> dict[str, object]:
785
+ """Provision the local EVM wallet (best-effort).
786
+
787
+ Mirrors _bootstrap_solana_wallet but for wdk_evm_local. Runs the onboard CLI,
788
+ which calls ensure_user_evm_wallet_ready: auto-starts the local Node service,
789
+ creates and seals the wallet password, and binds both base and ethereum.
790
+ Failures here never abort the install -- the lazy runtime path (ensure_ready on
791
+ first EVM use) remains the safety net.
792
+ """
793
+ env = _runtime_env_for_onboard(package_root)
794
+ env["OPENCLAW_EVM_WDK_WALLET_ROOT"] = str(wdk_evm_root)
795
+ try:
796
+ result = subprocess.run(
797
+ [
798
+ str(python_bin),
799
+ "-m",
800
+ "agent_wallet.openclaw_cli",
801
+ "onboard",
802
+ "--user-id",
803
+ args.user_id,
804
+ "--config-json",
805
+ json.dumps(_build_evm_onboard_config(args)),
806
+ ],
807
+ cwd=package_root,
808
+ capture_output=True,
809
+ text=True,
810
+ check=True,
811
+ env=env,
812
+ )
813
+ except subprocess.CalledProcessError as exc:
814
+ detail = (exc.stderr or exc.stdout or str(exc)).strip()
815
+ return {"ok": False, "error": detail[-2000:]}
816
+ try:
817
+ payload = json.loads(result.stdout)
818
+ except json.JSONDecodeError:
819
+ return {"ok": False, "error": "EVM onboard returned non-JSON output."}
820
+ session = dict(payload.get("session") or {})
821
+ wallet_path = str(session.get("wallet_path") or "")
822
+ wallet_id = wallet_path.split("walletId=", 1)[-1] if "walletId=" in wallet_path else None
823
+ return {
824
+ "ok": True,
825
+ "user_id": session.get("user_id") or args.user_id,
826
+ "address": session.get("address"),
827
+ "wallet_id": wallet_id,
828
+ "networks": ["base", "ethereum"],
829
+ "backend": session.get("backend"),
830
+ }
831
+
832
+
743
833
  def main() -> None:
744
834
  args = build_parser().parse_args()
745
835
  source_package_root = Path(args.package_root).expanduser().resolve()
@@ -872,6 +962,7 @@ def main() -> None:
872
962
  configured = False
873
963
  configure_stdout = ""
874
964
  solana_onboard_result: dict[str, object] | None = None
965
+ evm_onboard_result: dict[str, object] | None = None
875
966
  if backend_enabled and not pending_env and not args.dry_run:
876
967
  result = subprocess.run(
877
968
  _build_next_steps(
@@ -892,6 +983,22 @@ def main() -> None:
892
983
  package_root,
893
984
  args,
894
985
  )
986
+ # Both wallets are provisioned on every install. EVM provisioning is
987
+ # best-effort: a failure here must not abort the install, since the lazy
988
+ # runtime path will create the wallet on first EVM use.
989
+ evm_onboard_result = _bootstrap_evm_wallet(
990
+ python_bin,
991
+ package_root,
992
+ args,
993
+ wdk_evm_root,
994
+ )
995
+ if isinstance(evm_onboard_result, dict) and not evm_onboard_result.get("ok"):
996
+ print(
997
+ "warning: the EVM wallet was not provisioned during install; it will "
998
+ "be created automatically on first EVM use. Details: "
999
+ + str(evm_onboard_result.get("error") or "unknown"),
1000
+ file=sys.stderr,
1001
+ )
895
1002
 
896
1003
  print(
897
1004
  json.dumps(
@@ -917,6 +1024,7 @@ def main() -> None:
917
1024
  "configured": configured,
918
1025
  "pending_env": pending_env,
919
1026
  "solana_wallet": solana_onboard_result,
1027
+ "evm_wallet": evm_onboard_result,
920
1028
  "next_configure_command": _build_next_steps(
921
1029
  python_bin,
922
1030
  install_config_script,
@@ -13,6 +13,11 @@ from pathlib import Path
13
13
  sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
14
14
 
15
15
  from agent_wallet.file_ops import atomic_write_text, chmod_if_exists
16
+ from agent_wallet.config import (
17
+ normalize_btc_network,
18
+ normalize_evm_network,
19
+ normalize_solana_network,
20
+ )
16
21
  from agent_wallet.sealed_keys import resolve_sealed_keys_path, seal_keys, unseal_keys
17
22
  from security_utils import write_redacted_backup
18
23
 
@@ -24,10 +29,6 @@ LEGACY_ALLOWLIST_TOOLS = [
24
29
  "set_wallet_backend",
25
30
  "get_wallet_portfolio",
26
31
  "get_solana_token_prices",
27
- "swap_solana_privately",
28
- "continue_solana_private_swap",
29
- "list_pending_solana_private_swaps",
30
- "get_solana_private_swap_status",
31
32
  "get_kamino_lend_markets",
32
33
  "get_kamino_lend_market_reserves",
33
34
  "get_kamino_lend_user_obligations",
@@ -42,7 +43,6 @@ LEGACY_ALLOWLIST_TOOLS = [
42
43
  "transfer_spl_token",
43
44
  "swap_solana_tokens",
44
45
  "close_empty_token_accounts",
45
- "request_devnet_airdrop",
46
46
  "get_flash_trade_markets",
47
47
  "get_flash_trade_positions",
48
48
  "flash_trade_open_position",
@@ -181,10 +181,10 @@ def _normalize_network(backend: str, network: str) -> str:
181
181
  backend_name = backend.strip().lower()
182
182
  normalized = network.strip().lower()
183
183
  if backend_name in {"wdk_btc_local", "wdk-btc-local", "btc_local", "btc-local"}:
184
- if normalized == "mainnet":
185
- return "bitcoin"
186
- return normalized or "bitcoin"
187
- return normalized or "devnet"
184
+ return normalize_btc_network(normalized or "bitcoin")
185
+ if backend_name in {"wdk_evm_local", "wdk-evm-local", "evm_local", "evm-local"}:
186
+ return normalize_evm_network(normalized or "ethereum")
187
+ return normalize_solana_network(normalized or "mainnet")
188
188
 
189
189
 
190
190
  def build_parser() -> argparse.ArgumentParser:
@@ -193,7 +193,7 @@ def build_parser() -> argparse.ArgumentParser:
193
193
  parser.add_argument("--plugin-id", default="agent-wallet")
194
194
  parser.add_argument("--user-id", default="")
195
195
  parser.add_argument("--backend", default="solana_local")
196
- parser.add_argument("--network", default="devnet")
196
+ parser.add_argument("--network", default="mainnet")
197
197
  parser.add_argument("--rpc-url", default="")
198
198
  parser.add_argument("--rpc-urls", default="")
199
199
  parser.add_argument("--wdk-btc-service-url", default="")
@@ -21,13 +21,11 @@ from agent_wallet.btc_user_wallets import ( # noqa: E402
21
21
  reveal_user_btc_wallet_seed_phrase,
22
22
  unlock_user_btc_wallet,
23
23
  )
24
+ from agent_wallet.config import normalize_btc_network # noqa: E402
24
25
 
25
26
 
26
27
  def _normalize_network(value: str) -> str:
27
- network = str(value or "").strip().lower()
28
- if network == "mainnet":
29
- return "bitcoin"
30
- return network or "bitcoin"
28
+ return normalize_btc_network(value)
31
29
 
32
30
 
33
31
  def _read_secret(
@@ -15,7 +15,7 @@ PACKAGE_ROOT = Path(__file__).resolve().parents[1]
15
15
  if str(PACKAGE_ROOT) not in sys.path:
16
16
  sys.path.insert(0, str(PACKAGE_ROOT))
17
17
 
18
- from agent_wallet.config import settings # noqa: E402
18
+ from agent_wallet.config import normalize_evm_network, settings # noqa: E402
19
19
  from agent_wallet.evm_user_wallets import ( # noqa: E402
20
20
  bind_user_evm_wallet,
21
21
  create_user_evm_wallet,
@@ -29,26 +29,13 @@ from agent_wallet.providers.wdk_evm_local import WdkEvmLocalClient # noqa: E402
29
29
 
30
30
 
31
31
  def _normalize_network(value: str) -> str:
32
- network = str(value or "").strip().lower()
33
- aliases = {
34
- "mainnet": "ethereum",
35
- "eth": "ethereum",
36
- "eth-mainnet": "ethereum",
37
- "base-mainnet": "base",
38
- "base_sepolia": "base-sepolia",
39
- }
40
- network = aliases.get(network, network)
41
- if network not in {"ethereum", "sepolia", "base", "base-sepolia"}:
42
- return "ethereum"
43
- return network
32
+ return normalize_evm_network(value)
44
33
 
45
34
 
46
35
  def _paired_network(network: str) -> str | None:
47
36
  mapping = {
48
37
  "ethereum": "base",
49
38
  "base": "ethereum",
50
- "sepolia": "base-sepolia",
51
- "base-sepolia": "sepolia",
52
39
  }
53
40
  return mapping.get(_normalize_network(network))
54
41
 
@@ -65,12 +65,6 @@ normalize_network_value() {
65
65
  1|mainnet|bitcoin)
66
66
  printf "mainnet"
67
67
  ;;
68
- 2|testnet)
69
- printf "testnet"
70
- ;;
71
- 3|regtest)
72
- printf "regtest"
73
- ;;
74
68
  *)
75
69
  return 1
76
70
  ;;
@@ -84,18 +78,11 @@ prompt_network_choice() {
84
78
  return 0
85
79
  fi
86
80
 
87
- case "$default_value" in
88
- mainnet|bitcoin) default_hint="1" ;;
89
- testnet) default_hint="2" ;;
90
- regtest) default_hint="3" ;;
91
- *) default_hint="1" ;;
92
- esac
81
+ default_hint="1"
93
82
 
94
83
  while true; do
95
84
  printf "BTC network:\n" >&2
96
85
  printf " 1) mainnet\n" >&2
97
- printf " 2) testnet\n" >&2
98
- printf " 3) regtest\n" >&2
99
86
  printf "Choose network [%s]: " "$default_hint" >&2
100
87
  read -r choice
101
88
  if [ -z "${choice:-}" ]; then
@@ -105,12 +92,16 @@ prompt_network_choice() {
105
92
  printf "%s" "$network"
106
93
  return 0
107
94
  fi
108
- printf "Invalid choice. Enter 1, 2, 3, mainnet, testnet, or regtest.\n" >&2
95
+ printf "Invalid choice. Enter 1, mainnet, or bitcoin.\n" >&2
109
96
  done
110
97
  }
111
98
 
112
99
  DEFAULT_USER_ID=${OPENCLAW_BTC_USER_ID:-${USER:-openclaw-user}-local}
113
- DEFAULT_NETWORK=${OPENCLAW_BTC_NETWORK:-mainnet}
100
+ if DEFAULT_NETWORK=$(normalize_network_value "${OPENCLAW_BTC_NETWORK:-mainnet}" 2>/dev/null); then
101
+ :
102
+ else
103
+ DEFAULT_NETWORK=mainnet
104
+ fi
114
105
  DEFAULT_SERVICE_URL=${OPENCLAW_BTC_SERVICE_URL:-http://127.0.0.1:8080}
115
106
 
116
107
  if ! has_flag --user-id "$@"; then
@@ -65,12 +65,6 @@ normalize_network_value() {
65
65
  1|mainnet|bitcoin)
66
66
  printf "mainnet"
67
67
  ;;
68
- 2|testnet)
69
- printf "testnet"
70
- ;;
71
- 3|regtest)
72
- printf "regtest"
73
- ;;
74
68
  *)
75
69
  return 1
76
70
  ;;
@@ -84,18 +78,11 @@ prompt_network_choice() {
84
78
  return 0
85
79
  fi
86
80
 
87
- case "$default_value" in
88
- mainnet|bitcoin) default_hint="1" ;;
89
- testnet) default_hint="2" ;;
90
- regtest) default_hint="3" ;;
91
- *) default_hint="1" ;;
92
- esac
81
+ default_hint="1"
93
82
 
94
83
  while true; do
95
84
  printf "BTC network:\n" >&2
96
85
  printf " 1) mainnet\n" >&2
97
- printf " 2) testnet\n" >&2
98
- printf " 3) regtest\n" >&2
99
86
  printf "Choose network [%s]: " "$default_hint" >&2
100
87
  read -r choice
101
88
  if [ -z "${choice:-}" ]; then
@@ -105,12 +92,16 @@ prompt_network_choice() {
105
92
  printf "%s" "$network"
106
93
  return 0
107
94
  fi
108
- printf "Invalid choice. Enter 1, 2, 3, mainnet, testnet, or regtest.\n" >&2
95
+ printf "Invalid choice. Enter 1, mainnet, or bitcoin.\n" >&2
109
96
  done
110
97
  }
111
98
 
112
99
  DEFAULT_USER_ID=${OPENCLAW_BTC_USER_ID:-${USER:-openclaw-user}-local}
113
- DEFAULT_NETWORK=${OPENCLAW_BTC_NETWORK:-mainnet}
100
+ if DEFAULT_NETWORK=$(normalize_network_value "${OPENCLAW_BTC_NETWORK:-mainnet}" 2>/dev/null); then
101
+ :
102
+ else
103
+ DEFAULT_NETWORK=mainnet
104
+ fi
114
105
  DEFAULT_SERVICE_URL=${OPENCLAW_BTC_SERVICE_URL:-http://127.0.0.1:8080}
115
106
 
116
107
  if ! has_flag --user-id "$@"; then
@@ -68,12 +68,6 @@ normalize_network_value() {
68
68
  2|base)
69
69
  printf "base"
70
70
  ;;
71
- 3|sepolia)
72
- printf "sepolia"
73
- ;;
74
- 4|base-sepolia|base_sepolia)
75
- printf "base-sepolia"
76
- ;;
77
71
  *)
78
72
  return 1
79
73
  ;;
@@ -90,8 +84,6 @@ prompt_network_choice() {
90
84
  case "$default_value" in
91
85
  ethereum) default_hint="1" ;;
92
86
  base) default_hint="2" ;;
93
- sepolia) default_hint="3" ;;
94
- base-sepolia) default_hint="4" ;;
95
87
  *) default_hint="2" ;;
96
88
  esac
97
89
 
@@ -99,8 +91,6 @@ prompt_network_choice() {
99
91
  printf "EVM network:\n" >&2
100
92
  printf " 1) ethereum\n" >&2
101
93
  printf " 2) base\n" >&2
102
- printf " 3) sepolia\n" >&2
103
- printf " 4) base-sepolia\n" >&2
104
94
  printf "Choose network [%s]: " "$default_hint" >&2
105
95
  read -r choice
106
96
  if [ -z "${choice:-}" ]; then
@@ -110,7 +100,7 @@ prompt_network_choice() {
110
100
  printf "%s" "$network"
111
101
  return 0
112
102
  fi
113
- printf "Invalid choice. Enter 1, 2, 3, 4, ethereum, base, sepolia, or base-sepolia.\n" >&2
103
+ printf "Invalid choice. Enter 1, 2, ethereum, or base.\n" >&2
114
104
  done
115
105
  }
116
106
 
@@ -5,9 +5,12 @@ from __future__ import annotations
5
5
  import argparse
6
6
  import json
7
7
  import os
8
+ import sys
8
9
  from datetime import datetime, timezone
9
10
  from pathlib import Path
10
11
 
12
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
13
+
11
14
  from agent_wallet.file_ops import atomic_write_text, chmod_if_exists
12
15
  from security_utils import write_redacted_backup
13
16
 
@@ -20,7 +23,7 @@ def build_parser() -> argparse.ArgumentParser:
20
23
  parser = argparse.ArgumentParser(description=__doc__)
21
24
  parser.add_argument("--config-path", default=str(_default_config_path()))
22
25
  parser.add_argument("--plugin-id", default="agent-wallet")
23
- parser.add_argument("--network", required=True, choices=["mainnet", "devnet", "testnet"])
26
+ parser.add_argument("--network", required=True, choices=["mainnet"])
24
27
  parser.add_argument("--rpc-url", default="")
25
28
  parser.add_argument("--rpc-urls", default="")
26
29
  parser.add_argument("--sign-only", action=argparse.BooleanOptionalAction, default=None)