@agentlayer.tech/wallet 0.1.13 → 0.1.15
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/AGENTS.md +10 -1
- package/.openclaw/extensions/agent-wallet/README.md +20 -0
- package/.openclaw/extensions/agent-wallet/dist/index.js +1963 -0
- package/.openclaw/extensions/agent-wallet/index.ts +8 -1
- package/.openclaw/extensions/agent-wallet/package.json +44 -5
- package/.openclaw/extensions/pay-bridge/README.md +38 -0
- package/.openclaw/extensions/pay-bridge/core.mjs +287 -0
- package/.openclaw/extensions/pay-bridge/dist/core.mjs +287 -0
- package/.openclaw/extensions/pay-bridge/dist/index.js +196 -0
- package/.openclaw/extensions/pay-bridge/index.ts +196 -0
- package/.openclaw/extensions/pay-bridge/openclaw.plugin.json +34 -0
- package/.openclaw/extensions/pay-bridge/package.json +49 -0
- package/.openclaw/extensions/pay-bridge/skills/pay-operator/SKILL.md +20 -0
- package/.openclaw/extensions/pay-bridge/smoke_pay_bridge.mjs +38 -0
- package/CHANGELOG.md +29 -0
- package/README.md +33 -1
- package/RELEASING.md +70 -0
- package/agent-wallet/README.md +14 -0
- package/agent-wallet/pyproject.toml +1 -1
- package/agent-wallet/scripts/install_openclaw_local_config.py +53 -2
- package/package.json +5 -2
|
@@ -0,0 +1,1963 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
const PLUGIN_ID = "agent-wallet";
|
|
10
|
+
const PLUGIN_ROOT = path.dirname(new URL(import.meta.url).pathname);
|
|
11
|
+
let selectedWalletBackend = null;
|
|
12
|
+
let selectedSolanaNetwork = null;
|
|
13
|
+
let selectedEvmNetwork = null;
|
|
14
|
+
let selectedBtcNetwork = null;
|
|
15
|
+
const PREVIEW_CACHE_TTL_MS = 15 * 60 * 1000;
|
|
16
|
+
const PRIVATE_SWAP_CACHE_TTL_MS = 35 * 60 * 1000;
|
|
17
|
+
const PREVIEW_BOUND_SWAP_TOOLS = new Set(["swap_solana_tokens", "swap_solana_privately"]);
|
|
18
|
+
const PRIVATE_SWAP_APPROVAL_TOOL_NAME = "swap_solana_privately";
|
|
19
|
+
const approvalPreviewCache = new Map();
|
|
20
|
+
const privateSwapOrderCache = new Map();
|
|
21
|
+
const WALLET_TOOL_ONLY_GUIDANCE =
|
|
22
|
+
"Use this wallet tool instead of shelling out to solana CLI, spl-token CLI, curl, or exec. If it fails, surface the wallet-tool error and stop rather than falling back to terminal commands.";
|
|
23
|
+
|
|
24
|
+
function canonicalJsonText(payload) {
|
|
25
|
+
const normalize = (value) => {
|
|
26
|
+
if (Array.isArray(value)) {
|
|
27
|
+
return value.map(normalize);
|
|
28
|
+
}
|
|
29
|
+
if (value && typeof value === "object") {
|
|
30
|
+
return Object.fromEntries(
|
|
31
|
+
Object.keys(value)
|
|
32
|
+
.sort()
|
|
33
|
+
.map((key) => [key, normalize(value[key])])
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
};
|
|
38
|
+
return JSON.stringify(normalize(payload));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function previewDigest(preview) {
|
|
42
|
+
return crypto.createHash("sha256").update(canonicalJsonText(preview), "utf8").digest("hex");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function approvalCacheKey(userId, toolName) {
|
|
46
|
+
return `${userId}::${toolName}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function pruneApprovalPreviewCache() {
|
|
50
|
+
const now = Date.now();
|
|
51
|
+
for (const [key, value] of approvalPreviewCache.entries()) {
|
|
52
|
+
if (!value || typeof value !== "object" || Number(value.expiresAt || 0) <= now) {
|
|
53
|
+
approvalPreviewCache.delete(key);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function cachePreviewForApproval(userId, toolName, payload) {
|
|
59
|
+
if (!payload || payload.ok !== true || !payload.data || typeof payload.data !== "object") return;
|
|
60
|
+
const preview = payload.data;
|
|
61
|
+
if (preview.mode !== "preview") return;
|
|
62
|
+
if (!preview.confirmation_summary || typeof preview.confirmation_summary !== "object") return;
|
|
63
|
+
pruneApprovalPreviewCache();
|
|
64
|
+
const digest = previewDigest(preview);
|
|
65
|
+
approvalPreviewCache.set(approvalCacheKey(userId, toolName), {
|
|
66
|
+
digest,
|
|
67
|
+
expiresAt:
|
|
68
|
+
toolName === "swap_solana_privately"
|
|
69
|
+
? Date.now() + PRIVATE_SWAP_CACHE_TTL_MS
|
|
70
|
+
: Date.now() + PREVIEW_CACHE_TTL_MS,
|
|
71
|
+
preview,
|
|
72
|
+
summary: preview.confirmation_summary,
|
|
73
|
+
});
|
|
74
|
+
if (toolName === "swap_solana_privately") {
|
|
75
|
+
privateSwapOrderCache.delete(approvalCacheKey(userId, toolName));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function latestCachedPreview(userId, toolName) {
|
|
80
|
+
pruneApprovalPreviewCache();
|
|
81
|
+
return approvalPreviewCache.get(approvalCacheKey(userId, toolName)) || null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function approvalTokenPreviewDigest(token) {
|
|
85
|
+
if (typeof token !== "string" || !token.includes(".")) return "";
|
|
86
|
+
try {
|
|
87
|
+
const encoded = token.split(".", 1)[0];
|
|
88
|
+
const payload = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
|
|
89
|
+
const summary = payload?.binding?.summary;
|
|
90
|
+
return summary && typeof summary._preview_digest === "string" ? summary._preview_digest : "";
|
|
91
|
+
} catch {
|
|
92
|
+
return "";
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function cachedPreviewForToken(userId, toolName, token) {
|
|
97
|
+
const digest = approvalTokenPreviewDigest(token);
|
|
98
|
+
if (!digest) return null;
|
|
99
|
+
const cached = latestCachedPreview(userId, toolName);
|
|
100
|
+
if (!cached || cached.digest !== digest) return null;
|
|
101
|
+
return cached.preview && typeof cached.preview === "object" ? cached.preview : null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function cachePendingPrivateSwapOrder(userId, toolName, preview, details) {
|
|
105
|
+
if (toolName !== "swap_solana_privately") return;
|
|
106
|
+
if (!preview || typeof preview !== "object") return;
|
|
107
|
+
if (!details || typeof details !== "object") return;
|
|
108
|
+
const houdiniId = typeof details.houdini_id === "string" ? details.houdini_id.trim() : "";
|
|
109
|
+
const depositAddress =
|
|
110
|
+
typeof details.deposit_address === "string" ? details.deposit_address.trim() : "";
|
|
111
|
+
if (!houdiniId || !depositAddress) return;
|
|
112
|
+
privateSwapOrderCache.set(approvalCacheKey(userId, toolName), {
|
|
113
|
+
digest: previewDigest(preview),
|
|
114
|
+
expiresAt: Date.now() + PRIVATE_SWAP_CACHE_TTL_MS,
|
|
115
|
+
order: {
|
|
116
|
+
multi_id: typeof details.multi_id === "string" ? details.multi_id.trim() : null,
|
|
117
|
+
houdini_id: houdiniId,
|
|
118
|
+
deposit_address: depositAddress,
|
|
119
|
+
order: details.order && typeof details.order === "object" ? details.order : {},
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function latestPendingPrivateSwapOrder(userId, toolName, preview) {
|
|
125
|
+
if (toolName !== "swap_solana_privately") return null;
|
|
126
|
+
const cached = privateSwapOrderCache.get(approvalCacheKey(userId, toolName));
|
|
127
|
+
if (!cached || typeof cached !== "object") return null;
|
|
128
|
+
if (Number(cached.expiresAt || 0) <= Date.now()) {
|
|
129
|
+
privateSwapOrderCache.delete(approvalCacheKey(userId, toolName));
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
if (!preview || typeof preview !== "object") return null;
|
|
133
|
+
if (cached.digest !== previewDigest(preview)) return null;
|
|
134
|
+
return cached.order && typeof cached.order === "object" ? cached.order : null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function clearPendingPrivateSwapOrder(userId, toolName) {
|
|
138
|
+
if (toolName !== "swap_solana_privately") return;
|
|
139
|
+
privateSwapOrderCache.delete(approvalCacheKey(userId, toolName));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function formatPrivateSwapPendingOrderError(details) {
|
|
143
|
+
const houdiniId = typeof details?.houdini_id === "string" ? details.houdini_id.trim() : "";
|
|
144
|
+
const multiId = typeof details?.multi_id === "string" ? details.multi_id.trim() : "";
|
|
145
|
+
const depositAddress =
|
|
146
|
+
typeof details?.deposit_address === "string" ? details.deposit_address.trim() : "";
|
|
147
|
+
const orderStatus =
|
|
148
|
+
typeof details?.order_status === "string" ? details.order_status.trim() : "";
|
|
149
|
+
const parts = [
|
|
150
|
+
"Houdini order was created, but the Solana deposit account is not ready yet.",
|
|
151
|
+
];
|
|
152
|
+
if (houdiniId) parts.push(`houdini_id=${houdiniId}`);
|
|
153
|
+
if (multiId) parts.push(`multi_id=${multiId}`);
|
|
154
|
+
if (depositAddress) parts.push(`deposit_address=${depositAddress}`);
|
|
155
|
+
if (orderStatus) parts.push(`status=${orderStatus}`);
|
|
156
|
+
parts.push("Retry execute for this existing order instead of generating a new preview.");
|
|
157
|
+
return parts.join(" ");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function formatPrivateSwapRateLimitError(details) {
|
|
161
|
+
const retryAfter =
|
|
162
|
+
typeof details?.retry_after === "number"
|
|
163
|
+
? details.retry_after
|
|
164
|
+
: typeof details?.retry_after === "string"
|
|
165
|
+
? details.retry_after
|
|
166
|
+
: "";
|
|
167
|
+
const quoteId = typeof details?.quote_id === "string" ? details.quote_id.trim() : "";
|
|
168
|
+
const parts = [
|
|
169
|
+
"Houdini exchange create is rate-limited right now.",
|
|
170
|
+
];
|
|
171
|
+
if (retryAfter !== "") parts.push(`retry_after=${retryAfter}s`);
|
|
172
|
+
if (quoteId) parts.push(`quote_id=${quoteId}`);
|
|
173
|
+
parts.push("Do not generate a new preview yet; wait, then retry execute.");
|
|
174
|
+
return parts.join(" ");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function listPendingPrivateSwapOrders(userId) {
|
|
178
|
+
const key = approvalCacheKey(userId, PRIVATE_SWAP_APPROVAL_TOOL_NAME);
|
|
179
|
+
const pending = privateSwapOrderCache.get(key);
|
|
180
|
+
if (!pending || typeof pending !== "object" || Number(pending.expiresAt || 0) <= Date.now()) {
|
|
181
|
+
privateSwapOrderCache.delete(key);
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
return [
|
|
185
|
+
{
|
|
186
|
+
...(pending.order && typeof pending.order === "object" ? pending.order : {}),
|
|
187
|
+
expires_at_ms: Number(pending.expiresAt || 0),
|
|
188
|
+
},
|
|
189
|
+
];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function resolvePluginConfig(api) {
|
|
193
|
+
const globalConfig = api?.config ?? {};
|
|
194
|
+
const pluginEntry = globalConfig?.plugins?.entries?.[PLUGIN_ID];
|
|
195
|
+
return pluginEntry?.config ?? globalConfig?.config ?? {};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function resolveUserId(api, config) {
|
|
199
|
+
return (
|
|
200
|
+
config.userId ||
|
|
201
|
+
process.env.OPENCLAW_AGENT_WALLET_USER_ID ||
|
|
202
|
+
process.env.USER ||
|
|
203
|
+
"openclaw-main"
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function resolveBackend(api) {
|
|
208
|
+
const config = resolvePluginConfig(api);
|
|
209
|
+
return normalizeWalletBackend(config.backend || process.env.AGENT_WALLET_BACKEND || "solana_local");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function normalizeWalletBackend(value) {
|
|
213
|
+
const normalized = String(value || "").trim().toLowerCase();
|
|
214
|
+
const aliases = {
|
|
215
|
+
sol: "solana_local",
|
|
216
|
+
solana: "solana_local",
|
|
217
|
+
solana_local: "solana_local",
|
|
218
|
+
"solana-local": "solana_local",
|
|
219
|
+
evm: "wdk_evm_local",
|
|
220
|
+
ethereum: "wdk_evm_local",
|
|
221
|
+
eth: "wdk_evm_local",
|
|
222
|
+
base: "wdk_evm_local",
|
|
223
|
+
wdk_evm_local: "wdk_evm_local",
|
|
224
|
+
"wdk-evm-local": "wdk_evm_local",
|
|
225
|
+
evm_local: "wdk_evm_local",
|
|
226
|
+
"evm-local": "wdk_evm_local",
|
|
227
|
+
btc: "wdk_btc_local",
|
|
228
|
+
bitcoin: "wdk_btc_local",
|
|
229
|
+
wdk_btc_local: "wdk_btc_local",
|
|
230
|
+
"wdk-btc-local": "wdk_btc_local",
|
|
231
|
+
btc_local: "wdk_btc_local",
|
|
232
|
+
"btc-local": "wdk_btc_local",
|
|
233
|
+
};
|
|
234
|
+
const backend = aliases[normalized] || normalized;
|
|
235
|
+
if (!["solana_local", "wdk_evm_local", "wdk_btc_local"].includes(backend)) {
|
|
236
|
+
throw new Error("Wallet backend must be solana, evm, base, ethereum, btc, or bitcoin.");
|
|
237
|
+
}
|
|
238
|
+
return backend;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function backendLabel(backend) {
|
|
242
|
+
if (backend === "wdk_evm_local") return "evm";
|
|
243
|
+
if (backend === "wdk_btc_local") return "bitcoin";
|
|
244
|
+
return "solana";
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function normalizeEvmNetwork(value) {
|
|
248
|
+
const normalized = String(value || "").trim().toLowerCase();
|
|
249
|
+
const aliases = {
|
|
250
|
+
mainnet: "ethereum",
|
|
251
|
+
eth: "ethereum",
|
|
252
|
+
"eth-mainnet": "ethereum",
|
|
253
|
+
"base-mainnet": "base",
|
|
254
|
+
base_sepolia: "base-sepolia",
|
|
255
|
+
};
|
|
256
|
+
return aliases[normalized] || normalized;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function normalizeSelectableEvmNetwork(value) {
|
|
260
|
+
const network = normalizeEvmNetwork(value);
|
|
261
|
+
if (!["ethereum", "base"].includes(network)) {
|
|
262
|
+
throw new Error("EVM network must be 'ethereum' or 'base'.");
|
|
263
|
+
}
|
|
264
|
+
return network;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function normalizeSolanaNetwork(value) {
|
|
268
|
+
const network = String(value || "").trim().toLowerCase();
|
|
269
|
+
if (!network) return null;
|
|
270
|
+
const aliases = {
|
|
271
|
+
solana: "mainnet",
|
|
272
|
+
"solana-mainnet": "mainnet",
|
|
273
|
+
mainnet_beta: "mainnet",
|
|
274
|
+
"mainnet-beta": "mainnet",
|
|
275
|
+
};
|
|
276
|
+
const normalized = aliases[network] || network;
|
|
277
|
+
if (!["mainnet", "devnet", "testnet"].includes(normalized)) {
|
|
278
|
+
throw new Error("Solana network must be mainnet, devnet, or testnet.");
|
|
279
|
+
}
|
|
280
|
+
return normalized;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function normalizeBtcNetwork(value) {
|
|
284
|
+
const network = String(value || "").trim().toLowerCase();
|
|
285
|
+
if (!network) return null;
|
|
286
|
+
const aliases = {
|
|
287
|
+
btc: "bitcoin",
|
|
288
|
+
bitcoin_mainnet: "bitcoin",
|
|
289
|
+
"bitcoin-mainnet": "bitcoin",
|
|
290
|
+
mainnet: "bitcoin",
|
|
291
|
+
};
|
|
292
|
+
const normalized = aliases[network] || network;
|
|
293
|
+
if (!["bitcoin", "testnet", "regtest"].includes(normalized)) {
|
|
294
|
+
throw new Error("Bitcoin network must be bitcoin, testnet, or regtest.");
|
|
295
|
+
}
|
|
296
|
+
return normalized;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function defaultSelectableEvmNetwork(api) {
|
|
300
|
+
const config = resolvePluginConfig(api);
|
|
301
|
+
const configured = normalizeEvmNetwork(config.network || process.env.WDK_EVM_NETWORK);
|
|
302
|
+
return ["ethereum", "base"].includes(configured) ? configured : null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function defaultSolanaNetwork(api) {
|
|
306
|
+
const config = resolvePluginConfig(api);
|
|
307
|
+
try {
|
|
308
|
+
return normalizeSolanaNetwork(config.network || process.env.SOLANA_NETWORK) || "mainnet";
|
|
309
|
+
} catch {
|
|
310
|
+
return "mainnet";
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function defaultBtcNetwork(api) {
|
|
315
|
+
const config = resolvePluginConfig(api);
|
|
316
|
+
try {
|
|
317
|
+
return normalizeBtcNetwork(config.network || process.env.WDK_BTC_NETWORK) || "bitcoin";
|
|
318
|
+
} catch {
|
|
319
|
+
return "bitcoin";
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function inferBackendForTool(toolName) {
|
|
324
|
+
if (
|
|
325
|
+
toolName.startsWith("get_evm_") ||
|
|
326
|
+
toolName.startsWith("manage_evm_") ||
|
|
327
|
+
toolName.startsWith("swap_evm_") ||
|
|
328
|
+
toolName.startsWith("transfer_evm_") ||
|
|
329
|
+
toolName === "set_evm_network"
|
|
330
|
+
) {
|
|
331
|
+
return "wdk_evm_local";
|
|
332
|
+
}
|
|
333
|
+
if (toolName.startsWith("get_btc_") || toolName === "transfer_btc") {
|
|
334
|
+
return "wdk_btc_local";
|
|
335
|
+
}
|
|
336
|
+
if (
|
|
337
|
+
toolName.includes("solana") ||
|
|
338
|
+
toolName.includes("jupiter") ||
|
|
339
|
+
toolName.includes("kamino") ||
|
|
340
|
+
toolName.includes("bags") ||
|
|
341
|
+
toolName === "transfer_sol" ||
|
|
342
|
+
toolName === "transfer_spl_token" ||
|
|
343
|
+
toolName === "sign_wallet_message" ||
|
|
344
|
+
toolName === "close_empty_token_accounts" ||
|
|
345
|
+
toolName === "request_devnet_airdrop" ||
|
|
346
|
+
toolName === "get_wallet_portfolio" ||
|
|
347
|
+
toolName === "get_solana_token_prices"
|
|
348
|
+
) {
|
|
349
|
+
return "solana_local";
|
|
350
|
+
}
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function activeBackendForTool(api, toolName) {
|
|
355
|
+
return selectedWalletBackend || inferBackendForTool(toolName) || resolveBackend(api);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function networkForBackend(api, backend) {
|
|
359
|
+
const config = resolvePluginConfig(api);
|
|
360
|
+
if (backend === "wdk_evm_local") {
|
|
361
|
+
return selectedEvmNetwork || defaultSelectableEvmNetwork(api) || "ethereum";
|
|
362
|
+
}
|
|
363
|
+
if (backend === "wdk_btc_local") {
|
|
364
|
+
try {
|
|
365
|
+
return selectedBtcNetwork || defaultBtcNetwork(api);
|
|
366
|
+
} catch {
|
|
367
|
+
return "bitcoin";
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
try {
|
|
371
|
+
return (
|
|
372
|
+
selectedSolanaNetwork ||
|
|
373
|
+
normalizeSolanaNetwork(config.network || process.env.SOLANA_NETWORK) ||
|
|
374
|
+
"mainnet"
|
|
375
|
+
);
|
|
376
|
+
} catch {
|
|
377
|
+
return "mainnet";
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function effectiveConfigForBackend(api, backend) {
|
|
382
|
+
const config = resolvePluginConfig(api);
|
|
383
|
+
return {
|
|
384
|
+
...config,
|
|
385
|
+
backend,
|
|
386
|
+
network: networkForBackend(api, backend),
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function resolvePythonBin(config) {
|
|
391
|
+
return config.pythonBin || process.env.OPENCLAW_AGENT_WALLET_PYTHON || "python3";
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function resolveOpenclawHome(config) {
|
|
395
|
+
return path.resolve(config.openclawHome || process.env.OPENCLAW_HOME || path.join(os.homedir(), ".openclaw"));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function resolvePackageRoot(config) {
|
|
399
|
+
const openclawHome = resolveOpenclawHome(config);
|
|
400
|
+
const candidates = [
|
|
401
|
+
config.packageRoot,
|
|
402
|
+
process.env.OPENCLAW_AGENT_WALLET_PACKAGE_ROOT,
|
|
403
|
+
path.join(openclawHome, "agent-wallet-runtime/current/agent-wallet"),
|
|
404
|
+
path.resolve(PLUGIN_ROOT, "../../../agent-wallet"),
|
|
405
|
+
path.resolve(process.cwd(), "agent-wallet"),
|
|
406
|
+
].filter(Boolean);
|
|
407
|
+
|
|
408
|
+
for (const candidate of candidates) {
|
|
409
|
+
const resolved = path.resolve(candidate);
|
|
410
|
+
if (fs.existsSync(path.join(resolved, "agent_wallet", "__init__.py"))) {
|
|
411
|
+
return resolved;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
throw new Error(
|
|
415
|
+
`Could not resolve agent-wallet package root. Checked ${path.join(openclawHome, "agent-wallet-runtime/current/agent-wallet")} and local workspace fallbacks. Set plugins.entries.agent-wallet.config.packageRoot if your runtime lives elsewhere.`
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function buildCliEnv(packageRoot) {
|
|
420
|
+
const env = { ...process.env };
|
|
421
|
+
env.PYTHONPATH = env.PYTHONPATH
|
|
422
|
+
? `${packageRoot}${path.delimiter}${env.PYTHONPATH}`
|
|
423
|
+
: packageRoot;
|
|
424
|
+
return env;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async function callWalletCli(api, command, extraArgs = [], configOverride = null) {
|
|
428
|
+
const config = configOverride || resolvePluginConfig(api);
|
|
429
|
+
const packageRoot = resolvePackageRoot(config);
|
|
430
|
+
const pythonBin = resolvePythonBin(config);
|
|
431
|
+
const userId = resolveUserId(api, config);
|
|
432
|
+
const args = [
|
|
433
|
+
"-m",
|
|
434
|
+
"agent_wallet.openclaw_cli",
|
|
435
|
+
command,
|
|
436
|
+
"--user-id",
|
|
437
|
+
userId,
|
|
438
|
+
"--config-json",
|
|
439
|
+
JSON.stringify(config),
|
|
440
|
+
...extraArgs,
|
|
441
|
+
];
|
|
442
|
+
|
|
443
|
+
let stdout = "";
|
|
444
|
+
let stderr = "";
|
|
445
|
+
try {
|
|
446
|
+
const result = await execFileAsync(pythonBin, args, {
|
|
447
|
+
cwd: packageRoot,
|
|
448
|
+
env: buildCliEnv(packageRoot),
|
|
449
|
+
maxBuffer: 1024 * 1024 * 8,
|
|
450
|
+
});
|
|
451
|
+
stdout = result.stdout;
|
|
452
|
+
stderr = result.stderr;
|
|
453
|
+
} catch (error) {
|
|
454
|
+
stdout = typeof error?.stdout === "string" ? error.stdout : "";
|
|
455
|
+
stderr = typeof error?.stderr === "string" ? error.stderr : "";
|
|
456
|
+
const stderrText = String(stderr || "").trim();
|
|
457
|
+
if (stderrText) {
|
|
458
|
+
try {
|
|
459
|
+
const payload = JSON.parse(stderrText);
|
|
460
|
+
const wrapped = new Error(payload?.error || "agent-wallet CLI failed");
|
|
461
|
+
if (payload?.code) wrapped.code = payload.code;
|
|
462
|
+
if (payload?.details && typeof payload.details === "object") {
|
|
463
|
+
wrapped.details = payload.details;
|
|
464
|
+
}
|
|
465
|
+
throw wrapped;
|
|
466
|
+
} catch (parseError) {
|
|
467
|
+
if (parseError instanceof Error && parseError !== error) {
|
|
468
|
+
throw parseError;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
throw error;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (stderr && stderr.trim()) {
|
|
476
|
+
api?.logger?.debug?.(`[agent-wallet] stderr: ${stderr.trim()}`);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const payload = JSON.parse(stdout.trim() || "{}");
|
|
480
|
+
if (payload?.ok === false && payload?.error) {
|
|
481
|
+
const wrapped = new Error(payload.error);
|
|
482
|
+
if (payload?.error_code) wrapped.code = payload.error_code;
|
|
483
|
+
if (payload?.error_details && typeof payload.error_details === "object") {
|
|
484
|
+
wrapped.details = payload.error_details;
|
|
485
|
+
}
|
|
486
|
+
throw wrapped;
|
|
487
|
+
}
|
|
488
|
+
return payload;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
async function issueApprovalToken(api, config, userId, toolName, previewPayload) {
|
|
492
|
+
const summary = previewPayload?.confirmation_summary;
|
|
493
|
+
if (!summary || typeof summary !== "object") {
|
|
494
|
+
throw new Error(`No confirmation_summary available for ${toolName}.`);
|
|
495
|
+
}
|
|
496
|
+
const digest = previewDigest(previewPayload);
|
|
497
|
+
const summaryForToken = { ...summary, _preview_digest: digest };
|
|
498
|
+
const extraArgs = [
|
|
499
|
+
"--tool",
|
|
500
|
+
toolName,
|
|
501
|
+
"--summary-json",
|
|
502
|
+
JSON.stringify(summaryForToken),
|
|
503
|
+
];
|
|
504
|
+
if (previewPayload?.is_mainnet === true) {
|
|
505
|
+
extraArgs.push("--mainnet-confirmed");
|
|
506
|
+
}
|
|
507
|
+
if (toolName === "swap_solana_privately") {
|
|
508
|
+
extraArgs.push("--ttl-seconds", "1800");
|
|
509
|
+
}
|
|
510
|
+
const payload = await callWalletCli(api, "issue-approval", extraArgs, config);
|
|
511
|
+
const token = String(payload?.approval_token || "").trim();
|
|
512
|
+
if (!token) {
|
|
513
|
+
throw new Error(`issue-approval did not return an approval_token for ${toolName}.`);
|
|
514
|
+
}
|
|
515
|
+
return token;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function asContent(data) {
|
|
519
|
+
return {
|
|
520
|
+
content: [
|
|
521
|
+
{
|
|
522
|
+
type: "text",
|
|
523
|
+
text: JSON.stringify(data, null, 2),
|
|
524
|
+
},
|
|
525
|
+
],
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function registerTool(api, definition) {
|
|
530
|
+
api.registerTool({
|
|
531
|
+
name: definition.name,
|
|
532
|
+
description: definition.description,
|
|
533
|
+
parameters: definition.parameters,
|
|
534
|
+
returns: {
|
|
535
|
+
type: "object",
|
|
536
|
+
additionalProperties: true,
|
|
537
|
+
},
|
|
538
|
+
optional: Boolean(definition.optional),
|
|
539
|
+
async execute(_id, params = {}) {
|
|
540
|
+
if (definition.name === "get_active_wallet_backend") {
|
|
541
|
+
const configuredBackend = resolveBackend(api);
|
|
542
|
+
const activeBackend = selectedWalletBackend || configuredBackend;
|
|
543
|
+
const activeNetwork = networkForBackend(api, activeBackend);
|
|
544
|
+
return asContent({
|
|
545
|
+
active_backend: activeBackend,
|
|
546
|
+
active_wallet: backendLabel(activeBackend),
|
|
547
|
+
active_network: activeNetwork,
|
|
548
|
+
configured_backend: configuredBackend,
|
|
549
|
+
configured_network: String(resolvePluginConfig(api).network || "").trim() || null,
|
|
550
|
+
session_override_active: Boolean(selectedWalletBackend),
|
|
551
|
+
available_wallets: ["solana", "evm", "bitcoin"],
|
|
552
|
+
usage:
|
|
553
|
+
"Use set_wallet_backend to switch between Solana, EVM, and Bitcoin for this OpenClaw plugin session. Do not edit plugin config for normal wallet switching.",
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (definition.name === "set_wallet_backend") {
|
|
558
|
+
const requestedWallet = String(params?.backend || params?.wallet || "").trim().toLowerCase();
|
|
559
|
+
const backend = normalizeWalletBackend(requestedWallet);
|
|
560
|
+
const impliedNetwork =
|
|
561
|
+
["base", "base-mainnet"].includes(requestedWallet)
|
|
562
|
+
? "base"
|
|
563
|
+
: ["ethereum", "eth", "mainnet", "eth-mainnet"].includes(requestedWallet)
|
|
564
|
+
? "ethereum"
|
|
565
|
+
: null;
|
|
566
|
+
if (backend === "wdk_evm_local") {
|
|
567
|
+
selectedEvmNetwork = normalizeSelectableEvmNetwork(
|
|
568
|
+
params?.network || impliedNetwork || selectedEvmNetwork || defaultSelectableEvmNetwork(api) || "ethereum"
|
|
569
|
+
);
|
|
570
|
+
} else if (backend === "solana_local") {
|
|
571
|
+
selectedSolanaNetwork = normalizeSolanaNetwork(
|
|
572
|
+
params?.network || selectedSolanaNetwork || defaultSolanaNetwork(api)
|
|
573
|
+
);
|
|
574
|
+
} else if (backend === "wdk_btc_local") {
|
|
575
|
+
selectedBtcNetwork = normalizeBtcNetwork(
|
|
576
|
+
params?.network || selectedBtcNetwork || defaultBtcNetwork(api)
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
const configOverride = effectiveConfigForBackend(api, backend);
|
|
580
|
+
const payload = await callWalletCli(api, "invoke", [
|
|
581
|
+
"--tool",
|
|
582
|
+
backend === "wdk_evm_local" ? "get_evm_network" : "get_wallet_capabilities",
|
|
583
|
+
"--arguments-json",
|
|
584
|
+
JSON.stringify({}),
|
|
585
|
+
], configOverride);
|
|
586
|
+
if (payload?.ok === false) {
|
|
587
|
+
throw new Error(payload?.error || "set_wallet_backend failed");
|
|
588
|
+
}
|
|
589
|
+
selectedWalletBackend = backend;
|
|
590
|
+
return asContent({
|
|
591
|
+
selected_backend: backend,
|
|
592
|
+
selected_wallet: backendLabel(backend),
|
|
593
|
+
selected_network: networkForBackend(api, backend),
|
|
594
|
+
configured_backend: resolveBackend(api),
|
|
595
|
+
session_override_active: true,
|
|
596
|
+
config_file_changed: false,
|
|
597
|
+
usage:
|
|
598
|
+
"Subsequent wallet calls in this OpenClaw plugin session use this wallet backend by default. The startup plugin config remains unchanged.",
|
|
599
|
+
data: payload?.data ?? {},
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if (definition.name === "set_evm_network") {
|
|
604
|
+
const network = normalizeSelectableEvmNetwork(params?.network);
|
|
605
|
+
const configOverride = effectiveConfigForBackend(api, "wdk_evm_local");
|
|
606
|
+
configOverride.network = network;
|
|
607
|
+
const payload = await callWalletCli(api, "invoke", [
|
|
608
|
+
"--tool",
|
|
609
|
+
"get_evm_network",
|
|
610
|
+
"--arguments-json",
|
|
611
|
+
JSON.stringify({ network }),
|
|
612
|
+
], configOverride);
|
|
613
|
+
if (payload?.ok === false) {
|
|
614
|
+
throw new Error(payload?.error || "set_evm_network failed");
|
|
615
|
+
}
|
|
616
|
+
selectedWalletBackend = "wdk_evm_local";
|
|
617
|
+
selectedEvmNetwork = network;
|
|
618
|
+
return asContent({
|
|
619
|
+
selected_backend: "wdk_evm_local",
|
|
620
|
+
selected_wallet: "evm",
|
|
621
|
+
selected_network: network,
|
|
622
|
+
session_active_network: network,
|
|
623
|
+
session_override_active: true,
|
|
624
|
+
network_switch_persistent_for_runtime_session: true,
|
|
625
|
+
usage:
|
|
626
|
+
"Subsequent wallet calls in this OpenClaw plugin session use the EVM wallet on this network by default. You can still override a single EVM call with its network parameter.",
|
|
627
|
+
data: payload?.data ?? {},
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (definition.name === "list_pending_solana_private_swaps") {
|
|
632
|
+
return asContent({
|
|
633
|
+
orders: listPendingPrivateSwapOrders(resolveUserId(api, resolvePluginConfig(api))),
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
const effectiveParams = { ...(params ?? {}) };
|
|
638
|
+
const activeBackend = activeBackendForTool(api, definition.name);
|
|
639
|
+
const userId = resolveUserId(api, resolvePluginConfig(api));
|
|
640
|
+
if (
|
|
641
|
+
activeBackend === "wdk_evm_local" &&
|
|
642
|
+
selectedEvmNetwork &&
|
|
643
|
+
definition.parameters?.properties?.network &&
|
|
644
|
+
effectiveParams.network === undefined
|
|
645
|
+
) {
|
|
646
|
+
effectiveParams.network = selectedEvmNetwork;
|
|
647
|
+
}
|
|
648
|
+
const configOverride = effectiveConfigForBackend(api, activeBackend);
|
|
649
|
+
if (activeBackend === "wdk_evm_local" && effectiveParams.network !== undefined) {
|
|
650
|
+
configOverride.network = normalizeSelectableEvmNetwork(effectiveParams.network);
|
|
651
|
+
}
|
|
652
|
+
if (String(effectiveParams.mode || "") === "execute") {
|
|
653
|
+
if (
|
|
654
|
+
PREVIEW_BOUND_SWAP_TOOLS.has(definition.name) &&
|
|
655
|
+
typeof effectiveParams.approval_token === "string" &&
|
|
656
|
+
effectiveParams.approval_token.trim() &&
|
|
657
|
+
effectiveParams._approved_preview === undefined
|
|
658
|
+
) {
|
|
659
|
+
const cachedPreview = cachedPreviewForToken(userId, definition.name, effectiveParams.approval_token);
|
|
660
|
+
if (cachedPreview) {
|
|
661
|
+
effectiveParams._approved_preview = cachedPreview;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (!effectiveParams.approval_token) {
|
|
665
|
+
const cached = latestCachedPreview(userId, definition.name);
|
|
666
|
+
if (cached?.preview && cached?.summary) {
|
|
667
|
+
effectiveParams.approval_token = await issueApprovalToken(
|
|
668
|
+
api,
|
|
669
|
+
configOverride,
|
|
670
|
+
userId,
|
|
671
|
+
definition.name,
|
|
672
|
+
cached.preview
|
|
673
|
+
);
|
|
674
|
+
if (PREVIEW_BOUND_SWAP_TOOLS.has(definition.name) && effectiveParams._approved_preview === undefined) {
|
|
675
|
+
effectiveParams._approved_preview = cached.preview;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
if (definition.name === "continue_solana_private_swap") {
|
|
681
|
+
const cached = latestCachedPreview(userId, PRIVATE_SWAP_APPROVAL_TOOL_NAME);
|
|
682
|
+
if (cached?.preview && effectiveParams._approved_preview === undefined) {
|
|
683
|
+
effectiveParams._approved_preview = cached.preview;
|
|
684
|
+
}
|
|
685
|
+
if (!effectiveParams.approval_token && cached?.preview && cached?.summary) {
|
|
686
|
+
effectiveParams.approval_token = await issueApprovalToken(
|
|
687
|
+
api,
|
|
688
|
+
configOverride,
|
|
689
|
+
userId,
|
|
690
|
+
PRIVATE_SWAP_APPROVAL_TOOL_NAME,
|
|
691
|
+
cached.preview
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
if (effectiveParams._resume_private_swap_order === undefined && cached?.preview) {
|
|
695
|
+
const pendingOrder = latestPendingPrivateSwapOrder(
|
|
696
|
+
userId,
|
|
697
|
+
PRIVATE_SWAP_APPROVAL_TOOL_NAME,
|
|
698
|
+
cached.preview
|
|
699
|
+
);
|
|
700
|
+
if (pendingOrder) {
|
|
701
|
+
if (
|
|
702
|
+
effectiveParams.houdini_id &&
|
|
703
|
+
pendingOrder.houdini_id &&
|
|
704
|
+
String(effectiveParams.houdini_id).trim() !== String(pendingOrder.houdini_id).trim()
|
|
705
|
+
) {
|
|
706
|
+
throw new Error("The requested houdini_id does not match the cached pending private swap order.");
|
|
707
|
+
}
|
|
708
|
+
effectiveParams._resume_private_swap_order = pendingOrder;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
const executeWalletTool = async () =>
|
|
713
|
+
callWalletCli(api, "invoke", [
|
|
714
|
+
"--tool",
|
|
715
|
+
definition.name,
|
|
716
|
+
"--arguments-json",
|
|
717
|
+
JSON.stringify(effectiveParams),
|
|
718
|
+
], configOverride);
|
|
719
|
+
|
|
720
|
+
let payload;
|
|
721
|
+
if (definition.name === "swap_solana_privately" && String(effectiveParams.mode || "") === "execute") {
|
|
722
|
+
const approvedPreview =
|
|
723
|
+
effectiveParams._approved_preview && typeof effectiveParams._approved_preview === "object"
|
|
724
|
+
? effectiveParams._approved_preview
|
|
725
|
+
: null;
|
|
726
|
+
const pendingOrder = approvedPreview
|
|
727
|
+
? latestPendingPrivateSwapOrder(userId, definition.name, approvedPreview)
|
|
728
|
+
: null;
|
|
729
|
+
if (pendingOrder && effectiveParams._resume_private_swap_order === undefined) {
|
|
730
|
+
effectiveParams._resume_private_swap_order = pendingOrder;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
let remainingRetries = 3;
|
|
734
|
+
while (true) {
|
|
735
|
+
try {
|
|
736
|
+
payload = await executeWalletTool();
|
|
737
|
+
const executionState = payload?.data?.execution_state;
|
|
738
|
+
if (executionState === "awaiting_deposit_funding" && approvedPreview) {
|
|
739
|
+
cachePendingPrivateSwapOrder(userId, definition.name, approvedPreview, payload.data);
|
|
740
|
+
} else {
|
|
741
|
+
clearPendingPrivateSwapOrder(userId, definition.name);
|
|
742
|
+
}
|
|
743
|
+
break;
|
|
744
|
+
} catch (error) {
|
|
745
|
+
const errorCode = typeof error?.code === "string" ? error.code : "";
|
|
746
|
+
const errorDetails =
|
|
747
|
+
error?.details && typeof error.details === "object" ? error.details : null;
|
|
748
|
+
if (
|
|
749
|
+
(errorCode === "houdini_deposit_not_ready" ||
|
|
750
|
+
errorCode === "houdini_order_initializing_timeout") &&
|
|
751
|
+
approvedPreview &&
|
|
752
|
+
errorDetails &&
|
|
753
|
+
remainingRetries > 0
|
|
754
|
+
) {
|
|
755
|
+
cachePendingPrivateSwapOrder(userId, definition.name, approvedPreview, errorDetails);
|
|
756
|
+
effectiveParams._resume_private_swap_order =
|
|
757
|
+
latestPendingPrivateSwapOrder(userId, definition.name, approvedPreview) || undefined;
|
|
758
|
+
remainingRetries -= 1;
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
if (
|
|
762
|
+
(errorCode === "houdini_deposit_not_ready" ||
|
|
763
|
+
errorCode === "houdini_order_initializing_timeout") &&
|
|
764
|
+
errorDetails
|
|
765
|
+
) {
|
|
766
|
+
cachePendingPrivateSwapOrder(userId, definition.name, approvedPreview, errorDetails);
|
|
767
|
+
throw new Error(formatPrivateSwapPendingOrderError(errorDetails));
|
|
768
|
+
}
|
|
769
|
+
if (errorCode === "houdini_exchange_rate_limited" && errorDetails) {
|
|
770
|
+
throw new Error(formatPrivateSwapRateLimitError(errorDetails));
|
|
771
|
+
}
|
|
772
|
+
throw error;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
} else if (definition.name === "continue_solana_private_swap") {
|
|
776
|
+
payload = await executeWalletTool();
|
|
777
|
+
if (payload?.data?.execution_state === "funding_submitted") {
|
|
778
|
+
clearPendingPrivateSwapOrder(userId, PRIVATE_SWAP_APPROVAL_TOOL_NAME);
|
|
779
|
+
}
|
|
780
|
+
} else {
|
|
781
|
+
payload = await executeWalletTool();
|
|
782
|
+
}
|
|
783
|
+
cachePreviewForApproval(userId, definition.name, payload);
|
|
784
|
+
if (payload?.ok === false) {
|
|
785
|
+
throw new Error(payload?.error || `${definition.name} failed`);
|
|
786
|
+
}
|
|
787
|
+
return asContent(payload?.data ?? {});
|
|
788
|
+
},
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
const walletSessionToolDefinitions = [
|
|
793
|
+
{
|
|
794
|
+
name: "get_wallet_capabilities",
|
|
795
|
+
description: "Describe the active wallet backend, chain, network, address, and safety limits. Use set_wallet_backend to switch between Solana, EVM, and Bitcoin instead of editing plugin config.",
|
|
796
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
797
|
+
},
|
|
798
|
+
{
|
|
799
|
+
name: "get_wallet_address",
|
|
800
|
+
description: "Return the active wallet address for the current session-selected backend.",
|
|
801
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
name: "get_wallet_balance",
|
|
805
|
+
description: `Get the active wallet overview. Solana and EVM return native assets, discovered token balances, per-asset USD values when available, and total_value_usd. Use set_wallet_backend first when the user asks to switch wallets. ${WALLET_TOOL_ONLY_GUIDANCE}`,
|
|
806
|
+
parameters: {
|
|
807
|
+
type: "object",
|
|
808
|
+
properties: {
|
|
809
|
+
address: {
|
|
810
|
+
type: "string",
|
|
811
|
+
description: "Optional wallet address override.",
|
|
812
|
+
},
|
|
813
|
+
},
|
|
814
|
+
additionalProperties: false,
|
|
815
|
+
},
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
name: "get_active_wallet_backend",
|
|
819
|
+
description: "Show which wallet backend is active in this OpenClaw plugin session and whether it differs from the startup plugin config.",
|
|
820
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
name: "set_wallet_backend",
|
|
824
|
+
description:
|
|
825
|
+
"Switch the active wallet backend for this OpenClaw plugin session. Use this for user requests like 'switch to EVM wallet', 'use Base', 'switch back to Solana', or 'use Bitcoin'. This does not edit code, environment variables, or plugin config.",
|
|
826
|
+
parameters: {
|
|
827
|
+
type: "object",
|
|
828
|
+
properties: {
|
|
829
|
+
backend: {
|
|
830
|
+
type: "string",
|
|
831
|
+
enum: ["solana", "sol", "evm", "ethereum", "base", "bitcoin", "btc"],
|
|
832
|
+
description: "Wallet backend or common alias to make active.",
|
|
833
|
+
},
|
|
834
|
+
network: {
|
|
835
|
+
type: "string",
|
|
836
|
+
description: "Optional network for the selected wallet. Examples: mainnet, devnet, ethereum, base, bitcoin, testnet.",
|
|
837
|
+
},
|
|
838
|
+
},
|
|
839
|
+
required: ["backend"],
|
|
840
|
+
additionalProperties: false,
|
|
841
|
+
},
|
|
842
|
+
},
|
|
843
|
+
];
|
|
844
|
+
|
|
845
|
+
const solanaToolDefinitions = [
|
|
846
|
+
{
|
|
847
|
+
name: "list_pending_solana_private_swaps",
|
|
848
|
+
description:
|
|
849
|
+
"List cached pending Houdini private Solana orders from this OpenClaw session, including houdini_id, multi_id, deposit_address, and the last known order payload.",
|
|
850
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
851
|
+
},
|
|
852
|
+
{
|
|
853
|
+
name: "get_wallet_capabilities",
|
|
854
|
+
description: "Describe the connected wallet backend, chain, and safety limits.",
|
|
855
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
name: "get_wallet_address",
|
|
859
|
+
description: "Return the configured wallet address for the connected backend.",
|
|
860
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
name: "get_wallet_balance",
|
|
864
|
+
description: `Get the wallet overview: native balance, discovered token balances, per-asset USD values when available, and total_value_usd. Solana token discovery uses RPC; pricing uses Jupiter rather than RPC. ${WALLET_TOOL_ONLY_GUIDANCE}`,
|
|
865
|
+
parameters: {
|
|
866
|
+
type: "object",
|
|
867
|
+
properties: {
|
|
868
|
+
address: {
|
|
869
|
+
type: "string",
|
|
870
|
+
description: "Optional wallet address override.",
|
|
871
|
+
},
|
|
872
|
+
},
|
|
873
|
+
additionalProperties: false,
|
|
874
|
+
},
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
name: "get_lifi_supported_chains",
|
|
878
|
+
description: "List the LI.FI chains currently allowed for OpenClaw cross-chain routing.",
|
|
879
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
880
|
+
},
|
|
881
|
+
{
|
|
882
|
+
name: "get_lifi_quote",
|
|
883
|
+
description: "Get a read-only LI.FI cross-chain quote for Ethereum/Base/Solana routes. Execution is not enabled by this tool.",
|
|
884
|
+
parameters: {
|
|
885
|
+
type: "object",
|
|
886
|
+
properties: {
|
|
887
|
+
from_chain: { type: "string", description: "Source chain: ethereum, base, solana, or the LI.FI chain id." },
|
|
888
|
+
to_chain: { type: "string", description: "Destination chain: ethereum, base, solana, or the LI.FI chain id." },
|
|
889
|
+
from_token: { type: "string", description: "Source token address. Use native/eth/sol for native tokens." },
|
|
890
|
+
to_token: { type: "string", description: "Destination token address. Use native/eth/sol for native tokens." },
|
|
891
|
+
amount_in_raw: { type: "string", description: "Input amount in token base units as a base-10 integer string." },
|
|
892
|
+
from_address: { type: "string", description: "Optional source wallet address. Defaults to the active wallet when the source chain matches it." },
|
|
893
|
+
to_address: { type: "string", description: "Optional destination wallet address. Defaults to the active wallet when the destination chain matches it." },
|
|
894
|
+
slippage: { type: "number", description: "Optional decimal fraction, for example 0.01 for 1%." },
|
|
895
|
+
allow_bridges: { type: "array", items: { type: "string" } },
|
|
896
|
+
deny_bridges: { type: "array", items: { type: "string" } },
|
|
897
|
+
prefer_bridges: { type: "array", items: { type: "string" } },
|
|
898
|
+
},
|
|
899
|
+
required: ["from_chain", "to_chain", "from_token", "to_token", "amount_in_raw"],
|
|
900
|
+
additionalProperties: false,
|
|
901
|
+
},
|
|
902
|
+
},
|
|
903
|
+
{
|
|
904
|
+
name: "get_lifi_transfer_status",
|
|
905
|
+
description: "Get LI.FI cross-chain transfer status using a source/destination transaction hash or LI.FI step id.",
|
|
906
|
+
parameters: {
|
|
907
|
+
type: "object",
|
|
908
|
+
properties: {
|
|
909
|
+
tx_hash: { type: "string" },
|
|
910
|
+
bridge: { type: "string" },
|
|
911
|
+
from_chain: { type: "string" },
|
|
912
|
+
to_chain: { type: "string" },
|
|
913
|
+
},
|
|
914
|
+
required: ["tx_hash"],
|
|
915
|
+
additionalProperties: false,
|
|
916
|
+
},
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
name: "get_wallet_portfolio",
|
|
920
|
+
description: `Get the Solana wallet portfolio. This is the detailed equivalent of get_wallet_balance and includes native SOL, non-zero SPL token accounts, USD pricing when available, and total_value_usd. ${WALLET_TOOL_ONLY_GUIDANCE}`,
|
|
921
|
+
parameters: {
|
|
922
|
+
type: "object",
|
|
923
|
+
properties: {
|
|
924
|
+
address: {
|
|
925
|
+
type: "string",
|
|
926
|
+
description: "Optional wallet address override.",
|
|
927
|
+
},
|
|
928
|
+
},
|
|
929
|
+
additionalProperties: false,
|
|
930
|
+
},
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
name: "get_solana_token_prices",
|
|
934
|
+
description: "Get current token prices for one or more Solana mint addresses via Jupiter.",
|
|
935
|
+
parameters: {
|
|
936
|
+
type: "object",
|
|
937
|
+
properties: {
|
|
938
|
+
mints: {
|
|
939
|
+
type: "array",
|
|
940
|
+
items: { type: "string" },
|
|
941
|
+
description: "List of Solana token mint addresses.",
|
|
942
|
+
},
|
|
943
|
+
},
|
|
944
|
+
required: ["mints"],
|
|
945
|
+
additionalProperties: false,
|
|
946
|
+
},
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
name: "get_bags_claimable_positions",
|
|
950
|
+
description: "Get claimable Bags fee-share positions for a Solana wallet on mainnet.",
|
|
951
|
+
parameters: {
|
|
952
|
+
type: "object",
|
|
953
|
+
properties: {
|
|
954
|
+
wallet: {
|
|
955
|
+
type: "string",
|
|
956
|
+
description: "Optional wallet address override.",
|
|
957
|
+
},
|
|
958
|
+
},
|
|
959
|
+
additionalProperties: false,
|
|
960
|
+
},
|
|
961
|
+
},
|
|
962
|
+
{
|
|
963
|
+
name: "get_bags_fee_analytics",
|
|
964
|
+
description: "Get Bags fee analytics for a launched token, with optional claim event history.",
|
|
965
|
+
parameters: {
|
|
966
|
+
type: "object",
|
|
967
|
+
properties: {
|
|
968
|
+
token_mint: {
|
|
969
|
+
type: "string",
|
|
970
|
+
description: "Launched token mint address.",
|
|
971
|
+
},
|
|
972
|
+
include_claim_events: {
|
|
973
|
+
type: "boolean",
|
|
974
|
+
description: "If true, also fetch claim event history.",
|
|
975
|
+
},
|
|
976
|
+
mode: {
|
|
977
|
+
type: "string",
|
|
978
|
+
enum: ["offset", "time"],
|
|
979
|
+
},
|
|
980
|
+
limit: { type: "integer" },
|
|
981
|
+
offset: { type: "integer" },
|
|
982
|
+
from_ts: { type: "integer" },
|
|
983
|
+
to_ts: { type: "integer" },
|
|
984
|
+
},
|
|
985
|
+
required: ["token_mint"],
|
|
986
|
+
additionalProperties: false,
|
|
987
|
+
},
|
|
988
|
+
},
|
|
989
|
+
{
|
|
990
|
+
name: "get_jupiter_earn_tokens",
|
|
991
|
+
description: "List Jupiter Earn vault tokens currently supported on Solana mainnet.",
|
|
992
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
name: "get_jupiter_earn_positions",
|
|
996
|
+
description: "Get Jupiter Earn positions for one or more Solana wallet addresses on mainnet.",
|
|
997
|
+
parameters: {
|
|
998
|
+
type: "object",
|
|
999
|
+
properties: {
|
|
1000
|
+
users: {
|
|
1001
|
+
type: "array",
|
|
1002
|
+
items: { type: "string" },
|
|
1003
|
+
description: "Optional list of Solana wallet addresses. If omitted, use the configured wallet address.",
|
|
1004
|
+
},
|
|
1005
|
+
},
|
|
1006
|
+
additionalProperties: false,
|
|
1007
|
+
},
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
name: "get_jupiter_earn_earnings",
|
|
1011
|
+
description: "Get Jupiter Earn earnings for a wallet and one or more position addresses on mainnet.",
|
|
1012
|
+
parameters: {
|
|
1013
|
+
type: "object",
|
|
1014
|
+
properties: {
|
|
1015
|
+
user: {
|
|
1016
|
+
type: "string",
|
|
1017
|
+
description: "Optional Solana wallet address override.",
|
|
1018
|
+
},
|
|
1019
|
+
positions: {
|
|
1020
|
+
type: "array",
|
|
1021
|
+
items: { type: "string" },
|
|
1022
|
+
description: "List of Jupiter Earn position addresses.",
|
|
1023
|
+
},
|
|
1024
|
+
},
|
|
1025
|
+
required: ["positions"],
|
|
1026
|
+
additionalProperties: false,
|
|
1027
|
+
},
|
|
1028
|
+
},
|
|
1029
|
+
{
|
|
1030
|
+
name: "get_kamino_lend_markets",
|
|
1031
|
+
description: "List Kamino lending markets currently available on Solana mainnet.",
|
|
1032
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
1033
|
+
},
|
|
1034
|
+
{
|
|
1035
|
+
name: "get_kamino_lend_market_reserves",
|
|
1036
|
+
description: "Get reserve metrics for one Kamino lending market on Solana mainnet.",
|
|
1037
|
+
parameters: {
|
|
1038
|
+
type: "object",
|
|
1039
|
+
properties: {
|
|
1040
|
+
market: {
|
|
1041
|
+
type: "string",
|
|
1042
|
+
description: "Kamino market address.",
|
|
1043
|
+
},
|
|
1044
|
+
},
|
|
1045
|
+
required: ["market"],
|
|
1046
|
+
additionalProperties: false,
|
|
1047
|
+
},
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
name: "get_kamino_lend_user_obligations",
|
|
1051
|
+
description: "Get Kamino obligations for a wallet in a specific Kamino market on Solana mainnet.",
|
|
1052
|
+
parameters: {
|
|
1053
|
+
type: "object",
|
|
1054
|
+
properties: {
|
|
1055
|
+
market: {
|
|
1056
|
+
type: "string",
|
|
1057
|
+
description: "Kamino market address.",
|
|
1058
|
+
},
|
|
1059
|
+
user: {
|
|
1060
|
+
type: "string",
|
|
1061
|
+
description: "Optional Solana wallet address override.",
|
|
1062
|
+
},
|
|
1063
|
+
},
|
|
1064
|
+
required: ["market"],
|
|
1065
|
+
additionalProperties: false,
|
|
1066
|
+
},
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
name: "get_kamino_lend_user_rewards",
|
|
1070
|
+
description: "Get Kamino rewards summary for a Solana wallet on mainnet.",
|
|
1071
|
+
parameters: {
|
|
1072
|
+
type: "object",
|
|
1073
|
+
properties: {
|
|
1074
|
+
user: {
|
|
1075
|
+
type: "string",
|
|
1076
|
+
description: "Optional Solana wallet address override.",
|
|
1077
|
+
},
|
|
1078
|
+
},
|
|
1079
|
+
additionalProperties: false,
|
|
1080
|
+
},
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
name: "sign_wallet_message",
|
|
1084
|
+
description: "Sign an arbitrary message with the connected wallet after explicit approval.",
|
|
1085
|
+
optional: true,
|
|
1086
|
+
parameters: {
|
|
1087
|
+
type: "object",
|
|
1088
|
+
properties: {
|
|
1089
|
+
message: { type: "string" },
|
|
1090
|
+
purpose: { type: "string" },
|
|
1091
|
+
user_confirmed: { type: "boolean" },
|
|
1092
|
+
},
|
|
1093
|
+
required: ["message", "purpose", "user_confirmed"],
|
|
1094
|
+
additionalProperties: false,
|
|
1095
|
+
},
|
|
1096
|
+
},
|
|
1097
|
+
{
|
|
1098
|
+
name: "transfer_sol",
|
|
1099
|
+
description: "Preview, prepare, or execute a native SOL transfer. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1100
|
+
optional: true,
|
|
1101
|
+
parameters: {
|
|
1102
|
+
type: "object",
|
|
1103
|
+
properties: {
|
|
1104
|
+
recipient: { type: "string" },
|
|
1105
|
+
amount: { type: "number" },
|
|
1106
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1107
|
+
purpose: { type: "string" },
|
|
1108
|
+
user_intent: { type: "boolean" },
|
|
1109
|
+
approval_token: { type: "string" },
|
|
1110
|
+
},
|
|
1111
|
+
required: ["recipient", "amount", "mode", "purpose"],
|
|
1112
|
+
additionalProperties: false,
|
|
1113
|
+
},
|
|
1114
|
+
},
|
|
1115
|
+
{
|
|
1116
|
+
name: "transfer_spl_token",
|
|
1117
|
+
description: "Preview, prepare, or execute an SPL token transfer by mint address. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1118
|
+
optional: true,
|
|
1119
|
+
parameters: {
|
|
1120
|
+
type: "object",
|
|
1121
|
+
properties: {
|
|
1122
|
+
recipient: { type: "string" },
|
|
1123
|
+
mint: { type: "string" },
|
|
1124
|
+
amount: { type: "number" },
|
|
1125
|
+
decimals: { type: "integer" },
|
|
1126
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1127
|
+
purpose: { type: "string" },
|
|
1128
|
+
user_intent: { type: "boolean" },
|
|
1129
|
+
approval_token: { type: "string" },
|
|
1130
|
+
},
|
|
1131
|
+
required: ["recipient", "mint", "amount", "mode", "purpose"],
|
|
1132
|
+
additionalProperties: false,
|
|
1133
|
+
},
|
|
1134
|
+
},
|
|
1135
|
+
{
|
|
1136
|
+
name: "swap_solana_tokens",
|
|
1137
|
+
description: `Preview, prepare, or execute a Solana token swap via Jupiter. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation. ${WALLET_TOOL_ONLY_GUIDANCE}`,
|
|
1138
|
+
optional: true,
|
|
1139
|
+
parameters: {
|
|
1140
|
+
type: "object",
|
|
1141
|
+
properties: {
|
|
1142
|
+
input_mint: { type: "string" },
|
|
1143
|
+
output_mint: { type: "string" },
|
|
1144
|
+
amount: { type: "number" },
|
|
1145
|
+
slippage_bps: { type: "integer" },
|
|
1146
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1147
|
+
purpose: { type: "string" },
|
|
1148
|
+
user_intent: { type: "boolean" },
|
|
1149
|
+
approval_token: { type: "string" },
|
|
1150
|
+
},
|
|
1151
|
+
required: ["input_mint", "output_mint", "amount", "mode", "purpose"],
|
|
1152
|
+
additionalProperties: false,
|
|
1153
|
+
},
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
name: "swap_solana_privately",
|
|
1157
|
+
description: `Preview or create a Solana private payout through Houdini's anonymous routing. The initial implementation supports same-token private payouts only, such as SOL->SOL or USDC->USDC. Use preview first, then execute after explicit approval. The first execute creates the Houdini order and returns its deposit address; use continue_solana_private_swap to submit the funding transfer. ${WALLET_TOOL_ONLY_GUIDANCE}`,
|
|
1158
|
+
optional: true,
|
|
1159
|
+
parameters: {
|
|
1160
|
+
type: "object",
|
|
1161
|
+
properties: {
|
|
1162
|
+
input_token: { type: "string" },
|
|
1163
|
+
output_token: { type: "string" },
|
|
1164
|
+
destination_address: { type: "string" },
|
|
1165
|
+
amount: { type: "number" },
|
|
1166
|
+
use_xmr: { type: "boolean" },
|
|
1167
|
+
mode: { type: "string", enum: ["preview", "execute"] },
|
|
1168
|
+
purpose: { type: "string" },
|
|
1169
|
+
user_intent: { type: "boolean" },
|
|
1170
|
+
approval_token: { type: "string" },
|
|
1171
|
+
},
|
|
1172
|
+
required: ["input_token", "output_token", "destination_address", "amount", "mode", "purpose"],
|
|
1173
|
+
additionalProperties: false,
|
|
1174
|
+
},
|
|
1175
|
+
},
|
|
1176
|
+
{
|
|
1177
|
+
name: "continue_solana_private_swap",
|
|
1178
|
+
description:
|
|
1179
|
+
"Continue a previously created Houdini private Solana payout and submit the funding transfer to the cached deposit address. Use this after swap_solana_privately execute has returned a pending order.",
|
|
1180
|
+
optional: true,
|
|
1181
|
+
parameters: {
|
|
1182
|
+
type: "object",
|
|
1183
|
+
properties: {
|
|
1184
|
+
houdini_id: { type: "string" },
|
|
1185
|
+
approval_token: { type: "string" },
|
|
1186
|
+
},
|
|
1187
|
+
required: ["approval_token"],
|
|
1188
|
+
additionalProperties: false,
|
|
1189
|
+
},
|
|
1190
|
+
},
|
|
1191
|
+
{
|
|
1192
|
+
name: "get_solana_private_swap_status",
|
|
1193
|
+
description: "Check Houdini status for a Solana private payout created by swap_solana_privately. Prefer houdini_id from the execute result; multi_id is only needed for legacy multi-order flows.",
|
|
1194
|
+
optional: true,
|
|
1195
|
+
parameters: {
|
|
1196
|
+
type: "object",
|
|
1197
|
+
properties: {
|
|
1198
|
+
multi_id: { type: "string" },
|
|
1199
|
+
houdini_id: { type: "string" },
|
|
1200
|
+
},
|
|
1201
|
+
anyOf: [{ required: ["multi_id"] }, { required: ["houdini_id"] }],
|
|
1202
|
+
additionalProperties: false,
|
|
1203
|
+
},
|
|
1204
|
+
},
|
|
1205
|
+
{
|
|
1206
|
+
name: "swap_solana_lifi_cross_chain_tokens",
|
|
1207
|
+
description: "Preview, prepare, or execute a Solana-origin cross-chain swap through LI.FI. This currently supports Solana as the source chain and ethereum/base as the destination chain. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1208
|
+
optional: true,
|
|
1209
|
+
parameters: {
|
|
1210
|
+
type: "object",
|
|
1211
|
+
properties: {
|
|
1212
|
+
input_token: { type: "string" },
|
|
1213
|
+
destination_chain: { type: "string", enum: ["ethereum", "base", "1", "8453"] },
|
|
1214
|
+
output_token: { type: "string" },
|
|
1215
|
+
destination_address: { type: "string" },
|
|
1216
|
+
amount_in_raw: { type: "string" },
|
|
1217
|
+
slippage: { type: "number" },
|
|
1218
|
+
allow_bridges: { type: "array", items: { type: "string" } },
|
|
1219
|
+
deny_bridges: { type: "array", items: { type: "string" } },
|
|
1220
|
+
prefer_bridges: { type: "array", items: { type: "string" } },
|
|
1221
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1222
|
+
purpose: { type: "string" },
|
|
1223
|
+
user_intent: { type: "boolean" },
|
|
1224
|
+
approval_token: { type: "string" },
|
|
1225
|
+
},
|
|
1226
|
+
required: [
|
|
1227
|
+
"input_token",
|
|
1228
|
+
"destination_chain",
|
|
1229
|
+
"output_token",
|
|
1230
|
+
"destination_address",
|
|
1231
|
+
"amount_in_raw",
|
|
1232
|
+
"mode",
|
|
1233
|
+
"purpose",
|
|
1234
|
+
],
|
|
1235
|
+
additionalProperties: false,
|
|
1236
|
+
},
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
name: "claim_bags_fees",
|
|
1240
|
+
description: "Preview, prepare, or execute a Bags fee-share claim for the connected wallet on mainnet. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1241
|
+
optional: true,
|
|
1242
|
+
parameters: {
|
|
1243
|
+
type: "object",
|
|
1244
|
+
properties: {
|
|
1245
|
+
token_mint: { type: "string" },
|
|
1246
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1247
|
+
purpose: { type: "string" },
|
|
1248
|
+
user_intent: { type: "boolean" },
|
|
1249
|
+
approval_token: { type: "string" },
|
|
1250
|
+
},
|
|
1251
|
+
required: ["token_mint", "mode", "purpose"],
|
|
1252
|
+
additionalProperties: false,
|
|
1253
|
+
},
|
|
1254
|
+
},
|
|
1255
|
+
{
|
|
1256
|
+
name: "launch_bags_token",
|
|
1257
|
+
description: "Preview, prepare, or execute a Bags token launch with fee-share config on mainnet. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1258
|
+
optional: true,
|
|
1259
|
+
parameters: {
|
|
1260
|
+
type: "object",
|
|
1261
|
+
properties: {
|
|
1262
|
+
name: { type: "string" },
|
|
1263
|
+
symbol: { type: "string" },
|
|
1264
|
+
description: { type: "string" },
|
|
1265
|
+
image_url: { type: "string" },
|
|
1266
|
+
website: { type: "string" },
|
|
1267
|
+
twitter: { type: "string" },
|
|
1268
|
+
telegram: { type: "string" },
|
|
1269
|
+
discord: { type: "string" },
|
|
1270
|
+
base_mint: { type: "string" },
|
|
1271
|
+
claimers: {
|
|
1272
|
+
type: "array",
|
|
1273
|
+
items: { type: "string" },
|
|
1274
|
+
},
|
|
1275
|
+
basis_points: {
|
|
1276
|
+
type: "array",
|
|
1277
|
+
items: { type: "integer" },
|
|
1278
|
+
},
|
|
1279
|
+
initial_buy_sol: { type: "number" },
|
|
1280
|
+
bags_config_type: { type: "integer" },
|
|
1281
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1282
|
+
purpose: { type: "string" },
|
|
1283
|
+
user_intent: { type: "boolean" },
|
|
1284
|
+
approval_token: { type: "string" },
|
|
1285
|
+
},
|
|
1286
|
+
required: [
|
|
1287
|
+
"name",
|
|
1288
|
+
"symbol",
|
|
1289
|
+
"description",
|
|
1290
|
+
"base_mint",
|
|
1291
|
+
"claimers",
|
|
1292
|
+
"basis_points",
|
|
1293
|
+
"initial_buy_sol",
|
|
1294
|
+
"mode",
|
|
1295
|
+
"purpose",
|
|
1296
|
+
],
|
|
1297
|
+
additionalProperties: false,
|
|
1298
|
+
},
|
|
1299
|
+
},
|
|
1300
|
+
{
|
|
1301
|
+
name: "jupiter_earn_deposit",
|
|
1302
|
+
description: "Preview, prepare, or execute a Jupiter Earn deposit using a raw base-unit amount. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1303
|
+
optional: true,
|
|
1304
|
+
parameters: {
|
|
1305
|
+
type: "object",
|
|
1306
|
+
properties: {
|
|
1307
|
+
asset: { type: "string" },
|
|
1308
|
+
amount_raw: { type: "string" },
|
|
1309
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1310
|
+
purpose: { type: "string" },
|
|
1311
|
+
user_intent: { type: "boolean" },
|
|
1312
|
+
approval_token: { type: "string" },
|
|
1313
|
+
},
|
|
1314
|
+
required: ["asset", "amount_raw", "mode", "purpose"],
|
|
1315
|
+
additionalProperties: false,
|
|
1316
|
+
},
|
|
1317
|
+
},
|
|
1318
|
+
{
|
|
1319
|
+
name: "jupiter_earn_withdraw",
|
|
1320
|
+
description: "Preview, prepare, or execute a Jupiter Earn withdraw using a raw base-unit amount. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1321
|
+
optional: true,
|
|
1322
|
+
parameters: {
|
|
1323
|
+
type: "object",
|
|
1324
|
+
properties: {
|
|
1325
|
+
asset: { type: "string" },
|
|
1326
|
+
amount_raw: { type: "string" },
|
|
1327
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1328
|
+
purpose: { type: "string" },
|
|
1329
|
+
user_intent: { type: "boolean" },
|
|
1330
|
+
approval_token: { type: "string" },
|
|
1331
|
+
},
|
|
1332
|
+
required: ["asset", "amount_raw", "mode", "purpose"],
|
|
1333
|
+
additionalProperties: false,
|
|
1334
|
+
},
|
|
1335
|
+
},
|
|
1336
|
+
{
|
|
1337
|
+
name: "kamino_lend_deposit",
|
|
1338
|
+
description: "Preview, prepare, or execute a Kamino lending deposit using a decimal token amount. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1339
|
+
optional: true,
|
|
1340
|
+
parameters: {
|
|
1341
|
+
type: "object",
|
|
1342
|
+
properties: {
|
|
1343
|
+
market: { type: "string" },
|
|
1344
|
+
reserve: { type: "string" },
|
|
1345
|
+
amount_ui: { type: "string" },
|
|
1346
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1347
|
+
purpose: { type: "string" },
|
|
1348
|
+
user_intent: { type: "boolean" },
|
|
1349
|
+
approval_token: { type: "string" },
|
|
1350
|
+
},
|
|
1351
|
+
required: ["market", "reserve", "amount_ui", "mode", "purpose"],
|
|
1352
|
+
additionalProperties: false,
|
|
1353
|
+
},
|
|
1354
|
+
},
|
|
1355
|
+
{
|
|
1356
|
+
name: "kamino_lend_withdraw",
|
|
1357
|
+
description: "Preview, prepare, or execute a Kamino lending withdraw using a decimal token amount. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1358
|
+
optional: true,
|
|
1359
|
+
parameters: {
|
|
1360
|
+
type: "object",
|
|
1361
|
+
properties: {
|
|
1362
|
+
market: { type: "string" },
|
|
1363
|
+
reserve: { type: "string" },
|
|
1364
|
+
amount_ui: { type: "string" },
|
|
1365
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1366
|
+
purpose: { type: "string" },
|
|
1367
|
+
user_intent: { type: "boolean" },
|
|
1368
|
+
approval_token: { type: "string" },
|
|
1369
|
+
},
|
|
1370
|
+
required: ["market", "reserve", "amount_ui", "mode", "purpose"],
|
|
1371
|
+
additionalProperties: false,
|
|
1372
|
+
},
|
|
1373
|
+
},
|
|
1374
|
+
{
|
|
1375
|
+
name: "kamino_lend_borrow",
|
|
1376
|
+
description: "Preview, prepare, or execute a Kamino lending borrow using a decimal token amount. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1377
|
+
optional: true,
|
|
1378
|
+
parameters: {
|
|
1379
|
+
type: "object",
|
|
1380
|
+
properties: {
|
|
1381
|
+
market: { type: "string" },
|
|
1382
|
+
reserve: { type: "string" },
|
|
1383
|
+
amount_ui: { type: "string" },
|
|
1384
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1385
|
+
purpose: { type: "string" },
|
|
1386
|
+
user_intent: { type: "boolean" },
|
|
1387
|
+
approval_token: { type: "string" },
|
|
1388
|
+
},
|
|
1389
|
+
required: ["market", "reserve", "amount_ui", "mode", "purpose"],
|
|
1390
|
+
additionalProperties: false,
|
|
1391
|
+
},
|
|
1392
|
+
},
|
|
1393
|
+
{
|
|
1394
|
+
name: "kamino_lend_repay",
|
|
1395
|
+
description: "Preview, prepare, or execute a Kamino lending repay using a decimal token amount. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1396
|
+
optional: true,
|
|
1397
|
+
parameters: {
|
|
1398
|
+
type: "object",
|
|
1399
|
+
properties: {
|
|
1400
|
+
market: { type: "string" },
|
|
1401
|
+
reserve: { type: "string" },
|
|
1402
|
+
amount_ui: { type: "string" },
|
|
1403
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1404
|
+
purpose: { type: "string" },
|
|
1405
|
+
user_intent: { type: "boolean" },
|
|
1406
|
+
approval_token: { type: "string" },
|
|
1407
|
+
},
|
|
1408
|
+
required: ["market", "reserve", "amount_ui", "mode", "purpose"],
|
|
1409
|
+
additionalProperties: false,
|
|
1410
|
+
},
|
|
1411
|
+
},
|
|
1412
|
+
{
|
|
1413
|
+
name: "close_empty_token_accounts",
|
|
1414
|
+
description: "Preview or execute closing zero-balance token accounts. Execute requires a host-issued approval token bound to the previewed operation.",
|
|
1415
|
+
optional: true,
|
|
1416
|
+
parameters: {
|
|
1417
|
+
type: "object",
|
|
1418
|
+
properties: {
|
|
1419
|
+
limit: { type: "integer" },
|
|
1420
|
+
mode: { type: "string", enum: ["preview", "execute"] },
|
|
1421
|
+
purpose: { type: "string" },
|
|
1422
|
+
approval_token: { type: "string" },
|
|
1423
|
+
},
|
|
1424
|
+
required: ["limit", "mode", "purpose"],
|
|
1425
|
+
additionalProperties: false,
|
|
1426
|
+
},
|
|
1427
|
+
},
|
|
1428
|
+
{
|
|
1429
|
+
name: "request_devnet_airdrop",
|
|
1430
|
+
description: "Request devnet or testnet SOL from the faucet.",
|
|
1431
|
+
optional: true,
|
|
1432
|
+
parameters: {
|
|
1433
|
+
type: "object",
|
|
1434
|
+
properties: {
|
|
1435
|
+
amount: { type: "number" },
|
|
1436
|
+
},
|
|
1437
|
+
required: ["amount"],
|
|
1438
|
+
additionalProperties: false,
|
|
1439
|
+
},
|
|
1440
|
+
},
|
|
1441
|
+
];
|
|
1442
|
+
|
|
1443
|
+
const btcToolDefinitions = [
|
|
1444
|
+
{
|
|
1445
|
+
name: "get_wallet_capabilities",
|
|
1446
|
+
description: "Describe the connected wallet backend, chain, and safety limits.",
|
|
1447
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
1448
|
+
},
|
|
1449
|
+
{
|
|
1450
|
+
name: "get_wallet_address",
|
|
1451
|
+
description: "Return the configured wallet address for the connected backend.",
|
|
1452
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
1453
|
+
},
|
|
1454
|
+
{
|
|
1455
|
+
name: "get_wallet_balance",
|
|
1456
|
+
description: "Get the native BTC balance for the configured wallet address.",
|
|
1457
|
+
parameters: {
|
|
1458
|
+
type: "object",
|
|
1459
|
+
properties: {
|
|
1460
|
+
address: {
|
|
1461
|
+
type: "string",
|
|
1462
|
+
description: "Optional wallet address override.",
|
|
1463
|
+
},
|
|
1464
|
+
},
|
|
1465
|
+
additionalProperties: false,
|
|
1466
|
+
},
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
name: "get_btc_transfer_history",
|
|
1470
|
+
description: "Get BTC transfer history for the configured wallet account.",
|
|
1471
|
+
parameters: {
|
|
1472
|
+
type: "object",
|
|
1473
|
+
properties: {
|
|
1474
|
+
direction: { type: "string", enum: ["incoming", "outgoing", "all"] },
|
|
1475
|
+
limit: { type: "integer" },
|
|
1476
|
+
skip: { type: "integer" },
|
|
1477
|
+
},
|
|
1478
|
+
additionalProperties: false,
|
|
1479
|
+
},
|
|
1480
|
+
},
|
|
1481
|
+
{
|
|
1482
|
+
name: "get_btc_fee_rates",
|
|
1483
|
+
description: "Get current BTC fee-rate suggestions from the local BTC wallet service.",
|
|
1484
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
name: "get_btc_max_spendable",
|
|
1488
|
+
description: "Estimate the maximum BTC spendable amount after fees.",
|
|
1489
|
+
parameters: {
|
|
1490
|
+
type: "object",
|
|
1491
|
+
properties: {
|
|
1492
|
+
fee_rate: { type: "integer" },
|
|
1493
|
+
},
|
|
1494
|
+
additionalProperties: false,
|
|
1495
|
+
},
|
|
1496
|
+
},
|
|
1497
|
+
{
|
|
1498
|
+
name: "transfer_btc",
|
|
1499
|
+
description: "Preview, prepare, or execute a BTC transfer in satoshis. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1500
|
+
optional: true,
|
|
1501
|
+
parameters: {
|
|
1502
|
+
type: "object",
|
|
1503
|
+
properties: {
|
|
1504
|
+
recipient: { type: "string" },
|
|
1505
|
+
amount_sats: { type: "integer" },
|
|
1506
|
+
fee_rate: { type: "integer" },
|
|
1507
|
+
confirmation_target: { type: "integer" },
|
|
1508
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1509
|
+
purpose: { type: "string" },
|
|
1510
|
+
user_intent: { type: "boolean" },
|
|
1511
|
+
approval_token: { type: "string" },
|
|
1512
|
+
},
|
|
1513
|
+
required: ["recipient", "amount_sats", "mode", "purpose"],
|
|
1514
|
+
additionalProperties: false,
|
|
1515
|
+
},
|
|
1516
|
+
},
|
|
1517
|
+
];
|
|
1518
|
+
|
|
1519
|
+
const evmToolDefinitions = [
|
|
1520
|
+
{
|
|
1521
|
+
name: "get_wallet_capabilities",
|
|
1522
|
+
description: "Describe the connected wallet backend, chain, and safety limits.",
|
|
1523
|
+
parameters: {
|
|
1524
|
+
type: "object",
|
|
1525
|
+
properties: {
|
|
1526
|
+
network: {
|
|
1527
|
+
type: "string",
|
|
1528
|
+
enum: ["ethereum", "base"],
|
|
1529
|
+
description: "Optional EVM network override for this request.",
|
|
1530
|
+
},
|
|
1531
|
+
},
|
|
1532
|
+
additionalProperties: false,
|
|
1533
|
+
},
|
|
1534
|
+
},
|
|
1535
|
+
{
|
|
1536
|
+
name: "get_wallet_address",
|
|
1537
|
+
description: "Return the configured wallet address for the connected backend.",
|
|
1538
|
+
parameters: {
|
|
1539
|
+
type: "object",
|
|
1540
|
+
properties: {
|
|
1541
|
+
network: {
|
|
1542
|
+
type: "string",
|
|
1543
|
+
enum: ["ethereum", "base"],
|
|
1544
|
+
description: "Optional EVM network override for this request.",
|
|
1545
|
+
},
|
|
1546
|
+
},
|
|
1547
|
+
additionalProperties: false,
|
|
1548
|
+
},
|
|
1549
|
+
},
|
|
1550
|
+
{
|
|
1551
|
+
name: "get_wallet_balance",
|
|
1552
|
+
description: "Get the EVM wallet overview: native asset, discovered ERC-20 balances, per-asset USD values, assets, balance_usd, and total_value_usd when available. Pricing uses aggregator APIs rather than RPC.",
|
|
1553
|
+
parameters: {
|
|
1554
|
+
type: "object",
|
|
1555
|
+
properties: {
|
|
1556
|
+
address: {
|
|
1557
|
+
type: "string",
|
|
1558
|
+
description: "Optional wallet address override.",
|
|
1559
|
+
},
|
|
1560
|
+
network: {
|
|
1561
|
+
type: "string",
|
|
1562
|
+
enum: ["ethereum", "base"],
|
|
1563
|
+
description: "Optional EVM network override for this request.",
|
|
1564
|
+
},
|
|
1565
|
+
},
|
|
1566
|
+
additionalProperties: false,
|
|
1567
|
+
},
|
|
1568
|
+
},
|
|
1569
|
+
{
|
|
1570
|
+
name: "get_lifi_supported_chains",
|
|
1571
|
+
description: "List the LI.FI chains currently allowed for OpenClaw cross-chain routing.",
|
|
1572
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
1573
|
+
},
|
|
1574
|
+
{
|
|
1575
|
+
name: "get_lifi_quote",
|
|
1576
|
+
description: "Get a read-only LI.FI cross-chain quote for Ethereum/Base/Solana routes. Execution is not enabled by this tool.",
|
|
1577
|
+
parameters: {
|
|
1578
|
+
type: "object",
|
|
1579
|
+
properties: {
|
|
1580
|
+
from_chain: { type: "string", description: "Source chain: ethereum, base, solana, or the LI.FI chain id." },
|
|
1581
|
+
to_chain: { type: "string", description: "Destination chain: ethereum, base, solana, or the LI.FI chain id." },
|
|
1582
|
+
from_token: { type: "string", description: "Source token address. Use native/eth/sol for native tokens." },
|
|
1583
|
+
to_token: { type: "string", description: "Destination token address. Use native/eth/sol for native tokens." },
|
|
1584
|
+
amount_in_raw: { type: "string", description: "Input amount in token base units as a base-10 integer string." },
|
|
1585
|
+
from_address: { type: "string", description: "Optional source wallet address. Defaults to the active wallet when the source chain matches it." },
|
|
1586
|
+
to_address: { type: "string", description: "Optional destination wallet address. Defaults to the active wallet when the destination chain matches it." },
|
|
1587
|
+
slippage: { type: "number", description: "Optional decimal fraction, for example 0.01 for 1%." },
|
|
1588
|
+
allow_bridges: { type: "array", items: { type: "string" } },
|
|
1589
|
+
deny_bridges: { type: "array", items: { type: "string" } },
|
|
1590
|
+
prefer_bridges: { type: "array", items: { type: "string" } },
|
|
1591
|
+
},
|
|
1592
|
+
required: ["from_chain", "to_chain", "from_token", "to_token", "amount_in_raw"],
|
|
1593
|
+
additionalProperties: false,
|
|
1594
|
+
},
|
|
1595
|
+
},
|
|
1596
|
+
{
|
|
1597
|
+
name: "get_lifi_transfer_status",
|
|
1598
|
+
description: "Get LI.FI cross-chain transfer status using a source/destination transaction hash or LI.FI step id.",
|
|
1599
|
+
parameters: {
|
|
1600
|
+
type: "object",
|
|
1601
|
+
properties: {
|
|
1602
|
+
tx_hash: { type: "string" },
|
|
1603
|
+
bridge: { type: "string" },
|
|
1604
|
+
from_chain: { type: "string" },
|
|
1605
|
+
to_chain: { type: "string" },
|
|
1606
|
+
},
|
|
1607
|
+
required: ["tx_hash"],
|
|
1608
|
+
additionalProperties: false,
|
|
1609
|
+
},
|
|
1610
|
+
},
|
|
1611
|
+
{
|
|
1612
|
+
name: "get_evm_network",
|
|
1613
|
+
description: "Show the effective EVM network context, available networks, and swap-supported networks.",
|
|
1614
|
+
parameters: {
|
|
1615
|
+
type: "object",
|
|
1616
|
+
properties: {
|
|
1617
|
+
network: {
|
|
1618
|
+
type: "string",
|
|
1619
|
+
enum: ["ethereum", "base"],
|
|
1620
|
+
},
|
|
1621
|
+
},
|
|
1622
|
+
additionalProperties: false,
|
|
1623
|
+
},
|
|
1624
|
+
},
|
|
1625
|
+
{
|
|
1626
|
+
name: "set_evm_network",
|
|
1627
|
+
description:
|
|
1628
|
+
"Select the active EVM network for subsequent wallet tool calls in this OpenClaw plugin session. Use this to switch between ethereum and base instead of editing code or plugin configuration.",
|
|
1629
|
+
parameters: {
|
|
1630
|
+
type: "object",
|
|
1631
|
+
properties: {
|
|
1632
|
+
network: {
|
|
1633
|
+
type: "string",
|
|
1634
|
+
enum: ["ethereum", "base"],
|
|
1635
|
+
description: "EVM network to make active for subsequent calls.",
|
|
1636
|
+
},
|
|
1637
|
+
},
|
|
1638
|
+
required: ["network"],
|
|
1639
|
+
additionalProperties: false,
|
|
1640
|
+
},
|
|
1641
|
+
},
|
|
1642
|
+
{
|
|
1643
|
+
name: "get_evm_token_balance",
|
|
1644
|
+
description: "Get the ERC-20 balance for the configured EVM wallet account.",
|
|
1645
|
+
parameters: {
|
|
1646
|
+
type: "object",
|
|
1647
|
+
properties: {
|
|
1648
|
+
token_address: { type: "string" },
|
|
1649
|
+
network: { type: "string", enum: ["ethereum", "base"] },
|
|
1650
|
+
},
|
|
1651
|
+
required: ["token_address"],
|
|
1652
|
+
additionalProperties: false,
|
|
1653
|
+
},
|
|
1654
|
+
},
|
|
1655
|
+
{
|
|
1656
|
+
name: "get_evm_token_metadata",
|
|
1657
|
+
description: "Get ERC-20 token metadata for a contract address on the active EVM network.",
|
|
1658
|
+
parameters: {
|
|
1659
|
+
type: "object",
|
|
1660
|
+
properties: {
|
|
1661
|
+
token_address: { type: "string" },
|
|
1662
|
+
network: { type: "string", enum: ["ethereum", "base"] },
|
|
1663
|
+
},
|
|
1664
|
+
required: ["token_address"],
|
|
1665
|
+
additionalProperties: false,
|
|
1666
|
+
},
|
|
1667
|
+
},
|
|
1668
|
+
{
|
|
1669
|
+
name: "get_evm_fee_rates",
|
|
1670
|
+
description: "Get current EVM fee-rate suggestions for the active network.",
|
|
1671
|
+
parameters: {
|
|
1672
|
+
type: "object",
|
|
1673
|
+
properties: {
|
|
1674
|
+
network: { type: "string", enum: ["ethereum", "base"] },
|
|
1675
|
+
},
|
|
1676
|
+
additionalProperties: false,
|
|
1677
|
+
},
|
|
1678
|
+
},
|
|
1679
|
+
{
|
|
1680
|
+
name: "get_evm_transaction_receipt",
|
|
1681
|
+
description: "Get the transaction receipt for a broadcast EVM transaction hash.",
|
|
1682
|
+
parameters: {
|
|
1683
|
+
type: "object",
|
|
1684
|
+
properties: {
|
|
1685
|
+
tx_hash: { type: "string" },
|
|
1686
|
+
network: { type: "string", enum: ["ethereum", "base"] },
|
|
1687
|
+
},
|
|
1688
|
+
required: ["tx_hash"],
|
|
1689
|
+
additionalProperties: false,
|
|
1690
|
+
},
|
|
1691
|
+
},
|
|
1692
|
+
{
|
|
1693
|
+
name: "get_evm_aave_account",
|
|
1694
|
+
description: "Get read-only Aave V3 account data for the configured EVM wallet on supported mainnet networks.",
|
|
1695
|
+
parameters: {
|
|
1696
|
+
type: "object",
|
|
1697
|
+
properties: {
|
|
1698
|
+
network: { type: "string", enum: ["ethereum", "base"] },
|
|
1699
|
+
},
|
|
1700
|
+
additionalProperties: false,
|
|
1701
|
+
},
|
|
1702
|
+
},
|
|
1703
|
+
{
|
|
1704
|
+
name: "get_evm_aave_reserves",
|
|
1705
|
+
description: "Get the read-only Aave V3 reserve catalog for the configured EVM network, including reserve flags, pricing, and liquidity metadata.",
|
|
1706
|
+
parameters: {
|
|
1707
|
+
type: "object",
|
|
1708
|
+
properties: {
|
|
1709
|
+
network: { type: "string", enum: ["ethereum", "base"] },
|
|
1710
|
+
},
|
|
1711
|
+
additionalProperties: false,
|
|
1712
|
+
},
|
|
1713
|
+
},
|
|
1714
|
+
{
|
|
1715
|
+
name: "get_evm_aave_positions",
|
|
1716
|
+
description: "Get read-only Aave V3 per-reserve positions for the configured EVM wallet, including supplied and borrowed balances on supported mainnet networks.",
|
|
1717
|
+
parameters: {
|
|
1718
|
+
type: "object",
|
|
1719
|
+
properties: {
|
|
1720
|
+
network: { type: "string", enum: ["ethereum", "base"] },
|
|
1721
|
+
},
|
|
1722
|
+
additionalProperties: false,
|
|
1723
|
+
},
|
|
1724
|
+
},
|
|
1725
|
+
{
|
|
1726
|
+
name: "manage_evm_aave_position",
|
|
1727
|
+
description: "Preview, prepare, or execute a narrow Aave V3 lending operation on supported EVM mainnet networks. Supported operations are supply, withdraw, borrow, and repay. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1728
|
+
optional: true,
|
|
1729
|
+
parameters: {
|
|
1730
|
+
type: "object",
|
|
1731
|
+
properties: {
|
|
1732
|
+
operation: { type: "string", enum: ["supply", "withdraw", "borrow", "repay"] },
|
|
1733
|
+
token_address: { type: "string" },
|
|
1734
|
+
amount_raw: { type: "string" },
|
|
1735
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1736
|
+
purpose: { type: "string" },
|
|
1737
|
+
user_intent: { type: "boolean" },
|
|
1738
|
+
approval_token: { type: "string" },
|
|
1739
|
+
network: { type: "string", enum: ["ethereum", "base"] },
|
|
1740
|
+
},
|
|
1741
|
+
required: ["operation", "token_address", "amount_raw", "mode", "purpose"],
|
|
1742
|
+
additionalProperties: false,
|
|
1743
|
+
},
|
|
1744
|
+
},
|
|
1745
|
+
{
|
|
1746
|
+
name: "get_evm_lido_overview",
|
|
1747
|
+
description: "Get the read-only Lido staking overview for the configured EVM wallet on supported networks, including contract addresses and sample wrap rates.",
|
|
1748
|
+
parameters: {
|
|
1749
|
+
type: "object",
|
|
1750
|
+
properties: {
|
|
1751
|
+
network: { type: "string", enum: ["ethereum"] },
|
|
1752
|
+
},
|
|
1753
|
+
additionalProperties: false,
|
|
1754
|
+
},
|
|
1755
|
+
},
|
|
1756
|
+
{
|
|
1757
|
+
name: "get_evm_lido_positions",
|
|
1758
|
+
description: "Get read-only Lido positions for the configured EVM wallet, including stETH, wstETH, and stETH-equivalent balances.",
|
|
1759
|
+
parameters: {
|
|
1760
|
+
type: "object",
|
|
1761
|
+
properties: {
|
|
1762
|
+
network: { type: "string", enum: ["ethereum"] },
|
|
1763
|
+
},
|
|
1764
|
+
additionalProperties: false,
|
|
1765
|
+
},
|
|
1766
|
+
},
|
|
1767
|
+
{
|
|
1768
|
+
name: "manage_evm_lido_position",
|
|
1769
|
+
description: "Preview, prepare, or execute a narrow Lido staking operation on Ethereum mainnet. Supported operations are stake_eth_for_wsteth, wrap_steth, and unwrap_wsteth. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1770
|
+
optional: true,
|
|
1771
|
+
parameters: {
|
|
1772
|
+
type: "object",
|
|
1773
|
+
properties: {
|
|
1774
|
+
operation: { type: "string", enum: ["stake_eth_for_wsteth", "wrap_steth", "unwrap_wsteth"] },
|
|
1775
|
+
amount_raw: { type: "string" },
|
|
1776
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1777
|
+
purpose: { type: "string" },
|
|
1778
|
+
user_intent: { type: "boolean" },
|
|
1779
|
+
approval_token: { type: "string" },
|
|
1780
|
+
network: { type: "string", enum: ["ethereum"] },
|
|
1781
|
+
},
|
|
1782
|
+
required: ["operation", "amount_raw", "mode", "purpose"],
|
|
1783
|
+
additionalProperties: false,
|
|
1784
|
+
},
|
|
1785
|
+
},
|
|
1786
|
+
{
|
|
1787
|
+
name: "get_evm_lido_withdrawal_requests",
|
|
1788
|
+
description: "Get read-only Lido withdrawal queue requests for the configured EVM wallet, including finalized and claimable request statuses.",
|
|
1789
|
+
parameters: {
|
|
1790
|
+
type: "object",
|
|
1791
|
+
properties: {
|
|
1792
|
+
network: { type: "string", enum: ["ethereum"] },
|
|
1793
|
+
},
|
|
1794
|
+
additionalProperties: false,
|
|
1795
|
+
},
|
|
1796
|
+
},
|
|
1797
|
+
{
|
|
1798
|
+
name: "manage_evm_lido_withdrawal",
|
|
1799
|
+
description: "Preview, prepare, or execute a narrow Lido withdrawal queue operation on Ethereum mainnet. Supported operations are request_withdrawal_steth, request_withdrawal_wsteth, and claim_withdrawal. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1800
|
+
optional: true,
|
|
1801
|
+
parameters: {
|
|
1802
|
+
type: "object",
|
|
1803
|
+
properties: {
|
|
1804
|
+
operation: {
|
|
1805
|
+
type: "string",
|
|
1806
|
+
enum: ["request_withdrawal_steth", "request_withdrawal_wsteth", "claim_withdrawal"],
|
|
1807
|
+
},
|
|
1808
|
+
amount_raw: { type: "string" },
|
|
1809
|
+
request_id: { type: "string" },
|
|
1810
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1811
|
+
purpose: { type: "string" },
|
|
1812
|
+
user_intent: { type: "boolean" },
|
|
1813
|
+
approval_token: { type: "string" },
|
|
1814
|
+
network: { type: "string", enum: ["ethereum"] },
|
|
1815
|
+
},
|
|
1816
|
+
required: ["operation", "mode", "purpose"],
|
|
1817
|
+
additionalProperties: false,
|
|
1818
|
+
},
|
|
1819
|
+
},
|
|
1820
|
+
{
|
|
1821
|
+
name: "get_evm_swap_quote",
|
|
1822
|
+
description: "Get a read-only Velora quote for an ERC-20 to ERC-20 swap on supported EVM mainnet networks. This does not approve or execute a swap.",
|
|
1823
|
+
parameters: {
|
|
1824
|
+
type: "object",
|
|
1825
|
+
properties: {
|
|
1826
|
+
token_in: { type: "string" },
|
|
1827
|
+
token_out: { type: "string" },
|
|
1828
|
+
amount_in_raw: { type: "string" },
|
|
1829
|
+
network: { type: "string", enum: ["ethereum", "base"] },
|
|
1830
|
+
},
|
|
1831
|
+
required: ["token_in", "token_out", "amount_in_raw"],
|
|
1832
|
+
additionalProperties: false,
|
|
1833
|
+
},
|
|
1834
|
+
},
|
|
1835
|
+
{
|
|
1836
|
+
name: "swap_evm_tokens",
|
|
1837
|
+
description: "Preview, prepare, or execute an ERC-20 to ERC-20 swap through Velora on supported EVM mainnet networks. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1838
|
+
optional: true,
|
|
1839
|
+
parameters: {
|
|
1840
|
+
type: "object",
|
|
1841
|
+
properties: {
|
|
1842
|
+
token_in: { type: "string" },
|
|
1843
|
+
token_out: { type: "string" },
|
|
1844
|
+
amount_in_raw: { type: "string" },
|
|
1845
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1846
|
+
purpose: { type: "string" },
|
|
1847
|
+
user_intent: { type: "boolean" },
|
|
1848
|
+
approval_token: { type: "string" },
|
|
1849
|
+
network: { type: "string", enum: ["ethereum", "base"] },
|
|
1850
|
+
},
|
|
1851
|
+
required: ["token_in", "token_out", "amount_in_raw", "mode", "purpose"],
|
|
1852
|
+
additionalProperties: false,
|
|
1853
|
+
},
|
|
1854
|
+
},
|
|
1855
|
+
{
|
|
1856
|
+
name: "swap_evm_lifi_cross_chain_tokens",
|
|
1857
|
+
description: "Preview, prepare, or execute an EVM-origin cross-chain swap through LI.FI. This currently supports ethereum/base as the source network and ethereum/base/solana as the destination chain. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1858
|
+
optional: true,
|
|
1859
|
+
parameters: {
|
|
1860
|
+
type: "object",
|
|
1861
|
+
properties: {
|
|
1862
|
+
token_in: { type: "string" },
|
|
1863
|
+
destination_chain: { type: "string", enum: ["ethereum", "base", "solana", "1", "8453", "1151111081099710"] },
|
|
1864
|
+
output_token: { type: "string" },
|
|
1865
|
+
destination_address: { type: "string" },
|
|
1866
|
+
amount_in_raw: { type: "string" },
|
|
1867
|
+
slippage: { type: "number" },
|
|
1868
|
+
allow_bridges: { type: "array", items: { type: "string" } },
|
|
1869
|
+
deny_bridges: { type: "array", items: { type: "string" } },
|
|
1870
|
+
prefer_bridges: { type: "array", items: { type: "string" } },
|
|
1871
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1872
|
+
purpose: { type: "string" },
|
|
1873
|
+
user_intent: { type: "boolean" },
|
|
1874
|
+
approval_token: { type: "string" },
|
|
1875
|
+
network: { type: "string", enum: ["ethereum", "base"] },
|
|
1876
|
+
},
|
|
1877
|
+
required: [
|
|
1878
|
+
"token_in",
|
|
1879
|
+
"destination_chain",
|
|
1880
|
+
"output_token",
|
|
1881
|
+
"destination_address",
|
|
1882
|
+
"amount_in_raw",
|
|
1883
|
+
"mode",
|
|
1884
|
+
"purpose",
|
|
1885
|
+
],
|
|
1886
|
+
additionalProperties: false,
|
|
1887
|
+
},
|
|
1888
|
+
},
|
|
1889
|
+
{
|
|
1890
|
+
name: "transfer_evm_native",
|
|
1891
|
+
description: "Preview, prepare, or execute a native EVM transfer using a wei amount. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1892
|
+
optional: true,
|
|
1893
|
+
parameters: {
|
|
1894
|
+
type: "object",
|
|
1895
|
+
properties: {
|
|
1896
|
+
recipient: { type: "string" },
|
|
1897
|
+
amount_wei: { type: "string" },
|
|
1898
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1899
|
+
purpose: { type: "string" },
|
|
1900
|
+
user_intent: { type: "boolean" },
|
|
1901
|
+
approval_token: { type: "string" },
|
|
1902
|
+
network: { type: "string", enum: ["ethereum", "base"] },
|
|
1903
|
+
},
|
|
1904
|
+
required: ["recipient", "amount_wei", "mode", "purpose"],
|
|
1905
|
+
additionalProperties: false,
|
|
1906
|
+
},
|
|
1907
|
+
},
|
|
1908
|
+
{
|
|
1909
|
+
name: "transfer_evm_token",
|
|
1910
|
+
description: "Preview, prepare, or execute an ERC-20 transfer using a raw base-unit amount. Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation.",
|
|
1911
|
+
optional: true,
|
|
1912
|
+
parameters: {
|
|
1913
|
+
type: "object",
|
|
1914
|
+
properties: {
|
|
1915
|
+
token_address: { type: "string" },
|
|
1916
|
+
recipient: { type: "string" },
|
|
1917
|
+
amount_raw: { type: "string" },
|
|
1918
|
+
mode: { type: "string", enum: ["preview", "prepare", "execute"] },
|
|
1919
|
+
purpose: { type: "string" },
|
|
1920
|
+
user_intent: { type: "boolean" },
|
|
1921
|
+
approval_token: { type: "string" },
|
|
1922
|
+
network: { type: "string", enum: ["ethereum", "base"] },
|
|
1923
|
+
},
|
|
1924
|
+
required: ["token_address", "recipient", "amount_raw", "mode", "purpose"],
|
|
1925
|
+
additionalProperties: false,
|
|
1926
|
+
},
|
|
1927
|
+
},
|
|
1928
|
+
];
|
|
1929
|
+
|
|
1930
|
+
export default function registerAgentWalletPlugin(api) {
|
|
1931
|
+
api?.logger?.info?.("[agent-wallet] registering OpenClaw wallet plugin");
|
|
1932
|
+
|
|
1933
|
+
const backend = resolveBackend(api);
|
|
1934
|
+
selectedWalletBackend = null;
|
|
1935
|
+
selectedSolanaNetwork = defaultSolanaNetwork(api);
|
|
1936
|
+
selectedEvmNetwork = defaultSelectableEvmNetwork(api);
|
|
1937
|
+
selectedBtcNetwork = defaultBtcNetwork(api);
|
|
1938
|
+
const duplicateSessionToolNames = new Set(
|
|
1939
|
+
walletSessionToolDefinitions.map((definition) => definition.name)
|
|
1940
|
+
);
|
|
1941
|
+
const toolDefinitions = [];
|
|
1942
|
+
const seen = new Set();
|
|
1943
|
+
for (const definition of [
|
|
1944
|
+
...walletSessionToolDefinitions,
|
|
1945
|
+
...solanaToolDefinitions.filter((item) => !duplicateSessionToolNames.has(item.name)),
|
|
1946
|
+
...btcToolDefinitions.filter((item) => !duplicateSessionToolNames.has(item.name)),
|
|
1947
|
+
...evmToolDefinitions.filter((item) => !duplicateSessionToolNames.has(item.name)),
|
|
1948
|
+
]) {
|
|
1949
|
+
if (seen.has(definition.name)) {
|
|
1950
|
+
continue;
|
|
1951
|
+
}
|
|
1952
|
+
seen.add(definition.name);
|
|
1953
|
+
toolDefinitions.push(definition);
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
for (const definition of toolDefinitions) {
|
|
1957
|
+
registerTool(api, definition);
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
api?.logger?.info?.(
|
|
1961
|
+
`[agent-wallet] default wallet backend ${backend}; registered ${toolDefinitions.length} multi-wallet tools`
|
|
1962
|
+
);
|
|
1963
|
+
}
|