@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
|
@@ -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
|
-
|
|
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
|
-
|
|
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:
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"network": {
|
|
41
41
|
"type": "string",
|
|
42
|
-
"description": "Backend network selector. Solana uses mainnet
|
|
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.
|
|
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
|
-
|
|
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="
|
|
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
|
-
|
|
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:
|
|
@@ -264,16 +264,13 @@ function createReadOnlyWallet(web3, owner) {
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
function resolveClusterName(network) {
|
|
267
|
-
return
|
|
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":
|
|
677
|
-
"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
|
-
|
|
185
|
-
|
|
186
|
-
return normalized or "
|
|
187
|
-
return normalized or "
|
|
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="
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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"
|
|
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)
|