@t2000/mcp 0.22.26 → 0.24.0
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/README.md +3 -10
- package/dist/bin.js +122 -508
- package/dist/bin.js.map +1 -1
- package/dist/index.js +122 -508
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
3
3
|
import { readFile } from 'fs/promises';
|
|
4
4
|
import { resolve } from 'path';
|
|
5
5
|
import { homedir } from 'os';
|
|
6
|
-
import { T2000,
|
|
6
|
+
import { T2000, SafeguardError, T2000Error } from '@t2000/sdk';
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
|
|
9
9
|
// src/index.ts
|
|
@@ -58,14 +58,13 @@ function errorResult(err) {
|
|
|
58
58
|
function registerReadTools(server, agent) {
|
|
59
59
|
server.tool(
|
|
60
60
|
"t2000_overview",
|
|
61
|
-
"Complete account snapshot in ONE call \u2014 balance, savings positions,
|
|
61
|
+
"Complete account snapshot in ONE call \u2014 balance, savings positions, health factor, yield earnings, fund status, and pending rewards. Use this for morning briefings, general account questions, or any time you need the full picture. Prefer this over calling individual tools.",
|
|
62
62
|
{},
|
|
63
63
|
async () => {
|
|
64
64
|
try {
|
|
65
|
-
const [balance, positions,
|
|
65
|
+
const [balance, positions, health, earnings, fundStatus, pendingRewards] = await Promise.allSettled([
|
|
66
66
|
agent.balance(),
|
|
67
67
|
agent.positions(),
|
|
68
|
-
agent.getPortfolio(),
|
|
69
68
|
agent.healthFactor(),
|
|
70
69
|
agent.earnings(),
|
|
71
70
|
agent.fundStatus(),
|
|
@@ -74,13 +73,6 @@ function registerReadTools(server, agent) {
|
|
|
74
73
|
const result = {
|
|
75
74
|
balance: balance.status === "fulfilled" ? balance.value : null,
|
|
76
75
|
positions: positions.status === "fulfilled" ? positions.value : null,
|
|
77
|
-
portfolio: portfolio.status === "fulfilled" ? {
|
|
78
|
-
...portfolio.value,
|
|
79
|
-
positions: portfolio.value.positions.map((p) => ({
|
|
80
|
-
...p,
|
|
81
|
-
...p.currentPrice === 0 && p.totalAmount > 0 ? { note: "price unavailable" } : {}
|
|
82
|
-
}))
|
|
83
|
-
} : null,
|
|
84
76
|
health: health.status === "fulfilled" ? health.value : null,
|
|
85
77
|
earnings: earnings.status === "fulfilled" ? earnings.value : null,
|
|
86
78
|
fundStatus: fundStatus.status === "fulfilled" ? fundStatus.value : null,
|
|
@@ -120,7 +112,7 @@ function registerReadTools(server, agent) {
|
|
|
120
112
|
);
|
|
121
113
|
server.tool(
|
|
122
114
|
"t2000_positions",
|
|
123
|
-
"View current lending positions
|
|
115
|
+
"View current lending positions on NAVI \u2014 deposits, borrows, APYs. For a full account snapshot, prefer t2000_overview instead.",
|
|
124
116
|
{},
|
|
125
117
|
async () => {
|
|
126
118
|
try {
|
|
@@ -133,7 +125,7 @@ function registerReadTools(server, agent) {
|
|
|
133
125
|
);
|
|
134
126
|
server.tool(
|
|
135
127
|
"t2000_rates",
|
|
136
|
-
"Get best available interest rates per asset across all lending protocols. Use alongside t2000_positions to compare current vs best rates.
|
|
128
|
+
"Get best available interest rates per asset across all lending protocols. Use alongside t2000_positions to compare current vs best rates.",
|
|
137
129
|
{},
|
|
138
130
|
async () => {
|
|
139
131
|
try {
|
|
@@ -159,7 +151,7 @@ function registerReadTools(server, agent) {
|
|
|
159
151
|
);
|
|
160
152
|
server.tool(
|
|
161
153
|
"t2000_history",
|
|
162
|
-
"View recent transactions \u2014 sends, saves, borrows,
|
|
154
|
+
"View recent transactions \u2014 sends, saves, borrows, MPP (paid API) payments. Each entry includes a transaction digest that can be viewed on Suiscan (https://suiscan.xyz/mainnet/tx/{digest}). Use for activity summaries and weekly recaps.",
|
|
163
155
|
{ limit: z.number().optional().describe("Number of transactions to return (default: 20)") },
|
|
164
156
|
async ({ limit }) => {
|
|
165
157
|
try {
|
|
@@ -224,7 +216,7 @@ function registerReadTools(server, agent) {
|
|
|
224
216
|
);
|
|
225
217
|
server.tool(
|
|
226
218
|
"t2000_all_rates",
|
|
227
|
-
'Compare interest rates across
|
|
219
|
+
'Compare USDC (and other) interest rates across all protocols side-by-side. Use when the user asks "am I getting the best rate?" or wants to compare protocols. NOTE: Deposits are USDC-only \u2014 t2000_save always saves USDC at the best USDC rate. This tool is for informational comparisons.',
|
|
228
220
|
{},
|
|
229
221
|
async () => {
|
|
230
222
|
try {
|
|
@@ -276,26 +268,6 @@ Call t2000_services first to discover the right endpoint, then t2000_pay to exec
|
|
|
276
268
|
}
|
|
277
269
|
}
|
|
278
270
|
);
|
|
279
|
-
server.tool(
|
|
280
|
-
"t2000_portfolio",
|
|
281
|
-
"Show investment portfolio \u2014 positions, cost basis, current value, unrealized/realized P&L, strategy groupings. For a full account snapshot, prefer t2000_overview instead.",
|
|
282
|
-
{},
|
|
283
|
-
async () => {
|
|
284
|
-
try {
|
|
285
|
-
const result = await agent.getPortfolio();
|
|
286
|
-
const enriched = {
|
|
287
|
-
...result,
|
|
288
|
-
positions: result.positions.map((p) => ({
|
|
289
|
-
...p,
|
|
290
|
-
...p.currentPrice === 0 && p.totalAmount > 0 ? { note: "price unavailable" } : {}
|
|
291
|
-
}))
|
|
292
|
-
};
|
|
293
|
-
return { content: [{ type: "text", text: JSON.stringify(enriched) }] };
|
|
294
|
-
} catch (err) {
|
|
295
|
-
return errorResult(err);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
);
|
|
299
271
|
}
|
|
300
272
|
|
|
301
273
|
// src/mutex.ts
|
|
@@ -337,7 +309,7 @@ function registerWriteTools(server, agent) {
|
|
|
337
309
|
const mutex = new TxMutex();
|
|
338
310
|
server.tool(
|
|
339
311
|
"t2000_send",
|
|
340
|
-
"Send USDC
|
|
312
|
+
"Send USDC to a Sui address or contact name. Amount is in dollars. Subject to per-transaction and daily send limits. Set dryRun: true to preview without signing.",
|
|
341
313
|
{
|
|
342
314
|
to: z.string().describe("Recipient Sui address (0x...) or contact name (e.g. 'Tom')"),
|
|
343
315
|
amount: z.number().describe("Amount in dollars to send"),
|
|
@@ -380,17 +352,19 @@ function registerWriteTools(server, agent) {
|
|
|
380
352
|
);
|
|
381
353
|
server.tool(
|
|
382
354
|
"t2000_save",
|
|
383
|
-
'Deposit
|
|
355
|
+
'Deposit to NAVI lending to earn yield. Supports USDC (default), USDT, SUI, USDe, USDsui. Amount is in token units. Use "all" to save entire available balance. Set dryRun: true to preview.',
|
|
384
356
|
{
|
|
385
|
-
amount: z.union([z.number(), z.literal("all")]).describe('
|
|
357
|
+
amount: z.union([z.number(), z.literal("all")]).describe('Amount to save, or "all"'),
|
|
358
|
+
asset: z.enum(["USDC", "USDT", "SUI", "USDe", "USDsui"]).optional().describe("Asset to deposit (default: USDC)"),
|
|
386
359
|
dryRun: z.boolean().optional().describe("Preview without signing (default: false)")
|
|
387
360
|
},
|
|
388
|
-
async ({ amount, dryRun }) => {
|
|
361
|
+
async ({ amount, asset, dryRun }) => {
|
|
389
362
|
try {
|
|
390
363
|
if (dryRun) {
|
|
391
364
|
agent.enforcer.assertNotLocked();
|
|
392
365
|
const balance = await agent.balance();
|
|
393
366
|
const rates = await agent.rates();
|
|
367
|
+
const assetKey = asset ?? "USDC";
|
|
394
368
|
const saveAmount = amount === "all" ? balance.available - 1 : amount;
|
|
395
369
|
return {
|
|
396
370
|
content: [{
|
|
@@ -398,13 +372,14 @@ function registerWriteTools(server, agent) {
|
|
|
398
372
|
text: JSON.stringify({
|
|
399
373
|
preview: true,
|
|
400
374
|
amount: saveAmount,
|
|
401
|
-
|
|
375
|
+
asset: assetKey,
|
|
376
|
+
currentApy: rates[assetKey]?.saveApy ?? rates.USDC?.saveApy ?? 0,
|
|
402
377
|
savingsBalanceAfter: balance.savings + saveAmount
|
|
403
378
|
})
|
|
404
379
|
}]
|
|
405
380
|
};
|
|
406
381
|
}
|
|
407
|
-
const result = await mutex.run(() => agent.save({ amount }));
|
|
382
|
+
const result = await mutex.run(() => agent.save({ amount, asset }));
|
|
408
383
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
409
384
|
} catch (err) {
|
|
410
385
|
return errorResult(err);
|
|
@@ -413,31 +388,33 @@ function registerWriteTools(server, agent) {
|
|
|
413
388
|
);
|
|
414
389
|
server.tool(
|
|
415
390
|
"t2000_withdraw",
|
|
416
|
-
'Withdraw from
|
|
391
|
+
'Withdraw from NAVI lending back to wallet. Supports any deposited asset. Amount is in token units. Use "all" to withdraw everything. Set dryRun: true to preview.',
|
|
417
392
|
{
|
|
418
|
-
amount: z.union([z.number(), z.literal("all")]).describe('
|
|
393
|
+
amount: z.union([z.number(), z.literal("all")]).describe('Amount to withdraw, or "all"'),
|
|
394
|
+
asset: z.string().optional().describe("Asset to withdraw (default: auto-selects largest position)"),
|
|
419
395
|
dryRun: z.boolean().optional().describe("Preview without signing (default: false)")
|
|
420
396
|
},
|
|
421
|
-
async ({ amount, dryRun }) => {
|
|
397
|
+
async ({ amount, asset, dryRun }) => {
|
|
422
398
|
try {
|
|
423
399
|
if (dryRun) {
|
|
424
400
|
agent.enforcer.assertNotLocked();
|
|
425
401
|
const positions = await agent.positions();
|
|
426
402
|
const health = await agent.healthFactor();
|
|
427
|
-
const savings = positions.positions.filter((p) => p.type === "save").reduce((sum, p) => sum + p.amount, 0);
|
|
403
|
+
const savings = positions.positions.filter((p) => p.type === "save" && (!asset || p.asset === asset)).reduce((sum, p) => sum + p.amount, 0);
|
|
428
404
|
return {
|
|
429
405
|
content: [{
|
|
430
406
|
type: "text",
|
|
431
407
|
text: JSON.stringify({
|
|
432
408
|
preview: true,
|
|
433
409
|
amount: amount === "all" ? savings : amount,
|
|
410
|
+
asset: asset ?? "auto",
|
|
434
411
|
currentSavings: savings,
|
|
435
412
|
currentHealthFactor: health.healthFactor
|
|
436
413
|
})
|
|
437
414
|
}]
|
|
438
415
|
};
|
|
439
416
|
}
|
|
440
|
-
const result = await mutex.run(() => agent.withdraw({ amount }));
|
|
417
|
+
const result = await mutex.run(() => agent.withdraw({ amount, asset }));
|
|
441
418
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
442
419
|
} catch (err) {
|
|
443
420
|
return errorResult(err);
|
|
@@ -510,251 +487,9 @@ function registerWriteTools(server, agent) {
|
|
|
510
487
|
}
|
|
511
488
|
}
|
|
512
489
|
);
|
|
513
|
-
server.tool(
|
|
514
|
-
"t2000_swap",
|
|
515
|
-
"Swap assets via Cetus DEX (e.g. USDC to SUI, SUI to USDC). Amount is in source asset units. Set dryRun: true to get a quote without executing.",
|
|
516
|
-
{
|
|
517
|
-
amount: z.number().describe("Amount to swap (in source asset units)"),
|
|
518
|
-
from: z.string().describe("Source asset (e.g. USDC, SUI)"),
|
|
519
|
-
to: z.string().describe("Target asset (e.g. SUI, USDC)"),
|
|
520
|
-
maxSlippage: z.number().optional().describe("Max slippage percentage (default: 3%)"),
|
|
521
|
-
dryRun: z.boolean().optional().describe("Preview without signing (default: false)")
|
|
522
|
-
},
|
|
523
|
-
async ({ amount, from, to, maxSlippage, dryRun }) => {
|
|
524
|
-
try {
|
|
525
|
-
if (dryRun) {
|
|
526
|
-
agent.enforcer.assertNotLocked();
|
|
527
|
-
const quote = await agent.swapQuote({ from, to, amount });
|
|
528
|
-
return {
|
|
529
|
-
content: [{
|
|
530
|
-
type: "text",
|
|
531
|
-
text: JSON.stringify({
|
|
532
|
-
preview: true,
|
|
533
|
-
from,
|
|
534
|
-
to,
|
|
535
|
-
amount,
|
|
536
|
-
expectedOutput: quote.expectedOutput,
|
|
537
|
-
priceImpact: quote.priceImpact,
|
|
538
|
-
fee: quote.fee.amount
|
|
539
|
-
})
|
|
540
|
-
}]
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
const result = await mutex.run(
|
|
544
|
-
() => agent.swap({ from, to, amount, maxSlippage })
|
|
545
|
-
);
|
|
546
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
547
|
-
} catch (err) {
|
|
548
|
-
return errorResult(err);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
);
|
|
552
|
-
const investAssets = Object.keys(INVESTMENT_ASSETS);
|
|
553
|
-
server.tool(
|
|
554
|
-
"t2000_invest",
|
|
555
|
-
"Buy, sell, earn yield, or stop earning on investment assets. Actions: buy (invest USD), sell (convert to USDC), earn (deposit into best-rate lending for yield), unearn (withdraw from lending, keep in portfolio). Amount required for buy/sell only. If checking balance is insufficient for a buy, the SDK will auto-withdraw from savings \u2014 no manual withdraw needed.",
|
|
556
|
-
{
|
|
557
|
-
action: z.enum(["buy", "sell", "earn", "unearn"]).describe("'buy' to invest, 'sell' to liquidate, 'earn' to deposit into lending for yield, 'unearn' to withdraw from lending"),
|
|
558
|
-
asset: z.enum(investAssets).describe("Asset to invest in"),
|
|
559
|
-
amount: z.union([z.number(), z.literal("all")]).optional().describe("USD amount (required for buy/sell, ignored for earn/unearn)"),
|
|
560
|
-
slippage: z.number().optional().describe("Max slippage percent (default: 3, for buy/sell only)"),
|
|
561
|
-
dryRun: z.boolean().optional().describe("Preview without signing (default: false)")
|
|
562
|
-
},
|
|
563
|
-
async ({ action, asset, amount, slippage, dryRun }) => {
|
|
564
|
-
try {
|
|
565
|
-
if (dryRun) {
|
|
566
|
-
agent.enforcer.assertNotLocked();
|
|
567
|
-
const balance = await agent.balance();
|
|
568
|
-
const portfolio = await agent.getPortfolio();
|
|
569
|
-
const position = portfolio.positions.find((p) => p.asset === asset);
|
|
570
|
-
if (action === "sell" && amount === "all" && !position) {
|
|
571
|
-
return {
|
|
572
|
-
content: [{ type: "text", text: JSON.stringify({ preview: true, error: `No ${asset} position to sell` }) }]
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
return {
|
|
576
|
-
content: [{
|
|
577
|
-
type: "text",
|
|
578
|
-
text: JSON.stringify({
|
|
579
|
-
preview: true,
|
|
580
|
-
action,
|
|
581
|
-
asset,
|
|
582
|
-
amount: amount === "all" ? position?.currentValue ?? 0 : amount ?? position?.totalAmount ?? 0,
|
|
583
|
-
currentBalance: balance.available,
|
|
584
|
-
currentPosition: position ?? null,
|
|
585
|
-
earning: position?.earning ?? false,
|
|
586
|
-
earningProtocol: position?.earningProtocol ?? null,
|
|
587
|
-
earningApy: position?.earningApy ?? null
|
|
588
|
-
})
|
|
589
|
-
}]
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
|
-
const maxSlippage = slippage ? slippage / 100 : void 0;
|
|
593
|
-
if (action === "buy") {
|
|
594
|
-
if (typeof amount !== "number") throw new Error("Buy amount must be a number");
|
|
595
|
-
const result = await mutex.run(() => agent.investBuy({ asset, usdAmount: amount, maxSlippage }));
|
|
596
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
597
|
-
} else if (action === "sell") {
|
|
598
|
-
const usdAmount = amount === "all" ? "all" : amount;
|
|
599
|
-
const result = await mutex.run(() => agent.investSell({ asset, usdAmount, maxSlippage }));
|
|
600
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
601
|
-
} else if (action === "earn") {
|
|
602
|
-
const result = await mutex.run(() => agent.investEarn({ asset }));
|
|
603
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
604
|
-
} else {
|
|
605
|
-
const result = await mutex.run(() => agent.investUnearn({ asset }));
|
|
606
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
607
|
-
}
|
|
608
|
-
} catch (err) {
|
|
609
|
-
return errorResult(err);
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
);
|
|
613
|
-
server.tool(
|
|
614
|
-
"t2000_invest_rebalance",
|
|
615
|
-
"Move earning investment positions to better-rate protocols. Checks all earning assets and moves any where another protocol offers a higher APY.",
|
|
616
|
-
{
|
|
617
|
-
dryRun: z.boolean().optional().describe("Preview moves without executing (default: false)"),
|
|
618
|
-
minYieldDiff: z.number().optional().describe("Minimum APY difference to trigger a move (default: 0.1)")
|
|
619
|
-
},
|
|
620
|
-
async ({ dryRun, minYieldDiff }) => {
|
|
621
|
-
try {
|
|
622
|
-
const result = await mutex.run(() => agent.investRebalance({ dryRun, minYieldDiff }));
|
|
623
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
624
|
-
} catch (err) {
|
|
625
|
-
return errorResult(err);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
);
|
|
629
|
-
server.tool(
|
|
630
|
-
"t2000_strategy",
|
|
631
|
-
'Manage investment strategies \u2014 buy into predefined or custom allocations, sell entire strategies, check status, rebalance, or create/delete custom strategies. IMPORTANT: Before buying, ALWAYS call with action "list" first to show the user the strategy allocations (e.g. All-Weather = 30% BTC, 20% ETH, 20% SUI, 30% GOLD), then use dryRun: true to preview estimated amounts and prices. Only execute after the user confirms. If checking balance is insufficient, the SDK will auto-withdraw from savings \u2014 no manual withdraw needed.',
|
|
632
|
-
{
|
|
633
|
-
action: z.enum(["list", "buy", "sell", "status", "rebalance", "create", "delete"]).describe("Strategy action to perform"),
|
|
634
|
-
name: z.string().optional().describe("Strategy name (required for all actions except 'list')"),
|
|
635
|
-
amount: z.number().optional().describe("USD amount (required for 'buy')"),
|
|
636
|
-
allocations: z.record(z.number()).optional().describe("Allocation map e.g. {SUI: 40, BTC: 20, ETH: 20, GOLD: 20} (for 'create')"),
|
|
637
|
-
description: z.string().optional().describe("Strategy description (for 'create')"),
|
|
638
|
-
dryRun: z.boolean().optional().describe("Preview without signing (for 'buy')")
|
|
639
|
-
},
|
|
640
|
-
async ({ action, name, amount, allocations, description, dryRun }) => {
|
|
641
|
-
try {
|
|
642
|
-
if (action === "list") {
|
|
643
|
-
const all = agent.strategies.getAll();
|
|
644
|
-
return { content: [{ type: "text", text: JSON.stringify(all) }] };
|
|
645
|
-
}
|
|
646
|
-
if (!name) {
|
|
647
|
-
return { content: [{ type: "text", text: JSON.stringify({ error: "Strategy name is required" }) }] };
|
|
648
|
-
}
|
|
649
|
-
switch (action) {
|
|
650
|
-
case "buy": {
|
|
651
|
-
if (typeof amount !== "number") {
|
|
652
|
-
return { content: [{ type: "text", text: JSON.stringify({ error: "Amount is required for buy" }) }] };
|
|
653
|
-
}
|
|
654
|
-
const result = await mutex.run(() => agent.investStrategy({ strategy: name, usdAmount: amount, dryRun }));
|
|
655
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
656
|
-
}
|
|
657
|
-
case "sell": {
|
|
658
|
-
const result = await mutex.run(() => agent.sellStrategy({ strategy: name }));
|
|
659
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
660
|
-
}
|
|
661
|
-
case "status": {
|
|
662
|
-
const result = await agent.getStrategyStatus(name);
|
|
663
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
664
|
-
}
|
|
665
|
-
case "rebalance": {
|
|
666
|
-
const result = await mutex.run(() => agent.rebalanceStrategy({ strategy: name }));
|
|
667
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
668
|
-
}
|
|
669
|
-
case "create": {
|
|
670
|
-
if (!allocations) {
|
|
671
|
-
return { content: [{ type: "text", text: JSON.stringify({ error: "Allocations required for create" }) }] };
|
|
672
|
-
}
|
|
673
|
-
const def = agent.strategies.create({ name, allocations, description });
|
|
674
|
-
return { content: [{ type: "text", text: JSON.stringify(def) }] };
|
|
675
|
-
}
|
|
676
|
-
case "delete": {
|
|
677
|
-
agent.strategies.delete(name);
|
|
678
|
-
return { content: [{ type: "text", text: JSON.stringify({ deleted: name }) }] };
|
|
679
|
-
}
|
|
680
|
-
default:
|
|
681
|
-
return { content: [{ type: "text", text: JSON.stringify({ error: `Unknown action: ${action}` }) }] };
|
|
682
|
-
}
|
|
683
|
-
} catch (err) {
|
|
684
|
-
return errorResult(err);
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
);
|
|
688
|
-
server.tool(
|
|
689
|
-
"t2000_auto_invest",
|
|
690
|
-
"Dollar-cost averaging (DCA) \u2014 set up recurring purchases into strategies or individual assets. Actions: setup, status, run, stop.",
|
|
691
|
-
{
|
|
692
|
-
action: z.enum(["setup", "status", "run", "stop"]).describe("Auto-invest action"),
|
|
693
|
-
amount: z.number().optional().describe("USD amount per purchase (for 'setup')"),
|
|
694
|
-
frequency: z.enum(["daily", "weekly", "monthly"]).optional().describe("Purchase frequency (for 'setup')"),
|
|
695
|
-
strategy: z.string().optional().describe("Strategy name (for 'setup')"),
|
|
696
|
-
asset: z.string().optional().describe("Single asset (for 'setup', alternative to strategy)"),
|
|
697
|
-
scheduleId: z.string().optional().describe("Schedule ID (for 'stop')")
|
|
698
|
-
},
|
|
699
|
-
async ({ action, amount, frequency, strategy, asset, scheduleId }) => {
|
|
700
|
-
try {
|
|
701
|
-
switch (action) {
|
|
702
|
-
case "setup": {
|
|
703
|
-
if (!amount || !frequency) {
|
|
704
|
-
return { content: [{ type: "text", text: JSON.stringify({ error: "Amount and frequency required for setup" }) }] };
|
|
705
|
-
}
|
|
706
|
-
const schedule = agent.setupAutoInvest({ amount, frequency, strategy, asset });
|
|
707
|
-
return { content: [{ type: "text", text: JSON.stringify(schedule) }] };
|
|
708
|
-
}
|
|
709
|
-
case "status": {
|
|
710
|
-
const status = agent.getAutoInvestStatus();
|
|
711
|
-
return { content: [{ type: "text", text: JSON.stringify(status) }] };
|
|
712
|
-
}
|
|
713
|
-
case "run": {
|
|
714
|
-
const result = await mutex.run(() => agent.runAutoInvest());
|
|
715
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
716
|
-
}
|
|
717
|
-
case "stop": {
|
|
718
|
-
if (!scheduleId) {
|
|
719
|
-
return { content: [{ type: "text", text: JSON.stringify({ error: "Schedule ID required for stop" }) }] };
|
|
720
|
-
}
|
|
721
|
-
agent.stopAutoInvest(scheduleId);
|
|
722
|
-
return { content: [{ type: "text", text: JSON.stringify({ stopped: scheduleId }) }] };
|
|
723
|
-
}
|
|
724
|
-
default:
|
|
725
|
-
return { content: [{ type: "text", text: JSON.stringify({ error: `Unknown action: ${action}` }) }] };
|
|
726
|
-
}
|
|
727
|
-
} catch (err) {
|
|
728
|
-
return errorResult(err);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
);
|
|
732
|
-
server.tool(
|
|
733
|
-
"t2000_rebalance",
|
|
734
|
-
'Optimize savings yield \u2014 automatically finds the best rate across ALL assets and protocols, then handles everything: withdraw \u2192 swap \u2192 re-deposit. Works across different assets (e.g. USDC \u2192 USDe for higher APY). Always previews first \u2014 set dryRun: false to execute. Shows plan with APY gain, annual earnings increase, and break-even period. This is the ONE tool to use when the user asks "am I getting the best yield?" or "rebalance my savings".',
|
|
735
|
-
{
|
|
736
|
-
dryRun: z.boolean().optional().describe("Preview without executing (default: true)"),
|
|
737
|
-
minYieldDiff: z.number().optional().describe("Min APY difference to rebalance (default: 0.5%)"),
|
|
738
|
-
maxBreakEven: z.number().optional().describe("Max break-even days (default: 30)")
|
|
739
|
-
},
|
|
740
|
-
async ({ dryRun, minYieldDiff, maxBreakEven }) => {
|
|
741
|
-
try {
|
|
742
|
-
const result = await mutex.run(
|
|
743
|
-
() => agent.rebalance({
|
|
744
|
-
dryRun: dryRun ?? true,
|
|
745
|
-
minYieldDiff,
|
|
746
|
-
maxBreakEven
|
|
747
|
-
})
|
|
748
|
-
);
|
|
749
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
750
|
-
} catch (err) {
|
|
751
|
-
return errorResult(err);
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
);
|
|
755
490
|
server.tool(
|
|
756
491
|
"t2000_claim_rewards",
|
|
757
|
-
"Claim pending protocol rewards from lending positions
|
|
492
|
+
"Claim pending protocol rewards from lending positions to your wallet.",
|
|
758
493
|
{},
|
|
759
494
|
async () => {
|
|
760
495
|
try {
|
|
@@ -831,6 +566,54 @@ ${text}`;
|
|
|
831
566
|
}
|
|
832
567
|
}
|
|
833
568
|
);
|
|
569
|
+
server.tool(
|
|
570
|
+
"t2000_swap",
|
|
571
|
+
"Swap tokens on Sui via Cetus Aggregator (20+ DEXs). Supports any token pair with liquidity. Use user-friendly names (SUI, USDC, CETUS, DEEP, etc.) or full coin types.",
|
|
572
|
+
{
|
|
573
|
+
from: z.string().describe('Source token (e.g. "SUI", "USDC", or full coin type)'),
|
|
574
|
+
to: z.string().describe('Target token (e.g. "USDC", "CETUS", or full coin type)'),
|
|
575
|
+
amount: z.number().positive().describe("Amount to swap"),
|
|
576
|
+
slippage: z.number().min(1e-3).max(0.05).optional().describe("Max slippage (default 0.01 = 1%, max 5%)")
|
|
577
|
+
},
|
|
578
|
+
async ({ from, to, amount, slippage }) => {
|
|
579
|
+
try {
|
|
580
|
+
const result = await mutex.run(() => agent.swap({ from, to, amount, slippage }));
|
|
581
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
582
|
+
} catch (err) {
|
|
583
|
+
return errorResult(err);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
);
|
|
587
|
+
server.tool(
|
|
588
|
+
"t2000_stake",
|
|
589
|
+
"Stake SUI for vSUI via VOLO liquid staking. Earn ~3-5% APY. Rewards compound automatically via exchange rate. Minimum 1 SUI.",
|
|
590
|
+
{
|
|
591
|
+
amount: z.number().min(1).describe("Amount of SUI to stake (minimum 1)")
|
|
592
|
+
},
|
|
593
|
+
async ({ amount }) => {
|
|
594
|
+
try {
|
|
595
|
+
const result = await mutex.run(() => agent.stakeVSui({ amount }));
|
|
596
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
597
|
+
} catch (err) {
|
|
598
|
+
return errorResult(err);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
);
|
|
602
|
+
server.tool(
|
|
603
|
+
"t2000_unstake",
|
|
604
|
+
'Unstake vSUI back to SUI. Returns SUI including accumulated yield. Use amount in vSUI units or "all" to unstake entire position.',
|
|
605
|
+
{
|
|
606
|
+
amount: z.union([z.number().positive(), z.literal("all")]).describe('Amount of vSUI to unstake, or "all"')
|
|
607
|
+
},
|
|
608
|
+
async ({ amount }) => {
|
|
609
|
+
try {
|
|
610
|
+
const result = await mutex.run(() => agent.unstakeVSui({ amount }));
|
|
611
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
612
|
+
} catch (err) {
|
|
613
|
+
return errorResult(err);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
);
|
|
834
617
|
server.tool(
|
|
835
618
|
"t2000_contact_add",
|
|
836
619
|
'Save a contact name \u2192 Sui address mapping. After saving, use the name with t2000_send instead of pasting addresses. Example: save "Tom" as 0x1234... then send to "Tom".',
|
|
@@ -946,7 +729,7 @@ function registerPrompts(server) {
|
|
|
946
729
|
text: [
|
|
947
730
|
"You are a financial assistant for a t2000 AI agent bank account on Sui.",
|
|
948
731
|
"",
|
|
949
|
-
"IMPORTANT: Call t2000_overview FIRST \u2014 it returns everything (balance, positions,
|
|
732
|
+
"IMPORTANT: Call t2000_overview FIRST \u2014 it returns everything (balance, positions, health, earnings, fund status, pending rewards) in one call.",
|
|
950
733
|
"Then call t2000_rates for rate comparison across protocols.",
|
|
951
734
|
"",
|
|
952
735
|
"Present a comprehensive financial report:",
|
|
@@ -955,12 +738,11 @@ function registerPrompts(server) {
|
|
|
955
738
|
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
956
739
|
"",
|
|
957
740
|
"\u{1F4B0} Accounts",
|
|
958
|
-
" Checking / Savings / Credit
|
|
741
|
+
" Checking / Savings / Credit with totals",
|
|
959
742
|
" Net worth",
|
|
960
743
|
"",
|
|
961
744
|
"\u{1F4C8} Positions",
|
|
962
745
|
" Each savings position: protocol, asset, amount, APY",
|
|
963
|
-
" Each investment position: asset, amount, cost basis, current value, P&L",
|
|
964
746
|
"",
|
|
965
747
|
"\u{1F4B8} Yield",
|
|
966
748
|
" Current APY, daily/monthly/projected earnings",
|
|
@@ -980,7 +762,7 @@ function registerPrompts(server) {
|
|
|
980
762
|
);
|
|
981
763
|
server.prompt(
|
|
982
764
|
"optimize-yield",
|
|
983
|
-
"Analyze savings
|
|
765
|
+
"Analyze USDC savings \u2014 rate comparisons across protocols, idle funds, claimable rewards.",
|
|
984
766
|
async () => ({
|
|
985
767
|
messages: [{
|
|
986
768
|
role: "user",
|
|
@@ -991,29 +773,24 @@ function registerPrompts(server) {
|
|
|
991
773
|
"",
|
|
992
774
|
"IMPORTANT: Call these tools in parallel:",
|
|
993
775
|
" 1. t2000_overview \u2014 full account state including positions and pending rewards",
|
|
994
|
-
" 2. t2000_rates \u2014
|
|
995
|
-
" 3.
|
|
776
|
+
" 2. t2000_rates \u2014 USDC save and borrow rates",
|
|
777
|
+
" 3. t2000_all_rates \u2014 compare USDC lending APYs across protocols (informational)",
|
|
996
778
|
"",
|
|
997
779
|
"Present a structured yield analysis:",
|
|
998
780
|
"",
|
|
999
781
|
"\u{1F4CA} YIELD ANALYSIS",
|
|
1000
782
|
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1001
783
|
"",
|
|
1002
|
-
" Current
|
|
1003
|
-
" Best
|
|
1004
|
-
" APY
|
|
1005
|
-
" Break-even X days",
|
|
784
|
+
" Current USDC on [protocol] \xB7 X.XX% APY",
|
|
785
|
+
" Best USDC [protocol] \xB7 X.XX% APY",
|
|
786
|
+
" APY gap +X.XX% (if any)",
|
|
1006
787
|
"",
|
|
1007
|
-
|
|
1008
|
-
`If already
|
|
788
|
+
"t2000_save always deposits USDC at the best available USDC rate. If the user is already deposited elsewhere at a lower APY, explain the gap; moving funds requires withdraw then save (preview with dryRun: true).",
|
|
789
|
+
`If already at the best USDC rate: "You're at the best available USDC savings rate."`,
|
|
1009
790
|
"",
|
|
1010
791
|
"Also check:",
|
|
1011
|
-
" - Idle checking funds that could be earning yield",
|
|
1012
|
-
" -
|
|
1013
|
-
" - Claimable protocol rewards (suggest t2000_claim_rewards)",
|
|
1014
|
-
"",
|
|
1015
|
-
"On user confirmation, execute t2000_rebalance (dryRun: false).",
|
|
1016
|
-
"After execution, show: new APY, amount moved, transaction link."
|
|
792
|
+
" - Idle checking funds that could be earning yield (t2000_save)",
|
|
793
|
+
" - Claimable protocol rewards (suggest t2000_claim_rewards)"
|
|
1017
794
|
].join("\n")
|
|
1018
795
|
}
|
|
1019
796
|
}]
|
|
@@ -1101,10 +878,9 @@ ${context}
|
|
|
1101
878
|
"",
|
|
1102
879
|
"Analyze and recommend:",
|
|
1103
880
|
" - How much idle checking can move to savings (keep ~$5 buffer for gas)",
|
|
1104
|
-
" - Which protocol
|
|
881
|
+
" - Which protocol offers the best USDC savings APY (use t2000_all_rates)",
|
|
1105
882
|
" - Expected monthly/annual yield on the recommended amount",
|
|
1106
|
-
" -
|
|
1107
|
-
" - Whether investment assets could earn yield via t2000_invest earn",
|
|
883
|
+
" - Whether current USDC savings APY lags the best available (informational; new deposits use t2000_save at best USDC rate)",
|
|
1108
884
|
"",
|
|
1109
885
|
"If they want to proceed, use t2000_save to deposit."
|
|
1110
886
|
].join("\n")
|
|
@@ -1112,44 +888,9 @@ ${context}
|
|
|
1112
888
|
}]
|
|
1113
889
|
})
|
|
1114
890
|
);
|
|
1115
|
-
server.prompt(
|
|
1116
|
-
"investment-strategy",
|
|
1117
|
-
"Analyze investment portfolio, suggest strategies, review DCA schedules, and recommend next steps.",
|
|
1118
|
-
async () => ({
|
|
1119
|
-
messages: [{
|
|
1120
|
-
role: "user",
|
|
1121
|
-
content: {
|
|
1122
|
-
type: "text",
|
|
1123
|
-
text: [
|
|
1124
|
-
"You are an investment advisor for a t2000 AI agent bank account on Sui.",
|
|
1125
|
-
"",
|
|
1126
|
-
'IMPORTANT: Call t2000_overview FIRST, then t2000_strategy (action: "list") and t2000_auto_invest (action: "status") in parallel.',
|
|
1127
|
-
"",
|
|
1128
|
-
"Analyze and recommend:",
|
|
1129
|
-
" - Portfolio allocation (checking vs savings vs investment)",
|
|
1130
|
-
" - Best strategy for their profile (bluechip, all-weather, layer1, sui-heavy, safe-haven)",
|
|
1131
|
-
" - If strategy positions are drifting from target weights, suggest rebalancing",
|
|
1132
|
-
" - If no DCA schedule exists, recommend setting one up",
|
|
1133
|
-
" - Whether invested assets should earn yield (t2000_invest earn)",
|
|
1134
|
-
" - Risk: concentration, unrealized losses, strategy drift",
|
|
1135
|
-
"",
|
|
1136
|
-
"STRATEGY PRESENTATION: Always show the strategy allocations before buying.",
|
|
1137
|
-
' e.g. "All-Weather: 30% BTC, 20% ETH, 20% SUI, 30% GOLD \u2014 diversified crypto + commodities"',
|
|
1138
|
-
" Then use dryRun: true to preview estimated prices and amounts.",
|
|
1139
|
-
"",
|
|
1140
|
-
"AUTO-FUNDING: If checking balance is insufficient but savings exist,",
|
|
1141
|
-
" the SDK auto-withdraws from savings to fund the investment.",
|
|
1142
|
-
' Do NOT manually withdraw first \u2014 just call t2000_strategy buy or t2000_invest with action: "buy" directly.',
|
|
1143
|
-
"",
|
|
1144
|
-
'For DCA: use t2000_auto_invest action: "setup" to create recurring buys.'
|
|
1145
|
-
].join("\n")
|
|
1146
|
-
}
|
|
1147
|
-
}]
|
|
1148
|
-
})
|
|
1149
|
-
);
|
|
1150
891
|
server.prompt(
|
|
1151
892
|
"morning-briefing",
|
|
1152
|
-
"Daily financial snapshot \u2014 balance changes, yield earned,
|
|
893
|
+
"Daily financial snapshot \u2014 balance changes, yield earned, savings performance, health warnings.",
|
|
1153
894
|
async () => ({
|
|
1154
895
|
messages: [{
|
|
1155
896
|
role: "user",
|
|
@@ -1158,9 +899,8 @@ ${context}
|
|
|
1158
899
|
text: [
|
|
1159
900
|
"You are a personal financial briefing assistant for a t2000 AI agent bank account on Sui.",
|
|
1160
901
|
"",
|
|
1161
|
-
"IMPORTANT: Call t2000_overview FIRST \u2014 it returns balance, positions,
|
|
1162
|
-
|
|
1163
|
-
"Optionally call t2000_rebalance (dryRun: true) to check yield optimization opportunities.",
|
|
902
|
+
"IMPORTANT: Call t2000_overview FIRST \u2014 it returns balance, positions, health, earnings, fund status, and pending rewards in one call.",
|
|
903
|
+
"Optionally call t2000_all_rates to compare USDC APYs across protocols.",
|
|
1164
904
|
"",
|
|
1165
905
|
"Present everything as a single structured briefing. NEVER ask follow-up questions before presenting the briefing.",
|
|
1166
906
|
"",
|
|
@@ -1171,18 +911,15 @@ ${context}
|
|
|
1171
911
|
" Checking $XX.XX",
|
|
1172
912
|
" Savings $XX.XX \xB7 X.XX% APY",
|
|
1173
913
|
" Credit -$XX.XX (only if borrowed)",
|
|
1174
|
-
" Investment $XX.XX \xB7 +X.X% (only if positions exist)",
|
|
1175
914
|
" Net Worth $XX.XX",
|
|
1176
915
|
"",
|
|
1177
916
|
"If there are savings positions, show daily yield earned.",
|
|
1178
|
-
"If there are investment positions, show each asset with P&L.",
|
|
1179
917
|
"If pending rewards exist, mention them.",
|
|
1180
918
|
"",
|
|
1181
919
|
"\u{1F4CB} Action Items (max 4, only if applicable):",
|
|
1182
|
-
" - Idle funds in checking \u2192 suggest saving
|
|
920
|
+
" - Idle funds in checking \u2192 suggest saving",
|
|
1183
921
|
" - Outstanding debt \u2192 suggest repaying to stop interest",
|
|
1184
|
-
" -
|
|
1185
|
-
" - Better yield available \u2192 suggest rebalancing",
|
|
922
|
+
" - Better USDC yield elsewhere \u2192 note APY gap; suggest reviewing savings placement",
|
|
1186
923
|
" - Claimable rewards \u2192 suggest claiming",
|
|
1187
924
|
" - Low health factor \u2192 warn about liquidation risk",
|
|
1188
925
|
"",
|
|
@@ -1194,9 +931,9 @@ ${context}
|
|
|
1194
931
|
);
|
|
1195
932
|
server.prompt(
|
|
1196
933
|
"what-if",
|
|
1197
|
-
'Scenario planning \u2014 "What if I
|
|
934
|
+
'Scenario planning \u2014 "What if I save $X?" or "What if I borrow $X?" Shows projected impact on yield and risk.',
|
|
1198
935
|
{
|
|
1199
|
-
scenario: z.string().optional().describe('Scenario to evaluate, e.g. "
|
|
936
|
+
scenario: z.string().optional().describe('Scenario to evaluate, e.g. "save $500" or "withdraw all savings" or "borrow $200"')
|
|
1200
937
|
},
|
|
1201
938
|
async ({ scenario }) => ({
|
|
1202
939
|
messages: [{
|
|
@@ -1208,24 +945,15 @@ ${context}
|
|
|
1208
945
|
"",
|
|
1209
946
|
scenario ? `The user wants to evaluate this scenario: "${scenario}"` : "The user wants to explore a hypothetical financial scenario. Ask them what they're considering.",
|
|
1210
947
|
"",
|
|
1211
|
-
"IMPORTANT: Call t2000_overview FIRST to get the full current state (balance, positions,
|
|
948
|
+
"IMPORTANT: Call t2000_overview FIRST to get the full current state (balance, positions, health).",
|
|
1212
949
|
"Then preview the specific action with dryRun: true.",
|
|
1213
950
|
"",
|
|
1214
|
-
'For INVESTMENT scenarios ("invest $X in Y" or "buy $X of strategy"):',
|
|
1215
|
-
` - Call t2000_strategy (action: "list") to get allocations if it's a strategy`,
|
|
1216
|
-
' - ALWAYS show strategy allocations (e.g. "30% BTC, 20% ETH, 20% SUI, 30% GOLD")',
|
|
1217
|
-
" - Call t2000_invest or t2000_strategy with dryRun: true to preview",
|
|
1218
|
-
" - If checking is insufficient but savings exist, note that the SDK auto-withdraws \u2014 no manual step needed",
|
|
1219
|
-
"",
|
|
1220
951
|
'For SAVINGS scenarios ("save $X" or "withdraw $X"):',
|
|
1221
952
|
" - Show impact on yield and health factor",
|
|
1222
953
|
"",
|
|
1223
954
|
'For BORROW scenarios ("borrow $X"):',
|
|
1224
955
|
" - Show new health factor and interest cost",
|
|
1225
956
|
"",
|
|
1226
|
-
'For SWAP scenarios ("swap $X A to B", "buy $X BTC", "sell 0.1 ETH"):',
|
|
1227
|
-
" - Call t2000_swap with dryRun: true",
|
|
1228
|
-
"",
|
|
1229
957
|
"ALWAYS present results as a BEFORE \u2192 AFTER comparison table:",
|
|
1230
958
|
"",
|
|
1231
959
|
"\u{1F4CA} SCENARIO: [description]",
|
|
@@ -1234,12 +962,9 @@ ${context}
|
|
|
1234
962
|
" Before After",
|
|
1235
963
|
" Checking $XX.XX $XX.XX",
|
|
1236
964
|
" Savings $XX.XX $XX.XX",
|
|
1237
|
-
" Investment $XX.XX $XX.XX",
|
|
1238
965
|
"",
|
|
1239
966
|
"Then add a smart recommendation:",
|
|
1240
|
-
" - If amount exceeds checking, note that savings will be auto-withdrawn to fund it",
|
|
1241
967
|
" - If it would drain total funds (checking + savings) below $5, warn about gas needs",
|
|
1242
|
-
" - If the asset can earn yield after buying, mention it",
|
|
1243
968
|
"",
|
|
1244
969
|
'End with: "Want me to go ahead?" \u2014 ready to execute on confirmation.'
|
|
1245
970
|
].join("\n")
|
|
@@ -1249,7 +974,7 @@ ${context}
|
|
|
1249
974
|
);
|
|
1250
975
|
server.prompt(
|
|
1251
976
|
"sweep",
|
|
1252
|
-
"Find idle funds in checking and
|
|
977
|
+
"Find idle funds in checking and move to savings for maximum yield.",
|
|
1253
978
|
{
|
|
1254
979
|
keepBuffer: z.number().optional().describe("Dollar amount to keep in checking as spending buffer (default: $5)")
|
|
1255
980
|
},
|
|
@@ -1275,10 +1000,9 @@ ${context}
|
|
|
1275
1000
|
`Available to sweep: $X (keeping $${buffer} buffer)`,
|
|
1276
1001
|
"",
|
|
1277
1002
|
"Actions (in order):",
|
|
1278
|
-
" 1. Save $X to best
|
|
1279
|
-
" 2.
|
|
1280
|
-
" 3.
|
|
1281
|
-
" 4. Claim pending rewards (if available)",
|
|
1003
|
+
" 1. Save $X to best USDC rate (t2000_save; show APY from t2000_rates / t2000_all_rates)",
|
|
1004
|
+
" 2. Claim pending rewards (if available)",
|
|
1005
|
+
" 3. Optionally compare USDC APYs across protocols (t2000_all_rates) \u2014 informational",
|
|
1282
1006
|
"",
|
|
1283
1007
|
"Projected monthly yield: $X.XX (before) \u2192 $X.XX (after)",
|
|
1284
1008
|
"",
|
|
@@ -1300,7 +1024,7 @@ ${context}
|
|
|
1300
1024
|
text: [
|
|
1301
1025
|
"You are a risk assessment specialist for a t2000 AI agent bank account on Sui.",
|
|
1302
1026
|
"",
|
|
1303
|
-
"IMPORTANT: Call t2000_overview FIRST \u2014 it has balance, positions,
|
|
1027
|
+
"IMPORTANT: Call t2000_overview FIRST \u2014 it has balance, positions, health, everything.",
|
|
1304
1028
|
"",
|
|
1305
1029
|
"Analyze and report:",
|
|
1306
1030
|
"",
|
|
@@ -1309,9 +1033,8 @@ ${context}
|
|
|
1309
1033
|
"",
|
|
1310
1034
|
"1. LIQUIDATION RISK \u2014 Health factor, distance to liquidation",
|
|
1311
1035
|
"2. CONCENTRATION RISK \u2014 % in each account type, % per asset",
|
|
1312
|
-
"3. PROTOCOL EXPOSURE \u2014
|
|
1313
|
-
"4.
|
|
1314
|
-
"5. YIELD EFFICIENCY \u2014 Idle assets, sub-optimal rates",
|
|
1036
|
+
"3. PROTOCOL EXPOSURE \u2014 Lending protocol distribution",
|
|
1037
|
+
"4. YIELD EFFICIENCY \u2014 Idle assets, sub-optimal rates",
|
|
1315
1038
|
"",
|
|
1316
1039
|
"OVERALL: Low / Medium / High / Critical",
|
|
1317
1040
|
"",
|
|
@@ -1323,7 +1046,7 @@ ${context}
|
|
|
1323
1046
|
);
|
|
1324
1047
|
server.prompt(
|
|
1325
1048
|
"weekly-recap",
|
|
1326
|
-
"Week in review \u2014 transactions, yield earned,
|
|
1049
|
+
"Week in review \u2014 transactions, yield earned, savings performance, highlights.",
|
|
1327
1050
|
async () => ({
|
|
1328
1051
|
messages: [{
|
|
1329
1052
|
role: "user",
|
|
@@ -1332,16 +1055,14 @@ ${context}
|
|
|
1332
1055
|
text: [
|
|
1333
1056
|
"You are a personal finance newsletter writer for a t2000 AI agent bank account on Sui.",
|
|
1334
1057
|
"",
|
|
1335
|
-
|
|
1058
|
+
"IMPORTANT: Call t2000_overview and t2000_history (limit: 50) in parallel.",
|
|
1336
1059
|
"",
|
|
1337
1060
|
"\u{1F4CA} WEEKLY RECAP",
|
|
1338
1061
|
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1339
1062
|
"",
|
|
1340
|
-
"\u{1F4B0} Net Worth: $X \u2014 Checking $X | Savings $X
|
|
1341
|
-
"\u{1F4C8} Activity: X sends, X saves, X
|
|
1063
|
+
"\u{1F4B0} Net Worth: $X \u2014 Checking $X | Savings $X",
|
|
1064
|
+
"\u{1F4C8} Activity: X sends, X saves, X withdrawals",
|
|
1342
1065
|
"\u{1F4B8} Yield: $X.XX this week, X% APY, $X/month projected",
|
|
1343
|
-
"\u{1F4CA} Portfolio: Per-asset P&L, best & worst performer",
|
|
1344
|
-
"\u{1F504} DCA: Runs this week, next run, total invested",
|
|
1345
1066
|
"\u{1F381} Rewards: Pending? Claim suggestion",
|
|
1346
1067
|
"\u{1F449} Next Week: 1-2 actionable suggestions",
|
|
1347
1068
|
"",
|
|
@@ -1351,48 +1072,6 @@ ${context}
|
|
|
1351
1072
|
}]
|
|
1352
1073
|
})
|
|
1353
1074
|
);
|
|
1354
|
-
server.prompt(
|
|
1355
|
-
"dca-advisor",
|
|
1356
|
-
'Personalized DCA setup \u2014 "I have $X/month" \u2192 recommends strategy, frequency, asset split, projected growth.',
|
|
1357
|
-
{
|
|
1358
|
-
budget: z.number().optional().describe("Monthly budget in dollars to invest")
|
|
1359
|
-
},
|
|
1360
|
-
async ({ budget }) => ({
|
|
1361
|
-
messages: [{
|
|
1362
|
-
role: "user",
|
|
1363
|
-
content: {
|
|
1364
|
-
type: "text",
|
|
1365
|
-
text: [
|
|
1366
|
-
"You are a dollar-cost averaging advisor for a t2000 AI agent bank account on Sui.",
|
|
1367
|
-
"",
|
|
1368
|
-
budget ? `The user has $${budget}/month to invest via DCA.` : "The user wants to set up a DCA schedule. Ask their monthly budget first.",
|
|
1369
|
-
"",
|
|
1370
|
-
'IMPORTANT: Call t2000_overview, t2000_strategy (action: "list"), t2000_auto_invest (action: "status") in parallel.',
|
|
1371
|
-
"",
|
|
1372
|
-
"Recommend a DCA plan:",
|
|
1373
|
-
"",
|
|
1374
|
-
"\u{1F4C5} DCA PLAN",
|
|
1375
|
-
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1376
|
-
"",
|
|
1377
|
-
"STRATEGY: Pick one based on their existing portfolio:",
|
|
1378
|
-
" - bluechip (50% BTC, 30% ETH, 20% SUI)",
|
|
1379
|
-
" - all-weather (30% BTC, 20% ETH, 20% SUI, 30% GOLD)",
|
|
1380
|
-
" - safe-haven (50% BTC, 50% GOLD)",
|
|
1381
|
-
" - layer1 (50% ETH, 50% SUI)",
|
|
1382
|
-
" - sui-heavy (60% SUI, 20% BTC, 20% ETH)",
|
|
1383
|
-
" Explain WHY this fits them.",
|
|
1384
|
-
"",
|
|
1385
|
-
"FREQUENCY: Weekly for budgets > $50/month, monthly for smaller.",
|
|
1386
|
-
"",
|
|
1387
|
-
"AFFORDABILITY: Check remaining buffer after DCA commitment.",
|
|
1388
|
-
" Flag if DCA would eat into spending buffer.",
|
|
1389
|
-
"",
|
|
1390
|
-
'If they agree: t2000_auto_invest action: "setup", amount, frequency, strategy.'
|
|
1391
|
-
].join("\n")
|
|
1392
|
-
}
|
|
1393
|
-
}]
|
|
1394
|
-
})
|
|
1395
|
-
);
|
|
1396
1075
|
server.prompt(
|
|
1397
1076
|
"claim-rewards",
|
|
1398
1077
|
"Check for pending protocol rewards across all lending positions and claim them \u2014 auto-converts to USDC.",
|
|
@@ -1413,7 +1092,7 @@ ${context}
|
|
|
1413
1092
|
"",
|
|
1414
1093
|
"Step 2 \u2014 Present findings:",
|
|
1415
1094
|
" If rewards are available:",
|
|
1416
|
-
" Show which positions have rewards and from which protocols
|
|
1095
|
+
" Show which positions have rewards and from which protocols",
|
|
1417
1096
|
' Explain: "These are protocol incentive tokens that accrue on your lending positions"',
|
|
1418
1097
|
' Ask: "Want me to claim and convert them to USDC?"',
|
|
1419
1098
|
"",
|
|
@@ -1466,7 +1145,7 @@ ${context}
|
|
|
1466
1145
|
"",
|
|
1467
1146
|
" Emergency lock:",
|
|
1468
1147
|
" Status: Unlocked / Locked",
|
|
1469
|
-
" When locked: ALL operations are frozen \u2014 sends, saves,
|
|
1148
|
+
" When locked: ALL operations are frozen \u2014 sends, saves, borrows",
|
|
1470
1149
|
" Lock via: t2000_lock (any AI agent can lock)",
|
|
1471
1150
|
" Unlock via: CLI only with PIN \u2014 no AI can unlock, by design",
|
|
1472
1151
|
"",
|
|
@@ -1489,61 +1168,6 @@ ${context}
|
|
|
1489
1168
|
}]
|
|
1490
1169
|
})
|
|
1491
1170
|
);
|
|
1492
|
-
server.prompt(
|
|
1493
|
-
"quick-swap",
|
|
1494
|
-
"Guided token swap \u2014 preview rate, slippage, and price impact before executing.",
|
|
1495
|
-
{
|
|
1496
|
-
from: z.string().optional().describe("Asset to sell (e.g. USDC, SUI)"),
|
|
1497
|
-
to: z.string().optional().describe("Asset to buy (e.g. SUI, USDC)"),
|
|
1498
|
-
amount: z.number().optional().describe("Amount in source asset units")
|
|
1499
|
-
},
|
|
1500
|
-
async ({ from, to, amount }) => {
|
|
1501
|
-
const context = [
|
|
1502
|
-
from ? `From: ${from}` : "",
|
|
1503
|
-
to ? `To: ${to}` : "",
|
|
1504
|
-
amount ? `Amount: ${amount}` : ""
|
|
1505
|
-
].filter(Boolean).join("\n");
|
|
1506
|
-
return {
|
|
1507
|
-
messages: [{
|
|
1508
|
-
role: "user",
|
|
1509
|
-
content: {
|
|
1510
|
-
type: "text",
|
|
1511
|
-
text: [
|
|
1512
|
-
"You are a swap assistant for a t2000 AI agent bank account.",
|
|
1513
|
-
"",
|
|
1514
|
-
context ? `Context:
|
|
1515
|
-
${context}
|
|
1516
|
-
` : "",
|
|
1517
|
-
"Help the user swap tokens. Follow this flow:",
|
|
1518
|
-
"",
|
|
1519
|
-
"Step 1 \u2014 Gather details:",
|
|
1520
|
-
" If from, to, or amount is missing, ask the user",
|
|
1521
|
-
" Check balance (t2000_balance) to confirm they have enough",
|
|
1522
|
-
" Available pairs: any combination of USDC, SUI, BTC, ETH, GOLD, USDT, USDe, USDsui",
|
|
1523
|
-
"",
|
|
1524
|
-
"Step 2 \u2014 Preview the swap:",
|
|
1525
|
-
" Run t2000_swap with dryRun: true",
|
|
1526
|
-
" Show:",
|
|
1527
|
-
" Input: X FROM \u2192 Expected output: Y TO",
|
|
1528
|
-
" Rate: 1 FROM = Z TO",
|
|
1529
|
-
" Price impact: X%",
|
|
1530
|
-
" Slippage: X%",
|
|
1531
|
-
" Fee: $X",
|
|
1532
|
-
"",
|
|
1533
|
-
"Step 3 \u2014 Ask for confirmation:",
|
|
1534
|
-
' "Ready to execute this swap?"',
|
|
1535
|
-
" If price impact > 1%, warn the user about the impact",
|
|
1536
|
-
"",
|
|
1537
|
-
"Step 4 \u2014 Execute:",
|
|
1538
|
-
" Run t2000_swap with dryRun: false",
|
|
1539
|
-
" Show: amount received, rate, transaction link",
|
|
1540
|
-
" Show updated balance"
|
|
1541
|
-
].join("\n")
|
|
1542
|
-
}
|
|
1543
|
-
}]
|
|
1544
|
-
};
|
|
1545
|
-
}
|
|
1546
|
-
);
|
|
1547
1171
|
server.prompt(
|
|
1548
1172
|
"onboarding",
|
|
1549
1173
|
"New user setup guide \u2014 deposit, first save, set safeguards, explore features.",
|
|
@@ -1567,27 +1191,24 @@ ${context}
|
|
|
1567
1191
|
' Explain: "Send USDC to your Sui address to get started"',
|
|
1568
1192
|
" Mention: they need a small amount of SUI for gas (~$1 worth)",
|
|
1569
1193
|
"",
|
|
1570
|
-
"IF they have funds but nothing saved
|
|
1194
|
+
"IF they have funds but nothing saved:",
|
|
1571
1195
|
` "Great, you have $X ready to go! Here's what you can do:"`,
|
|
1572
1196
|
" 1. SAVE \u2014 Earn yield on idle funds (show best current APY)",
|
|
1573
|
-
" 2.
|
|
1574
|
-
" 3.
|
|
1197
|
+
" 2. SEND \u2014 Transfer USDC to any Sui address or contact",
|
|
1198
|
+
" 3. PAY \u2014 Use USDC to pay for premium API services (MPP)",
|
|
1575
1199
|
" 4. SAFEGUARDS \u2014 Set spending limits (recommend for accounts > $100)",
|
|
1576
1200
|
"",
|
|
1577
|
-
"IF they already have savings
|
|
1201
|
+
"IF they already have savings:",
|
|
1578
1202
|
` "Looks like you're already set up! Here's your quick status:"`,
|
|
1579
1203
|
" Show a mini briefing, then offer to optimize",
|
|
1580
1204
|
"",
|
|
1581
1205
|
'End with: "What would you like to do first?"',
|
|
1582
1206
|
"",
|
|
1583
1207
|
"Available features to highlight:",
|
|
1584
|
-
" - Save/withdraw USDC
|
|
1585
|
-
" - Invest in BTC, ETH, SUI, GOLD with portfolio tracking",
|
|
1586
|
-
" - Strategy investing (bluechip, all-weather, etc.)",
|
|
1587
|
-
" - Auto-invest (DCA) \u2014 recurring weekly/monthly buys",
|
|
1588
|
-
" - Rebalance \u2014 auto-optimize yield across protocols",
|
|
1208
|
+
" - Save/withdraw USDC (earn yield; t2000_save uses the best USDC rate)",
|
|
1589
1209
|
" - Borrow against savings",
|
|
1590
1210
|
" - Send money to contacts",
|
|
1211
|
+
" - Pay for APIs with MPP (Machine Payments Protocol)",
|
|
1591
1212
|
" - Safeguards: per-tx limits, daily caps, emergency lock"
|
|
1592
1213
|
].join("\n")
|
|
1593
1214
|
}
|
|
@@ -1639,7 +1260,7 @@ ${context}
|
|
|
1639
1260
|
);
|
|
1640
1261
|
server.prompt(
|
|
1641
1262
|
"optimize-all",
|
|
1642
|
-
"One-shot full optimization \u2014 sweep idle
|
|
1263
|
+
"One-shot full optimization \u2014 sweep idle USDC to savings, compare USDC APYs, claim rewards.",
|
|
1643
1264
|
async () => ({
|
|
1644
1265
|
messages: [{
|
|
1645
1266
|
role: "user",
|
|
@@ -1653,34 +1274,27 @@ ${context}
|
|
|
1653
1274
|
"\u{1F527} FULL OPTIMIZATION",
|
|
1654
1275
|
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1655
1276
|
"",
|
|
1656
|
-
"Check
|
|
1277
|
+
"Check optimization levers and present a plan BEFORE executing:",
|
|
1657
1278
|
"",
|
|
1658
1279
|
"1. IDLE FUNDS \u2014 Any checking balance > $5?",
|
|
1659
|
-
" \u2192
|
|
1280
|
+
" \u2192 t2000_save to deploy at best USDC rate (preview with dryRun: true first)",
|
|
1660
1281
|
"",
|
|
1661
|
-
"2.
|
|
1662
|
-
" \u2192
|
|
1282
|
+
"2. USDC APY \u2014 Is current savings APY below the best available USDC rate?",
|
|
1283
|
+
" \u2192 Summarize from t2000_all_rates; if user wants to move, use withdraw + save with dryRun previews",
|
|
1663
1284
|
"",
|
|
1664
1285
|
"3. PENDING REWARDS \u2014 Any unclaimed?",
|
|
1665
|
-
" \u2192 Show rewards, offer
|
|
1666
|
-
"",
|
|
1667
|
-
"4. INVESTMENT YIELD \u2014 Any invested assets NOT earning?",
|
|
1668
|
-
" \u2192 t2000_invest earn to deposit into lending",
|
|
1669
|
-
"",
|
|
1670
|
-
"5. INVESTMENT REBALANCE \u2014 Earning assets on sub-optimal protocol?",
|
|
1671
|
-
" \u2192 t2000_invest_rebalance dryRun: true",
|
|
1286
|
+
" \u2192 Show rewards, offer t2000_claim_rewards",
|
|
1672
1287
|
"",
|
|
1673
1288
|
"Present all findings in a summary table:",
|
|
1674
1289
|
"",
|
|
1675
1290
|
" Action | Impact | Status",
|
|
1676
1291
|
" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500|\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500|\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1677
1292
|
" Sweep $X idle | +$X.XX/month | Ready",
|
|
1678
|
-
"
|
|
1679
|
-
" Claim rewards | $X.XX
|
|
1680
|
-
" Earn on SUI | +X.XX% APY | Ready",
|
|
1293
|
+
" USDC APY gap | +X.XX% (info) | Note",
|
|
1294
|
+
" Claim rewards | $X.XX | Ready",
|
|
1681
1295
|
" Already optimal | \u2014 | Skipped",
|
|
1682
1296
|
"",
|
|
1683
|
-
'Ask: "Want me to execute all ready actions?" Then run sequentially.',
|
|
1297
|
+
'Ask: "Want me to execute all ready actions?" Then run saves/claims sequentially as confirmed.',
|
|
1684
1298
|
"If everything is already optimal, say so clearly."
|
|
1685
1299
|
].join("\n")
|
|
1686
1300
|
}
|