@agentlayer.tech/wallet 0.1.75 → 0.1.77

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 (36) hide show
  1. package/.openclaw/extensions/agent-wallet/README.md +4 -4
  2. package/.openclaw/extensions/agent-wallet/dist/index.js +57 -19
  3. package/.openclaw/extensions/agent-wallet/index.ts +57 -19
  4. package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +1 -1
  5. package/.openclaw/extensions/agent-wallet/package.json +1 -1
  6. package/CHANGELOG.md +19 -0
  7. package/VERSION +1 -1
  8. package/agent-wallet/AGENTS.md +1 -0
  9. package/agent-wallet/UPGRADE_COMPATIBILITY.md +36 -0
  10. package/agent-wallet/agent_wallet/__init__.py +1 -1
  11. package/agent-wallet/agent_wallet/autonomous_permissions.py +2 -2
  12. package/agent-wallet/agent_wallet/autonomous_policy.py +1 -1
  13. package/agent-wallet/agent_wallet/boot_key_recovery.py +10 -1
  14. package/agent-wallet/agent_wallet/config.py +3 -3
  15. package/agent-wallet/agent_wallet/openclaw_adapter.py +24 -24
  16. package/agent-wallet/agent_wallet/providers/evm_portfolio.py +2 -2
  17. package/agent-wallet/agent_wallet/wallet_layer/wdk_evm.py +2 -2
  18. package/agent-wallet/openclaw.plugin.json +1 -1
  19. package/agent-wallet/pyproject.toml +1 -1
  20. package/agent-wallet/skills/wallet-operator/SKILL.md +4 -1
  21. package/bin/lib/boot-key.mjs +185 -0
  22. package/bin/lib/integrations.mjs +424 -0
  23. package/bin/lib/update-transaction.mjs +235 -0
  24. package/bin/openclaw-agent-wallet.mjs +225 -520
  25. package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +1 -1
  26. package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +1 -1
  27. package/codex/plugins/agent-wallet/server.py +13 -10
  28. package/codex/plugins/agent-wallet/skills/wallet-operator/SKILL.md +1 -1
  29. package/hermes/plugins/agent_wallet/plugin.yaml +1 -1
  30. package/package.json +2 -1
  31. package/wdk-btc-wallet/package.json +1 -1
  32. package/wdk-evm-wallet/.env.example +3 -2
  33. package/wdk-evm-wallet/package.json +5 -2
  34. package/wdk-evm-wallet/src/config.js +16 -2
  35. package/wdk-evm-wallet/src/network_state.js +5 -2
  36. package/wdk-evm-wallet/src/wdk_evm_wallet.js +10 -4
@@ -39,7 +39,7 @@ If the preview result includes a confirmation_summary or mainnet_warning, surfac
39
39
  Never bypass the approval token requirement for wallet writes.
40
40
  In OpenClaw, switch between Solana, EVM, and Bitcoin wallets with set_wallet_backend.
41
41
  The plugin config is the startup default, not something to edit during a normal conversation.
42
- For EVM wallets, switch between Ethereum and Base with set_evm_network or by passing the
42
+ For EVM wallets, switch between Ethereum, Base, and Robinhood with set_evm_network or by passing the
43
43
  network argument to EVM tools. Do not edit code, plugin config, or environment variables
44
44
  just to switch the active EVM network.
45
45
  """.strip()
@@ -74,7 +74,7 @@ class OpenClawWalletAdapter:
74
74
  if chain == "bitcoin":
75
75
  return normalized == "bitcoin"
76
76
  if chain == "evm":
77
- return normalized in {"ethereum", "base", "eip155:1", "eip155:8453"}
77
+ return normalized in {"ethereum", "base", "robinhood", "eip155:1", "eip155:8453", "eip155:4663"}
78
78
  if chain == "solana":
79
79
  return normalized in {"mainnet", "solana:5eykt4usfv8p8njdtrepy1vzkqzkvdp"}
80
80
  return normalized == "mainnet"
@@ -101,9 +101,9 @@ class OpenClawWalletAdapter:
101
101
  }
102
102
  network = aliases.get(network, network)
103
103
  if network in {"sepolia", "base-sepolia", "base_sepolia"}:
104
- raise WalletBackendError("EVM testnets are no longer supported. Use ethereum or base.")
105
- if network not in {"ethereum", "base"}:
106
- raise WalletBackendError("EVM network must be 'ethereum' or 'base'.")
104
+ raise WalletBackendError("EVM testnets are no longer supported. Use ethereum, base, or robinhood.")
105
+ if network not in {"ethereum", "base", "robinhood"}:
106
+ raise WalletBackendError("EVM network must be 'ethereum', 'base', or 'robinhood'.")
107
107
  return network
108
108
 
109
109
  def _resolve_backend_for_args(self, args: dict[str, Any]) -> AgentWalletBackend:
@@ -386,7 +386,7 @@ class OpenClawWalletAdapter:
386
386
  elif scope == autonomous_permissions.DEFI_TOOLS_SCOPE:
387
387
  if network not in autonomous_permissions.DEFI_TOOLS_NETWORKS:
388
388
  raise WalletBackendError(
389
- "Autonomous DeFi permission only applies on ethereum or base. "
389
+ "Autonomous DeFi permission only applies on ethereum, base, or robinhood. "
390
390
  "Use set_evm_network or the network parameter to select a supported network."
391
391
  )
392
392
  if not autonomous_permissions.is_defi_tools_approved():
@@ -1178,7 +1178,7 @@ class OpenClawWalletAdapter:
1178
1178
  "properties": {
1179
1179
  "network": {
1180
1180
  "type": "string",
1181
- "enum": ["ethereum", "base"],
1181
+ "enum": ["ethereum", "base", "robinhood"],
1182
1182
  "description": "Optional EVM network override for this request.",
1183
1183
  },
1184
1184
  },
@@ -1195,7 +1195,7 @@ class OpenClawWalletAdapter:
1195
1195
  "properties": {
1196
1196
  "network": {
1197
1197
  "type": "string",
1198
- "enum": ["ethereum", "base"],
1198
+ "enum": ["ethereum", "base", "robinhood"],
1199
1199
  "description": "Optional EVM network override for this request.",
1200
1200
  },
1201
1201
  },
@@ -1220,7 +1220,7 @@ class OpenClawWalletAdapter:
1220
1220
  },
1221
1221
  "network": {
1222
1222
  "type": "string",
1223
- "enum": ["ethereum", "base"],
1223
+ "enum": ["ethereum", "base", "robinhood"],
1224
1224
  "description": "Optional EVM network override for this request.",
1225
1225
  },
1226
1226
  },
@@ -1301,7 +1301,7 @@ class OpenClawWalletAdapter:
1301
1301
  "properties": {
1302
1302
  "network": {
1303
1303
  "type": "string",
1304
- "enum": ["ethereum", "base"],
1304
+ "enum": ["ethereum", "base", "robinhood"],
1305
1305
  "description": "Optional EVM network override for this request.",
1306
1306
  },
1307
1307
  },
@@ -1314,7 +1314,7 @@ class OpenClawWalletAdapter:
1314
1314
  name="set_evm_network",
1315
1315
  description=(
1316
1316
  "Select the active EVM network for subsequent wallet tool calls in this "
1317
- "runtime session. Use this to switch between ethereum and base instead "
1317
+ "runtime session. Use this to switch between ethereum, base, and robinhood instead "
1318
1318
  "of editing code or plugin configuration."
1319
1319
  ),
1320
1320
  input_schema={
@@ -1322,7 +1322,7 @@ class OpenClawWalletAdapter:
1322
1322
  "properties": {
1323
1323
  "network": {
1324
1324
  "type": "string",
1325
- "enum": ["ethereum", "base"],
1325
+ "enum": ["ethereum", "base", "robinhood"],
1326
1326
  "description": "EVM network to make active for subsequent calls.",
1327
1327
  },
1328
1328
  },
@@ -1344,7 +1344,7 @@ class OpenClawWalletAdapter:
1344
1344
  },
1345
1345
  "network": {
1346
1346
  "type": "string",
1347
- "enum": ["ethereum", "base"],
1347
+ "enum": ["ethereum", "base", "robinhood"],
1348
1348
  "description": "Optional EVM network override for this request.",
1349
1349
  },
1350
1350
  },
@@ -1366,7 +1366,7 @@ class OpenClawWalletAdapter:
1366
1366
  },
1367
1367
  "network": {
1368
1368
  "type": "string",
1369
- "enum": ["ethereum", "base"],
1369
+ "enum": ["ethereum", "base", "robinhood"],
1370
1370
  "description": "Optional EVM network override for this request.",
1371
1371
  },
1372
1372
  },
@@ -1384,7 +1384,7 @@ class OpenClawWalletAdapter:
1384
1384
  "properties": {
1385
1385
  "network": {
1386
1386
  "type": "string",
1387
- "enum": ["ethereum", "base"],
1387
+ "enum": ["ethereum", "base", "robinhood"],
1388
1388
  "description": "Optional EVM network override for this request.",
1389
1389
  },
1390
1390
  },
@@ -1405,7 +1405,7 @@ class OpenClawWalletAdapter:
1405
1405
  },
1406
1406
  "network": {
1407
1407
  "type": "string",
1408
- "enum": ["ethereum", "base"],
1408
+ "enum": ["ethereum", "base", "robinhood"],
1409
1409
  "description": "Optional EVM network override for this request.",
1410
1410
  },
1411
1411
  },
@@ -1438,7 +1438,7 @@ class OpenClawWalletAdapter:
1438
1438
  "approval_token": {"type": "string"},
1439
1439
  "network": {
1440
1440
  "type": "string",
1441
- "enum": ["ethereum", "base"],
1441
+ "enum": ["ethereum", "base", "robinhood"],
1442
1442
  "description": "Optional EVM network override for this request.",
1443
1443
  },
1444
1444
  },
@@ -1473,7 +1473,7 @@ class OpenClawWalletAdapter:
1473
1473
  "approval_token": {"type": "string"},
1474
1474
  "network": {
1475
1475
  "type": "string",
1476
- "enum": ["ethereum", "base"],
1476
+ "enum": ["ethereum", "base", "robinhood"],
1477
1477
  "description": "Optional EVM network override for this request.",
1478
1478
  },
1479
1479
  },
@@ -2175,7 +2175,7 @@ class OpenClawWalletAdapter:
2175
2175
  name="get_uniswap_swap_quote",
2176
2176
  description=(
2177
2177
  "Get a read-only Uniswap Trading API quote (CLASSIC routing) for an ERC-20 or native ETH swap "
2178
- "on ethereum or base. Supports full EIP-712 Permit2 path for ERC-20 inputs. "
2178
+ "on ethereum, base, or robinhood. Supports full EIP-712 Permit2 path for ERC-20 inputs. "
2179
2179
  "This does not approve, sign, or execute a swap."
2180
2180
  ),
2181
2181
  input_schema={
@@ -2199,8 +2199,8 @@ class OpenClawWalletAdapter:
2199
2199
  },
2200
2200
  "network": {
2201
2201
  "type": "string",
2202
- "enum": ["ethereum", "base"],
2203
- "description": "EVM network. Uniswap Trading API supports ethereum and base only.",
2202
+ "enum": ["ethereum", "base", "robinhood"],
2203
+ "description": "EVM network. Uniswap Trading API supports ethereum, base, and robinhood.",
2204
2204
  },
2205
2205
  },
2206
2206
  "required": ["token_in", "token_out", "amount_in_raw"],
@@ -2216,7 +2216,7 @@ class OpenClawWalletAdapter:
2216
2216
  name="swap_evm_uniswap_tokens",
2217
2217
  description=(
2218
2218
  "Preview, prepare, or execute an ERC-20 or native ETH swap through the Uniswap Trading API "
2219
- "(CLASSIC routing) on ethereum or base. ERC-20 inputs use Permit2 EIP-712 signing automatically. "
2219
+ "(CLASSIC routing) on ethereum, base, or robinhood. ERC-20 inputs use Permit2 EIP-712 signing automatically. "
2220
2220
  "Prepare returns an execution plan only. Execute requires a host-issued approval token bound to the previewed operation "
2221
2221
  "unless the high-trust autonomous permission group is enabled."
2222
2222
  ),
@@ -2248,8 +2248,8 @@ class OpenClawWalletAdapter:
2248
2248
  "approval_token": {"type": "string"},
2249
2249
  "network": {
2250
2250
  "type": "string",
2251
- "enum": ["ethereum", "base"],
2252
- "description": "EVM network. Uniswap Trading API supports ethereum and base only.",
2251
+ "enum": ["ethereum", "base", "robinhood"],
2252
+ "description": "EVM network. Uniswap Trading API supports ethereum, base, and robinhood.",
2253
2253
  },
2254
2254
  },
2255
2255
  "required": ["token_in", "token_out", "amount_in_raw", "mode", "purpose"],
@@ -142,7 +142,7 @@ _PRICE_CACHE: dict[str, tuple[float, float]] = {}
142
142
 
143
143
  def _normalize_network(network: str) -> str:
144
144
  normalized = str(network or "").strip().lower()
145
- if normalized not in {"ethereum", "base"}:
145
+ if normalized not in {"ethereum", "base", "robinhood"}:
146
146
  raise ProviderError("evm-portfolio", f"Unsupported EVM portfolio network: {network}")
147
147
  return normalized
148
148
 
@@ -207,7 +207,7 @@ async def _gateway_rpc_call(network: str, method: str, params: list[Any]) -> dic
207
207
  if not gateway_url:
208
208
  raise ProviderError(
209
209
  "evm-portfolio",
210
- "Provider gateway URL is required for EVM portfolio lookup on ethereum/base.",
210
+ "Provider gateway URL is required for EVM portfolio lookup on ethereum/base/robinhood.",
211
211
  )
212
212
  try:
213
213
  response = await client.post(
@@ -668,8 +668,8 @@ class WdkEvmLocalWalletBackend(AgentWalletBackend):
668
668
  "configured_network": self.network,
669
669
  "service_active_network": str(data.get("activeNetwork") or "").strip() or None,
670
670
  "available_networks": sorted(str(key) for key in profiles.keys()),
671
- "agent_selectable_networks": ["ethereum", "base"],
672
- "swap_supported_networks": ["ethereum", "base"],
671
+ "agent_selectable_networks": ["ethereum", "base", "robinhood"],
672
+ "swap_supported_networks": ["ethereum", "base", "robinhood"],
673
673
  "network_profiles": {
674
674
  str(network): {
675
675
  **dict(profile),
@@ -2,7 +2,7 @@
2
2
  "id": "agent-wallet",
3
3
  "name": "Agent Wallet",
4
4
  "description": "Plugin-friendly wallet backend for OpenClaw agents with safe wallet tools and runtime instructions across Solana, local BTC, and local EVM.",
5
- "version": "0.1.75",
5
+ "version": "0.1.77",
6
6
  "skills": ["skills/wallet-operator"],
7
7
  "configSchema": {
8
8
  "type": "object",
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "openclaw-agent-wallet"
7
- version = "0.1.75"
7
+ version = "0.1.77"
8
8
  description = "Plugin-friendly wallet backend for OpenClaw agents"
9
9
  requires-python = ">=3.10"
10
10
  dependencies = [
@@ -23,7 +23,7 @@ Use this skill before calling OpenClaw wallet tools. It is the routing guide for
23
23
  ## Provider Map
24
24
 
25
25
  - Solana same-chain swap: `swap_solana_tokens` via Jupiter.
26
- - EVM same-chain swap: `swap_evm_tokens` via Velora, ERC-20 to ERC-20 on `ethereum` or `base`.
26
+ - EVM same-chain swap: `swap_evm_tokens` via Velora, ERC-20 to ERC-20 on `ethereum` or `base`; `swap_evm_uniswap_tokens` via Uniswap Trading API on `ethereum`, `base`, or `robinhood`.
27
27
  - Cross-chain Solana -> EVM: `swap_solana_lifi_cross_chain_tokens` via LI.FI.
28
28
  - Cross-chain EVM -> EVM/Solana: `swap_evm_lifi_cross_chain_tokens` via LI.FI.
29
29
  - SOL/SPL transfers: `transfer_sol`, `transfer_spl_token`.
@@ -97,6 +97,9 @@ Use this skill before calling OpenClaw wallet tools. It is the routing guide for
97
97
  - EVM same-chain Velora swap: `swap_evm_tokens`
98
98
  - Params: `token_in`, `token_out`, `amount_in_raw` base-unit string, `mode`, `purpose`, optional `network`.
99
99
  - Current intended path is ERC-20 to ERC-20 on `ethereum` or `base`.
100
+ - EVM same-chain Uniswap swap: `swap_evm_uniswap_tokens`
101
+ - Params: `token_in`, `token_out`, `amount_in_raw` base-unit string, `mode`, `purpose`, optional `network`.
102
+ - Current intended path supports `ethereum`, `base`, and `robinhood` with CLASSIC routing.
100
103
  - EVM swap quote only: `get_evm_swap_quote`
101
104
  - Params: `token_in`, `token_out`, `amount_in_raw`, optional `network`.
102
105
 
@@ -0,0 +1,185 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { spawnSync } from "node:child_process";
4
+
5
+ function readEnv(pathname) {
6
+ try {
7
+ const result = {};
8
+ for (const line of fs.readFileSync(pathname, "utf8").split(/\r?\n/)) {
9
+ const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
10
+ if (match) result[match[1]] = match[2];
11
+ }
12
+ return result;
13
+ } catch (error) {
14
+ if (error?.code === "ENOENT") return {};
15
+ throw error;
16
+ }
17
+ }
18
+
19
+ function readText(pathname) {
20
+ try {
21
+ return fs.readFileSync(pathname, "utf8");
22
+ } catch (error) {
23
+ if (error?.code === "ENOENT") return "";
24
+ throw error;
25
+ }
26
+ }
27
+
28
+ function writeSecret(pathname, value) {
29
+ fs.mkdirSync(path.dirname(pathname), { recursive: true });
30
+ fs.writeFileSync(pathname, `${String(value || "").trim()}\n`, { mode: 0o600 });
31
+ try {
32
+ fs.chmodSync(pathname, 0o600);
33
+ } catch {
34
+ // Best effort on filesystems without POSIX modes.
35
+ }
36
+ }
37
+
38
+ export function createBootKeyManager({
39
+ runtimeBase,
40
+ openclawHome,
41
+ currentRuntimeRoot,
42
+ resolveVenvPython,
43
+ expandHome,
44
+ bridgeTimeoutMs,
45
+ env = process.env,
46
+ }) {
47
+ const defaultFile = path.join(runtimeBase, "boot-key");
48
+
49
+ function currentKey() {
50
+ const root = currentRuntimeRoot();
51
+ if (!root) return "";
52
+ return readEnv(path.join(root, "agent-wallet", ".env")).AGENT_WALLET_BOOT_KEY || "";
53
+ }
54
+
55
+ function configuredFileKey() {
56
+ const value = String(env.AGENT_WALLET_BOOT_KEY_FILE || "").trim();
57
+ return value ? readText(path.resolve(expandHome(value))).trim() : "";
58
+ }
59
+
60
+ function ensureFile() {
61
+ const configured = String(env.AGENT_WALLET_BOOT_KEY_FILE || "").trim();
62
+ const keyFile = configured ? path.resolve(expandHome(configured)) : defaultFile;
63
+ if (readText(keyFile).trim()) return { path: keyFile, status: "existing" };
64
+ const key = String(env.AGENT_WALLET_BOOT_KEY || "").trim() || configuredFileKey() || currentKey();
65
+ if (!key) return { path: keyFile, status: "missing" };
66
+ writeSecret(keyFile, key);
67
+ return { path: keyFile, status: "created" };
68
+ }
69
+
70
+ function readKeystore() {
71
+ const root = currentRuntimeRoot();
72
+ if (!root) return "";
73
+ const python = resolveVenvPython(root);
74
+ if (!python) return "";
75
+ try {
76
+ const result = spawnSync(
77
+ python,
78
+ ["-c", "from agent_wallet.config import read_boot_key_from_keystore as r; print(r())"],
79
+ {
80
+ cwd: path.join(root, "agent-wallet"),
81
+ encoding: "utf8",
82
+ timeout: bridgeTimeoutMs,
83
+ env: { ...env, OPENCLAW_HOME: openclawHome },
84
+ },
85
+ );
86
+ return result.status === 0 ? String(result.stdout || "").trim() : "";
87
+ } catch {
88
+ return "";
89
+ }
90
+ }
91
+
92
+ function resolveFromRuntime() {
93
+ const root = currentRuntimeRoot();
94
+ if (!root) return { supported: false, key: "" };
95
+ const python = resolveVenvPython(root);
96
+ if (!python) return { supported: false, key: "" };
97
+ try {
98
+ const result = spawnSync(
99
+ python,
100
+ ["-c", "from agent_wallet.config import resolve_boot_key_for_installer as r; print(r())"],
101
+ {
102
+ cwd: path.join(root, "agent-wallet"),
103
+ encoding: "utf8",
104
+ timeout: bridgeTimeoutMs,
105
+ env: { ...env, OPENCLAW_HOME: openclawHome },
106
+ },
107
+ );
108
+ return result.status === 0
109
+ ? { supported: true, key: String(result.stdout || "").trim() }
110
+ : { supported: false, key: "" };
111
+ } catch {
112
+ return { supported: false, key: "" };
113
+ }
114
+ }
115
+
116
+ function resolveLegacy() {
117
+ for (const [source, key] of [
118
+ ["legacy_keystore", readKeystore()],
119
+ ["current_runtime_env", currentKey()],
120
+ ["configured_file", configuredFileKey()],
121
+ ["default_file", readText(defaultFile).trim()],
122
+ ]) {
123
+ if (key) return { key, source };
124
+ }
125
+ return { key: "", source: "none" };
126
+ }
127
+
128
+ function provision(releaseRoot, bootKey) {
129
+ const key = String(bootKey || "").trim();
130
+ if (!key) return false;
131
+ const python = resolveVenvPython(releaseRoot);
132
+ if (!python) return false;
133
+ try {
134
+ const result = spawnSync(
135
+ python,
136
+ ["-m", "agent_wallet.openclaw_cli", "boot-key-import", "--key-stdin"],
137
+ {
138
+ cwd: path.join(releaseRoot, "agent-wallet"),
139
+ input: key,
140
+ encoding: "utf8",
141
+ timeout: bridgeTimeoutMs,
142
+ env: { ...env, OPENCLAW_HOME: openclawHome },
143
+ },
144
+ );
145
+ return result.status === 0;
146
+ } catch {
147
+ return false;
148
+ }
149
+ }
150
+
151
+ function verifyWithRuntime(releaseRoot) {
152
+ const sealedPath = path.join(openclawHome, "sealed_keys.json");
153
+ if (!fs.existsSync(sealedPath)) return { ok: true, required: false };
154
+ const key = String(env.AGENT_WALLET_BOOT_KEY || "").trim();
155
+ if (!key) return { ok: false, required: true, error: "no verified boot key is available" };
156
+ const python =
157
+ env.AGENT_WALLET_PYTHON ||
158
+ env.OPENCLAW_AGENT_WALLET_PYTHON ||
159
+ resolveVenvPython(releaseRoot);
160
+ if (!python) {
161
+ if (String(env.AGENT_WALLET_VERIFY_DISABLE || "") === "1") {
162
+ return { ok: true, required: true, skipped: true };
163
+ }
164
+ return { ok: false, required: true, error: "staged Python runtime is unavailable" };
165
+ }
166
+ const result = spawnSync(
167
+ python,
168
+ [
169
+ "-c",
170
+ "from agent_wallet.sealed_keys import unseal_keys; import os; unseal_keys(os.environ['AGENT_WALLET_BOOT_KEY']); print('ok')",
171
+ ],
172
+ {
173
+ cwd: path.join(releaseRoot, "agent-wallet"),
174
+ encoding: "utf8",
175
+ timeout: bridgeTimeoutMs,
176
+ env: { ...env, OPENCLAW_HOME: openclawHome },
177
+ },
178
+ );
179
+ return result.status === 0
180
+ ? { ok: true, required: true }
181
+ : { ok: false, required: true, error: "selected boot key does not unlock sealed wallet state" };
182
+ }
183
+
184
+ return { ensureFile, resolveFromRuntime, resolveLegacy, provision, verifyWithRuntime };
185
+ }