@t2000/cli 0.22.3 → 0.22.5
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/index.js
CHANGED
|
@@ -133,10 +133,14 @@ async function clearSession() {
|
|
|
133
133
|
async function resolvePin(opts) {
|
|
134
134
|
const envPin = getPinFromEnv();
|
|
135
135
|
if (envPin) return envPin;
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
if (!opts?.skipSession) {
|
|
137
|
+
const sessionPin = await readSession();
|
|
138
|
+
if (sessionPin) return sessionPin;
|
|
139
|
+
}
|
|
138
140
|
const pin = opts?.confirm ? await askPinConfirm() : await askPin();
|
|
139
|
-
|
|
141
|
+
if (!opts?.skipSession) {
|
|
142
|
+
await saveSession(pin);
|
|
143
|
+
}
|
|
140
144
|
return pin;
|
|
141
145
|
}
|
|
142
146
|
|
|
@@ -493,7 +497,27 @@ function registerDeposit(program2) {
|
|
|
493
497
|
}
|
|
494
498
|
|
|
495
499
|
// src/commands/history.ts
|
|
500
|
+
import pc4 from "picocolors";
|
|
496
501
|
import { T2000 as T20006, truncateAddress as truncateAddress2 } from "@t2000/sdk";
|
|
502
|
+
var ACTION_LABELS = {
|
|
503
|
+
send: "\u2197 send",
|
|
504
|
+
lending: "\u{1F3E6} lend",
|
|
505
|
+
swap: "\u{1F504} swap",
|
|
506
|
+
"mpp payment": "\u{1F4B3} mpp",
|
|
507
|
+
split: "\u2702 split",
|
|
508
|
+
transaction: "\u{1F4E6} tx"
|
|
509
|
+
};
|
|
510
|
+
function relativeTime(ts) {
|
|
511
|
+
const diff = Date.now() - ts;
|
|
512
|
+
const mins = Math.floor(diff / 6e4);
|
|
513
|
+
if (mins < 1) return "just now";
|
|
514
|
+
if (mins < 60) return `${mins}m ago`;
|
|
515
|
+
const hours = Math.floor(mins / 60);
|
|
516
|
+
if (hours < 24) return `${hours}h ago`;
|
|
517
|
+
const days = Math.floor(hours / 24);
|
|
518
|
+
if (days < 7) return `${days}d ago`;
|
|
519
|
+
return new Date(ts).toLocaleDateString();
|
|
520
|
+
}
|
|
497
521
|
function registerHistory(program2) {
|
|
498
522
|
program2.command("history").description("Show transaction history").option("--limit <n>", "Number of transactions", "20").option("--key <path>", "Key file path").action(async (opts) => {
|
|
499
523
|
try {
|
|
@@ -509,11 +533,19 @@ function registerHistory(program2) {
|
|
|
509
533
|
printInfo("No transactions yet.");
|
|
510
534
|
} else {
|
|
511
535
|
for (const tx of txns) {
|
|
512
|
-
const
|
|
513
|
-
const
|
|
514
|
-
|
|
536
|
+
const label = ACTION_LABELS[tx.action] ?? `\u{1F4E6} ${tx.action}`;
|
|
537
|
+
const time = tx.timestamp ? relativeTime(tx.timestamp) : "";
|
|
538
|
+
const amount = tx.amount ? pc4.bold(`${tx.amount.toFixed(tx.amount < 0.01 ? 4 : 2)} ${tx.asset ?? ""}`) : "";
|
|
539
|
+
const recipient = tx.recipient ? pc4.dim(`\u2192 ${truncateAddress2(tx.recipient)}`) : "";
|
|
540
|
+
const link = pc4.dim(explorerUrl(tx.digest));
|
|
541
|
+
printLine(`${label} ${amount} ${recipient}`);
|
|
542
|
+
printLine(` ${pc4.dim(truncateAddress2(tx.digest))} ${pc4.dim(time)}`);
|
|
543
|
+
printLine(` ${link}`);
|
|
544
|
+
printBlank();
|
|
515
545
|
}
|
|
516
546
|
}
|
|
547
|
+
printDivider();
|
|
548
|
+
printInfo(`${txns.length} transaction${txns.length === 1 ? "" : "s"} shown`);
|
|
517
549
|
printBlank();
|
|
518
550
|
} catch (error) {
|
|
519
551
|
handleError(error);
|
|
@@ -625,7 +657,7 @@ function registerImport(program2) {
|
|
|
625
657
|
}
|
|
626
658
|
|
|
627
659
|
// src/commands/save.ts
|
|
628
|
-
import
|
|
660
|
+
import pc5 from "picocolors";
|
|
629
661
|
import { T2000 as T20009, formatUsd as formatUsd3 } from "@t2000/sdk";
|
|
630
662
|
function registerSave(program2) {
|
|
631
663
|
const action = async (amountStr, opts) => {
|
|
@@ -647,16 +679,16 @@ function registerSave(program2) {
|
|
|
647
679
|
}
|
|
648
680
|
printBlank();
|
|
649
681
|
if (gasManagerUsdc > 0) {
|
|
650
|
-
printSuccess(`Gas manager: ${
|
|
682
|
+
printSuccess(`Gas manager: ${pc5.yellow(formatUsd3(gasManagerUsdc))} USDC \u2192 SUI`);
|
|
651
683
|
}
|
|
652
684
|
const protocolName = opts.protocol ?? "best rate";
|
|
653
|
-
printSuccess(`Saved ${
|
|
685
|
+
printSuccess(`Saved ${pc5.yellow(formatUsd3(result.amount))} USDC to ${protocolName}`);
|
|
654
686
|
if (result.fee > 0) {
|
|
655
687
|
const feeRate = (result.fee / result.amount * 100).toFixed(1);
|
|
656
|
-
printSuccess(`Protocol fee: ${
|
|
688
|
+
printSuccess(`Protocol fee: ${pc5.dim(`${formatUsd3(result.fee)} USDC (${feeRate}%)`)}`);
|
|
657
689
|
}
|
|
658
|
-
printSuccess(`Current APY: ${
|
|
659
|
-
printSuccess(`Savings balance: ${
|
|
690
|
+
printSuccess(`Current APY: ${pc5.green(`${result.apy.toFixed(2)}%`)}`);
|
|
691
|
+
printSuccess(`Savings balance: ${pc5.yellow(formatUsd3(result.savingsBalance))} USDC`);
|
|
660
692
|
printKeyValue("Tx", explorerUrl(result.tx));
|
|
661
693
|
printBlank();
|
|
662
694
|
} catch (error) {
|
|
@@ -788,7 +820,7 @@ function registerHealth(program2) {
|
|
|
788
820
|
}
|
|
789
821
|
|
|
790
822
|
// src/commands/rates.ts
|
|
791
|
-
import
|
|
823
|
+
import pc6 from "picocolors";
|
|
792
824
|
import { T2000 as T200014, SUPPORTED_ASSETS, INVESTMENT_ASSETS, STABLE_ASSETS } from "@t2000/sdk";
|
|
793
825
|
var INVEST_ASSETS = Object.keys(INVESTMENT_ASSETS);
|
|
794
826
|
function registerRates(program2) {
|
|
@@ -805,14 +837,14 @@ function registerRates(program2) {
|
|
|
805
837
|
if (allRates.length > 0) {
|
|
806
838
|
const best = allRates.reduce((a, b) => b.rates.saveApy > a.rates.saveApy ? b : a);
|
|
807
839
|
const bestDisplay = SUPPORTED_ASSETS[best.asset]?.displayName ?? best.asset;
|
|
808
|
-
printLine(
|
|
840
|
+
printLine(pc6.bold(pc6.green(`Best yield: ${best.rates.saveApy.toFixed(2)}% APY`)) + pc6.dim(` (${bestDisplay} on ${best.protocol})`));
|
|
809
841
|
printBlank();
|
|
810
842
|
}
|
|
811
843
|
for (const asset of STABLE_ASSETS) {
|
|
812
844
|
const assetRates = allRates.filter((r) => r.asset === asset);
|
|
813
845
|
if (assetRates.length === 0) continue;
|
|
814
846
|
const display = SUPPORTED_ASSETS[asset]?.displayName ?? asset;
|
|
815
|
-
printLine(
|
|
847
|
+
printLine(pc6.bold(display));
|
|
816
848
|
printDivider();
|
|
817
849
|
for (const entry of assetRates) {
|
|
818
850
|
printKeyValue(entry.protocol, `Save ${entry.rates.saveApy.toFixed(2)}% Borrow ${entry.rates.borrowApy.toFixed(2)}%`);
|
|
@@ -821,7 +853,7 @@ function registerRates(program2) {
|
|
|
821
853
|
}
|
|
822
854
|
const investRates = allRates.filter((r) => INVEST_ASSETS.includes(r.asset));
|
|
823
855
|
if (investRates.length > 0) {
|
|
824
|
-
printLine(
|
|
856
|
+
printLine(pc6.bold("Investment Assets"));
|
|
825
857
|
printDivider();
|
|
826
858
|
for (const asset of INVEST_ASSETS) {
|
|
827
859
|
const assetRates = investRates.filter((r) => r.asset === asset);
|
|
@@ -844,7 +876,7 @@ function registerRates(program2) {
|
|
|
844
876
|
}
|
|
845
877
|
|
|
846
878
|
// src/commands/positions.ts
|
|
847
|
-
import
|
|
879
|
+
import pc7 from "picocolors";
|
|
848
880
|
import { T2000 as T200015, formatUsd as formatUsd8, formatAssetAmount } from "@t2000/sdk";
|
|
849
881
|
function registerPositions(program2) {
|
|
850
882
|
program2.command("positions").description("Show savings & borrow positions across all protocols and assets").option("--key <path>", "Key file path").action(async (opts) => {
|
|
@@ -873,10 +905,10 @@ function registerPositions(program2) {
|
|
|
873
905
|
const saves = result.positions.filter((p) => p.type === "save");
|
|
874
906
|
const borrows = result.positions.filter((p) => p.type === "borrow");
|
|
875
907
|
if (saves.length > 0) {
|
|
876
|
-
printLine(
|
|
908
|
+
printLine(pc7.bold("Savings"));
|
|
877
909
|
printDivider();
|
|
878
910
|
for (const pos of saves) {
|
|
879
|
-
const earning = rewardsByKey.has(`${pos.protocol}:${pos.asset}`) ? ` ${
|
|
911
|
+
const earning = rewardsByKey.has(`${pos.protocol}:${pos.asset}`) ? ` ${pc7.yellow("+rewards")}` : "";
|
|
880
912
|
const usd = formatUsd8(pos.amountUsd ?? pos.amount);
|
|
881
913
|
printKeyValue(pos.protocol, `${formatAssetAmount(pos.amount, pos.asset)} ${pos.asset} (${usd}) @ ${pos.apy.toFixed(2)}% APY${earning}`);
|
|
882
914
|
}
|
|
@@ -885,12 +917,12 @@ function registerPositions(program2) {
|
|
|
885
917
|
printKeyValue("Total", formatUsd8(totalSaved));
|
|
886
918
|
}
|
|
887
919
|
if (hasRewards) {
|
|
888
|
-
printLine(` ${
|
|
920
|
+
printLine(` ${pc7.dim("Run claim-rewards to collect and convert to USDC")}`);
|
|
889
921
|
}
|
|
890
922
|
printBlank();
|
|
891
923
|
}
|
|
892
924
|
if (borrows.length > 0) {
|
|
893
|
-
printLine(
|
|
925
|
+
printLine(pc7.bold("Borrows"));
|
|
894
926
|
printDivider();
|
|
895
927
|
for (const pos of borrows) {
|
|
896
928
|
const usd = formatUsd8(pos.amountUsd ?? pos.amount);
|
|
@@ -910,7 +942,7 @@ function registerPositions(program2) {
|
|
|
910
942
|
}
|
|
911
943
|
|
|
912
944
|
// src/commands/earnings.ts
|
|
913
|
-
import
|
|
945
|
+
import pc8 from "picocolors";
|
|
914
946
|
import { T2000 as T200016, formatUsd as formatUsd9, formatAssetAmount as formatAssetAmount2 } from "@t2000/sdk";
|
|
915
947
|
function registerEarnings(program2) {
|
|
916
948
|
program2.command("earnings").description("Show yield earned to date").option("--key <path>", "Key file path").action(async (opts) => {
|
|
@@ -928,7 +960,7 @@ function registerEarnings(program2) {
|
|
|
928
960
|
printKeyValue("Total Saved", formatUsd9(result.supplied));
|
|
929
961
|
if (savePositions.length > 0) {
|
|
930
962
|
for (const p of savePositions) {
|
|
931
|
-
printLine(` ${
|
|
963
|
+
printLine(` ${pc8.dim("\u2022")} ${formatAssetAmount2(p.amount, p.asset)} ${p.asset} (${formatUsd9(p.amountUsd ?? p.amount)}) on ${p.protocol} @ ${p.apy.toFixed(2)}% APY`);
|
|
932
964
|
}
|
|
933
965
|
}
|
|
934
966
|
printKeyValue("Blended APY", `${result.currentApy.toFixed(2)}%`);
|
|
@@ -942,7 +974,7 @@ function registerEarnings(program2) {
|
|
|
942
974
|
}
|
|
943
975
|
|
|
944
976
|
// src/commands/fundStatus.ts
|
|
945
|
-
import
|
|
977
|
+
import pc9 from "picocolors";
|
|
946
978
|
import { T2000 as T200017, formatUsd as formatUsd10, formatAssetAmount as formatAssetAmount3 } from "@t2000/sdk";
|
|
947
979
|
function registerFundStatus(program2) {
|
|
948
980
|
program2.command("fund-status").description("Full savings summary").option("--key <path>", "Key file path").action(async (opts) => {
|
|
@@ -966,7 +998,7 @@ function registerFundStatus(program2) {
|
|
|
966
998
|
printKeyValue("Total Saved", formatUsd10(result.supplied));
|
|
967
999
|
if (savePositions.length > 0) {
|
|
968
1000
|
for (const p of savePositions) {
|
|
969
|
-
printLine(` ${
|
|
1001
|
+
printLine(` ${pc9.dim("\u2022")} ${formatAssetAmount3(p.amount, p.asset)} ${p.asset} (${formatUsd10(p.amountUsd ?? p.amount)}) on ${p.protocol} @ ${p.apy.toFixed(2)}% APY`);
|
|
970
1002
|
}
|
|
971
1003
|
}
|
|
972
1004
|
printKeyValue("Blended APY", `${result.apy.toFixed(2)}%`);
|
|
@@ -1451,7 +1483,7 @@ function isRetryable(code) {
|
|
|
1451
1483
|
}
|
|
1452
1484
|
|
|
1453
1485
|
// src/commands/pay.ts
|
|
1454
|
-
import
|
|
1486
|
+
import pc10 from "picocolors";
|
|
1455
1487
|
import { T2000 as T200019 } from "@t2000/sdk";
|
|
1456
1488
|
function registerPay(program2) {
|
|
1457
1489
|
program2.command("pay <url>").description("Pay for an MPP-protected API resource").option("--key <path>", "Key file path").option("--method <method>", "HTTP method (GET, POST, PUT)", "GET").option("--data <json>", "Request body for POST/PUT").option("--header <key=value>", "Additional HTTP header (repeatable)", collectHeaders, {}).option("--max-price <amount>", "Max USDC price to auto-approve", "1.00").action(async (url, opts) => {
|
|
@@ -1480,7 +1512,7 @@ function registerPay(program2) {
|
|
|
1480
1512
|
if (result.paid && result.receipt) {
|
|
1481
1513
|
printSuccess(`Paid via MPP (tx: ${result.receipt.reference.slice(0, 10)}...)`);
|
|
1482
1514
|
}
|
|
1483
|
-
printInfo(`\u2190 ${result.status} OK ${
|
|
1515
|
+
printInfo(`\u2190 ${result.status} OK ${pc10.dim(`[${elapsed}ms]`)}`);
|
|
1484
1516
|
}
|
|
1485
1517
|
if (isJsonMode()) {
|
|
1486
1518
|
printJson({
|
|
@@ -1540,12 +1572,34 @@ function registerLock(program2) {
|
|
|
1540
1572
|
});
|
|
1541
1573
|
program2.command("unlock").description("Unlock agent \u2014 resume operations").action(async () => {
|
|
1542
1574
|
try {
|
|
1543
|
-
const pin = await resolvePin();
|
|
1544
|
-
if (!pin) {
|
|
1545
|
-
throw new Error("PIN required to unlock agent");
|
|
1546
|
-
}
|
|
1547
1575
|
const { T2000: T200027 } = await import("@t2000/sdk");
|
|
1548
|
-
|
|
1576
|
+
const MAX_ATTEMPTS2 = 3;
|
|
1577
|
+
let pin;
|
|
1578
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS2; attempt++) {
|
|
1579
|
+
pin = await resolvePin({ skipSession: true });
|
|
1580
|
+
if (!pin) {
|
|
1581
|
+
throw new Error("PIN required to unlock agent");
|
|
1582
|
+
}
|
|
1583
|
+
try {
|
|
1584
|
+
await T200027.create({ pin });
|
|
1585
|
+
break;
|
|
1586
|
+
} catch (error) {
|
|
1587
|
+
const msg = error instanceof Error ? error.message : "";
|
|
1588
|
+
if (msg.includes("Invalid PIN")) {
|
|
1589
|
+
const remaining = MAX_ATTEMPTS2 - attempt;
|
|
1590
|
+
if (remaining > 0) {
|
|
1591
|
+
printError(`Invalid PIN. ${remaining} attempt${remaining > 1 ? "s" : ""} remaining.`);
|
|
1592
|
+
pin = void 0;
|
|
1593
|
+
continue;
|
|
1594
|
+
}
|
|
1595
|
+
printError("Invalid PIN. No attempts remaining.");
|
|
1596
|
+
return;
|
|
1597
|
+
}
|
|
1598
|
+
throw error;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
if (!pin) return;
|
|
1602
|
+
await saveSession(pin);
|
|
1549
1603
|
const enforcer = new SafeguardEnforcer3(CONFIG_DIR4);
|
|
1550
1604
|
enforcer.load();
|
|
1551
1605
|
enforcer.unlock();
|
|
@@ -1571,7 +1625,7 @@ function registerLock(program2) {
|
|
|
1571
1625
|
|
|
1572
1626
|
// src/commands/sentinel.ts
|
|
1573
1627
|
import { T2000 as T200020, MIST_PER_SUI } from "@t2000/sdk";
|
|
1574
|
-
import
|
|
1628
|
+
import pc11 from "picocolors";
|
|
1575
1629
|
function formatSui(mist) {
|
|
1576
1630
|
return (Number(mist) / Number(MIST_PER_SUI)).toFixed(2);
|
|
1577
1631
|
}
|
|
@@ -1601,8 +1655,8 @@ function registerSentinel(program2) {
|
|
|
1601
1655
|
const pool = `${formatSui(s.prizePool)} SUI`.padEnd(12);
|
|
1602
1656
|
const fee = `${formatSui(s.attackFee)} SUI`.padEnd(12);
|
|
1603
1657
|
printLine(` ${s.name}`);
|
|
1604
|
-
printLine(` ${
|
|
1605
|
-
printLine(` ${
|
|
1658
|
+
printLine(` ${pc11.dim(`Pool: ${pool}Fee: ${fee}Attacks: ${s.totalAttacks}`)}`);
|
|
1659
|
+
printLine(` ${pc11.dim(s.objectId)}`);
|
|
1606
1660
|
printBlank();
|
|
1607
1661
|
});
|
|
1608
1662
|
printBlank();
|
|
@@ -1637,7 +1691,7 @@ function registerSentinel(program2) {
|
|
|
1637
1691
|
if (s.systemPrompt) {
|
|
1638
1692
|
printBlank();
|
|
1639
1693
|
printKeyValue("System Prompt", "");
|
|
1640
|
-
printLine(` ${
|
|
1694
|
+
printLine(` ${pc11.dim(s.systemPrompt.slice(0, 500))}`);
|
|
1641
1695
|
}
|
|
1642
1696
|
printBlank();
|
|
1643
1697
|
} catch (error) {
|
|
@@ -1663,7 +1717,7 @@ function registerSentinel(program2) {
|
|
|
1663
1717
|
return;
|
|
1664
1718
|
}
|
|
1665
1719
|
printBlank();
|
|
1666
|
-
printLine(` ${
|
|
1720
|
+
printLine(` ${pc11.dim("\u23F3")} Requesting attack...`);
|
|
1667
1721
|
const result = await agent.sentinelAttack(id, prompt, feeMist);
|
|
1668
1722
|
printBlank();
|
|
1669
1723
|
if (result.won) {
|
|
@@ -1690,7 +1744,7 @@ function registerSentinel(program2) {
|
|
|
1690
1744
|
|
|
1691
1745
|
// src/commands/earn.ts
|
|
1692
1746
|
import { T2000 as T200021, MIST_PER_SUI as MIST_PER_SUI2, listSentinels, formatUsd as formatUsd12 } from "@t2000/sdk";
|
|
1693
|
-
import
|
|
1747
|
+
import pc12 from "picocolors";
|
|
1694
1748
|
function mistToSui(mist) {
|
|
1695
1749
|
return Number(mist) / Number(MIST_PER_SUI2);
|
|
1696
1750
|
}
|
|
@@ -1762,7 +1816,7 @@ function registerEarn(program2) {
|
|
|
1762
1816
|
return;
|
|
1763
1817
|
}
|
|
1764
1818
|
printHeader("Earning Opportunities");
|
|
1765
|
-
printLine(
|
|
1819
|
+
printLine(pc12.bold("SAVINGS") + pc12.dim(" \u2014 Passive Yield"));
|
|
1766
1820
|
printDivider();
|
|
1767
1821
|
if (savePositions.length > 0) {
|
|
1768
1822
|
for (const pos of savePositions) {
|
|
@@ -1771,7 +1825,7 @@ function registerEarn(program2) {
|
|
|
1771
1825
|
if (dailyYield > 1e-4) {
|
|
1772
1826
|
const dailyStr = dailyYield < 0.01 ? `$${dailyYield.toFixed(4)}` : formatUsd12(dailyYield);
|
|
1773
1827
|
const monthlyStr = dailyYield * 30 < 0.01 ? `$${(dailyYield * 30).toFixed(4)}` : formatUsd12(dailyYield * 30);
|
|
1774
|
-
printLine(
|
|
1828
|
+
printLine(pc12.dim(` ~${dailyStr}/day \xB7 ~${monthlyStr}/month`));
|
|
1775
1829
|
}
|
|
1776
1830
|
}
|
|
1777
1831
|
if (savePositions.length > 1) {
|
|
@@ -1786,7 +1840,7 @@ function registerEarn(program2) {
|
|
|
1786
1840
|
const example = 100;
|
|
1787
1841
|
const daily = example * bestSaveApy / 100 / 365;
|
|
1788
1842
|
const monthly = daily * 30;
|
|
1789
|
-
printLine(
|
|
1843
|
+
printLine(pc12.dim(` Save $${example} \u2192 ~$${daily.toFixed(2)}/day \xB7 ~$${monthly.toFixed(2)}/month`));
|
|
1790
1844
|
printBlank();
|
|
1791
1845
|
printInfo("No savings yet \u2014 run `t2000 save <amount>` to start");
|
|
1792
1846
|
} else if (posData) {
|
|
@@ -1796,7 +1850,7 @@ function registerEarn(program2) {
|
|
|
1796
1850
|
}
|
|
1797
1851
|
if (earningInvestments.length > 0) {
|
|
1798
1852
|
printBlank();
|
|
1799
|
-
printLine(
|
|
1853
|
+
printLine(pc12.bold("INVESTMENTS") + pc12.dim(" \u2014 Earning Yield"));
|
|
1800
1854
|
printDivider();
|
|
1801
1855
|
let totalInvestValue = 0;
|
|
1802
1856
|
for (const pos of earningInvestments) {
|
|
@@ -1809,7 +1863,7 @@ function registerEarn(program2) {
|
|
|
1809
1863
|
if (dailyYield > 1e-4) {
|
|
1810
1864
|
const dailyStr = dailyYield < 0.01 ? `$${dailyYield.toFixed(4)}` : formatUsd12(dailyYield);
|
|
1811
1865
|
const monthlyStr = dailyYield * 30 < 0.01 ? `$${(dailyYield * 30).toFixed(4)}` : formatUsd12(dailyYield * 30);
|
|
1812
|
-
printLine(
|
|
1866
|
+
printLine(pc12.dim(` ~${dailyStr}/day \xB7 ~${monthlyStr}/month`));
|
|
1813
1867
|
}
|
|
1814
1868
|
totalInvestValue += pos.currentValue;
|
|
1815
1869
|
}
|
|
@@ -1819,7 +1873,7 @@ function registerEarn(program2) {
|
|
|
1819
1873
|
}
|
|
1820
1874
|
}
|
|
1821
1875
|
printBlank();
|
|
1822
|
-
printLine(
|
|
1876
|
+
printLine(pc12.bold("SENTINEL BOUNTIES") + pc12.dim(" \u2014 Active Red Teaming"));
|
|
1823
1877
|
printDivider();
|
|
1824
1878
|
if (agents && agents.length > 0) {
|
|
1825
1879
|
const totalPool = agents.reduce((sum, s) => sum + mistToSui(s.prizePool), 0);
|
|
@@ -1838,12 +1892,12 @@ function registerEarn(program2) {
|
|
|
1838
1892
|
printInfo("Sentinel data unavailable");
|
|
1839
1893
|
}
|
|
1840
1894
|
printBlank();
|
|
1841
|
-
printLine(
|
|
1895
|
+
printLine(pc12.bold("Quick Actions"));
|
|
1842
1896
|
printDivider();
|
|
1843
|
-
printLine(` ${
|
|
1844
|
-
printLine(` ${
|
|
1845
|
-
printLine(` ${
|
|
1846
|
-
printLine(` ${
|
|
1897
|
+
printLine(` ${pc12.dim("t2000 save <amount> [asset]")} Save stablecoins for yield`);
|
|
1898
|
+
printLine(` ${pc12.dim("t2000 invest earn <asset>")} Earn yield on investments`);
|
|
1899
|
+
printLine(` ${pc12.dim("t2000 sentinel list")} Browse sentinel bounties`);
|
|
1900
|
+
printLine(` ${pc12.dim("t2000 sentinel attack <id>")} Attack a sentinel`);
|
|
1847
1901
|
printBlank();
|
|
1848
1902
|
} catch (error) {
|
|
1849
1903
|
handleError(error);
|
|
@@ -1852,7 +1906,7 @@ function registerEarn(program2) {
|
|
|
1852
1906
|
}
|
|
1853
1907
|
|
|
1854
1908
|
// src/commands/rebalance.ts
|
|
1855
|
-
import
|
|
1909
|
+
import pc13 from "picocolors";
|
|
1856
1910
|
import { T2000 as T200022, formatUsd as formatUsd13, SUPPORTED_ASSETS as SUPPORTED_ASSETS2 } from "@t2000/sdk";
|
|
1857
1911
|
function registerRebalance(program2) {
|
|
1858
1912
|
program2.command("rebalance").description("Optimize yield \u2014 move savings to the best rate across protocols and stablecoins").option("--key <path>", "Key file path").option("--dry-run", "Show what would happen without executing").option("--min-diff <pct>", "Minimum APY difference to trigger (default: 0.5)", "0.5").option("--max-break-even <days>", "Max break-even days for cross-asset moves (default: 30)", "30").action(async (opts) => {
|
|
@@ -1880,24 +1934,24 @@ function registerRebalance(program2) {
|
|
|
1880
1934
|
const diff = plan.newApy - plan.currentApy;
|
|
1881
1935
|
if (diff < minYieldDiff) {
|
|
1882
1936
|
printInfo(`Already optimized \u2014 ${plan.currentApy.toFixed(2)}% APY on ${plan.fromProtocol}`);
|
|
1883
|
-
printLine(
|
|
1884
|
-
printLine(
|
|
1937
|
+
printLine(pc13.dim(` Best available: ${plan.newApy.toFixed(2)}% (${displayAsset(plan.toAsset)} on ${plan.toProtocol})`));
|
|
1938
|
+
printLine(pc13.dim(` Difference: ${diff.toFixed(2)}% (below ${minYieldDiff}% threshold)`));
|
|
1885
1939
|
} else if (plan.breakEvenDays > maxBreakEven && plan.estimatedSwapCost > 0) {
|
|
1886
1940
|
printInfo(`Skipped \u2014 break-even of ${plan.breakEvenDays} days exceeds ${maxBreakEven}-day limit`);
|
|
1887
|
-
printLine(
|
|
1941
|
+
printLine(pc13.dim(` ${displayAsset(plan.fromAsset)} on ${plan.fromProtocol} (${plan.currentApy.toFixed(2)}%) \u2192 ${displayAsset(plan.toAsset)} on ${plan.toProtocol} (${plan.newApy.toFixed(2)}%)`));
|
|
1888
1942
|
} else {
|
|
1889
1943
|
printInfo("Already at the best rate. Nothing to rebalance.");
|
|
1890
1944
|
}
|
|
1891
1945
|
printBlank();
|
|
1892
1946
|
return;
|
|
1893
1947
|
}
|
|
1894
|
-
printLine(
|
|
1948
|
+
printLine(pc13.bold("Rebalance Plan"));
|
|
1895
1949
|
printDivider();
|
|
1896
1950
|
printKeyValue("From", `${displayAsset(plan.fromAsset)} on ${plan.fromProtocol} (${plan.currentApy.toFixed(2)}% APY)`);
|
|
1897
1951
|
printKeyValue("To", `${displayAsset(plan.toAsset)} on ${plan.toProtocol} (${plan.newApy.toFixed(2)}% APY)`);
|
|
1898
1952
|
printKeyValue("Amount", formatUsd13(plan.amount));
|
|
1899
1953
|
printBlank();
|
|
1900
|
-
printLine(
|
|
1954
|
+
printLine(pc13.bold("Economics"));
|
|
1901
1955
|
printDivider();
|
|
1902
1956
|
printKeyValue("APY Gain", `+${(plan.newApy - plan.currentApy).toFixed(2)}%`);
|
|
1903
1957
|
printKeyValue("Annual Gain", `${formatUsd13(plan.annualGain)}/year`);
|
|
@@ -1907,7 +1961,7 @@ function registerRebalance(program2) {
|
|
|
1907
1961
|
}
|
|
1908
1962
|
printBlank();
|
|
1909
1963
|
if (plan.steps.length > 0) {
|
|
1910
|
-
printLine(
|
|
1964
|
+
printLine(pc13.bold("Steps"));
|
|
1911
1965
|
printDivider();
|
|
1912
1966
|
for (let i = 0; i < plan.steps.length; i++) {
|
|
1913
1967
|
const step = plan.steps[i];
|
|
@@ -1923,8 +1977,8 @@ function registerRebalance(program2) {
|
|
|
1923
1977
|
printBlank();
|
|
1924
1978
|
}
|
|
1925
1979
|
if (opts.dryRun) {
|
|
1926
|
-
printLine(
|
|
1927
|
-
printLine(
|
|
1980
|
+
printLine(pc13.bold(pc13.yellow("DRY RUN \u2014 Preview only, no transactions executed")));
|
|
1981
|
+
printLine(pc13.dim(" Run `t2000 rebalance` to execute."));
|
|
1928
1982
|
printBlank();
|
|
1929
1983
|
return;
|
|
1930
1984
|
}
|
|
@@ -2037,7 +2091,7 @@ function registerMcp(program2) {
|
|
|
2037
2091
|
mcp.command("start", { isDefault: true }).description("Start MCP server (stdio transport)").option("--key <path>", "Key file path").action(async (opts) => {
|
|
2038
2092
|
let mod;
|
|
2039
2093
|
try {
|
|
2040
|
-
mod = await import("./dist-
|
|
2094
|
+
mod = await import("./dist-NXFA54RO.js");
|
|
2041
2095
|
} catch {
|
|
2042
2096
|
console.error(
|
|
2043
2097
|
"MCP server not installed. Run:\n npm install -g @t2000/mcp"
|
|
@@ -2189,7 +2243,7 @@ function registerContacts(program2) {
|
|
|
2189
2243
|
}
|
|
2190
2244
|
|
|
2191
2245
|
// src/commands/invest.ts
|
|
2192
|
-
import
|
|
2246
|
+
import pc14 from "picocolors";
|
|
2193
2247
|
import { T2000 as T200024, formatUsd as formatUsd15, formatAssetAmount as formatAssetAmount4, INVESTMENT_ASSETS as INVESTMENT_ASSETS2 } from "@t2000/sdk";
|
|
2194
2248
|
function registerInvest(program2) {
|
|
2195
2249
|
const investCmd = program2.command("invest").description("Buy or sell investment assets");
|
|
@@ -2197,7 +2251,7 @@ function registerInvest(program2) {
|
|
|
2197
2251
|
try {
|
|
2198
2252
|
const parsed = parseFloat(amount);
|
|
2199
2253
|
if (isNaN(parsed) || parsed <= 0 || !isFinite(parsed)) {
|
|
2200
|
-
console.error(
|
|
2254
|
+
console.error(pc14.red(" \u2717 Amount must be greater than $0"));
|
|
2201
2255
|
process.exitCode = 1;
|
|
2202
2256
|
return;
|
|
2203
2257
|
}
|
|
@@ -2229,7 +2283,7 @@ function registerInvest(program2) {
|
|
|
2229
2283
|
if (!isAll) {
|
|
2230
2284
|
const parsed = parseFloat(amount);
|
|
2231
2285
|
if (isNaN(parsed) || parsed <= 0 || !isFinite(parsed)) {
|
|
2232
|
-
console.error(
|
|
2286
|
+
console.error(pc14.red(" \u2717 Amount must be greater than $0"));
|
|
2233
2287
|
process.exitCode = 1;
|
|
2234
2288
|
return;
|
|
2235
2289
|
}
|
|
@@ -2251,7 +2305,7 @@ function registerInvest(program2) {
|
|
|
2251
2305
|
printSuccess(`Sold ${formatAssetAmount4(result.amount, sym)} ${sym} at ${formatUsd15(result.price)}`);
|
|
2252
2306
|
printKeyValue("Proceeds", formatUsd15(result.usdValue));
|
|
2253
2307
|
if (result.realizedPnL !== void 0) {
|
|
2254
|
-
const pnlColor = result.realizedPnL >= 0 ?
|
|
2308
|
+
const pnlColor = result.realizedPnL >= 0 ? pc14.green : pc14.red;
|
|
2255
2309
|
const pnlSign = result.realizedPnL >= 0 ? "+" : "";
|
|
2256
2310
|
printKeyValue("Realized P&L", pnlColor(`${pnlSign}${formatUsd15(result.realizedPnL)}`));
|
|
2257
2311
|
}
|
|
@@ -2382,9 +2436,9 @@ function registerInvest(program2) {
|
|
|
2382
2436
|
printSeparator();
|
|
2383
2437
|
for (const [key, def] of Object.entries(all)) {
|
|
2384
2438
|
const allocs = Object.entries(def.allocations).map(([a, p]) => `${a} ${p}%`).join(", ");
|
|
2385
|
-
const tag = def.custom ?
|
|
2439
|
+
const tag = def.custom ? pc14.dim(" (custom)") : "";
|
|
2386
2440
|
printKeyValue(key, `${allocs}${tag}`);
|
|
2387
|
-
printLine(` ${
|
|
2441
|
+
printLine(` ${pc14.dim(def.description)}`);
|
|
2388
2442
|
}
|
|
2389
2443
|
printSeparator();
|
|
2390
2444
|
const hasPositions = Object.keys(all).some((k) => agent.portfolio.hasStrategyPositions(k));
|
|
@@ -2400,7 +2454,7 @@ function registerInvest(program2) {
|
|
|
2400
2454
|
try {
|
|
2401
2455
|
const parsed = parseFloat(amount);
|
|
2402
2456
|
if (isNaN(parsed) || parsed <= 0) {
|
|
2403
|
-
console.error(
|
|
2457
|
+
console.error(pc14.red(" \u2717 Amount must be greater than $0"));
|
|
2404
2458
|
process.exitCode = 1;
|
|
2405
2459
|
return;
|
|
2406
2460
|
}
|
|
@@ -2434,7 +2488,7 @@ function registerInvest(program2) {
|
|
|
2434
2488
|
printKeyValue("Tx", explorerUrl(txDigests[0]));
|
|
2435
2489
|
} else {
|
|
2436
2490
|
for (const buy of result.buys) {
|
|
2437
|
-
printLine(` ${
|
|
2491
|
+
printLine(` ${pc14.dim(`${buy.asset}: ${explorerUrl(buy.tx)}`)}`);
|
|
2438
2492
|
}
|
|
2439
2493
|
}
|
|
2440
2494
|
}
|
|
@@ -2456,18 +2510,18 @@ function registerInvest(program2) {
|
|
|
2456
2510
|
printSuccess(`Sold all ${name} strategy positions`);
|
|
2457
2511
|
printSeparator();
|
|
2458
2512
|
for (const sell of result.sells) {
|
|
2459
|
-
const pnlColor = sell.realizedPnL >= 0 ?
|
|
2513
|
+
const pnlColor = sell.realizedPnL >= 0 ? pc14.green : pc14.red;
|
|
2460
2514
|
const pnlSign = sell.realizedPnL >= 0 ? "+" : "";
|
|
2461
2515
|
printKeyValue(sell.asset, `${formatAssetAmount4(sell.amount, sell.asset)} \u2192 ${formatUsd15(sell.usdValue)} ${pnlColor(`${pnlSign}${formatUsd15(sell.realizedPnL)}`)}`);
|
|
2462
2516
|
}
|
|
2463
2517
|
if (result.failed && result.failed.length > 0) {
|
|
2464
2518
|
for (const f of result.failed) {
|
|
2465
|
-
console.error(
|
|
2519
|
+
console.error(pc14.yellow(` \u26A0 ${f.asset}: ${f.reason}`));
|
|
2466
2520
|
}
|
|
2467
2521
|
}
|
|
2468
2522
|
printSeparator();
|
|
2469
2523
|
printKeyValue("Total proceeds", formatUsd15(result.totalProceeds));
|
|
2470
|
-
const rpnlColor = result.realizedPnL >= 0 ?
|
|
2524
|
+
const rpnlColor = result.realizedPnL >= 0 ? pc14.green : pc14.red;
|
|
2471
2525
|
const rpnlSign = result.realizedPnL >= 0 ? "+" : "";
|
|
2472
2526
|
printKeyValue("Realized P&L", rpnlColor(`${rpnlSign}${formatUsd15(result.realizedPnL)}`));
|
|
2473
2527
|
printBlank();
|
|
@@ -2494,8 +2548,8 @@ function registerInvest(program2) {
|
|
|
2494
2548
|
const target = status.definition.allocations[pos.asset] ?? 0;
|
|
2495
2549
|
const actual = status.currentWeights[pos.asset] ?? 0;
|
|
2496
2550
|
const drift = actual - target;
|
|
2497
|
-
const driftColor = Math.abs(drift) > 3 ?
|
|
2498
|
-
const pnlColor = pos.unrealizedPnL >= 0 ?
|
|
2551
|
+
const driftColor = Math.abs(drift) > 3 ? pc14.yellow : pc14.dim;
|
|
2552
|
+
const pnlColor = pos.unrealizedPnL >= 0 ? pc14.green : pc14.red;
|
|
2499
2553
|
const pnlSign = pos.unrealizedPnL >= 0 ? "+" : "";
|
|
2500
2554
|
printKeyValue(
|
|
2501
2555
|
pos.asset,
|
|
@@ -2526,7 +2580,7 @@ function registerInvest(program2) {
|
|
|
2526
2580
|
printSuccess(`Rebalanced ${name} strategy`);
|
|
2527
2581
|
printSeparator();
|
|
2528
2582
|
for (const t of result.trades) {
|
|
2529
|
-
const action = t.action === "buy" ?
|
|
2583
|
+
const action = t.action === "buy" ? pc14.green("BUY") : pc14.red("SELL");
|
|
2530
2584
|
printKeyValue(t.asset, `${action} ${formatUsd15(t.usdAmount)} (${formatAssetAmount4(t.amount, t.asset)})`);
|
|
2531
2585
|
}
|
|
2532
2586
|
printSeparator();
|
|
@@ -2543,7 +2597,7 @@ function registerInvest(program2) {
|
|
|
2543
2597
|
for (const pair of opts.alloc) {
|
|
2544
2598
|
const [asset, pctStr] = pair.split(":");
|
|
2545
2599
|
if (!asset || !pctStr) {
|
|
2546
|
-
console.error(
|
|
2600
|
+
console.error(pc14.red(` \u2717 Invalid allocation: '${pair}'. Use ASSET:PCT format (e.g. SUI:60)`));
|
|
2547
2601
|
process.exitCode = 1;
|
|
2548
2602
|
return;
|
|
2549
2603
|
}
|
|
@@ -2570,7 +2624,7 @@ function registerInvest(program2) {
|
|
|
2570
2624
|
const pin = await resolvePin();
|
|
2571
2625
|
const agent = await T200024.create({ pin, keyPath: opts.key });
|
|
2572
2626
|
if (agent.portfolio.hasStrategyPositions(name.toLowerCase())) {
|
|
2573
|
-
console.error(
|
|
2627
|
+
console.error(pc14.red(` \u2717 Strategy '${name}' has open positions. Sell first: t2000 invest strategy sell ${name}`));
|
|
2574
2628
|
process.exitCode = 1;
|
|
2575
2629
|
return;
|
|
2576
2630
|
}
|
|
@@ -2591,12 +2645,12 @@ function registerInvest(program2) {
|
|
|
2591
2645
|
try {
|
|
2592
2646
|
const parsed = parseFloat(amount);
|
|
2593
2647
|
if (isNaN(parsed) || parsed < 1) {
|
|
2594
|
-
console.error(
|
|
2648
|
+
console.error(pc14.red(" \u2717 Amount must be at least $1"));
|
|
2595
2649
|
process.exitCode = 1;
|
|
2596
2650
|
return;
|
|
2597
2651
|
}
|
|
2598
2652
|
if (!["daily", "weekly", "monthly"].includes(frequency)) {
|
|
2599
|
-
console.error(
|
|
2653
|
+
console.error(pc14.red(" \u2717 Frequency must be daily, weekly, or monthly"));
|
|
2600
2654
|
process.exitCode = 1;
|
|
2601
2655
|
return;
|
|
2602
2656
|
}
|
|
@@ -2606,7 +2660,7 @@ function registerInvest(program2) {
|
|
|
2606
2660
|
const isStrategy = target ? target.toLowerCase() in allStrategies : false;
|
|
2607
2661
|
const isAsset = target ? target.toUpperCase() in INVESTMENT_ASSETS2 : false;
|
|
2608
2662
|
if (target && !isStrategy && !isAsset) {
|
|
2609
|
-
console.error(
|
|
2663
|
+
console.error(pc14.red(` \u2717 '${target}' is not a valid strategy or asset`));
|
|
2610
2664
|
process.exitCode = 1;
|
|
2611
2665
|
return;
|
|
2612
2666
|
}
|
|
@@ -2653,9 +2707,9 @@ function registerInvest(program2) {
|
|
|
2653
2707
|
printSeparator();
|
|
2654
2708
|
for (const s of status.schedules) {
|
|
2655
2709
|
const target = s.strategy ?? s.asset ?? "?";
|
|
2656
|
-
const statusTag = s.enabled ?
|
|
2710
|
+
const statusTag = s.enabled ? pc14.green("active") : pc14.dim("paused");
|
|
2657
2711
|
printKeyValue(s.id, `${formatUsd15(s.amount)} ${s.frequency} \u2192 ${target} ${statusTag}`);
|
|
2658
|
-
printLine(` ${
|
|
2712
|
+
printLine(` ${pc14.dim(`Next: ${new Date(s.nextRun).toLocaleDateString()} \xB7 Runs: ${s.runCount} \xB7 Total: ${formatUsd15(s.totalInvested)}`)}`);
|
|
2659
2713
|
}
|
|
2660
2714
|
printSeparator();
|
|
2661
2715
|
if (status.pendingRuns.length > 0) {
|
|
@@ -2698,7 +2752,7 @@ function registerInvest(program2) {
|
|
|
2698
2752
|
}
|
|
2699
2753
|
if (result.skipped.length > 0) {
|
|
2700
2754
|
for (const skip of result.skipped) {
|
|
2701
|
-
printLine(` ${
|
|
2755
|
+
printLine(` ${pc14.yellow("\u26A0")} Skipped ${skip.scheduleId}: ${skip.reason}`);
|
|
2702
2756
|
}
|
|
2703
2757
|
}
|
|
2704
2758
|
printBlank();
|
|
@@ -2725,22 +2779,22 @@ function registerInvest(program2) {
|
|
|
2725
2779
|
}
|
|
2726
2780
|
|
|
2727
2781
|
// src/commands/portfolio.ts
|
|
2728
|
-
import
|
|
2782
|
+
import pc15 from "picocolors";
|
|
2729
2783
|
import { T2000 as T200025, formatUsd as formatUsd16, formatAssetAmount as formatAssetAmount5 } from "@t2000/sdk";
|
|
2730
2784
|
function printPositionLine(pos, rewardKeys) {
|
|
2731
2785
|
if (pos.currentPrice === 0 && pos.totalAmount > 0) {
|
|
2732
2786
|
printKeyValue(
|
|
2733
2787
|
pos.asset,
|
|
2734
|
-
`${formatAssetAmount5(pos.totalAmount, pos.asset)} Avg: ${formatUsd16(pos.avgPrice)} Now: ${
|
|
2788
|
+
`${formatAssetAmount5(pos.totalAmount, pos.asset)} Avg: ${formatUsd16(pos.avgPrice)} Now: ${pc15.yellow("unavailable")}`
|
|
2735
2789
|
);
|
|
2736
2790
|
} else {
|
|
2737
|
-
const pnlColor = pos.unrealizedPnL >= 0 ?
|
|
2791
|
+
const pnlColor = pos.unrealizedPnL >= 0 ? pc15.green : pc15.red;
|
|
2738
2792
|
const pnlSign = pos.unrealizedPnL >= 0 ? "+" : "";
|
|
2739
2793
|
let yieldSuffix = "";
|
|
2740
2794
|
if (pos.earning && pos.earningApy) {
|
|
2741
2795
|
const hasRewards = rewardKeys?.has(`${pos.earningProtocol}:${pos.asset}`);
|
|
2742
|
-
const rewardTag = hasRewards ? ` ${
|
|
2743
|
-
yieldSuffix = ` ${
|
|
2796
|
+
const rewardTag = hasRewards ? ` ${pc15.yellow("+rewards")}` : "";
|
|
2797
|
+
yieldSuffix = ` ${pc15.cyan(`${pos.earningApy.toFixed(1)}% APY (${pos.earningProtocol})`)}${rewardTag}`;
|
|
2744
2798
|
}
|
|
2745
2799
|
printKeyValue(
|
|
2746
2800
|
pos.asset,
|
|
@@ -2781,19 +2835,19 @@ function registerPortfolio(program2) {
|
|
|
2781
2835
|
stratLabel = def.name;
|
|
2782
2836
|
} catch {
|
|
2783
2837
|
}
|
|
2784
|
-
printLine(` ${
|
|
2838
|
+
printLine(` ${pc15.bold(pc15.cyan(`\u25B8 ${stratLabel}`))}`);
|
|
2785
2839
|
printSeparator();
|
|
2786
2840
|
for (const pos of positions) {
|
|
2787
2841
|
printPositionLine(pos, rewardKeys);
|
|
2788
2842
|
}
|
|
2789
2843
|
const stratValue = positions.reduce((s, p) => s + p.currentValue, 0);
|
|
2790
|
-
printLine(` ${
|
|
2844
|
+
printLine(` ${pc15.dim(`Subtotal: ${formatUsd16(stratValue)}`)}`);
|
|
2791
2845
|
printBlank();
|
|
2792
2846
|
}
|
|
2793
2847
|
}
|
|
2794
2848
|
if (hasDirectPositions) {
|
|
2795
2849
|
if (hasStrategyPositions) {
|
|
2796
|
-
printLine(` ${
|
|
2850
|
+
printLine(` ${pc15.bold(pc15.cyan("\u25B8 Direct"))}`);
|
|
2797
2851
|
}
|
|
2798
2852
|
printSeparator();
|
|
2799
2853
|
for (const pos of portfolio.positions) {
|
|
@@ -2801,21 +2855,21 @@ function registerPortfolio(program2) {
|
|
|
2801
2855
|
}
|
|
2802
2856
|
if (hasStrategyPositions) {
|
|
2803
2857
|
const directValue = portfolio.positions.reduce((s, p) => s + p.currentValue, 0);
|
|
2804
|
-
printLine(` ${
|
|
2858
|
+
printLine(` ${pc15.dim(`Subtotal: ${formatUsd16(directValue)}`)}`);
|
|
2805
2859
|
}
|
|
2806
2860
|
}
|
|
2807
2861
|
printSeparator();
|
|
2808
2862
|
const hasPriceUnavailable = portfolio.positions.some((p) => p.currentPrice === 0 && p.totalAmount > 0);
|
|
2809
2863
|
if (hasPriceUnavailable) {
|
|
2810
|
-
printInfo(
|
|
2864
|
+
printInfo(pc15.yellow("\u26A0 Price data unavailable for some assets. Values may be inaccurate."));
|
|
2811
2865
|
}
|
|
2812
2866
|
printKeyValue("Total invested", formatUsd16(portfolio.totalInvested));
|
|
2813
2867
|
printKeyValue("Current value", formatUsd16(portfolio.totalValue));
|
|
2814
|
-
const upnlColor = portfolio.unrealizedPnL >= 0 ?
|
|
2868
|
+
const upnlColor = portfolio.unrealizedPnL >= 0 ? pc15.green : pc15.red;
|
|
2815
2869
|
const upnlSign = portfolio.unrealizedPnL >= 0 ? "+" : "";
|
|
2816
2870
|
printKeyValue("Unrealized P&L", upnlColor(`${upnlSign}${formatUsd16(portfolio.unrealizedPnL)} (${upnlSign}${portfolio.unrealizedPnLPct.toFixed(1)}%)`));
|
|
2817
2871
|
if (portfolio.realizedPnL !== 0) {
|
|
2818
|
-
const rpnlColor = portfolio.realizedPnL >= 0 ?
|
|
2872
|
+
const rpnlColor = portfolio.realizedPnL >= 0 ? pc15.green : pc15.red;
|
|
2819
2873
|
const rpnlSign = portfolio.realizedPnL >= 0 ? "+" : "";
|
|
2820
2874
|
printKeyValue("Realized P&L", rpnlColor(`${rpnlSign}${formatUsd16(portfolio.realizedPnL)}`));
|
|
2821
2875
|
}
|
|
@@ -2827,7 +2881,7 @@ function registerPortfolio(program2) {
|
|
|
2827
2881
|
}
|
|
2828
2882
|
|
|
2829
2883
|
// src/commands/claimRewards.ts
|
|
2830
|
-
import
|
|
2884
|
+
import pc16 from "picocolors";
|
|
2831
2885
|
import { T2000 as T200026, formatUsd as formatUsd17 } from "@t2000/sdk";
|
|
2832
2886
|
function registerClaimRewards(program2) {
|
|
2833
2887
|
program2.command("claim-rewards").description("Claim pending protocol rewards").option("--key <path>", "Key file path").action(async (opts) => {
|
|
@@ -2841,20 +2895,20 @@ function registerClaimRewards(program2) {
|
|
|
2841
2895
|
}
|
|
2842
2896
|
printBlank();
|
|
2843
2897
|
if (result.rewards.length === 0) {
|
|
2844
|
-
printLine(` ${
|
|
2898
|
+
printLine(` ${pc16.dim("No rewards to claim")}`);
|
|
2845
2899
|
printBlank();
|
|
2846
2900
|
return;
|
|
2847
2901
|
}
|
|
2848
2902
|
const protocols = [...new Set(result.rewards.map((r) => r.protocol))];
|
|
2849
|
-
printLine(` ${
|
|
2903
|
+
printLine(` ${pc16.green("\u2713")} Claimed and converted rewards to USDC`);
|
|
2850
2904
|
printSeparator();
|
|
2851
2905
|
const received = result.usdcReceived;
|
|
2852
2906
|
if (received >= 0.01) {
|
|
2853
|
-
printKeyValue("Received", `${
|
|
2907
|
+
printKeyValue("Received", `${pc16.green(formatUsd17(received))} USDC`);
|
|
2854
2908
|
} else if (received > 0) {
|
|
2855
|
-
printKeyValue("Received", `${
|
|
2909
|
+
printKeyValue("Received", `${pc16.green("< $0.01")} USDC`);
|
|
2856
2910
|
} else {
|
|
2857
|
-
printKeyValue("Received", `${
|
|
2911
|
+
printKeyValue("Received", `${pc16.dim("< $0.01 USDC (rewards are still accruing)")}`);
|
|
2858
2912
|
}
|
|
2859
2913
|
printKeyValue("Source", protocols.join(", "));
|
|
2860
2914
|
if (result.tx) {
|