@agentlayer.tech/wallet 0.1.11 → 0.1.13

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 (29) hide show
  1. package/.openclaw/extensions/agent-wallet/index.ts +454 -18
  2. package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +96 -0
  3. package/.openclaw/extensions/agent-wallet/skills/wallet-operator/SKILL.md +2 -0
  4. package/CHANGELOG.md +25 -0
  5. package/README.md +43 -51
  6. package/agent-wallet/.env.example +11 -0
  7. package/agent-wallet/README.md +53 -0
  8. package/agent-wallet/agent_wallet/approval.py +4 -0
  9. package/agent-wallet/agent_wallet/config.py +6 -0
  10. package/agent-wallet/agent_wallet/exceptions.py +2 -1
  11. package/agent-wallet/agent_wallet/openclaw_adapter.py +361 -2
  12. package/agent-wallet/agent_wallet/openclaw_cli.py +13 -1
  13. package/agent-wallet/agent_wallet/openclaw_runtime.py +2 -5
  14. package/agent-wallet/agent_wallet/providers/houdini.py +539 -0
  15. package/agent-wallet/agent_wallet/transaction_policy.py +251 -0
  16. package/agent-wallet/agent_wallet/user_wallets.py +83 -0
  17. package/agent-wallet/agent_wallet/wallet_layer/base.py +40 -0
  18. package/agent-wallet/agent_wallet/wallet_layer/solana.py +885 -16
  19. package/agent-wallet/pyproject.toml +1 -1
  20. package/agent-wallet/scripts/bootstrap_openclaw_evm.py +291 -0
  21. package/agent-wallet/scripts/install_agent_wallet.py +54 -2
  22. package/agent-wallet/scripts/install_openclaw_local_config.py +84 -4
  23. package/agent-wallet/scripts/manage_openclaw_evm_wallet.py +343 -0
  24. package/agent-wallet/scripts/setup_evm_wallet.sh +151 -0
  25. package/hermes/plugins/agent_wallet/__init__.py +28 -2
  26. package/hermes/plugins/agent_wallet/plugin.yaml +2 -0
  27. package/hermes/plugins/agent_wallet/schemas.py +72 -0
  28. package/hermes/plugins/agent_wallet/tools.py +193 -9
  29. package/package.json +2 -2
@@ -0,0 +1,343 @@
1
+ #!/usr/bin/env python3
2
+ """Host-side helper for managing a local OpenClaw EVM wallet binding."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import json
8
+ import sys
9
+ from getpass import getpass
10
+ from pathlib import Path
11
+ from urllib.error import URLError
12
+ from urllib.request import urlopen
13
+
14
+ PACKAGE_ROOT = Path(__file__).resolve().parents[1]
15
+ if str(PACKAGE_ROOT) not in sys.path:
16
+ sys.path.insert(0, str(PACKAGE_ROOT))
17
+
18
+ from agent_wallet.config import settings # noqa: E402
19
+ from agent_wallet.evm_user_wallets import ( # noqa: E402
20
+ bind_user_evm_wallet,
21
+ create_user_evm_wallet,
22
+ get_user_evm_wallet_binding,
23
+ import_user_evm_wallet,
24
+ list_user_evm_wallet_bindings,
25
+ lock_user_evm_wallet,
26
+ unlock_user_evm_wallet,
27
+ )
28
+ from agent_wallet.providers.wdk_evm_local import WdkEvmLocalClient # noqa: E402
29
+
30
+
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
44
+
45
+
46
+ def _paired_network(network: str) -> str | None:
47
+ mapping = {
48
+ "ethereum": "base",
49
+ "base": "ethereum",
50
+ "sepolia": "base-sepolia",
51
+ "base-sepolia": "sepolia",
52
+ }
53
+ return mapping.get(_normalize_network(network))
54
+
55
+
56
+ def _read_secret(
57
+ *,
58
+ prompt: str,
59
+ confirm_prompt: str | None = None,
60
+ stdin_mode: bool = False,
61
+ ) -> str:
62
+ if stdin_mode:
63
+ value = sys.stdin.read().strip()
64
+ if not value:
65
+ raise SystemExit(f"{prompt.rstrip(':')} is required on stdin.")
66
+ return value
67
+ value = getpass(prompt)
68
+ if confirm_prompt is not None:
69
+ confirmed = getpass(confirm_prompt)
70
+ if value != confirmed:
71
+ raise SystemExit("Secrets did not match.")
72
+ if not value.strip():
73
+ raise SystemExit(f"{prompt.rstrip(':')} is required.")
74
+ return value.strip()
75
+
76
+
77
+ def _read_password_and_seed_from_stdin() -> tuple[str, str]:
78
+ raw = sys.stdin.read().strip()
79
+ if not raw:
80
+ raise SystemExit("Password and seed phrase payload is required on stdin.")
81
+ lines = raw.splitlines()
82
+ if len(lines) < 2:
83
+ raise SystemExit(
84
+ "For import via stdin, provide password on the first line and the seed phrase on the remaining lines."
85
+ )
86
+ password = lines[0].strip()
87
+ seed_phrase = " ".join(line.strip() for line in lines[1:] if line.strip())
88
+ if not password or not seed_phrase:
89
+ raise SystemExit("Both password and seed phrase are required.")
90
+ return password, seed_phrase
91
+
92
+
93
+ def _service_health(service_url: str | None) -> dict[str, object]:
94
+ target = str(service_url or "").strip()
95
+ if not target:
96
+ return {"service_url": None, "healthy": False, "error": "service_url is not configured"}
97
+ health_url = f"{target.rstrip('/')}/health"
98
+ try:
99
+ with urlopen(health_url, timeout=1.5) as response:
100
+ payload = json.loads(response.read().decode("utf-8"))
101
+ return {
102
+ "service_url": target,
103
+ "healthy": int(getattr(response, "status", 0) or 0) == 200,
104
+ "health": payload,
105
+ }
106
+ except (URLError, TimeoutError, OSError, ValueError) as exc:
107
+ return {"service_url": target, "healthy": False, "error": str(exc)}
108
+
109
+
110
+ def _status_payload(user_id: str | None, network: str | None, service_url: str | None) -> dict[str, object]:
111
+ target_service_url = str(service_url or settings.wdk_evm_service_url).strip() or None
112
+ payload: dict[str, object] = {
113
+ "ok": True,
114
+ "network": _normalize_network(network or "ethereum"),
115
+ "service": _service_health(target_service_url),
116
+ }
117
+ target_network = _normalize_network(network or "ethereum")
118
+ if target_service_url:
119
+ try:
120
+ payload["network_info"] = WdkEvmLocalClient(target_service_url).get_sync("/v1/evm/network")
121
+ except Exception as exc: # pragma: no cover - defensive
122
+ payload["network_info_error"] = str(exc)
123
+ if user_id:
124
+ payload["bindings"] = list_user_evm_wallet_bindings(user_id)
125
+ try:
126
+ payload["binding"] = get_user_evm_wallet_binding(user_id, network=target_network)
127
+ except Exception as exc:
128
+ payload["binding_error"] = str(exc)
129
+ return payload
130
+
131
+
132
+ def main() -> int:
133
+ parser = argparse.ArgumentParser(description="Manage a local OpenClaw EVM wallet binding")
134
+ subparsers = parser.add_subparsers(dest="command", required=True)
135
+
136
+ common_parent = argparse.ArgumentParser(add_help=False)
137
+ common_parent.add_argument("--network", default="ethereum")
138
+ common_parent.add_argument("--service-url")
139
+
140
+ get_parser = subparsers.add_parser("get", parents=[common_parent])
141
+ get_parser.add_argument("--user-id", required=True)
142
+
143
+ list_parser = subparsers.add_parser("list", parents=[common_parent])
144
+ list_parser.add_argument("--user-id", required=True)
145
+
146
+ status_parser = subparsers.add_parser("status", parents=[common_parent])
147
+ status_parser.add_argument("--user-id", default="")
148
+
149
+ setup_parser = subparsers.add_parser("setup", parents=[common_parent])
150
+ setup_parser.add_argument("--user-id", required=True)
151
+ setup_parser.add_argument("--label")
152
+ setup_parser.add_argument("--account-index", type=int)
153
+ setup_parser.add_argument("--password-stdin", action="store_true")
154
+ setup_parser.add_argument("--bind-network-pair", action=argparse.BooleanOptionalAction, default=True)
155
+
156
+ create_parser = subparsers.add_parser("create", parents=[common_parent])
157
+ create_parser.add_argument("--user-id", required=True)
158
+ create_parser.add_argument("--label")
159
+ create_parser.add_argument("--account-index", type=int)
160
+ create_parser.add_argument("--reveal-seed", action="store_true")
161
+ create_parser.add_argument("--password-stdin", action="store_true")
162
+ create_parser.add_argument("--bind-network-pair", action=argparse.BooleanOptionalAction, default=True)
163
+
164
+ import_parser = subparsers.add_parser("import", parents=[common_parent])
165
+ import_parser.add_argument("--user-id", required=True)
166
+ import_parser.add_argument("--label")
167
+ import_parser.add_argument("--account-index", type=int)
168
+ import_parser.add_argument("--password-stdin", action="store_true")
169
+ import_parser.add_argument("--seed-stdin", action="store_true")
170
+ import_parser.add_argument("--bind-network-pair", action=argparse.BooleanOptionalAction, default=True)
171
+
172
+ unlock_parser = subparsers.add_parser("unlock", parents=[common_parent])
173
+ unlock_parser.add_argument("--user-id", required=True)
174
+ unlock_parser.add_argument("--password-stdin", action="store_true")
175
+ unlock_parser.add_argument("--wallet-id", default="")
176
+ unlock_parser.add_argument("--account-index", type=int)
177
+ unlock_parser.add_argument("--bind-network-pair", action=argparse.BooleanOptionalAction, default=True)
178
+
179
+ lock_parser = subparsers.add_parser("lock", parents=[common_parent])
180
+ lock_parser.add_argument("--user-id", required=True)
181
+ lock_parser.add_argument("--wallet-id", default="")
182
+ lock_parser.add_argument("--account-index", type=int)
183
+
184
+ args = parser.parse_args()
185
+ effective_network = _normalize_network(args.network)
186
+
187
+ def _config_hint(wallet: dict[str, object]) -> dict[str, object]:
188
+ return {
189
+ "backend": "wdk_evm_local",
190
+ "network": effective_network,
191
+ "wdkEvmServiceUrl": args.service_url,
192
+ "wdkEvmWalletId": wallet.get("wallet_id"),
193
+ "wdkEvmAccountIndex": wallet.get("account_index"),
194
+ }
195
+
196
+ def _bind_pair(wallet: dict[str, object]) -> dict[str, object] | None:
197
+ if not getattr(args, "bind_network_pair", False):
198
+ return None
199
+ paired = _paired_network(effective_network)
200
+ if not paired:
201
+ return None
202
+ return bind_user_evm_wallet(
203
+ args.user_id,
204
+ wallet_id=str(wallet.get("wallet_id") or ""),
205
+ network=paired,
206
+ service_url=args.service_url,
207
+ account_index=wallet.get("account_index"),
208
+ tolerate_locked=True,
209
+ fallback_address=str(wallet.get("address") or "").strip() or None,
210
+ )
211
+
212
+ if args.command == "status":
213
+ payload = _status_payload(args.user_id or None, effective_network, args.service_url)
214
+ elif args.command == "list":
215
+ payload = {"ok": True, "wallets": list_user_evm_wallet_bindings(args.user_id)}
216
+ elif args.command == "get":
217
+ wallet = get_user_evm_wallet_binding(args.user_id, network=effective_network)
218
+ payload = {"ok": True, "wallet": wallet, "openclaw_config_hint": _config_hint(wallet)}
219
+ elif args.command == "setup":
220
+ password = _read_secret(
221
+ prompt="EVM wallet password: ",
222
+ confirm_prompt=None,
223
+ stdin_mode=bool(args.password_stdin),
224
+ )
225
+ try:
226
+ existing = get_user_evm_wallet_binding(args.user_id, network=effective_network)
227
+ except Exception:
228
+ existing = None
229
+
230
+ if existing is None:
231
+ wallet = create_user_evm_wallet(
232
+ args.user_id,
233
+ password=password,
234
+ label=args.label,
235
+ network=effective_network,
236
+ service_url=args.service_url,
237
+ account_index=args.account_index,
238
+ )
239
+ paired_binding = _bind_pair(wallet)
240
+ payload = {
241
+ "ok": True,
242
+ "action": "created",
243
+ "wallet": wallet,
244
+ "paired_binding": paired_binding,
245
+ "openclaw_config_hint": _config_hint(wallet),
246
+ }
247
+ else:
248
+ wallet = unlock_user_evm_wallet(
249
+ args.user_id,
250
+ password=password,
251
+ network=effective_network,
252
+ service_url=args.service_url,
253
+ account_index=args.account_index,
254
+ )
255
+ paired_binding = _bind_pair(wallet)
256
+ payload = {
257
+ "ok": True,
258
+ "action": "unlocked",
259
+ "wallet": wallet,
260
+ "paired_binding": paired_binding,
261
+ "openclaw_config_hint": _config_hint(wallet),
262
+ }
263
+ elif args.command == "create":
264
+ wallet = create_user_evm_wallet(
265
+ args.user_id,
266
+ password=_read_secret(
267
+ prompt="EVM wallet password: ",
268
+ confirm_prompt="Confirm EVM wallet password: ",
269
+ stdin_mode=bool(args.password_stdin),
270
+ ),
271
+ label=args.label,
272
+ network=effective_network,
273
+ service_url=args.service_url,
274
+ reveal_seed_phrase=bool(args.reveal_seed),
275
+ account_index=args.account_index,
276
+ )
277
+ payload = {
278
+ "ok": True,
279
+ "wallet": wallet,
280
+ "paired_binding": _bind_pair(wallet),
281
+ }
282
+ elif args.command == "import":
283
+ if args.password_stdin and args.seed_stdin:
284
+ password, seed_phrase = _read_password_and_seed_from_stdin()
285
+ else:
286
+ password = _read_secret(
287
+ prompt="EVM wallet password: ",
288
+ confirm_prompt="Confirm EVM wallet password: ",
289
+ stdin_mode=bool(args.password_stdin),
290
+ )
291
+ seed_phrase = _read_secret(
292
+ prompt="EVM seed phrase: ",
293
+ stdin_mode=bool(args.seed_stdin),
294
+ )
295
+ wallet = import_user_evm_wallet(
296
+ args.user_id,
297
+ password=password,
298
+ seed_phrase=seed_phrase,
299
+ label=args.label,
300
+ network=effective_network,
301
+ service_url=args.service_url,
302
+ account_index=args.account_index,
303
+ )
304
+ payload = {
305
+ "ok": True,
306
+ "wallet": wallet,
307
+ "paired_binding": _bind_pair(wallet),
308
+ }
309
+ elif args.command == "unlock":
310
+ wallet = unlock_user_evm_wallet(
311
+ args.user_id,
312
+ password=_read_secret(
313
+ prompt="EVM wallet password: ",
314
+ stdin_mode=bool(args.password_stdin),
315
+ ),
316
+ network=effective_network,
317
+ service_url=args.service_url,
318
+ wallet_id=args.wallet_id or None,
319
+ account_index=args.account_index,
320
+ )
321
+ payload = {
322
+ "ok": True,
323
+ "wallet": wallet,
324
+ "paired_binding": _bind_pair(wallet),
325
+ }
326
+ else:
327
+ payload = {
328
+ "ok": True,
329
+ "wallet": lock_user_evm_wallet(
330
+ args.user_id,
331
+ network=effective_network,
332
+ service_url=args.service_url,
333
+ wallet_id=args.wallet_id or None,
334
+ account_index=args.account_index,
335
+ ),
336
+ }
337
+
338
+ print(json.dumps(payload))
339
+ return 0
340
+
341
+
342
+ if __name__ == "__main__":
343
+ raise SystemExit(main())
@@ -0,0 +1,151 @@
1
+ #!/bin/sh
2
+ set -eu
3
+
4
+ SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
5
+ PACKAGE_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)
6
+
7
+ resolve_python_bin() {
8
+ if [ -n "${OPENCLAW_AGENT_WALLET_PYTHON:-}" ] && [ -x "${OPENCLAW_AGENT_WALLET_PYTHON}" ]; then
9
+ printf "%s" "${OPENCLAW_AGENT_WALLET_PYTHON}"
10
+ return 0
11
+ fi
12
+
13
+ if [ -x "/tmp/agent-wallet-venv/bin/python" ]; then
14
+ printf "%s" "/tmp/agent-wallet-venv/bin/python"
15
+ return 0
16
+ fi
17
+
18
+ if [ -x "$PACKAGE_ROOT/.venv/bin/python" ]; then
19
+ printf "%s" "$PACKAGE_ROOT/.venv/bin/python"
20
+ return 0
21
+ fi
22
+
23
+ if command -v python3 >/dev/null 2>&1; then
24
+ command -v python3
25
+ return 0
26
+ fi
27
+
28
+ command -v python
29
+ }
30
+
31
+ PYTHON_BIN=$(resolve_python_bin)
32
+ export OPENCLAW_AGENT_WALLET_PYTHON="$PYTHON_BIN"
33
+
34
+ has_flag() {
35
+ flag=$1
36
+ shift
37
+ for arg in "$@"; do
38
+ case "$arg" in
39
+ "$flag"|"$flag"=*)
40
+ return 0
41
+ ;;
42
+ esac
43
+ done
44
+ return 1
45
+ }
46
+
47
+ prompt_with_default() {
48
+ label=$1
49
+ default_value=$2
50
+ if [ -t 0 ]; then
51
+ printf "%s [%s]: " "$label" "$default_value" >&2
52
+ read -r value
53
+ if [ -z "${value:-}" ]; then
54
+ printf "%s" "$default_value"
55
+ else
56
+ printf "%s" "$value"
57
+ fi
58
+ return 0
59
+ fi
60
+ printf "%s" "$default_value"
61
+ }
62
+
63
+ normalize_network_value() {
64
+ case $(printf "%s" "$1" | tr '[:upper:]' '[:lower:]') in
65
+ 1|ethereum|eth|mainnet)
66
+ printf "ethereum"
67
+ ;;
68
+ 2|base)
69
+ printf "base"
70
+ ;;
71
+ 3|sepolia)
72
+ printf "sepolia"
73
+ ;;
74
+ 4|base-sepolia|base_sepolia)
75
+ printf "base-sepolia"
76
+ ;;
77
+ *)
78
+ return 1
79
+ ;;
80
+ esac
81
+ }
82
+
83
+ prompt_network_choice() {
84
+ default_value=$1
85
+ if ! [ -t 0 ]; then
86
+ printf "%s" "$default_value"
87
+ return 0
88
+ fi
89
+
90
+ case "$default_value" in
91
+ ethereum) default_hint="1" ;;
92
+ base) default_hint="2" ;;
93
+ sepolia) default_hint="3" ;;
94
+ base-sepolia) default_hint="4" ;;
95
+ *) default_hint="2" ;;
96
+ esac
97
+
98
+ while true; do
99
+ printf "EVM network:\n" >&2
100
+ printf " 1) ethereum\n" >&2
101
+ printf " 2) base\n" >&2
102
+ printf " 3) sepolia\n" >&2
103
+ printf " 4) base-sepolia\n" >&2
104
+ printf "Choose network [%s]: " "$default_hint" >&2
105
+ read -r choice
106
+ if [ -z "${choice:-}" ]; then
107
+ choice=$default_hint
108
+ fi
109
+ if network=$(normalize_network_value "$choice"); then
110
+ printf "%s" "$network"
111
+ return 0
112
+ fi
113
+ printf "Invalid choice. Enter 1, 2, 3, 4, ethereum, base, sepolia, or base-sepolia.\n" >&2
114
+ done
115
+ }
116
+
117
+ DEFAULT_USER_ID=${OPENCLAW_EVM_USER_ID:-${USER:-openclaw-user}-local}
118
+ DEFAULT_NETWORK=${OPENCLAW_EVM_NETWORK:-base}
119
+ DEFAULT_SERVICE_URL=${OPENCLAW_EVM_SERVICE_URL:-http://127.0.0.1:8081}
120
+
121
+ if ! has_flag --user-id "$@"; then
122
+ USER_ID=$(prompt_with_default "OpenClaw user id" "$DEFAULT_USER_ID")
123
+ set -- "$@" --user-id "$USER_ID"
124
+ fi
125
+
126
+ if ! has_flag --network "$@"; then
127
+ NETWORK=$(prompt_network_choice "$DEFAULT_NETWORK")
128
+ set -- "$@" --network "$NETWORK"
129
+ fi
130
+
131
+ if ! has_flag --service-url "$@"; then
132
+ set -- "$@" --service-url "$DEFAULT_SERVICE_URL"
133
+ fi
134
+
135
+ if ! has_flag --config-path "$@" && [ -n "${OPENCLAW_EVM_CONFIG_PATH:-}" ]; then
136
+ set -- "$@" --config-path "$OPENCLAW_EVM_CONFIG_PATH"
137
+ fi
138
+
139
+ if ! has_flag --wdk-wallet-root "$@" && [ -n "${OPENCLAW_EVM_WDK_WALLET_ROOT:-}" ]; then
140
+ set -- "$@" --wdk-wallet-root "$OPENCLAW_EVM_WDK_WALLET_ROOT"
141
+ fi
142
+
143
+ if ! has_flag --python-bin "$@"; then
144
+ set -- "$@" --python-bin "$PYTHON_BIN"
145
+ fi
146
+
147
+ if ! has_flag --package-root "$@"; then
148
+ set -- "$@" --package-root "$PACKAGE_ROOT"
149
+ fi
150
+
151
+ exec "$PYTHON_BIN" "$SCRIPT_DIR/bootstrap_openclaw_evm.py" "$@"
@@ -1,7 +1,19 @@
1
1
  """Hermes Agent plugin bridge for AgentLayer wallet tools."""
2
2
 
3
- from .schemas import AGENT_WALLET_APPROVE, AGENT_WALLET_INVOKE, AGENT_WALLET_TOOLS
4
- from .tools import agent_wallet_approve, agent_wallet_invoke, agent_wallet_tools
3
+ from .schemas import (
4
+ AGENT_WALLET_APPROVE,
5
+ AGENT_WALLET_EVM_SETUP,
6
+ AGENT_WALLET_EVM_STATUS,
7
+ AGENT_WALLET_INVOKE,
8
+ AGENT_WALLET_TOOLS,
9
+ )
10
+ from .tools import (
11
+ agent_wallet_approve,
12
+ agent_wallet_evm_setup,
13
+ agent_wallet_evm_status,
14
+ agent_wallet_invoke,
15
+ agent_wallet_tools,
16
+ )
5
17
 
6
18
 
7
19
  def register(ctx):
@@ -27,3 +39,17 @@ def register(ctx):
27
39
  handler=agent_wallet_approve,
28
40
  description=AGENT_WALLET_APPROVE["description"],
29
41
  )
42
+ ctx.register_tool(
43
+ name=AGENT_WALLET_EVM_STATUS["name"],
44
+ toolset="agent_wallet",
45
+ schema=AGENT_WALLET_EVM_STATUS,
46
+ handler=agent_wallet_evm_status,
47
+ description=AGENT_WALLET_EVM_STATUS["description"],
48
+ )
49
+ ctx.register_tool(
50
+ name=AGENT_WALLET_EVM_SETUP["name"],
51
+ toolset="agent_wallet",
52
+ schema=AGENT_WALLET_EVM_SETUP,
53
+ handler=agent_wallet_evm_setup,
54
+ description=AGENT_WALLET_EVM_SETUP["description"],
55
+ )
@@ -5,3 +5,5 @@ provides_tools:
5
5
  - agent_wallet_tools
6
6
  - agent_wallet_invoke
7
7
  - agent_wallet_approve
8
+ - agent_wallet_evm_status
9
+ - agent_wallet_evm_setup
@@ -132,3 +132,75 @@ AGENT_WALLET_APPROVE = {
132
132
  "additionalProperties": False,
133
133
  },
134
134
  }
135
+
136
+ AGENT_WALLET_EVM_STATUS = {
137
+ "name": "agent_wallet_evm_status",
138
+ "description": (
139
+ "Inspect the local EVM wallet runtime used by AgentLayer/OpenClaw. "
140
+ "Returns wdk-evm-wallet health, network info, and existing user wallet "
141
+ "bindings without changing wallet state."
142
+ ),
143
+ "parameters": {
144
+ "type": "object",
145
+ "properties": {
146
+ "user_id": {
147
+ "type": "string",
148
+ "description": "Optional local wallet owner id to inspect.",
149
+ },
150
+ "network": {
151
+ "type": "string",
152
+ "description": "Optional EVM network hint, such as ethereum, base, sepolia, or base-sepolia.",
153
+ },
154
+ "service_url": {
155
+ "type": "string",
156
+ "description": "Optional localhost override for the wdk-evm-wallet service.",
157
+ },
158
+ },
159
+ "additionalProperties": False,
160
+ },
161
+ }
162
+
163
+ AGENT_WALLET_EVM_SETUP = {
164
+ "name": "agent_wallet_evm_setup",
165
+ "description": (
166
+ "Create or unlock the local EVM wallet binding used by AgentLayer/OpenClaw "
167
+ "for Hermes. This can auto-start the localhost-only wdk-evm-wallet service, "
168
+ "set up the selected network, and bind the same wallet to the paired EVM network "
169
+ "such as ethereum/base."
170
+ ),
171
+ "parameters": {
172
+ "type": "object",
173
+ "properties": {
174
+ "password": {
175
+ "type": "string",
176
+ "description": "Local EVM wallet password used to create or unlock the vault wallet.",
177
+ },
178
+ "user_id": {
179
+ "type": "string",
180
+ "description": "Optional local wallet owner id. Defaults to AGENT_WALLET_USER_ID, USER, or hermes-local-user.",
181
+ },
182
+ "network": {
183
+ "type": "string",
184
+ "description": "Selected EVM network, typically ethereum or base.",
185
+ },
186
+ "label": {
187
+ "type": "string",
188
+ "description": "Optional wallet label used when creating a new local EVM wallet.",
189
+ },
190
+ "service_url": {
191
+ "type": "string",
192
+ "description": "Optional localhost override for the wdk-evm-wallet service.",
193
+ },
194
+ "auto_start_service": {
195
+ "type": "boolean",
196
+ "description": "Whether to auto-start the local wdk-evm-wallet service when it is not healthy. Defaults to true.",
197
+ },
198
+ "bind_network_pair": {
199
+ "type": "boolean",
200
+ "description": "Whether to also bind the paired EVM network such as ethereum/base. Defaults to true.",
201
+ },
202
+ },
203
+ "required": ["password"],
204
+ "additionalProperties": False,
205
+ },
206
+ }