@moly-mcp/lido 1.0.6 → 1.0.8
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/dist/alerts-ARQAPRIT.js +16 -0
- package/dist/bin.js +278 -15
- package/dist/chunk-6F64RPQQ.js +65 -0
- package/dist/chunk-6UIRFWG4.js +73 -0
- package/dist/chunk-CH4MXPWS.js +41 -0
- package/dist/chunk-CQ6ZSCMZ.js +97 -0
- package/dist/chunk-EKZFGIVK.js +289 -0
- package/dist/chunk-EQYEWCQO.js +233 -0
- package/dist/chunk-GL6TLHSF.js +215 -0
- package/dist/{chunk-PIFEXJ56.js → chunk-P6VFMSPM.js} +19 -2
- package/dist/chunk-PDX44BCA.js +11 -0
- package/dist/chunk-RR74UAKD.js +163 -0
- package/dist/daemon-GU45VVNU.js +214 -0
- package/dist/position-LKVHTEKX.js +10 -0
- package/dist/server/index.js +190 -12
- package/dist/{session-RFQTJ6WZ.js → session-37TYTCEU.js} +350 -36
- package/dist/store-5CEITPDY.js +14 -0
- package/dist/store-SKFUVSK4.js +19 -0
- package/dist/store-WRLUM7OW.js +16 -0
- package/package.json +6 -1
- package/dist/chunk-RE3UIDLV.js +0 -545
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
configureAlertChannels,
|
|
4
|
+
listAlerts,
|
|
5
|
+
removeAlertById,
|
|
6
|
+
setAlert
|
|
7
|
+
} from "./chunk-6UIRFWG4.js";
|
|
8
|
+
import "./chunk-6F64RPQQ.js";
|
|
9
|
+
import "./chunk-P6VFMSPM.js";
|
|
10
|
+
import "./chunk-PDX44BCA.js";
|
|
11
|
+
export {
|
|
12
|
+
configureAlertChannels,
|
|
13
|
+
listAlerts,
|
|
14
|
+
removeAlertById,
|
|
15
|
+
setAlert
|
|
16
|
+
};
|
package/dist/bin.js
CHANGED
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
loadConfig,
|
|
7
7
|
redactedConfig,
|
|
8
8
|
saveConfig
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-P6VFMSPM.js";
|
|
10
|
+
import "./chunk-PDX44BCA.js";
|
|
10
11
|
|
|
11
12
|
// src/setup/wizard.ts
|
|
12
13
|
import {
|
|
@@ -15,7 +16,6 @@ import {
|
|
|
15
16
|
select,
|
|
16
17
|
text,
|
|
17
18
|
password,
|
|
18
|
-
confirm,
|
|
19
19
|
note,
|
|
20
20
|
cancel,
|
|
21
21
|
isCancel
|
|
@@ -29,7 +29,7 @@ function check(value) {
|
|
|
29
29
|
return value;
|
|
30
30
|
}
|
|
31
31
|
function clientSnippet(client) {
|
|
32
|
-
const entry = `"moly": { "command": "npx", "args": ["@moly/lido", "--server"] }`;
|
|
32
|
+
const entry = `"moly": { "command": "npx", "args": ["@moly-mcp/lido", "--server"] }`;
|
|
33
33
|
switch (client) {
|
|
34
34
|
case "claude-desktop":
|
|
35
35
|
return `Add to ~/Library/Application Support/Claude/claude_desktop_config.json
|
|
@@ -73,6 +73,7 @@ var GEMINI_MODELS = [
|
|
|
73
73
|
{ value: "__custom__", label: "Enter custom model ID" }
|
|
74
74
|
];
|
|
75
75
|
var OPENROUTER_MODELS = [
|
|
76
|
+
{ value: "nvidia/nemotron-3-super-120b-a12b:free", label: "nvidia/nemotron-3-super-120b-a12b:free (default, free)" },
|
|
76
77
|
{ value: "anthropic/claude-sonnet-4-6", label: "anthropic/claude-sonnet-4-6" },
|
|
77
78
|
{ value: "anthropic/claude-opus-4-6", label: "anthropic/claude-opus-4-6" },
|
|
78
79
|
{ value: "google/gemini-2.0-flash", label: "google/gemini-2.0-flash" },
|
|
@@ -95,7 +96,7 @@ async function pickModel(provider) {
|
|
|
95
96
|
return picked;
|
|
96
97
|
}
|
|
97
98
|
async function runWizard() {
|
|
98
|
-
intro(" Moly
|
|
99
|
+
intro(" Moly - Lido MCP Server");
|
|
99
100
|
const network = check(
|
|
100
101
|
await select({
|
|
101
102
|
message: "Which network?",
|
|
@@ -105,6 +106,18 @@ async function runWizard() {
|
|
|
105
106
|
]
|
|
106
107
|
})
|
|
107
108
|
);
|
|
109
|
+
let chainScope = "ethereum";
|
|
110
|
+
if (network === "mainnet") {
|
|
111
|
+
chainScope = check(
|
|
112
|
+
await select({
|
|
113
|
+
message: "Chain scope?",
|
|
114
|
+
options: [
|
|
115
|
+
{ value: "ethereum", label: "Ethereum only (L1 staking, governance, wrapping)" },
|
|
116
|
+
{ value: "all", label: "All chains (L1 + Base/Arbitrum bridging via LI.FI)" }
|
|
117
|
+
]
|
|
118
|
+
})
|
|
119
|
+
);
|
|
120
|
+
}
|
|
108
121
|
const rpcInput = check(
|
|
109
122
|
await text({
|
|
110
123
|
message: "Custom RPC URL? (optional \u2014 leave blank to use public RPC)",
|
|
@@ -122,13 +135,46 @@ async function runWizard() {
|
|
|
122
135
|
})
|
|
123
136
|
);
|
|
124
137
|
let privateKey = null;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
138
|
+
let ows = null;
|
|
139
|
+
const keySource = check(
|
|
140
|
+
await select({
|
|
141
|
+
message: "Key source? (needed for Live mode)",
|
|
142
|
+
options: [
|
|
143
|
+
{ value: "ows", label: "OWS Wallet (encrypted via Open Wallet Standard)" },
|
|
144
|
+
{ value: "raw", label: "Raw private key (stored in ~/.moly/config.json, chmod 600)" },
|
|
145
|
+
{ value: "none", label: "None / Skip" }
|
|
146
|
+
]
|
|
129
147
|
})
|
|
130
148
|
);
|
|
131
|
-
if (
|
|
149
|
+
if (keySource === "ows") {
|
|
150
|
+
let wallets = [];
|
|
151
|
+
try {
|
|
152
|
+
const owsSdk = await import("@open-wallet-standard/core");
|
|
153
|
+
wallets = owsSdk.listWallets();
|
|
154
|
+
} catch {
|
|
155
|
+
note("Could not load OWS. Install it first:\ncurl -fsSL https://openwallet.sh/install.sh | bash\nnpm install @open-wallet-standard/core", "OWS not found");
|
|
156
|
+
}
|
|
157
|
+
if (wallets.length > 0) {
|
|
158
|
+
const walletName = check(
|
|
159
|
+
await select({
|
|
160
|
+
message: "Which OWS wallet?",
|
|
161
|
+
options: wallets.map((w) => ({
|
|
162
|
+
value: w.name,
|
|
163
|
+
label: `${w.name} (${w.address.slice(0, 8)}...)`
|
|
164
|
+
}))
|
|
165
|
+
})
|
|
166
|
+
);
|
|
167
|
+
const passphrase = check(
|
|
168
|
+
await password({
|
|
169
|
+
message: "OWS passphrase:",
|
|
170
|
+
mask: "*"
|
|
171
|
+
})
|
|
172
|
+
);
|
|
173
|
+
ows = { walletName, passphrase: passphrase.trim() };
|
|
174
|
+
} else if (wallets.length === 0) {
|
|
175
|
+
note('No OWS wallets found. Create one first:\nows wallet create --name "moly"', "Empty vault");
|
|
176
|
+
}
|
|
177
|
+
} else if (keySource === "raw") {
|
|
132
178
|
const pk = check(
|
|
133
179
|
await password({
|
|
134
180
|
message: "Private key (0x...):",
|
|
@@ -176,7 +222,9 @@ async function runWizard() {
|
|
|
176
222
|
mode,
|
|
177
223
|
rpc,
|
|
178
224
|
privateKey,
|
|
225
|
+
ows,
|
|
179
226
|
ai,
|
|
227
|
+
chainScope,
|
|
180
228
|
setupComplete: true
|
|
181
229
|
};
|
|
182
230
|
saveConfig(cfg);
|
|
@@ -206,7 +254,7 @@ async function main() {
|
|
|
206
254
|
case "setup": {
|
|
207
255
|
const { cfg, terminalMode } = await runWizard();
|
|
208
256
|
if (terminalMode) {
|
|
209
|
-
const { startChatSession } = await import("./session-
|
|
257
|
+
const { startChatSession } = await import("./session-37TYTCEU.js");
|
|
210
258
|
await startChatSession(cfg);
|
|
211
259
|
} else {
|
|
212
260
|
await startServer();
|
|
@@ -216,7 +264,7 @@ async function main() {
|
|
|
216
264
|
// ── moly config ───────────────────────────────────────────────────
|
|
217
265
|
case "config": {
|
|
218
266
|
if (!configExists()) {
|
|
219
|
-
console.log("No config found. Run: npx @moly/lido");
|
|
267
|
+
console.log("No config found. Run: npx @moly-mcp/lido");
|
|
220
268
|
process.exit(1);
|
|
221
269
|
}
|
|
222
270
|
const cfg = loadConfig();
|
|
@@ -232,14 +280,229 @@ async function main() {
|
|
|
232
280
|
process.exit(0);
|
|
233
281
|
}
|
|
234
282
|
deleteConfig();
|
|
235
|
-
console.log("Config deleted. Run: npx @moly/lido to set up again.");
|
|
283
|
+
console.log("Config deleted. Run: npx @moly-mcp/lido to set up again.");
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
// ── moly alert ─────────────────────────────────────────────────────
|
|
287
|
+
case "alert": {
|
|
288
|
+
const sub = args[1];
|
|
289
|
+
const getFlag = (flag) => {
|
|
290
|
+
const i = args.indexOf(flag);
|
|
291
|
+
return i !== -1 ? args[i + 1] : void 0;
|
|
292
|
+
};
|
|
293
|
+
switch (sub) {
|
|
294
|
+
case "add": {
|
|
295
|
+
const condition = args[2];
|
|
296
|
+
if (!condition) {
|
|
297
|
+
console.log("Usage: moly alert add <condition> [threshold] [--channel webhook]");
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
const threshold = args[3] && !args[3].startsWith("-") ? parseFloat(args[3]) : void 0;
|
|
301
|
+
const channel = getFlag("--channel") ?? "telegram";
|
|
302
|
+
const { setAlert } = await import("./alerts-ARQAPRIT.js");
|
|
303
|
+
const alert = setAlert({ condition, threshold, channel });
|
|
304
|
+
console.log("Alert created:", JSON.stringify(alert, null, 2));
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
case "list": {
|
|
308
|
+
const { listAlerts } = await import("./alerts-ARQAPRIT.js");
|
|
309
|
+
console.log(JSON.stringify(listAlerts(), null, 2));
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
case "remove": {
|
|
313
|
+
const id = args[2];
|
|
314
|
+
if (!id) {
|
|
315
|
+
console.log("Usage: moly alert remove <id>");
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
const { removeAlertById } = await import("./alerts-ARQAPRIT.js");
|
|
319
|
+
console.log(JSON.stringify(removeAlertById(id), null, 2));
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
case "channels": {
|
|
323
|
+
const { configureAlertChannels } = await import("./alerts-ARQAPRIT.js");
|
|
324
|
+
const result = configureAlertChannels({
|
|
325
|
+
telegram_token: getFlag("--telegram-token"),
|
|
326
|
+
telegram_chat_id: getFlag("--telegram-chat"),
|
|
327
|
+
webhook_url: getFlag("--webhook-url")
|
|
328
|
+
});
|
|
329
|
+
console.log("Channels configured:", JSON.stringify(result, null, 2));
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
case "daemon": {
|
|
333
|
+
if (!configExists()) {
|
|
334
|
+
console.log("No config. Run: moly setup");
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
const { runDaemon } = await import("./daemon-GU45VVNU.js");
|
|
338
|
+
await runDaemon();
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
case "start": {
|
|
342
|
+
const { spawn } = await import("child_process");
|
|
343
|
+
const child = spawn(process.argv[0], [process.argv[1], "alert", "daemon"], {
|
|
344
|
+
detached: true,
|
|
345
|
+
stdio: "ignore"
|
|
346
|
+
});
|
|
347
|
+
child.unref();
|
|
348
|
+
console.log(`Alert daemon started (PID: ${child.pid})`);
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
default:
|
|
352
|
+
console.log("Usage: moly alert <add|list|remove|channels|daemon|start>");
|
|
353
|
+
}
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
// ── moly monitor ──────────────────────────────────────────────────
|
|
357
|
+
case "monitor": {
|
|
358
|
+
const sub = args[1];
|
|
359
|
+
switch (sub) {
|
|
360
|
+
case "start": {
|
|
361
|
+
const { spawn } = await import("child_process");
|
|
362
|
+
const child = spawn(process.argv[0], [process.argv[1], "alert", "daemon"], {
|
|
363
|
+
detached: true,
|
|
364
|
+
stdio: "ignore"
|
|
365
|
+
});
|
|
366
|
+
child.unref();
|
|
367
|
+
console.log(`Monitor daemon started (PID: ${child.pid})`);
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
case "status": {
|
|
371
|
+
const { loadAlerts } = await import("./store-SKFUVSK4.js");
|
|
372
|
+
const data = loadAlerts();
|
|
373
|
+
if (!data.daemonPid) {
|
|
374
|
+
console.log("No daemon running (or never started).");
|
|
375
|
+
} else {
|
|
376
|
+
let alive = false;
|
|
377
|
+
try {
|
|
378
|
+
process.kill(data.daemonPid, 0);
|
|
379
|
+
alive = true;
|
|
380
|
+
} catch {
|
|
381
|
+
}
|
|
382
|
+
console.log(`PID: ${data.daemonPid} alive: ${alive}`);
|
|
383
|
+
if (data.daemonStartedAt) console.log(`Started: ${data.daemonStartedAt}`);
|
|
384
|
+
if (data.lastCheckAt) console.log(`Last check: ${data.lastCheckAt}`);
|
|
385
|
+
console.log(`Active alerts: ${data.alerts.filter((a) => a.enabled).length}`);
|
|
386
|
+
}
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
case "stop": {
|
|
390
|
+
const { loadAlerts, saveAlerts } = await import("./store-SKFUVSK4.js");
|
|
391
|
+
const data = loadAlerts();
|
|
392
|
+
if (!data.daemonPid) {
|
|
393
|
+
console.log("No daemon PID recorded.");
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
try {
|
|
397
|
+
process.kill(data.daemonPid, "SIGTERM");
|
|
398
|
+
console.log(`Sent SIGTERM to PID ${data.daemonPid}`);
|
|
399
|
+
} catch {
|
|
400
|
+
console.log(`PID ${data.daemonPid} not running.`);
|
|
401
|
+
}
|
|
402
|
+
data.daemonPid = void 0;
|
|
403
|
+
saveAlerts(data);
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
406
|
+
default:
|
|
407
|
+
console.log("Usage: moly monitor <start|status|stop>");
|
|
408
|
+
}
|
|
409
|
+
break;
|
|
410
|
+
}
|
|
411
|
+
// ── moly bounds ──────────────────────────────────────────────────
|
|
412
|
+
case "bounds": {
|
|
413
|
+
const sub = args[1];
|
|
414
|
+
const getFlag = (flag) => {
|
|
415
|
+
const i = args.indexOf(flag);
|
|
416
|
+
return i !== -1 ? args[i + 1] : void 0;
|
|
417
|
+
};
|
|
418
|
+
switch (sub) {
|
|
419
|
+
case "show":
|
|
420
|
+
case void 0: {
|
|
421
|
+
const { loadBounds } = await import("./store-5CEITPDY.js");
|
|
422
|
+
console.log(JSON.stringify(loadBounds(), null, 2));
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
case "set": {
|
|
426
|
+
const { loadBounds, saveBounds } = await import("./store-5CEITPDY.js");
|
|
427
|
+
const b = loadBounds();
|
|
428
|
+
const mst = getFlag("--max-stake-per-tx");
|
|
429
|
+
const mds = getFlag("--max-daily-stake");
|
|
430
|
+
const mer = getFlag("--min-eth-reserve");
|
|
431
|
+
const art = getFlag("--auto-restake-threshold");
|
|
432
|
+
const gov = getFlag("--governance-auto-vote");
|
|
433
|
+
if (mst) b.maxStakePerTx = parseFloat(mst);
|
|
434
|
+
if (mds) b.maxDailyStake = parseFloat(mds);
|
|
435
|
+
if (mer) b.minEthReserve = parseFloat(mer);
|
|
436
|
+
if (art) b.autoRestakeThreshold = parseFloat(art);
|
|
437
|
+
if (gov) b.governanceAutoVote = gov === "true";
|
|
438
|
+
saveBounds(b);
|
|
439
|
+
console.log("Bounds updated:", JSON.stringify(b, null, 2));
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
case "reset": {
|
|
443
|
+
const { resetBounds } = await import("./store-5CEITPDY.js");
|
|
444
|
+
resetBounds();
|
|
445
|
+
console.log("Bounds reset to defaults.");
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
default:
|
|
449
|
+
console.log("Usage: moly bounds [show|set|reset]");
|
|
450
|
+
console.log(" set flags: --max-stake-per-tx, --max-daily-stake, --min-eth-reserve, --auto-restake-threshold, --governance-auto-vote");
|
|
451
|
+
}
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
// ── moly ledger ──────────────────────────────────────────────────
|
|
455
|
+
case "ledger": {
|
|
456
|
+
const sub = args[1];
|
|
457
|
+
const getFlag = (flag) => {
|
|
458
|
+
const i = args.indexOf(flag);
|
|
459
|
+
return i !== -1 ? args[i + 1] : void 0;
|
|
460
|
+
};
|
|
461
|
+
const { initLedger, queryLedger, ledgerStats, exportLedger } = await import("./store-WRLUM7OW.js");
|
|
462
|
+
initLedger();
|
|
463
|
+
switch (sub) {
|
|
464
|
+
case "list": {
|
|
465
|
+
const tool = getFlag("--tool");
|
|
466
|
+
const since = getFlag("--since");
|
|
467
|
+
const limit = getFlag("--limit");
|
|
468
|
+
const rows = queryLedger({ tool: tool ?? void 0, since: since ?? void 0, limit: limit ? parseInt(limit) : 50 });
|
|
469
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
case "stats": {
|
|
473
|
+
const since = getFlag("--since");
|
|
474
|
+
console.log(JSON.stringify(ledgerStats(since ?? void 0), null, 2));
|
|
475
|
+
break;
|
|
476
|
+
}
|
|
477
|
+
case "export": {
|
|
478
|
+
const format = getFlag("--format") ?? "json";
|
|
479
|
+
console.log(exportLedger(format));
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
default:
|
|
483
|
+
console.log("Usage: moly ledger <list|stats|export>");
|
|
484
|
+
console.log(" list: --tool <name> --since <ISO date> --limit <n>");
|
|
485
|
+
console.log(" export: --format <json|csv>");
|
|
486
|
+
}
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
// ── moly position ────────────────────────────────────────────────
|
|
490
|
+
case "position": {
|
|
491
|
+
if (!configExists()) {
|
|
492
|
+
console.log("No config. Run: moly setup");
|
|
493
|
+
process.exit(1);
|
|
494
|
+
}
|
|
495
|
+
const { getTotalPosition } = await import("./position-LKVHTEKX.js");
|
|
496
|
+
const address = args[1];
|
|
497
|
+
const pos = await getTotalPosition(address);
|
|
498
|
+
console.log(JSON.stringify(pos, null, 2));
|
|
236
499
|
break;
|
|
237
500
|
}
|
|
238
501
|
// ── moly --server (force-start, used in AI client configs) ────────
|
|
239
502
|
case "--server": {
|
|
240
503
|
if (!configExists()) {
|
|
241
504
|
process.stderr.write(
|
|
242
|
-
"ERROR: No config found. Run: npx @moly/lido to set up first.\n"
|
|
505
|
+
"ERROR: No config found. Run: npx @moly-mcp/lido to set up first.\n"
|
|
243
506
|
);
|
|
244
507
|
process.exit(1);
|
|
245
508
|
}
|
|
@@ -251,7 +514,7 @@ async function main() {
|
|
|
251
514
|
if (!configExists()) {
|
|
252
515
|
const { cfg, terminalMode } = await runWizard();
|
|
253
516
|
if (terminalMode) {
|
|
254
|
-
const { startChatSession } = await import("./session-
|
|
517
|
+
const { startChatSession } = await import("./session-37TYTCEU.js");
|
|
255
518
|
await startChatSession(cfg);
|
|
256
519
|
} else {
|
|
257
520
|
await startServer();
|
|
@@ -259,7 +522,7 @@ async function main() {
|
|
|
259
522
|
} else {
|
|
260
523
|
const cfg = loadConfig();
|
|
261
524
|
process.stderr.write(
|
|
262
|
-
`@moly/lido \u2014 starting MCP server (${cfg.mode} \xB7 ${cfg.network})
|
|
525
|
+
`@moly-mcp/lido \u2014 starting MCP server (${cfg.mode} \xB7 ${cfg.network})
|
|
263
526
|
`
|
|
264
527
|
);
|
|
265
528
|
await startServer();
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
loadConfig,
|
|
4
|
+
updateConfig
|
|
5
|
+
} from "./chunk-P6VFMSPM.js";
|
|
6
|
+
|
|
7
|
+
// src/alerts/store.ts
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync, chmodSync } from "fs";
|
|
9
|
+
import { homedir } from "os";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
import { randomUUID } from "crypto";
|
|
12
|
+
var ALERTS_PATH = join(homedir(), ".moly", "alerts.json");
|
|
13
|
+
function loadAlerts() {
|
|
14
|
+
if (!existsSync(ALERTS_PATH)) return { alerts: [] };
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(readFileSync(ALERTS_PATH, "utf-8"));
|
|
17
|
+
} catch {
|
|
18
|
+
return { alerts: [] };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function saveAlerts(data) {
|
|
22
|
+
writeFileSync(ALERTS_PATH, JSON.stringify(data, null, 2), "utf-8");
|
|
23
|
+
try {
|
|
24
|
+
chmodSync(ALERTS_PATH, 384);
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function addAlert(params) {
|
|
29
|
+
const data = loadAlerts();
|
|
30
|
+
const alert = {
|
|
31
|
+
id: randomUUID(),
|
|
32
|
+
condition: params.condition,
|
|
33
|
+
threshold: params.threshold,
|
|
34
|
+
channel: params.channel ?? "telegram",
|
|
35
|
+
enabled: true,
|
|
36
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
37
|
+
};
|
|
38
|
+
data.alerts.push(alert);
|
|
39
|
+
saveAlerts(data);
|
|
40
|
+
return alert;
|
|
41
|
+
}
|
|
42
|
+
function removeAlert(id) {
|
|
43
|
+
const data = loadAlerts();
|
|
44
|
+
const before = data.alerts.length;
|
|
45
|
+
data.alerts = data.alerts.filter((a) => a.id !== id);
|
|
46
|
+
if (data.alerts.length === before) return false;
|
|
47
|
+
saveAlerts(data);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
function loadChannelConfig() {
|
|
51
|
+
const cfg = loadConfig();
|
|
52
|
+
return cfg.alertChannels ?? {};
|
|
53
|
+
}
|
|
54
|
+
function saveChannelConfig(channels) {
|
|
55
|
+
updateConfig({ alertChannels: channels });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
loadAlerts,
|
|
60
|
+
saveAlerts,
|
|
61
|
+
addAlert,
|
|
62
|
+
removeAlert,
|
|
63
|
+
loadChannelConfig,
|
|
64
|
+
saveChannelConfig
|
|
65
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
addAlert,
|
|
4
|
+
loadAlerts,
|
|
5
|
+
loadChannelConfig,
|
|
6
|
+
removeAlert,
|
|
7
|
+
saveChannelConfig
|
|
8
|
+
} from "./chunk-6F64RPQQ.js";
|
|
9
|
+
|
|
10
|
+
// src/tools/alerts.ts
|
|
11
|
+
var VALID_CONDITIONS = [
|
|
12
|
+
"balance_below",
|
|
13
|
+
"balance_above",
|
|
14
|
+
"reward_rate_below",
|
|
15
|
+
"reward_rate_above",
|
|
16
|
+
"withdrawal_ready",
|
|
17
|
+
"proposal_new",
|
|
18
|
+
"conversion_rate_above",
|
|
19
|
+
"conversion_rate_below",
|
|
20
|
+
"reward_delta",
|
|
21
|
+
"governance_expiring"
|
|
22
|
+
];
|
|
23
|
+
var NEEDS_THRESHOLD = /* @__PURE__ */ new Set([
|
|
24
|
+
"balance_below",
|
|
25
|
+
"balance_above",
|
|
26
|
+
"reward_rate_below",
|
|
27
|
+
"reward_rate_above",
|
|
28
|
+
"conversion_rate_above",
|
|
29
|
+
"conversion_rate_below",
|
|
30
|
+
"reward_delta"
|
|
31
|
+
]);
|
|
32
|
+
function setAlert(params) {
|
|
33
|
+
const condition = params.condition;
|
|
34
|
+
if (!VALID_CONDITIONS.includes(condition)) {
|
|
35
|
+
throw new Error(`Invalid condition: ${params.condition}. Valid: ${VALID_CONDITIONS.join(", ")}`);
|
|
36
|
+
}
|
|
37
|
+
if (NEEDS_THRESHOLD.has(condition) && params.threshold === void 0) {
|
|
38
|
+
throw new Error(`Condition "${condition}" requires a threshold value`);
|
|
39
|
+
}
|
|
40
|
+
const channel = params.channel ?? "telegram";
|
|
41
|
+
if (channel !== "telegram" && channel !== "webhook") {
|
|
42
|
+
throw new Error(`Invalid channel: ${params.channel}. Valid: telegram, webhook`);
|
|
43
|
+
}
|
|
44
|
+
return addAlert({ condition, threshold: params.threshold, channel });
|
|
45
|
+
}
|
|
46
|
+
function listAlerts() {
|
|
47
|
+
return loadAlerts();
|
|
48
|
+
}
|
|
49
|
+
function removeAlertById(id) {
|
|
50
|
+
const removed = removeAlert(id);
|
|
51
|
+
return { removed, id };
|
|
52
|
+
}
|
|
53
|
+
function configureAlertChannels(params) {
|
|
54
|
+
const current = loadChannelConfig();
|
|
55
|
+
if (params.telegram_token && params.telegram_chat_id) {
|
|
56
|
+
current.telegram = { token: params.telegram_token, chatId: params.telegram_chat_id };
|
|
57
|
+
}
|
|
58
|
+
if (params.webhook_url) {
|
|
59
|
+
current.webhook = { url: params.webhook_url };
|
|
60
|
+
}
|
|
61
|
+
saveChannelConfig(current);
|
|
62
|
+
return {
|
|
63
|
+
telegram: current.telegram ? { chatId: current.telegram.chatId, token: "*** configured" } : void 0,
|
|
64
|
+
webhook: current.webhook ? { url: current.webhook.url } : void 0
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
setAlert,
|
|
70
|
+
listAlerts,
|
|
71
|
+
removeAlertById,
|
|
72
|
+
configureAlertChannels
|
|
73
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/bounds/store.ts
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync, chmodSync } from "fs";
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
var BOUNDS_PATH = join(homedir(), ".moly", "bounds.json");
|
|
8
|
+
var DEFAULT_BOUNDS = {
|
|
9
|
+
maxStakePerTx: 10,
|
|
10
|
+
maxDailyStake: 50,
|
|
11
|
+
minEthReserve: 0.5,
|
|
12
|
+
autoRestakeThreshold: 0.05,
|
|
13
|
+
governanceAutoVote: false,
|
|
14
|
+
dailyStaked: 0,
|
|
15
|
+
lastResetDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
|
|
16
|
+
};
|
|
17
|
+
function loadBounds() {
|
|
18
|
+
if (!existsSync(BOUNDS_PATH)) return { ...DEFAULT_BOUNDS };
|
|
19
|
+
try {
|
|
20
|
+
return { ...DEFAULT_BOUNDS, ...JSON.parse(readFileSync(BOUNDS_PATH, "utf-8")) };
|
|
21
|
+
} catch {
|
|
22
|
+
return { ...DEFAULT_BOUNDS };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function saveBounds(b) {
|
|
26
|
+
writeFileSync(BOUNDS_PATH, JSON.stringify(b, null, 2), "utf-8");
|
|
27
|
+
try {
|
|
28
|
+
chmodSync(BOUNDS_PATH, 384);
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function resetBounds() {
|
|
33
|
+
if (existsSync(BOUNDS_PATH)) unlinkSync(BOUNDS_PATH);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export {
|
|
37
|
+
DEFAULT_BOUNDS,
|
|
38
|
+
loadBounds,
|
|
39
|
+
saveBounds,
|
|
40
|
+
resetBounds
|
|
41
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
applySettingsUpdate,
|
|
4
|
+
getRuntime
|
|
5
|
+
} from "./chunk-EKZFGIVK.js";
|
|
6
|
+
import {
|
|
7
|
+
loadConfig,
|
|
8
|
+
redactedConfig
|
|
9
|
+
} from "./chunk-P6VFMSPM.js";
|
|
10
|
+
|
|
11
|
+
// src/tools/stake.ts
|
|
12
|
+
import { parseEther, formatEther } from "viem";
|
|
13
|
+
var SUBMIT_ABI = [
|
|
14
|
+
{
|
|
15
|
+
name: "submit",
|
|
16
|
+
type: "function",
|
|
17
|
+
inputs: [{ name: "_referral", type: "address" }],
|
|
18
|
+
outputs: [{ type: "uint256" }],
|
|
19
|
+
stateMutability: "payable"
|
|
20
|
+
}
|
|
21
|
+
];
|
|
22
|
+
var REFERRAL = "0x0000000000000000000000000000000000000000";
|
|
23
|
+
async function stakeEth(amountEth, dryRun) {
|
|
24
|
+
const rt = getRuntime();
|
|
25
|
+
const value = parseEther(amountEth);
|
|
26
|
+
const account = rt.getAddress();
|
|
27
|
+
const lidoAddress = rt.chainAddresses.stETH;
|
|
28
|
+
const shouldDryRun = rt.config.mode === "simulation" ? dryRun !== false : !!dryRun;
|
|
29
|
+
if (shouldDryRun) {
|
|
30
|
+
let estimatedGas = "unavailable";
|
|
31
|
+
try {
|
|
32
|
+
const gas = await rt.publicClient.estimateContractGas({
|
|
33
|
+
address: lidoAddress,
|
|
34
|
+
abi: SUBMIT_ABI,
|
|
35
|
+
functionName: "submit",
|
|
36
|
+
args: [REFERRAL],
|
|
37
|
+
value,
|
|
38
|
+
account: "0x0000000000000000000000000000000000000001"
|
|
39
|
+
});
|
|
40
|
+
estimatedGas = gas.toString();
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
simulated: true,
|
|
45
|
+
mode: rt.config.mode,
|
|
46
|
+
network: rt.chainAddresses.name,
|
|
47
|
+
action: "stake",
|
|
48
|
+
amountEth,
|
|
49
|
+
estimatedGas,
|
|
50
|
+
expectedStETH: amountEth,
|
|
51
|
+
note: "stETH rebases daily \u2014 your balance grows automatically after staking."
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const tx = await rt.sdk.stake.stakeEth({
|
|
55
|
+
value,
|
|
56
|
+
account: { address: account },
|
|
57
|
+
callback: () => {
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
simulated: false,
|
|
62
|
+
mode: rt.config.mode,
|
|
63
|
+
network: rt.chainAddresses.name,
|
|
64
|
+
action: "stake",
|
|
65
|
+
amountEth,
|
|
66
|
+
txHash: tx.hash,
|
|
67
|
+
stethReceived: formatEther(tx.result?.stethReceived ?? 0n),
|
|
68
|
+
sharesReceived: formatEther(tx.result?.sharesReceived ?? 0n)
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/tools/settings.ts
|
|
73
|
+
function getSettings() {
|
|
74
|
+
const cfg = loadConfig();
|
|
75
|
+
return {
|
|
76
|
+
...redactedConfig(cfg),
|
|
77
|
+
note: "Use update_settings to change mode, network, or rpc. Private key and API keys can only be changed via: moly setup"
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function updateSettings(patch) {
|
|
81
|
+
if (Object.keys(patch).length === 0) {
|
|
82
|
+
return { error: "No settings provided to update." };
|
|
83
|
+
}
|
|
84
|
+
const updated = applySettingsUpdate(patch);
|
|
85
|
+
return {
|
|
86
|
+
updated: true,
|
|
87
|
+
changes: patch,
|
|
88
|
+
current: redactedConfig(updated),
|
|
89
|
+
note: "Settings saved and applied. SDK reinitialized with new config."
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export {
|
|
94
|
+
stakeEth,
|
|
95
|
+
getSettings,
|
|
96
|
+
updateSettings
|
|
97
|
+
};
|