@t2000/cli 0.22.26 → 0.23.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 +11 -97
- package/dist/{ccip-JEEJV65M.js → ccip-XP27NGI7.js} +3 -3
- package/dist/{chunk-A5X4KG7U.js → chunk-3I6VJOM6.js} +210 -970
- package/dist/chunk-3I6VJOM6.js.map +1 -0
- package/dist/chunk-4N76GQCL.js +35382 -0
- package/dist/chunk-4N76GQCL.js.map +1 -0
- package/dist/{chunk-XOAZJ42V.js → chunk-DWOZSUBE.js} +588 -584
- package/dist/{chunk-XOAZJ42V.js.map → chunk-DWOZSUBE.js.map} +1 -1
- package/dist/chunk-EI3GHTKX.js +968 -0
- package/dist/chunk-EI3GHTKX.js.map +1 -0
- package/dist/{chunk-EEPD7SHV.js → chunk-TYYJRUQI.js} +15755 -15918
- package/dist/chunk-TYYJRUQI.js.map +1 -0
- package/dist/client-5KJQTN5X.js +84 -0
- package/dist/client-5KJQTN5X.js.map +1 -0
- package/dist/{client-R3NRAXMD.js → client-IXUBQ3HM.js} +334 -638
- package/dist/client-IXUBQ3HM.js.map +1 -0
- package/dist/{dist-NBWIWHHS.js → dist-E7HUP73Q.js} +5 -29
- package/dist/{dist-KJM2NT74.js → dist-W4Q4YXD7.js} +4200 -603
- package/dist/dist-W4Q4YXD7.js.map +1 -0
- package/dist/index.js +29 -1016
- package/dist/index.js.map +1 -1
- package/package.json +4 -5
- package/dist/chunk-3WKGZRWT.js +0 -77733
- package/dist/chunk-3WKGZRWT.js.map +0 -1
- package/dist/chunk-77SWBATH.js +0 -204
- package/dist/chunk-77SWBATH.js.map +0 -1
- package/dist/chunk-A5X4KG7U.js.map +0 -1
- package/dist/chunk-EEPD7SHV.js.map +0 -1
- package/dist/client-CK5OR2TP.js +0 -746
- package/dist/client-CK5OR2TP.js.map +0 -1
- package/dist/client-R3NRAXMD.js.map +0 -1
- package/dist/dist-KJM2NT74.js.map +0 -1
- /package/dist/{ccip-JEEJV65M.js.map → ccip-XP27NGI7.js.map} +0 -0
- /package/dist/{dist-NBWIWHHS.js.map → dist-E7HUP73Q.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
} from "./chunk-QM6OQC5D.js";
|
|
7
7
|
import {
|
|
8
8
|
ContactManager,
|
|
9
|
-
INVESTMENT_ASSETS,
|
|
10
9
|
STABLE_ASSETS,
|
|
11
10
|
SUPPORTED_ASSETS,
|
|
12
11
|
SafeguardEnforcer,
|
|
@@ -18,9 +17,9 @@ import {
|
|
|
18
17
|
saveKey,
|
|
19
18
|
truncateAddress,
|
|
20
19
|
walletExists
|
|
21
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-4N76GQCL.js";
|
|
22
21
|
import "./chunk-V7PXDEKG.js";
|
|
23
|
-
import "./chunk-
|
|
22
|
+
import "./chunk-DWOZSUBE.js";
|
|
24
23
|
import "./chunk-3XUF7GM3.js";
|
|
25
24
|
import {
|
|
26
25
|
__commonJS,
|
|
@@ -3649,7 +3648,7 @@ function registerInit(program3) {
|
|
|
3649
3648
|
printBlank();
|
|
3650
3649
|
printInfo("Setting up accounts...");
|
|
3651
3650
|
printLine(
|
|
3652
|
-
` ${import_picocolors2.default.green("\u2713")} Checking ${import_picocolors2.default.green("\u2713")} Savings ${import_picocolors2.default.green("\u2713")} Credit
|
|
3651
|
+
` ${import_picocolors2.default.green("\u2713")} Checking ${import_picocolors2.default.green("\u2713")} Savings ${import_picocolors2.default.green("\u2713")} Credit`
|
|
3653
3652
|
);
|
|
3654
3653
|
printBlank();
|
|
3655
3654
|
printLine(` \u{1F389} ${import_picocolors2.default.green("Bank account created")}`);
|
|
@@ -3707,7 +3706,7 @@ function registerInit(program3) {
|
|
|
3707
3706
|
} else {
|
|
3708
3707
|
console.log(` \u2502 Use the CLI directly: \u2502`);
|
|
3709
3708
|
console.log(` \u2502 ${import_picocolors2.default.cyan("t2000 balance")} \u2502`);
|
|
3710
|
-
console.log(` \u2502 ${import_picocolors2.default.cyan("t2000
|
|
3709
|
+
console.log(` \u2502 ${import_picocolors2.default.cyan("t2000 save 100")} \u2502`);
|
|
3711
3710
|
console.log(` \u2502 \u2502`);
|
|
3712
3711
|
}
|
|
3713
3712
|
console.log(` \u2502 Your address: \u2502`);
|
|
@@ -3805,24 +3804,6 @@ function registerBalance(program3) {
|
|
|
3805
3804
|
const borrowApy = borrows.length > 0 ? borrows.reduce((sum, p) => sum + (p.amountUsd ?? p.amount) * p.apy, 0) / borrows.reduce((sum, p) => sum + (p.amountUsd ?? p.amount), 0) : 0;
|
|
3806
3805
|
printKeyValue("Credit", `${import_picocolors3.default.red(`-${formatUsd(bal.debt)}`)} ${import_picocolors3.default.dim(`(${borrowApy.toFixed(2)}% APY)`)}`);
|
|
3807
3806
|
}
|
|
3808
|
-
if (bal.investment > 0.01) {
|
|
3809
|
-
const pnlColor = bal.investmentPnL >= 0 ? import_picocolors3.default.green : import_picocolors3.default.red;
|
|
3810
|
-
const pnlSign = bal.investmentPnL >= 0 ? "+" : "";
|
|
3811
|
-
const pnlPct = bal.investment > 0 ? bal.investmentPnL / (bal.investment - bal.investmentPnL) * 100 : 0;
|
|
3812
|
-
let earningInfo = "";
|
|
3813
|
-
try {
|
|
3814
|
-
const portfolio = await agent.getPortfolio();
|
|
3815
|
-
const earningPositions = portfolio.positions.filter((p) => p.earning && p.earningApy);
|
|
3816
|
-
if (earningPositions.length > 0) {
|
|
3817
|
-
const avgApy = earningPositions.reduce((s, p) => s + (p.earningApy ?? 0) * p.currentValue, 0) / earningPositions.reduce((s, p) => s + p.currentValue, 0);
|
|
3818
|
-
earningInfo = `, earning ${avgApy.toFixed(1)}% APY`;
|
|
3819
|
-
}
|
|
3820
|
-
} catch {
|
|
3821
|
-
}
|
|
3822
|
-
printKeyValue("Investment", `${formatUsd(bal.investment)} ${pnlColor(`(${pnlSign}${pnlPct.toFixed(1)}%${earningInfo})`)}`);
|
|
3823
|
-
} else {
|
|
3824
|
-
printKeyValue("Investment", import_picocolors3.default.dim("\u2014"));
|
|
3825
|
-
}
|
|
3826
3807
|
if (bal.gasReserve && bal.gasReserve.usdEquiv >= 0.01) {
|
|
3827
3808
|
printKeyValue("Gas reserve", `${formatUsd(bal.gasReserve.usdEquiv)} ${import_picocolors3.default.dim(`(${bal.gasReserve.sui.toFixed(2)} SUI)`)}`);
|
|
3828
3809
|
}
|
|
@@ -3838,24 +3819,6 @@ function registerBalance(program3) {
|
|
|
3838
3819
|
if (bal.savings > 5e-3) {
|
|
3839
3820
|
printKeyValue("Savings", `${formatUsd(bal.savings)}`);
|
|
3840
3821
|
}
|
|
3841
|
-
if (bal.investment > 0.01) {
|
|
3842
|
-
const pnlColor = bal.investmentPnL >= 0 ? import_picocolors3.default.green : import_picocolors3.default.red;
|
|
3843
|
-
const pnlSign = bal.investmentPnL >= 0 ? "+" : "";
|
|
3844
|
-
const pnlPct = bal.investment > 0 ? bal.investmentPnL / (bal.investment - bal.investmentPnL) * 100 : 0;
|
|
3845
|
-
let earningInfo = "";
|
|
3846
|
-
try {
|
|
3847
|
-
const portfolio = await agent.getPortfolio();
|
|
3848
|
-
const earningPositions = portfolio.positions.filter((p) => p.earning && p.earningApy);
|
|
3849
|
-
if (earningPositions.length > 0) {
|
|
3850
|
-
const avgApy = earningPositions.reduce((s, p) => s + (p.earningApy ?? 0) * p.currentValue, 0) / earningPositions.reduce((s, p) => s + p.currentValue, 0);
|
|
3851
|
-
earningInfo = `, earning ${avgApy.toFixed(1)}% APY`;
|
|
3852
|
-
}
|
|
3853
|
-
} catch {
|
|
3854
|
-
}
|
|
3855
|
-
printKeyValue("Investment", `${formatUsd(bal.investment)} ${pnlColor(`(${pnlSign}${pnlPct.toFixed(1)}%${earningInfo})`)}`);
|
|
3856
|
-
} else {
|
|
3857
|
-
printKeyValue("Investment", import_picocolors3.default.dim("\u2014"));
|
|
3858
|
-
}
|
|
3859
3822
|
if (bal.gasReserve && bal.gasReserve.usdEquiv >= 0.01) {
|
|
3860
3823
|
printKeyValue("Gas reserve", `${formatUsd(bal.gasReserve.usdEquiv)} ${import_picocolors3.default.dim(`(${bal.gasReserve.sui.toFixed(2)} SUI)`)}`);
|
|
3861
3824
|
}
|
|
@@ -3921,7 +3884,6 @@ var import_picocolors4 = __toESM(require_picocolors(), 1);
|
|
|
3921
3884
|
var ACTION_LABELS = {
|
|
3922
3885
|
send: "\u2197 send",
|
|
3923
3886
|
lending: "\u{1F3E6} lend",
|
|
3924
|
-
swap: "\u{1F504} swap",
|
|
3925
3887
|
transaction: "\u{1F4E6} tx"
|
|
3926
3888
|
};
|
|
3927
3889
|
function relativeTime(ts) {
|
|
@@ -4157,13 +4119,13 @@ function registerSave(program3) {
|
|
|
4157
4119
|
handleError(error);
|
|
4158
4120
|
}
|
|
4159
4121
|
};
|
|
4160
|
-
program3.command("save").description("Deposit USDC into savings").argument("<amount>", 'Amount to save (or "all")').option("--key <path>", "Key file path").option("--protocol <name>", "Protocol to use (e.g. navi
|
|
4161
|
-
program3.command("supply").description("Deposit USDC into savings (alias for save)").argument("<amount>", 'Amount to save (or "all")').option("--key <path>", "Key file path").option("--protocol <name>", "Protocol to use (e.g. navi
|
|
4122
|
+
program3.command("save").description("Deposit USDC into savings").argument("<amount>", 'Amount to save (or "all")').option("--key <path>", "Key file path").option("--protocol <name>", "Protocol to use (e.g. navi)").action(action);
|
|
4123
|
+
program3.command("supply").description("Deposit USDC into savings (alias for save)").argument("<amount>", 'Amount to save (or "all")').option("--key <path>", "Key file path").option("--protocol <name>", "Protocol to use (e.g. navi)").action(action);
|
|
4162
4124
|
}
|
|
4163
4125
|
|
|
4164
4126
|
// src/commands/withdraw.ts
|
|
4165
4127
|
function registerWithdraw(program3) {
|
|
4166
|
-
program3.command("withdraw").description("Withdraw USDC from savings").argument("<amount>", 'Amount to withdraw (or "all")').option("--key <path>", "Key file path").option("--protocol <name>", "Protocol to use (e.g. navi
|
|
4128
|
+
program3.command("withdraw").description("Withdraw USDC from savings").argument("<amount>", 'Amount to withdraw (or "all")').option("--key <path>", "Key file path").option("--protocol <name>", "Protocol to use (e.g. navi)").action(async (amountStr, opts) => {
|
|
4167
4129
|
try {
|
|
4168
4130
|
const amount = amountStr === "all" ? "all" : parseFloat(amountStr);
|
|
4169
4131
|
if (amount !== "all" && (isNaN(amount) || amount <= 0)) {
|
|
@@ -4279,7 +4241,6 @@ function registerHealth(program3) {
|
|
|
4279
4241
|
|
|
4280
4242
|
// src/commands/rates.ts
|
|
4281
4243
|
var import_picocolors6 = __toESM(require_picocolors(), 1);
|
|
4282
|
-
var INVEST_ASSETS = Object.keys(INVESTMENT_ASSETS);
|
|
4283
4244
|
function registerRates(program3) {
|
|
4284
4245
|
program3.command("rates").description("Show current APY rates across protocols and stablecoins").option("--key <path>", "Key file path").action(async (opts) => {
|
|
4285
4246
|
try {
|
|
@@ -4308,20 +4269,6 @@ function registerRates(program3) {
|
|
|
4308
4269
|
}
|
|
4309
4270
|
printBlank();
|
|
4310
4271
|
}
|
|
4311
|
-
const investRates = allRates.filter((r) => INVEST_ASSETS.includes(r.asset));
|
|
4312
|
-
if (investRates.length > 0) {
|
|
4313
|
-
printLine(import_picocolors6.default.bold("Investment Assets"));
|
|
4314
|
-
printDivider();
|
|
4315
|
-
for (const asset of INVEST_ASSETS) {
|
|
4316
|
-
const assetRates = investRates.filter((r) => r.asset === asset);
|
|
4317
|
-
if (assetRates.length === 0) continue;
|
|
4318
|
-
const display = SUPPORTED_ASSETS[asset]?.displayName ?? asset;
|
|
4319
|
-
for (const entry of assetRates) {
|
|
4320
|
-
printKeyValue(`${display} (${entry.protocol})`, `Lend ${entry.rates.saveApy.toFixed(2)}%`);
|
|
4321
|
-
}
|
|
4322
|
-
}
|
|
4323
|
-
printBlank();
|
|
4324
|
-
}
|
|
4325
4272
|
if (allRates.length === 0) {
|
|
4326
4273
|
printInfo("No protocol rates available");
|
|
4327
4274
|
printBlank();
|
|
@@ -7887,7 +7834,7 @@ function registerLock(program3) {
|
|
|
7887
7834
|
});
|
|
7888
7835
|
program3.command("unlock").description("Unlock agent \u2014 resume operations").action(async () => {
|
|
7889
7836
|
try {
|
|
7890
|
-
const { T2000: T20003 } = await import("./dist-
|
|
7837
|
+
const { T2000: T20003 } = await import("./dist-E7HUP73Q.js");
|
|
7891
7838
|
const MAX_ATTEMPTS2 = 3;
|
|
7892
7839
|
let pin;
|
|
7893
7840
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS2; attempt++) {
|
|
@@ -7941,21 +7888,18 @@ function registerLock(program3) {
|
|
|
7941
7888
|
// src/commands/earn.ts
|
|
7942
7889
|
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
7943
7890
|
function registerEarn(program3) {
|
|
7944
|
-
program3.command("earn").description("Show all earning opportunities \u2014 savings yield
|
|
7891
|
+
program3.command("earn").description("Show all earning opportunities \u2014 savings yield").option("--key <path>", "Key file path").action(async (opts) => {
|
|
7945
7892
|
try {
|
|
7946
7893
|
const pin = await resolvePin();
|
|
7947
7894
|
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
7948
|
-
const [positionsResult,
|
|
7895
|
+
const [positionsResult, ratesResult] = await Promise.allSettled([
|
|
7949
7896
|
agent.positions(),
|
|
7950
|
-
agent.getPortfolio(),
|
|
7951
7897
|
agent.allRates("USDC")
|
|
7952
7898
|
]);
|
|
7953
7899
|
const posData = positionsResult.status === "fulfilled" ? positionsResult.value : null;
|
|
7954
|
-
const portfolio = portfolioResult.status === "fulfilled" ? portfolioResult.value : null;
|
|
7955
7900
|
const ratesData = ratesResult.status === "fulfilled" ? ratesResult.value : null;
|
|
7956
7901
|
const savePositions = posData?.positions.filter((p) => p.type === "save") ?? [];
|
|
7957
7902
|
const totalSaved = savePositions.reduce((s, p) => s + p.amount, 0);
|
|
7958
|
-
const earningInvestments = portfolio?.positions.filter((p) => p.earning && p.currentValue > 0) ?? [];
|
|
7959
7903
|
const bestSaveApy = ratesData?.length ? Math.max(...ratesData.map((r) => r.rates.saveApy)) : 0;
|
|
7960
7904
|
if (isJsonMode()) {
|
|
7961
7905
|
printJson({
|
|
@@ -7970,14 +7914,7 @@ function registerEarn(program3) {
|
|
|
7970
7914
|
protocol: r.protocol,
|
|
7971
7915
|
asset: "USDC",
|
|
7972
7916
|
saveApy: r.rates.saveApy
|
|
7973
|
-
})) ?? []
|
|
7974
|
-
investments: earningInvestments.map((p) => ({
|
|
7975
|
-
asset: p.asset,
|
|
7976
|
-
amount: p.totalAmount,
|
|
7977
|
-
value: p.currentValue,
|
|
7978
|
-
protocol: p.earningProtocol,
|
|
7979
|
-
apy: p.earningApy
|
|
7980
|
-
}))
|
|
7917
|
+
})) ?? []
|
|
7981
7918
|
});
|
|
7982
7919
|
return;
|
|
7983
7920
|
}
|
|
@@ -8014,298 +7951,16 @@ function registerEarn(program3) {
|
|
|
8014
7951
|
} else {
|
|
8015
7952
|
printInfo("Savings data unavailable");
|
|
8016
7953
|
}
|
|
8017
|
-
if (earningInvestments.length > 0) {
|
|
8018
|
-
printBlank();
|
|
8019
|
-
printLine(import_picocolors11.default.bold("INVESTMENTS") + import_picocolors11.default.dim(" \u2014 Earning Yield"));
|
|
8020
|
-
printDivider();
|
|
8021
|
-
let totalInvestValue = 0;
|
|
8022
|
-
for (const pos of earningInvestments) {
|
|
8023
|
-
const dailyYield = pos.currentValue * (pos.earningApy ?? 0) / 100 / 365;
|
|
8024
|
-
const apyStr = pos.earningApy ? `${pos.earningApy.toFixed(2)}%` : "\u2014";
|
|
8025
|
-
printKeyValue(
|
|
8026
|
-
`${pos.asset} via ${pos.earningProtocol ?? "unknown"}`,
|
|
8027
|
-
`${formatUsd(pos.currentValue)} (${pos.totalAmount.toFixed(4)} ${pos.asset}) @ ${apyStr} APY`
|
|
8028
|
-
);
|
|
8029
|
-
if (dailyYield > 1e-4) {
|
|
8030
|
-
const dailyStr = dailyYield < 0.01 ? `$${dailyYield.toFixed(4)}` : formatUsd(dailyYield);
|
|
8031
|
-
const monthlyStr = dailyYield * 30 < 0.01 ? `$${(dailyYield * 30).toFixed(4)}` : formatUsd(dailyYield * 30);
|
|
8032
|
-
printLine(import_picocolors11.default.dim(` ~${dailyStr}/day \xB7 ~${monthlyStr}/month`));
|
|
8033
|
-
}
|
|
8034
|
-
totalInvestValue += pos.currentValue;
|
|
8035
|
-
}
|
|
8036
|
-
if (earningInvestments.length > 1) {
|
|
8037
|
-
printBlank();
|
|
8038
|
-
printKeyValue("Total Earning", formatUsd(totalInvestValue));
|
|
8039
|
-
}
|
|
8040
|
-
}
|
|
8041
7954
|
printBlank();
|
|
8042
7955
|
printLine(import_picocolors11.default.bold("Quick Actions"));
|
|
8043
7956
|
printDivider();
|
|
8044
7957
|
printLine(` ${import_picocolors11.default.dim("t2000 save <amount> [asset]")} Save stablecoins for yield`);
|
|
8045
|
-
printLine(` ${import_picocolors11.default.dim("t2000 invest earn <asset>")} Earn yield on investments`);
|
|
8046
|
-
printBlank();
|
|
8047
|
-
} catch (error) {
|
|
8048
|
-
handleError(error);
|
|
8049
|
-
}
|
|
8050
|
-
});
|
|
8051
|
-
}
|
|
8052
|
-
|
|
8053
|
-
// src/commands/rebalance.ts
|
|
8054
|
-
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
8055
|
-
function registerRebalance(program3) {
|
|
8056
|
-
program3.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) => {
|
|
8057
|
-
try {
|
|
8058
|
-
const pin = await resolvePin();
|
|
8059
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8060
|
-
const minYieldDiff = parseFloat(opts.minDiff ?? "0.5");
|
|
8061
|
-
const maxBreakEven = parseInt(opts.maxBreakEven ?? "30", 10);
|
|
8062
|
-
const plan = await agent.rebalance({
|
|
8063
|
-
dryRun: true,
|
|
8064
|
-
minYieldDiff,
|
|
8065
|
-
maxBreakEven
|
|
8066
|
-
});
|
|
8067
|
-
if (isJsonMode()) {
|
|
8068
|
-
if (opts.dryRun) {
|
|
8069
|
-
printJson(plan);
|
|
8070
|
-
} else {
|
|
8071
|
-
const result2 = await agent.rebalance({ dryRun: false, minYieldDiff, maxBreakEven });
|
|
8072
|
-
printJson(result2);
|
|
8073
|
-
}
|
|
8074
|
-
return;
|
|
8075
|
-
}
|
|
8076
|
-
printBlank();
|
|
8077
|
-
if (plan.steps.length === 0) {
|
|
8078
|
-
const diff = plan.newApy - plan.currentApy;
|
|
8079
|
-
if (diff < minYieldDiff) {
|
|
8080
|
-
printInfo(`Already optimized \u2014 ${plan.currentApy.toFixed(2)}% APY on ${plan.fromProtocol}`);
|
|
8081
|
-
printLine(import_picocolors12.default.dim(` Best available: ${plan.newApy.toFixed(2)}% (${displayAsset(plan.toAsset)} on ${plan.toProtocol})`));
|
|
8082
|
-
printLine(import_picocolors12.default.dim(` Difference: ${diff.toFixed(2)}% (below ${minYieldDiff}% threshold)`));
|
|
8083
|
-
} else if (plan.breakEvenDays > maxBreakEven && plan.estimatedSwapCost > 0) {
|
|
8084
|
-
printInfo(`Skipped \u2014 break-even of ${plan.breakEvenDays} days exceeds ${maxBreakEven}-day limit`);
|
|
8085
|
-
printLine(import_picocolors12.default.dim(` ${displayAsset(plan.fromAsset)} on ${plan.fromProtocol} (${plan.currentApy.toFixed(2)}%) \u2192 ${displayAsset(plan.toAsset)} on ${plan.toProtocol} (${plan.newApy.toFixed(2)}%)`));
|
|
8086
|
-
} else {
|
|
8087
|
-
printInfo("Already at the best rate. Nothing to rebalance.");
|
|
8088
|
-
}
|
|
8089
|
-
printBlank();
|
|
8090
|
-
return;
|
|
8091
|
-
}
|
|
8092
|
-
printLine(import_picocolors12.default.bold("Rebalance Plan"));
|
|
8093
|
-
printDivider();
|
|
8094
|
-
printKeyValue("From", `${displayAsset(plan.fromAsset)} on ${plan.fromProtocol} (${plan.currentApy.toFixed(2)}% APY)`);
|
|
8095
|
-
printKeyValue("To", `${displayAsset(plan.toAsset)} on ${plan.toProtocol} (${plan.newApy.toFixed(2)}% APY)`);
|
|
8096
|
-
printKeyValue("Amount", formatUsd(plan.amount));
|
|
8097
|
-
printBlank();
|
|
8098
|
-
printLine(import_picocolors12.default.bold("Economics"));
|
|
8099
|
-
printDivider();
|
|
8100
|
-
printKeyValue("APY Gain", `+${(plan.newApy - plan.currentApy).toFixed(2)}%`);
|
|
8101
|
-
printKeyValue("Annual Gain", `${formatUsd(plan.annualGain)}/year`);
|
|
8102
|
-
if (plan.estimatedSwapCost > 0) {
|
|
8103
|
-
printKeyValue("Swap Cost", `~${formatUsd(plan.estimatedSwapCost)}`);
|
|
8104
|
-
printKeyValue("Break-even", `${plan.breakEvenDays} days`);
|
|
8105
|
-
}
|
|
8106
|
-
printBlank();
|
|
8107
|
-
if (plan.steps.length > 0) {
|
|
8108
|
-
printLine(import_picocolors12.default.bold("Steps"));
|
|
8109
|
-
printDivider();
|
|
8110
|
-
for (let i = 0; i < plan.steps.length; i++) {
|
|
8111
|
-
const step = plan.steps[i];
|
|
8112
|
-
const num = `${i + 1}.`;
|
|
8113
|
-
if (step.action === "withdraw") {
|
|
8114
|
-
printLine(` ${num} Withdraw ${formatUsd(step.amount)} ${displayAsset(step.fromAsset ?? "")} from ${step.protocol}`);
|
|
8115
|
-
} else if (step.action === "swap") {
|
|
8116
|
-
printLine(` ${num} Swap ${displayAsset(step.fromAsset ?? "")} \u2192 ${displayAsset(step.toAsset ?? "")} (~${formatUsd(step.estimatedOutput ?? 0)})`);
|
|
8117
|
-
} else if (step.action === "deposit") {
|
|
8118
|
-
printLine(` ${num} Deposit ${formatUsd(step.amount)} ${displayAsset(step.toAsset ?? "")} into ${step.protocol}`);
|
|
8119
|
-
}
|
|
8120
|
-
}
|
|
8121
|
-
printBlank();
|
|
8122
|
-
}
|
|
8123
|
-
if (opts.dryRun) {
|
|
8124
|
-
printLine(import_picocolors12.default.bold(import_picocolors12.default.yellow("DRY RUN \u2014 Preview only, no transactions executed")));
|
|
8125
|
-
printLine(import_picocolors12.default.dim(" Run `t2000 rebalance` to execute."));
|
|
8126
|
-
printBlank();
|
|
8127
|
-
return;
|
|
8128
|
-
}
|
|
8129
|
-
const result = await agent.rebalance({ dryRun: false, minYieldDiff, maxBreakEven });
|
|
8130
|
-
if (result.executed) {
|
|
8131
|
-
printSuccess(`Rebalanced ${formatUsd(result.amount)} \u2192 ${result.newApy.toFixed(2)}% APY`);
|
|
8132
|
-
for (const digest of result.txDigests) {
|
|
8133
|
-
printKeyValue("Tx", explorerUrl(digest));
|
|
8134
|
-
}
|
|
8135
|
-
printKeyValue("Gas", `${result.totalGasCost.toFixed(4)} SUI`);
|
|
8136
|
-
}
|
|
8137
7958
|
printBlank();
|
|
8138
7959
|
} catch (error) {
|
|
8139
7960
|
handleError(error);
|
|
8140
7961
|
}
|
|
8141
7962
|
});
|
|
8142
7963
|
}
|
|
8143
|
-
function displayAsset(asset) {
|
|
8144
|
-
return SUPPORTED_ASSETS[asset]?.displayName ?? asset;
|
|
8145
|
-
}
|
|
8146
|
-
|
|
8147
|
-
// src/commands/exchange.ts
|
|
8148
|
-
var import_picocolors13 = __toESM(require_picocolors(), 1);
|
|
8149
|
-
function resolveAssetName(input) {
|
|
8150
|
-
const upper = input.toUpperCase();
|
|
8151
|
-
for (const key of Object.keys(SUPPORTED_ASSETS)) {
|
|
8152
|
-
if (key.toUpperCase() === upper) return key;
|
|
8153
|
-
}
|
|
8154
|
-
return input;
|
|
8155
|
-
}
|
|
8156
|
-
function registerExchange(program3) {
|
|
8157
|
-
program3.command("exchange <amount> <from> <to>").description('[deprecated \u2014 use "swap" instead] Exchange between tokens').option("--key <path>", "Key file path").option("--slippage <pct>", "Max slippage percentage (default: 3)", "3").action(async (amount, from, to, opts) => {
|
|
8158
|
-
try {
|
|
8159
|
-
console.error(import_picocolors13.default.yellow(' \u26A0 "exchange" is deprecated. Use "swap" instead: t2000 swap %s %s %s'), amount, from, to);
|
|
8160
|
-
const pin = await resolvePin();
|
|
8161
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8162
|
-
const parsedAmount = parseFloat(amount);
|
|
8163
|
-
if (isNaN(parsedAmount) || parsedAmount <= 0) throw new Error("Amount must be a positive number");
|
|
8164
|
-
const result = await agent.swap({
|
|
8165
|
-
from: resolveAssetName(from),
|
|
8166
|
-
to: resolveAssetName(to),
|
|
8167
|
-
amount: parsedAmount,
|
|
8168
|
-
maxSlippage: parseFloat(opts.slippage ?? "3") / 100
|
|
8169
|
-
});
|
|
8170
|
-
if (isJsonMode()) {
|
|
8171
|
-
printJson(result);
|
|
8172
|
-
return;
|
|
8173
|
-
}
|
|
8174
|
-
console.log(import_picocolors13.default.green(" \u2713 Swapped. Tx: %s"), result.tx);
|
|
8175
|
-
} catch (error) {
|
|
8176
|
-
handleError(error);
|
|
8177
|
-
}
|
|
8178
|
-
});
|
|
8179
|
-
}
|
|
8180
|
-
|
|
8181
|
-
// src/commands/swap.ts
|
|
8182
|
-
function resolveAssetName2(input) {
|
|
8183
|
-
const upper = input.toUpperCase();
|
|
8184
|
-
for (const key of Object.keys(SUPPORTED_ASSETS)) {
|
|
8185
|
-
if (key.toUpperCase() === upper) return key;
|
|
8186
|
-
}
|
|
8187
|
-
return input;
|
|
8188
|
-
}
|
|
8189
|
-
function fmtTokenAmount(amount, asset) {
|
|
8190
|
-
if (["USDC", "USDT", "USDE"].includes(asset)) return formatUsd(amount);
|
|
8191
|
-
if (amount > 0 && amount < 1e-3) return amount.toFixed(8);
|
|
8192
|
-
if (amount > 0 && amount < 1) return amount.toFixed(6);
|
|
8193
|
-
return amount.toFixed(4);
|
|
8194
|
-
}
|
|
8195
|
-
async function executeSwap(from, to, amount, opts, label) {
|
|
8196
|
-
const pin = await resolvePin();
|
|
8197
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8198
|
-
const fromAsset = resolveAssetName2(from);
|
|
8199
|
-
const toAsset = resolveAssetName2(to);
|
|
8200
|
-
const result = await agent.swap({
|
|
8201
|
-
from: fromAsset,
|
|
8202
|
-
to: toAsset,
|
|
8203
|
-
amount,
|
|
8204
|
-
maxSlippage: parseFloat(opts.slippage ?? "3") / 100
|
|
8205
|
-
});
|
|
8206
|
-
if (isJsonMode()) {
|
|
8207
|
-
printJson(result);
|
|
8208
|
-
return;
|
|
8209
|
-
}
|
|
8210
|
-
const fromDisplay = SUPPORTED_ASSETS[fromAsset]?.displayName ?? fromAsset;
|
|
8211
|
-
const toDisplay = SUPPORTED_ASSETS[toAsset]?.displayName ?? toAsset;
|
|
8212
|
-
printBlank();
|
|
8213
|
-
if (label === "Bought") {
|
|
8214
|
-
printSuccess(`Bought ${fmtTokenAmount(result.toAmount, toAsset)} ${toDisplay} for ${formatUsd(amount)}`);
|
|
8215
|
-
} else if (label === "Sold") {
|
|
8216
|
-
printSuccess(`Sold ${fmtTokenAmount(amount, fromAsset)} ${fromDisplay} for ${formatUsd(result.toAmount)}`);
|
|
8217
|
-
} else {
|
|
8218
|
-
printSuccess(`Swapped ${fmtTokenAmount(amount, fromAsset)} ${fromDisplay} \u2192 ${fmtTokenAmount(result.toAmount, toAsset)} ${toDisplay}`);
|
|
8219
|
-
}
|
|
8220
|
-
printKeyValue("Tx", explorerUrl(result.tx));
|
|
8221
|
-
printKeyValue("Gas", `${result.gasCost.toFixed(4)} SUI (${result.gasMethod})`);
|
|
8222
|
-
printBlank();
|
|
8223
|
-
}
|
|
8224
|
-
function registerSwap(program3) {
|
|
8225
|
-
program3.command("swap <amount> <from> <to>").description("Swap tokens (e.g. swap 100 USDC SUI)").option("--key <path>", "Key file path").option("--slippage <pct>", "Max slippage percentage (default: 3)", "3").action(async (amount, from, to, opts) => {
|
|
8226
|
-
try {
|
|
8227
|
-
const parsedAmount = parseFloat(amount);
|
|
8228
|
-
if (isNaN(parsedAmount) || parsedAmount <= 0) {
|
|
8229
|
-
throw new Error("Amount must be a positive number");
|
|
8230
|
-
}
|
|
8231
|
-
await executeSwap(from, to, parsedAmount, opts, "Swapped");
|
|
8232
|
-
} catch (error) {
|
|
8233
|
-
handleError(error);
|
|
8234
|
-
}
|
|
8235
|
-
});
|
|
8236
|
-
program3.command("buy <amount> <asset>").description("Buy an asset with USDC (e.g. buy 100 BTC)").option("--key <path>", "Key file path").option("--slippage <pct>", "Max slippage percentage (default: 3)", "3").action(async (amount, asset, opts) => {
|
|
8237
|
-
try {
|
|
8238
|
-
const parsedAmount = parseFloat(amount);
|
|
8239
|
-
if (isNaN(parsedAmount) || parsedAmount <= 0) {
|
|
8240
|
-
throw new Error("Amount must be a positive number");
|
|
8241
|
-
}
|
|
8242
|
-
const resolved = resolveAssetName2(asset);
|
|
8243
|
-
if (resolved in INVESTMENT_ASSETS) {
|
|
8244
|
-
const pin = await resolvePin();
|
|
8245
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8246
|
-
const result = await agent.investBuy({
|
|
8247
|
-
asset: resolved,
|
|
8248
|
-
usdAmount: parsedAmount,
|
|
8249
|
-
maxSlippage: parseFloat(opts.slippage ?? "3") / 100
|
|
8250
|
-
});
|
|
8251
|
-
if (isJsonMode()) {
|
|
8252
|
-
printJson(result);
|
|
8253
|
-
return;
|
|
8254
|
-
}
|
|
8255
|
-
const display = SUPPORTED_ASSETS[resolved]?.displayName ?? resolved;
|
|
8256
|
-
printBlank();
|
|
8257
|
-
printSuccess(`Bought ${fmtTokenAmount(result.amount, resolved)} ${display} for ${formatUsd(parsedAmount)}`);
|
|
8258
|
-
printKeyValue("Tx", explorerUrl(result.tx));
|
|
8259
|
-
printBlank();
|
|
8260
|
-
} else {
|
|
8261
|
-
await executeSwap("USDC", asset, parsedAmount, opts, "Bought");
|
|
8262
|
-
}
|
|
8263
|
-
} catch (error) {
|
|
8264
|
-
handleError(error);
|
|
8265
|
-
}
|
|
8266
|
-
});
|
|
8267
|
-
program3.command("sell <amount> <asset>").description("Sell an asset for USDC (e.g. sell 0.001 BTC, sell all SUI)").option("--key <path>", "Key file path").option("--slippage <pct>", "Max slippage percentage (default: 3)", "3").action(async (amount, asset, opts) => {
|
|
8268
|
-
try {
|
|
8269
|
-
const resolved = resolveAssetName2(asset);
|
|
8270
|
-
const isAll = amount.toLowerCase() === "all";
|
|
8271
|
-
if (resolved in INVESTMENT_ASSETS) {
|
|
8272
|
-
const pin = await resolvePin();
|
|
8273
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8274
|
-
const usdAmount = isAll ? "all" : parseFloat(amount);
|
|
8275
|
-
if (usdAmount !== "all" && (isNaN(usdAmount) || usdAmount <= 0)) {
|
|
8276
|
-
throw new Error('Amount must be a positive number or "all"');
|
|
8277
|
-
}
|
|
8278
|
-
const result = await agent.investSell({
|
|
8279
|
-
asset: resolved,
|
|
8280
|
-
usdAmount,
|
|
8281
|
-
maxSlippage: parseFloat(opts.slippage ?? "3") / 100
|
|
8282
|
-
});
|
|
8283
|
-
if (isJsonMode()) {
|
|
8284
|
-
printJson(result);
|
|
8285
|
-
return;
|
|
8286
|
-
}
|
|
8287
|
-
const display = SUPPORTED_ASSETS[resolved]?.displayName ?? resolved;
|
|
8288
|
-
printBlank();
|
|
8289
|
-
printSuccess(`Sold ${fmtTokenAmount(result.amount, resolved)} ${display} at ${formatUsd(result.price)}`);
|
|
8290
|
-
printKeyValue("Proceeds", formatUsd(result.usdValue));
|
|
8291
|
-
if (result.realizedPnL !== void 0) {
|
|
8292
|
-
const pnlSign = result.realizedPnL >= 0 ? "+" : "";
|
|
8293
|
-
printKeyValue("Realized P&L", `${pnlSign}${formatUsd(result.realizedPnL)}`);
|
|
8294
|
-
}
|
|
8295
|
-
printKeyValue("Tx", explorerUrl(result.tx));
|
|
8296
|
-
printBlank();
|
|
8297
|
-
} else {
|
|
8298
|
-
const parsedAmount = parseFloat(amount);
|
|
8299
|
-
if (isNaN(parsedAmount) || parsedAmount <= 0) {
|
|
8300
|
-
throw new Error("Amount must be a positive number");
|
|
8301
|
-
}
|
|
8302
|
-
await executeSwap(asset, "USDC", parsedAmount, opts, "Sold");
|
|
8303
|
-
}
|
|
8304
|
-
} catch (error) {
|
|
8305
|
-
handleError(error);
|
|
8306
|
-
}
|
|
8307
|
-
});
|
|
8308
|
-
}
|
|
8309
7964
|
|
|
8310
7965
|
// src/commands/mcp.ts
|
|
8311
7966
|
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
@@ -8353,7 +8008,7 @@ function registerMcp(program3) {
|
|
|
8353
8008
|
mcp.command("start", { isDefault: true }).description("Start MCP server (stdio transport)").option("--key <path>", "Key file path").action(async (opts) => {
|
|
8354
8009
|
let mod;
|
|
8355
8010
|
try {
|
|
8356
|
-
mod = await import("./dist-
|
|
8011
|
+
mod = await import("./dist-W4Q4YXD7.js");
|
|
8357
8012
|
} catch {
|
|
8358
8013
|
console.error(
|
|
8359
8014
|
"MCP server not installed. Run:\n npm install -g @t2000/mcp"
|
|
@@ -8503,642 +8158,8 @@ function registerContacts(program3) {
|
|
|
8503
8158
|
});
|
|
8504
8159
|
}
|
|
8505
8160
|
|
|
8506
|
-
// src/commands/invest.ts
|
|
8507
|
-
var import_picocolors14 = __toESM(require_picocolors(), 1);
|
|
8508
|
-
function registerInvest(program3) {
|
|
8509
|
-
const investCmd = program3.command("invest").description("Investment strategies, DCA, and yield earning");
|
|
8510
|
-
investCmd.command("buy <amount> <asset>").description('[deprecated \u2014 use "buy" instead] Buy an asset').option("--key <path>", "Key file path").option("--slippage <pct>", "Max slippage percent", "3").action(async (amount, asset, opts) => {
|
|
8511
|
-
try {
|
|
8512
|
-
console.error(import_picocolors14.default.yellow(` \u26A0 "invest buy" is deprecated. Use: t2000 buy ${amount} ${asset}`));
|
|
8513
|
-
const parsed = parseFloat(amount);
|
|
8514
|
-
if (isNaN(parsed) || parsed <= 0 || !isFinite(parsed)) {
|
|
8515
|
-
console.error(import_picocolors14.default.red(" \u2717 Amount must be greater than $0"));
|
|
8516
|
-
process.exitCode = 1;
|
|
8517
|
-
return;
|
|
8518
|
-
}
|
|
8519
|
-
const pin = await resolvePin();
|
|
8520
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8521
|
-
const result = await agent.investBuy({
|
|
8522
|
-
asset: asset.toUpperCase(),
|
|
8523
|
-
usdAmount: parsed,
|
|
8524
|
-
maxSlippage: parseFloat(opts.slippage) / 100
|
|
8525
|
-
});
|
|
8526
|
-
if (isJsonMode()) {
|
|
8527
|
-
printJson(result);
|
|
8528
|
-
return;
|
|
8529
|
-
}
|
|
8530
|
-
printBlank();
|
|
8531
|
-
const sym = asset.toUpperCase();
|
|
8532
|
-
printSuccess(`Bought ${formatAssetAmount(result.amount, sym)} ${sym} at ${formatUsd(result.price)}`);
|
|
8533
|
-
printKeyValue("Invested", formatUsd(result.usdValue));
|
|
8534
|
-
printKeyValue("Tx", explorerUrl(result.tx));
|
|
8535
|
-
printBlank();
|
|
8536
|
-
} catch (error) {
|
|
8537
|
-
handleError(error);
|
|
8538
|
-
}
|
|
8539
|
-
});
|
|
8540
|
-
investCmd.command("sell <amount> <asset>").description('[deprecated \u2014 use "sell" instead] Sell an asset').option("--key <path>", "Key file path").option("--slippage <pct>", "Max slippage percent", "3").action(async (amount, asset, opts) => {
|
|
8541
|
-
try {
|
|
8542
|
-
console.error(import_picocolors14.default.yellow(` \u26A0 "invest sell" is deprecated. Use: t2000 sell ${amount} ${asset}`));
|
|
8543
|
-
const isAll = amount.toLowerCase() === "all";
|
|
8544
|
-
if (!isAll) {
|
|
8545
|
-
const parsed = parseFloat(amount);
|
|
8546
|
-
if (isNaN(parsed) || parsed <= 0 || !isFinite(parsed)) {
|
|
8547
|
-
console.error(import_picocolors14.default.red(" \u2717 Amount must be greater than $0"));
|
|
8548
|
-
process.exitCode = 1;
|
|
8549
|
-
return;
|
|
8550
|
-
}
|
|
8551
|
-
}
|
|
8552
|
-
const pin = await resolvePin();
|
|
8553
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8554
|
-
const sym = asset.toUpperCase();
|
|
8555
|
-
const usdAmount = isAll ? "all" : parseFloat(amount);
|
|
8556
|
-
const result = await agent.investSell({
|
|
8557
|
-
asset: sym,
|
|
8558
|
-
usdAmount,
|
|
8559
|
-
maxSlippage: parseFloat(opts.slippage) / 100
|
|
8560
|
-
});
|
|
8561
|
-
if (isJsonMode()) {
|
|
8562
|
-
printJson(result);
|
|
8563
|
-
return;
|
|
8564
|
-
}
|
|
8565
|
-
printBlank();
|
|
8566
|
-
printSuccess(`Sold ${formatAssetAmount(result.amount, sym)} ${sym} at ${formatUsd(result.price)}`);
|
|
8567
|
-
printKeyValue("Proceeds", formatUsd(result.usdValue));
|
|
8568
|
-
if (result.realizedPnL !== void 0) {
|
|
8569
|
-
const pnlColor = result.realizedPnL >= 0 ? import_picocolors14.default.green : import_picocolors14.default.red;
|
|
8570
|
-
const pnlSign = result.realizedPnL >= 0 ? "+" : "";
|
|
8571
|
-
printKeyValue("Realized P&L", pnlColor(`${pnlSign}${formatUsd(result.realizedPnL)}`));
|
|
8572
|
-
}
|
|
8573
|
-
printKeyValue("Tx", explorerUrl(result.tx));
|
|
8574
|
-
printBlank();
|
|
8575
|
-
} catch (error) {
|
|
8576
|
-
handleError(error);
|
|
8577
|
-
}
|
|
8578
|
-
});
|
|
8579
|
-
investCmd.command("earn <asset>").description("Deposit invested asset into best-rate lending protocol").option("--key <path>", "Key file path").option("--protocol <name>", "Force a specific protocol (navi, suilend)").action(async (asset, opts) => {
|
|
8580
|
-
try {
|
|
8581
|
-
const pin = await resolvePin();
|
|
8582
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8583
|
-
const result = await agent.investEarn({
|
|
8584
|
-
asset: asset.toUpperCase(),
|
|
8585
|
-
protocol: opts.protocol?.toLowerCase()
|
|
8586
|
-
});
|
|
8587
|
-
if (isJsonMode()) {
|
|
8588
|
-
printJson(result);
|
|
8589
|
-
return;
|
|
8590
|
-
}
|
|
8591
|
-
printBlank();
|
|
8592
|
-
const sym = asset.toUpperCase();
|
|
8593
|
-
if (result.amount === 0 && !result.tx) {
|
|
8594
|
-
printSuccess(`${sym} is already fully earning via ${result.protocol} (${result.apy.toFixed(1)}% APY)`);
|
|
8595
|
-
} else {
|
|
8596
|
-
printSuccess(`${sym} deposited into ${result.protocol} (${result.apy.toFixed(1)}% APY)`);
|
|
8597
|
-
printKeyValue("Amount", `${formatAssetAmount(result.amount, sym)} ${sym}`);
|
|
8598
|
-
printKeyValue("Protocol", result.protocol);
|
|
8599
|
-
printKeyValue("APY", `${result.apy.toFixed(2)}%`);
|
|
8600
|
-
printKeyValue("Tx", explorerUrl(result.tx));
|
|
8601
|
-
}
|
|
8602
|
-
printBlank();
|
|
8603
|
-
} catch (error) {
|
|
8604
|
-
handleError(error);
|
|
8605
|
-
}
|
|
8606
|
-
});
|
|
8607
|
-
investCmd.command("unearn <asset>").description("Withdraw invested asset from lending (keeps in portfolio)").option("--key <path>", "Key file path").action(async (asset, opts) => {
|
|
8608
|
-
try {
|
|
8609
|
-
const pin = await resolvePin();
|
|
8610
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8611
|
-
const result = await agent.investUnearn({
|
|
8612
|
-
asset: asset.toUpperCase()
|
|
8613
|
-
});
|
|
8614
|
-
if (isJsonMode()) {
|
|
8615
|
-
printJson(result);
|
|
8616
|
-
return;
|
|
8617
|
-
}
|
|
8618
|
-
printBlank();
|
|
8619
|
-
const sym = asset.toUpperCase();
|
|
8620
|
-
printSuccess(`Withdrew ${formatAssetAmount(result.amount, sym)} ${sym} from ${result.protocol}`);
|
|
8621
|
-
printKeyValue("Status", `${sym} withdrawn to wallet`);
|
|
8622
|
-
printKeyValue("Tx", explorerUrl(result.tx));
|
|
8623
|
-
printBlank();
|
|
8624
|
-
} catch (error) {
|
|
8625
|
-
handleError(error);
|
|
8626
|
-
}
|
|
8627
|
-
});
|
|
8628
|
-
investCmd.command("rebalance").description("Move earning positions to better-rate protocols").option("--key <path>", "Key file path").option("--dry-run", "Preview moves without executing").option("--min-diff <pct>", "Minimum APY difference to trigger move", "0.1").action(async (opts) => {
|
|
8629
|
-
try {
|
|
8630
|
-
const pin = await resolvePin();
|
|
8631
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8632
|
-
const result = await agent.investRebalance({
|
|
8633
|
-
dryRun: opts.dryRun,
|
|
8634
|
-
minYieldDiff: opts.minDiff ? parseFloat(opts.minDiff) : void 0
|
|
8635
|
-
});
|
|
8636
|
-
if (isJsonMode()) {
|
|
8637
|
-
printJson(result);
|
|
8638
|
-
return;
|
|
8639
|
-
}
|
|
8640
|
-
printBlank();
|
|
8641
|
-
if (result.moves.length === 0) {
|
|
8642
|
-
printInfo("All earning positions are already on the best rate");
|
|
8643
|
-
if (result.skipped.length > 0) {
|
|
8644
|
-
for (const s of result.skipped) {
|
|
8645
|
-
printLine(` ${s.asset}: ${s.apy.toFixed(2)}% on ${s.protocol} (best: ${s.bestApy.toFixed(2)}% \u2014 ${s.reason.replace(/_/g, " ")})`);
|
|
8646
|
-
}
|
|
8647
|
-
}
|
|
8648
|
-
printBlank();
|
|
8649
|
-
return;
|
|
8650
|
-
}
|
|
8651
|
-
if (opts.dryRun) {
|
|
8652
|
-
printHeader("Rebalance Preview");
|
|
8653
|
-
for (const m of result.moves) {
|
|
8654
|
-
printLine(` ${m.asset}: ${m.fromProtocol} (${m.oldApy.toFixed(2)}%) \u2192 ${m.toProtocol} (${m.newApy.toFixed(2)}%)`);
|
|
8655
|
-
printLine(` Gain: +${(m.newApy - m.oldApy).toFixed(2)}% APY`);
|
|
8656
|
-
}
|
|
8657
|
-
} else {
|
|
8658
|
-
printSuccess("Rebalanced earning positions");
|
|
8659
|
-
printSeparator();
|
|
8660
|
-
for (const m of result.moves) {
|
|
8661
|
-
printLine(` ${m.asset}: ${m.fromProtocol} (${m.oldApy.toFixed(2)}%) \u2192 ${m.toProtocol} (${m.newApy.toFixed(2)}%)`);
|
|
8662
|
-
printKeyValue("Amount", `${formatAssetAmount(m.amount, m.asset)} ${m.asset}`);
|
|
8663
|
-
printKeyValue("APY gain", `+${(m.newApy - m.oldApy).toFixed(2)}%`);
|
|
8664
|
-
if (m.txDigests.length > 0) {
|
|
8665
|
-
printKeyValue("Tx", explorerUrl(m.txDigests[m.txDigests.length - 1]));
|
|
8666
|
-
}
|
|
8667
|
-
}
|
|
8668
|
-
printSeparator();
|
|
8669
|
-
printKeyValue("Gas", `${result.totalGasCost.toFixed(6)} SUI`);
|
|
8670
|
-
}
|
|
8671
|
-
if (result.skipped.length > 0) {
|
|
8672
|
-
printBlank();
|
|
8673
|
-
for (const s of result.skipped) {
|
|
8674
|
-
printLine(` ${s.asset}: kept on ${s.protocol} (${s.reason.replace(/_/g, " ")})`);
|
|
8675
|
-
}
|
|
8676
|
-
}
|
|
8677
|
-
printBlank();
|
|
8678
|
-
} catch (error) {
|
|
8679
|
-
handleError(error);
|
|
8680
|
-
}
|
|
8681
|
-
});
|
|
8682
|
-
const strategyCmd = investCmd.command("strategy").description("Manage investment strategies");
|
|
8683
|
-
strategyCmd.command("list").description("List available strategies").option("--key <path>", "Key file path").action(async (opts) => {
|
|
8684
|
-
try {
|
|
8685
|
-
const pin = await resolvePin();
|
|
8686
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8687
|
-
const all = agent.strategies.getAll();
|
|
8688
|
-
if (isJsonMode()) {
|
|
8689
|
-
printJson(all);
|
|
8690
|
-
return;
|
|
8691
|
-
}
|
|
8692
|
-
printBlank();
|
|
8693
|
-
printHeader("Investment Strategies");
|
|
8694
|
-
printSeparator();
|
|
8695
|
-
for (const [key, def] of Object.entries(all)) {
|
|
8696
|
-
const allocs = Object.entries(def.allocations).map(([a, p]) => `${a} ${p}%`).join(", ");
|
|
8697
|
-
const tag = def.custom ? import_picocolors14.default.dim(" (custom)") : "";
|
|
8698
|
-
printKeyValue(key, `${allocs}${tag}`);
|
|
8699
|
-
printLine(` ${import_picocolors14.default.dim(def.description)}`);
|
|
8700
|
-
}
|
|
8701
|
-
printSeparator();
|
|
8702
|
-
const hasPositions = Object.keys(all).some((k) => agent.portfolio.hasStrategyPositions(k));
|
|
8703
|
-
if (!hasPositions) {
|
|
8704
|
-
printInfo("Buy into a strategy: t2000 invest strategy buy bluechip 100");
|
|
8705
|
-
}
|
|
8706
|
-
printBlank();
|
|
8707
|
-
} catch (error) {
|
|
8708
|
-
handleError(error);
|
|
8709
|
-
}
|
|
8710
|
-
});
|
|
8711
|
-
strategyCmd.command("buy <name> <amount>").description("Buy into a strategy").option("--key <path>", "Key file path").option("--dry-run", "Preview allocation without executing").action(async (name, amount, opts) => {
|
|
8712
|
-
try {
|
|
8713
|
-
const parsed = parseFloat(amount);
|
|
8714
|
-
if (isNaN(parsed) || parsed <= 0) {
|
|
8715
|
-
console.error(import_picocolors14.default.red(" \u2717 Amount must be greater than $0"));
|
|
8716
|
-
process.exitCode = 1;
|
|
8717
|
-
return;
|
|
8718
|
-
}
|
|
8719
|
-
const pin = await resolvePin();
|
|
8720
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8721
|
-
const result = await agent.investStrategy({ strategy: name.toLowerCase(), usdAmount: parsed, dryRun: opts.dryRun });
|
|
8722
|
-
if (isJsonMode()) {
|
|
8723
|
-
printJson(result);
|
|
8724
|
-
return;
|
|
8725
|
-
}
|
|
8726
|
-
printBlank();
|
|
8727
|
-
if (opts.dryRun) {
|
|
8728
|
-
printHeader(`Strategy: ${name} \u2014 Dry Run (${formatUsd(parsed)})`);
|
|
8729
|
-
printSeparator();
|
|
8730
|
-
for (const buy of result.buys) {
|
|
8731
|
-
printKeyValue(buy.asset, `${formatUsd(buy.usdAmount)} \u2192 ~${formatAssetAmount(buy.amount, buy.asset)} ${buy.asset} @ ${formatUsd(buy.price)}`);
|
|
8732
|
-
}
|
|
8733
|
-
printSeparator();
|
|
8734
|
-
printInfo("Run without --dry-run to execute");
|
|
8735
|
-
} else {
|
|
8736
|
-
const txDigests = [...new Set(result.buys.map((b) => b.tx))];
|
|
8737
|
-
const isSingleTx = txDigests.length === 1;
|
|
8738
|
-
printSuccess(`Invested ${formatUsd(parsed)} in ${name} strategy`);
|
|
8739
|
-
printSeparator();
|
|
8740
|
-
for (const buy of result.buys) {
|
|
8741
|
-
printKeyValue(buy.asset, `${formatAssetAmount(buy.amount, buy.asset)} @ ${formatUsd(buy.price)}`);
|
|
8742
|
-
}
|
|
8743
|
-
printSeparator();
|
|
8744
|
-
printKeyValue("Total invested", formatUsd(result.totalInvested));
|
|
8745
|
-
if (isSingleTx) {
|
|
8746
|
-
printKeyValue("Tx", explorerUrl(txDigests[0]));
|
|
8747
|
-
} else {
|
|
8748
|
-
for (const buy of result.buys) {
|
|
8749
|
-
printLine(` ${import_picocolors14.default.dim(`${buy.asset}: ${explorerUrl(buy.tx)}`)}`);
|
|
8750
|
-
}
|
|
8751
|
-
}
|
|
8752
|
-
}
|
|
8753
|
-
printBlank();
|
|
8754
|
-
} catch (error) {
|
|
8755
|
-
handleError(error);
|
|
8756
|
-
}
|
|
8757
|
-
});
|
|
8758
|
-
strategyCmd.command("sell <name>").description("Sell all positions in a strategy").option("--key <path>", "Key file path").action(async (name, opts) => {
|
|
8759
|
-
try {
|
|
8760
|
-
const pin = await resolvePin();
|
|
8761
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8762
|
-
const result = await agent.sellStrategy({ strategy: name.toLowerCase() });
|
|
8763
|
-
if (isJsonMode()) {
|
|
8764
|
-
printJson(result);
|
|
8765
|
-
return;
|
|
8766
|
-
}
|
|
8767
|
-
printBlank();
|
|
8768
|
-
printSuccess(`Sold all ${name} strategy positions`);
|
|
8769
|
-
printSeparator();
|
|
8770
|
-
for (const sell of result.sells) {
|
|
8771
|
-
const pnlColor = sell.realizedPnL >= 0 ? import_picocolors14.default.green : import_picocolors14.default.red;
|
|
8772
|
-
const pnlSign = sell.realizedPnL >= 0 ? "+" : "";
|
|
8773
|
-
printKeyValue(sell.asset, `${formatAssetAmount(sell.amount, sell.asset)} \u2192 ${formatUsd(sell.usdValue)} ${pnlColor(`${pnlSign}${formatUsd(sell.realizedPnL)}`)}`);
|
|
8774
|
-
}
|
|
8775
|
-
if (result.failed && result.failed.length > 0) {
|
|
8776
|
-
for (const f of result.failed) {
|
|
8777
|
-
console.error(import_picocolors14.default.yellow(` \u26A0 ${f.asset}: ${f.reason}`));
|
|
8778
|
-
}
|
|
8779
|
-
}
|
|
8780
|
-
printSeparator();
|
|
8781
|
-
printKeyValue("Total proceeds", formatUsd(result.totalProceeds));
|
|
8782
|
-
const rpnlColor = result.realizedPnL >= 0 ? import_picocolors14.default.green : import_picocolors14.default.red;
|
|
8783
|
-
const rpnlSign = result.realizedPnL >= 0 ? "+" : "";
|
|
8784
|
-
printKeyValue("Realized P&L", rpnlColor(`${rpnlSign}${formatUsd(result.realizedPnL)}`));
|
|
8785
|
-
printBlank();
|
|
8786
|
-
} catch (error) {
|
|
8787
|
-
handleError(error);
|
|
8788
|
-
}
|
|
8789
|
-
});
|
|
8790
|
-
strategyCmd.command("status <name>").description("Show current status and weights of a strategy").option("--key <path>", "Key file path").action(async (name, opts) => {
|
|
8791
|
-
try {
|
|
8792
|
-
const pin = await resolvePin();
|
|
8793
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8794
|
-
const status = await agent.getStrategyStatus(name.toLowerCase());
|
|
8795
|
-
if (isJsonMode()) {
|
|
8796
|
-
printJson(status);
|
|
8797
|
-
return;
|
|
8798
|
-
}
|
|
8799
|
-
printBlank();
|
|
8800
|
-
printHeader(`Strategy: ${status.definition.name}`);
|
|
8801
|
-
printSeparator();
|
|
8802
|
-
if (status.positions.length === 0) {
|
|
8803
|
-
printInfo("No positions yet. Buy in with: t2000 invest strategy buy " + name + " 100");
|
|
8804
|
-
} else {
|
|
8805
|
-
for (const pos of status.positions) {
|
|
8806
|
-
const target = status.definition.allocations[pos.asset] ?? 0;
|
|
8807
|
-
const actual = status.currentWeights[pos.asset] ?? 0;
|
|
8808
|
-
const drift = actual - target;
|
|
8809
|
-
const driftColor = Math.abs(drift) > 3 ? import_picocolors14.default.yellow : import_picocolors14.default.dim;
|
|
8810
|
-
const pnlColor = pos.unrealizedPnL >= 0 ? import_picocolors14.default.green : import_picocolors14.default.red;
|
|
8811
|
-
const pnlSign = pos.unrealizedPnL >= 0 ? "+" : "";
|
|
8812
|
-
printKeyValue(
|
|
8813
|
-
pos.asset,
|
|
8814
|
-
`${formatAssetAmount(pos.totalAmount, pos.asset)} ${formatUsd(pos.currentValue)} ${pnlColor(`${pnlSign}${formatUsd(pos.unrealizedPnL)}`)} ${driftColor(`${actual.toFixed(0)}% / ${target}% target`)}`
|
|
8815
|
-
);
|
|
8816
|
-
}
|
|
8817
|
-
printSeparator();
|
|
8818
|
-
printKeyValue("Total value", formatUsd(status.totalValue));
|
|
8819
|
-
}
|
|
8820
|
-
printBlank();
|
|
8821
|
-
} catch (error) {
|
|
8822
|
-
handleError(error);
|
|
8823
|
-
}
|
|
8824
|
-
});
|
|
8825
|
-
strategyCmd.command("rebalance <name>").description("Rebalance a strategy to target weights").option("--key <path>", "Key file path").action(async (name, opts) => {
|
|
8826
|
-
try {
|
|
8827
|
-
const pin = await resolvePin();
|
|
8828
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8829
|
-
const result = await agent.rebalanceStrategy({ strategy: name.toLowerCase() });
|
|
8830
|
-
if (isJsonMode()) {
|
|
8831
|
-
printJson(result);
|
|
8832
|
-
return;
|
|
8833
|
-
}
|
|
8834
|
-
printBlank();
|
|
8835
|
-
if (result.trades.length === 0) {
|
|
8836
|
-
printInfo(`Strategy '${name}' is already balanced (within 3% threshold)`);
|
|
8837
|
-
} else {
|
|
8838
|
-
printSuccess(`Rebalanced ${name} strategy`);
|
|
8839
|
-
printSeparator();
|
|
8840
|
-
for (const t of result.trades) {
|
|
8841
|
-
const action = t.action === "buy" ? import_picocolors14.default.green("BUY") : import_picocolors14.default.red("SELL");
|
|
8842
|
-
printKeyValue(t.asset, `${action} ${formatUsd(t.usdAmount)} (${formatAssetAmount(t.amount, t.asset)})`);
|
|
8843
|
-
}
|
|
8844
|
-
printSeparator();
|
|
8845
|
-
printInfo("Weights: " + Object.entries(result.afterWeights).map(([a, w]) => `${a} ${w.toFixed(0)}%`).join(", "));
|
|
8846
|
-
}
|
|
8847
|
-
printBlank();
|
|
8848
|
-
} catch (error) {
|
|
8849
|
-
handleError(error);
|
|
8850
|
-
}
|
|
8851
|
-
});
|
|
8852
|
-
strategyCmd.command("create <name>").description("Create a custom strategy").requiredOption("--alloc <pairs...>", "Allocations e.g. SUI:40 BTC:20 ETH:20 GOLD:20").option("--description <desc>", "Strategy description").action(async (name, opts) => {
|
|
8853
|
-
try {
|
|
8854
|
-
const allocations = {};
|
|
8855
|
-
for (const pair of opts.alloc) {
|
|
8856
|
-
const [asset, pctStr] = pair.split(":");
|
|
8857
|
-
if (!asset || !pctStr) {
|
|
8858
|
-
console.error(import_picocolors14.default.red(` \u2717 Invalid allocation: '${pair}'. Use ASSET:PCT format (e.g. SUI:60)`));
|
|
8859
|
-
process.exitCode = 1;
|
|
8860
|
-
return;
|
|
8861
|
-
}
|
|
8862
|
-
allocations[asset.toUpperCase()] = parseFloat(pctStr);
|
|
8863
|
-
}
|
|
8864
|
-
const pin = await resolvePin();
|
|
8865
|
-
const agent = await T2000.create({ pin });
|
|
8866
|
-
const definition = agent.strategies.create({ name, allocations, description: opts.description });
|
|
8867
|
-
if (isJsonMode()) {
|
|
8868
|
-
printJson(definition);
|
|
8869
|
-
return;
|
|
8870
|
-
}
|
|
8871
|
-
printBlank();
|
|
8872
|
-
printSuccess(`Created strategy '${name}'`);
|
|
8873
|
-
const allocs = Object.entries(definition.allocations).map(([a, p]) => `${a} ${p}%`).join(", ");
|
|
8874
|
-
printKeyValue("Allocations", allocs);
|
|
8875
|
-
printBlank();
|
|
8876
|
-
} catch (error) {
|
|
8877
|
-
handleError(error);
|
|
8878
|
-
}
|
|
8879
|
-
});
|
|
8880
|
-
strategyCmd.command("delete <name>").description("Delete a custom strategy").option("--key <path>", "Key file path").action(async (name, opts) => {
|
|
8881
|
-
try {
|
|
8882
|
-
const pin = await resolvePin();
|
|
8883
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8884
|
-
if (agent.portfolio.hasStrategyPositions(name.toLowerCase())) {
|
|
8885
|
-
console.error(import_picocolors14.default.red(` \u2717 Strategy '${name}' has open positions. Sell first: t2000 invest strategy sell ${name}`));
|
|
8886
|
-
process.exitCode = 1;
|
|
8887
|
-
return;
|
|
8888
|
-
}
|
|
8889
|
-
agent.strategies.delete(name.toLowerCase());
|
|
8890
|
-
if (isJsonMode()) {
|
|
8891
|
-
printJson({ deleted: name });
|
|
8892
|
-
return;
|
|
8893
|
-
}
|
|
8894
|
-
printBlank();
|
|
8895
|
-
printSuccess(`Deleted strategy '${name}'`);
|
|
8896
|
-
printBlank();
|
|
8897
|
-
} catch (error) {
|
|
8898
|
-
handleError(error);
|
|
8899
|
-
}
|
|
8900
|
-
});
|
|
8901
|
-
const autoCmd = investCmd.command("auto").description("Dollar-cost averaging (DCA) schedules");
|
|
8902
|
-
autoCmd.command("setup <amount> <frequency> [target]").description("Create a DCA schedule (target = strategy name or asset)").option("--key <path>", "Key file path").option("--day <num>", "Day of week (1-7) or month (1-28)").action(async (amount, frequency, target, opts) => {
|
|
8903
|
-
try {
|
|
8904
|
-
const parsed = parseFloat(amount);
|
|
8905
|
-
if (isNaN(parsed) || parsed < 1) {
|
|
8906
|
-
console.error(import_picocolors14.default.red(" \u2717 Amount must be at least $1"));
|
|
8907
|
-
process.exitCode = 1;
|
|
8908
|
-
return;
|
|
8909
|
-
}
|
|
8910
|
-
if (!["daily", "weekly", "monthly"].includes(frequency)) {
|
|
8911
|
-
console.error(import_picocolors14.default.red(" \u2717 Frequency must be daily, weekly, or monthly"));
|
|
8912
|
-
process.exitCode = 1;
|
|
8913
|
-
return;
|
|
8914
|
-
}
|
|
8915
|
-
const pin = await resolvePin();
|
|
8916
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8917
|
-
const allStrategies = agent.strategies.getAll();
|
|
8918
|
-
const isStrategy = target ? target.toLowerCase() in allStrategies : false;
|
|
8919
|
-
const isAsset = target ? target.toUpperCase() in INVESTMENT_ASSETS : false;
|
|
8920
|
-
if (target && !isStrategy && !isAsset) {
|
|
8921
|
-
console.error(import_picocolors14.default.red(` \u2717 '${target}' is not a valid strategy or asset`));
|
|
8922
|
-
process.exitCode = 1;
|
|
8923
|
-
return;
|
|
8924
|
-
}
|
|
8925
|
-
const dayNum = opts.day ? parseInt(opts.day, 10) : void 0;
|
|
8926
|
-
const schedule = agent.setupAutoInvest({
|
|
8927
|
-
amount: parsed,
|
|
8928
|
-
frequency,
|
|
8929
|
-
strategy: isStrategy ? target.toLowerCase() : void 0,
|
|
8930
|
-
asset: isAsset ? target.toUpperCase() : void 0,
|
|
8931
|
-
dayOfWeek: frequency === "weekly" ? dayNum : void 0,
|
|
8932
|
-
dayOfMonth: frequency === "monthly" ? dayNum : void 0
|
|
8933
|
-
});
|
|
8934
|
-
if (isJsonMode()) {
|
|
8935
|
-
printJson(schedule);
|
|
8936
|
-
return;
|
|
8937
|
-
}
|
|
8938
|
-
printBlank();
|
|
8939
|
-
const targetLabel = schedule.strategy ?? schedule.asset ?? "unknown";
|
|
8940
|
-
printSuccess(`Auto-invest created: ${formatUsd(schedule.amount)} ${schedule.frequency} \u2192 ${targetLabel}`);
|
|
8941
|
-
printKeyValue("Schedule ID", schedule.id);
|
|
8942
|
-
printKeyValue("Next run", new Date(schedule.nextRun).toLocaleDateString());
|
|
8943
|
-
printInfo("Run manually: t2000 invest auto run");
|
|
8944
|
-
printBlank();
|
|
8945
|
-
} catch (error) {
|
|
8946
|
-
handleError(error);
|
|
8947
|
-
}
|
|
8948
|
-
});
|
|
8949
|
-
autoCmd.command("status").description("Show all DCA schedules").option("--key <path>", "Key file path").action(async (opts) => {
|
|
8950
|
-
try {
|
|
8951
|
-
const pin = await resolvePin();
|
|
8952
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8953
|
-
const status = agent.getAutoInvestStatus();
|
|
8954
|
-
if (isJsonMode()) {
|
|
8955
|
-
printJson(status);
|
|
8956
|
-
return;
|
|
8957
|
-
}
|
|
8958
|
-
printBlank();
|
|
8959
|
-
if (status.schedules.length === 0) {
|
|
8960
|
-
printInfo("No auto-invest schedules. Set one up: t2000 invest auto setup 50 weekly bluechip");
|
|
8961
|
-
printBlank();
|
|
8962
|
-
return;
|
|
8963
|
-
}
|
|
8964
|
-
printHeader("Auto-Invest Schedules");
|
|
8965
|
-
printSeparator();
|
|
8966
|
-
for (const s of status.schedules) {
|
|
8967
|
-
const target = s.strategy ?? s.asset ?? "?";
|
|
8968
|
-
const statusTag = s.enabled ? import_picocolors14.default.green("active") : import_picocolors14.default.dim("paused");
|
|
8969
|
-
printKeyValue(s.id, `${formatUsd(s.amount)} ${s.frequency} \u2192 ${target} ${statusTag}`);
|
|
8970
|
-
printLine(` ${import_picocolors14.default.dim(`Next: ${new Date(s.nextRun).toLocaleDateString()} \xB7 Runs: ${s.runCount} \xB7 Total: ${formatUsd(s.totalInvested)}`)}`);
|
|
8971
|
-
}
|
|
8972
|
-
printSeparator();
|
|
8973
|
-
if (status.pendingRuns.length > 0) {
|
|
8974
|
-
printInfo(`${status.pendingRuns.length} pending run(s). Execute: t2000 invest auto run`);
|
|
8975
|
-
} else {
|
|
8976
|
-
printInfo("All schedules up to date");
|
|
8977
|
-
}
|
|
8978
|
-
printBlank();
|
|
8979
|
-
} catch (error) {
|
|
8980
|
-
handleError(error);
|
|
8981
|
-
}
|
|
8982
|
-
});
|
|
8983
|
-
autoCmd.command("run").description("Execute pending DCA purchases").option("--key <path>", "Key file path").action(async (opts) => {
|
|
8984
|
-
try {
|
|
8985
|
-
const pin = await resolvePin();
|
|
8986
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
8987
|
-
const status = agent.getAutoInvestStatus();
|
|
8988
|
-
if (status.pendingRuns.length === 0) {
|
|
8989
|
-
if (isJsonMode()) {
|
|
8990
|
-
printJson({ executed: [], skipped: [] });
|
|
8991
|
-
return;
|
|
8992
|
-
}
|
|
8993
|
-
printBlank();
|
|
8994
|
-
printInfo("No pending auto-invest runs. All schedules are up to date.");
|
|
8995
|
-
printBlank();
|
|
8996
|
-
return;
|
|
8997
|
-
}
|
|
8998
|
-
const result = await agent.runAutoInvest();
|
|
8999
|
-
if (isJsonMode()) {
|
|
9000
|
-
printJson(result);
|
|
9001
|
-
return;
|
|
9002
|
-
}
|
|
9003
|
-
printBlank();
|
|
9004
|
-
if (result.executed.length > 0) {
|
|
9005
|
-
printSuccess(`Executed ${result.executed.length} auto-invest run(s)`);
|
|
9006
|
-
for (const exec of result.executed) {
|
|
9007
|
-
const target = exec.strategy ?? exec.asset ?? "?";
|
|
9008
|
-
printKeyValue(target, formatUsd(exec.amount));
|
|
9009
|
-
}
|
|
9010
|
-
}
|
|
9011
|
-
if (result.skipped.length > 0) {
|
|
9012
|
-
for (const skip of result.skipped) {
|
|
9013
|
-
printLine(` ${import_picocolors14.default.yellow("\u26A0")} Skipped ${skip.scheduleId}: ${skip.reason}`);
|
|
9014
|
-
}
|
|
9015
|
-
}
|
|
9016
|
-
printBlank();
|
|
9017
|
-
} catch (error) {
|
|
9018
|
-
handleError(error);
|
|
9019
|
-
}
|
|
9020
|
-
});
|
|
9021
|
-
autoCmd.command("stop <id>").description("Stop an auto-invest schedule").option("--key <path>", "Key file path").action(async (id, opts) => {
|
|
9022
|
-
try {
|
|
9023
|
-
const pin = await resolvePin();
|
|
9024
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
9025
|
-
agent.stopAutoInvest(id);
|
|
9026
|
-
if (isJsonMode()) {
|
|
9027
|
-
printJson({ stopped: id });
|
|
9028
|
-
return;
|
|
9029
|
-
}
|
|
9030
|
-
printBlank();
|
|
9031
|
-
printSuccess(`Stopped auto-invest schedule ${id}`);
|
|
9032
|
-
printBlank();
|
|
9033
|
-
} catch (error) {
|
|
9034
|
-
handleError(error);
|
|
9035
|
-
}
|
|
9036
|
-
});
|
|
9037
|
-
}
|
|
9038
|
-
|
|
9039
|
-
// src/commands/portfolio.ts
|
|
9040
|
-
var import_picocolors15 = __toESM(require_picocolors(), 1);
|
|
9041
|
-
function printPositionLine(pos, rewardKeys) {
|
|
9042
|
-
if (pos.currentPrice === 0 && pos.totalAmount > 0) {
|
|
9043
|
-
printKeyValue(
|
|
9044
|
-
pos.asset,
|
|
9045
|
-
`${formatAssetAmount(pos.totalAmount, pos.asset)} Avg: ${formatUsd(pos.avgPrice)} Now: ${import_picocolors15.default.yellow("unavailable")}`
|
|
9046
|
-
);
|
|
9047
|
-
} else {
|
|
9048
|
-
const pnlColor = pos.unrealizedPnL >= 0 ? import_picocolors15.default.green : import_picocolors15.default.red;
|
|
9049
|
-
const pnlSign = pos.unrealizedPnL >= 0 ? "+" : "";
|
|
9050
|
-
let yieldSuffix = "";
|
|
9051
|
-
if (pos.earning && pos.earningApy) {
|
|
9052
|
-
const hasRewards = rewardKeys?.has(`${pos.earningProtocol}:${pos.asset}`);
|
|
9053
|
-
const rewardTag = hasRewards ? ` ${import_picocolors15.default.yellow("+rewards")}` : "";
|
|
9054
|
-
yieldSuffix = ` ${import_picocolors15.default.cyan(`${pos.earningApy.toFixed(1)}% APY (${pos.earningProtocol})`)}${rewardTag}`;
|
|
9055
|
-
}
|
|
9056
|
-
printKeyValue(
|
|
9057
|
-
pos.asset,
|
|
9058
|
-
`${formatAssetAmount(pos.totalAmount, pos.asset)} Avg: ${formatUsd(pos.avgPrice)} Now: ${formatUsd(pos.currentPrice)} ${pnlColor(`${pnlSign}${formatUsd(pos.unrealizedPnL)} (${pnlSign}${pos.unrealizedPnLPct.toFixed(1)}%)`)}${yieldSuffix}`
|
|
9059
|
-
);
|
|
9060
|
-
}
|
|
9061
|
-
}
|
|
9062
|
-
function registerPortfolio(program3) {
|
|
9063
|
-
program3.command("portfolio").description("Show investment portfolio").option("--key <path>", "Key file path").action(async (opts) => {
|
|
9064
|
-
try {
|
|
9065
|
-
const pin = await resolvePin();
|
|
9066
|
-
const agent = await T2000.create({ pin, keyPath: opts.key });
|
|
9067
|
-
const portfolio = await agent.getPortfolio();
|
|
9068
|
-
if (isJsonMode()) {
|
|
9069
|
-
printJson(portfolio);
|
|
9070
|
-
return;
|
|
9071
|
-
}
|
|
9072
|
-
const rewardKeys = /* @__PURE__ */ new Set();
|
|
9073
|
-
try {
|
|
9074
|
-
const pending = await agent.getPendingRewards();
|
|
9075
|
-
for (const r of pending) rewardKeys.add(`${r.protocol}:${r.asset}`);
|
|
9076
|
-
} catch {
|
|
9077
|
-
}
|
|
9078
|
-
printBlank();
|
|
9079
|
-
const hasDirectPositions = portfolio.positions.length > 0;
|
|
9080
|
-
const hasStrategyPositions = portfolio.strategyPositions && Object.keys(portfolio.strategyPositions).length > 0;
|
|
9081
|
-
if (!hasDirectPositions && !hasStrategyPositions) {
|
|
9082
|
-
printInfo("No investments yet. Try: t2000 buy 100 SUI");
|
|
9083
|
-
printBlank();
|
|
9084
|
-
return;
|
|
9085
|
-
}
|
|
9086
|
-
printHeader("Investment Portfolio");
|
|
9087
|
-
if (hasStrategyPositions) {
|
|
9088
|
-
for (const [key, positions] of Object.entries(portfolio.strategyPositions)) {
|
|
9089
|
-
let stratLabel = key;
|
|
9090
|
-
try {
|
|
9091
|
-
const def = agent.strategies.get(key);
|
|
9092
|
-
stratLabel = def.name;
|
|
9093
|
-
} catch {
|
|
9094
|
-
}
|
|
9095
|
-
printLine(` ${import_picocolors15.default.bold(import_picocolors15.default.cyan(`\u25B8 ${stratLabel}`))}`);
|
|
9096
|
-
printSeparator();
|
|
9097
|
-
for (const pos of positions) {
|
|
9098
|
-
printPositionLine(pos, rewardKeys);
|
|
9099
|
-
}
|
|
9100
|
-
const stratValue = positions.reduce((s, p) => s + p.currentValue, 0);
|
|
9101
|
-
printLine(` ${import_picocolors15.default.dim(`Subtotal: ${formatUsd(stratValue)}`)}`);
|
|
9102
|
-
printBlank();
|
|
9103
|
-
}
|
|
9104
|
-
}
|
|
9105
|
-
if (hasDirectPositions) {
|
|
9106
|
-
if (hasStrategyPositions) {
|
|
9107
|
-
printLine(` ${import_picocolors15.default.bold(import_picocolors15.default.cyan("\u25B8 Direct"))}`);
|
|
9108
|
-
}
|
|
9109
|
-
printSeparator();
|
|
9110
|
-
for (const pos of portfolio.positions) {
|
|
9111
|
-
printPositionLine(pos, rewardKeys);
|
|
9112
|
-
}
|
|
9113
|
-
if (hasStrategyPositions) {
|
|
9114
|
-
const directValue = portfolio.positions.reduce((s, p) => s + p.currentValue, 0);
|
|
9115
|
-
printLine(` ${import_picocolors15.default.dim(`Subtotal: ${formatUsd(directValue)}`)}`);
|
|
9116
|
-
}
|
|
9117
|
-
}
|
|
9118
|
-
printSeparator();
|
|
9119
|
-
const hasPriceUnavailable = portfolio.positions.some((p) => p.currentPrice === 0 && p.totalAmount > 0);
|
|
9120
|
-
if (hasPriceUnavailable) {
|
|
9121
|
-
printInfo(import_picocolors15.default.yellow("\u26A0 Price data unavailable for some assets. Values may be inaccurate."));
|
|
9122
|
-
}
|
|
9123
|
-
printKeyValue("Total invested", formatUsd(portfolio.totalInvested));
|
|
9124
|
-
printKeyValue("Current value", formatUsd(portfolio.totalValue));
|
|
9125
|
-
const upnlColor = portfolio.unrealizedPnL >= 0 ? import_picocolors15.default.green : import_picocolors15.default.red;
|
|
9126
|
-
const upnlSign = portfolio.unrealizedPnL >= 0 ? "+" : "";
|
|
9127
|
-
printKeyValue("Unrealized P&L", upnlColor(`${upnlSign}${formatUsd(portfolio.unrealizedPnL)} (${upnlSign}${portfolio.unrealizedPnLPct.toFixed(1)}%)`));
|
|
9128
|
-
if (portfolio.realizedPnL !== 0) {
|
|
9129
|
-
const rpnlColor = portfolio.realizedPnL >= 0 ? import_picocolors15.default.green : import_picocolors15.default.red;
|
|
9130
|
-
const rpnlSign = portfolio.realizedPnL >= 0 ? "+" : "";
|
|
9131
|
-
printKeyValue("Realized P&L", rpnlColor(`${rpnlSign}${formatUsd(portfolio.realizedPnL)}`));
|
|
9132
|
-
}
|
|
9133
|
-
printBlank();
|
|
9134
|
-
} catch (error) {
|
|
9135
|
-
handleError(error);
|
|
9136
|
-
}
|
|
9137
|
-
});
|
|
9138
|
-
}
|
|
9139
|
-
|
|
9140
8161
|
// src/commands/claimRewards.ts
|
|
9141
|
-
var
|
|
8162
|
+
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
9142
8163
|
function registerClaimRewards(program3) {
|
|
9143
8164
|
program3.command("claim-rewards").description("Claim pending protocol rewards").option("--key <path>", "Key file path").action(async (opts) => {
|
|
9144
8165
|
try {
|
|
@@ -9151,20 +8172,20 @@ function registerClaimRewards(program3) {
|
|
|
9151
8172
|
}
|
|
9152
8173
|
printBlank();
|
|
9153
8174
|
if (result.rewards.length === 0) {
|
|
9154
|
-
printLine(` ${
|
|
8175
|
+
printLine(` ${import_picocolors12.default.dim("No rewards to claim")}`);
|
|
9155
8176
|
printBlank();
|
|
9156
8177
|
return;
|
|
9157
8178
|
}
|
|
9158
8179
|
const protocols = [...new Set(result.rewards.map((r) => r.protocol))];
|
|
9159
|
-
printLine(` ${
|
|
8180
|
+
printLine(` ${import_picocolors12.default.green("\u2713")} Claimed rewards`);
|
|
9160
8181
|
printSeparator();
|
|
9161
|
-
const received = result.
|
|
8182
|
+
const received = result.totalValueUsd;
|
|
9162
8183
|
if (received >= 0.01) {
|
|
9163
|
-
printKeyValue("
|
|
8184
|
+
printKeyValue("Value", `${import_picocolors12.default.green(formatUsd(received))}`);
|
|
9164
8185
|
} else if (received > 0) {
|
|
9165
|
-
printKeyValue("
|
|
8186
|
+
printKeyValue("Value", `${import_picocolors12.default.green("< $0.01")}`);
|
|
9166
8187
|
} else {
|
|
9167
|
-
printKeyValue("
|
|
8188
|
+
printKeyValue("Value", `${import_picocolors12.default.dim("< $0.01 (rewards are still accruing)")}`);
|
|
9168
8189
|
}
|
|
9169
8190
|
printKeyValue("Source", protocols.join(", "));
|
|
9170
8191
|
if (result.tx) {
|
|
@@ -9178,7 +8199,7 @@ function registerClaimRewards(program3) {
|
|
|
9178
8199
|
}
|
|
9179
8200
|
|
|
9180
8201
|
// src/commands/gas.ts
|
|
9181
|
-
var
|
|
8202
|
+
var import_picocolors13 = __toESM(require_picocolors(), 1);
|
|
9182
8203
|
function registerGas(program3) {
|
|
9183
8204
|
program3.command("gas").description("Check gas station status and wallet gas balance").option("--key <path>", "Key file path").action(async (opts) => {
|
|
9184
8205
|
try {
|
|
@@ -9200,36 +8221,36 @@ function registerGas(program3) {
|
|
|
9200
8221
|
}
|
|
9201
8222
|
printHeader("Gas Status");
|
|
9202
8223
|
if (gasStatus) {
|
|
9203
|
-
const cbStatus = gasStatus.circuitBreaker ?
|
|
8224
|
+
const cbStatus = gasStatus.circuitBreaker ? import_picocolors13.default.red("TRIPPED \u2014 sponsorship paused") : import_picocolors13.default.green("OK");
|
|
9204
8225
|
printKeyValue("Gas Station", cbStatus);
|
|
9205
8226
|
printKeyValue("SUI Price (TWAP)", `$${gasStatus.suiPrice.toFixed(4)}`);
|
|
9206
8227
|
if (gasStatus.bootstrapRemaining !== void 0) {
|
|
9207
8228
|
printKeyValue("Bootstrap", `${gasStatus.bootstrapUsed}/10 used (${gasStatus.bootstrapRemaining} remaining)`);
|
|
9208
8229
|
}
|
|
9209
8230
|
} else {
|
|
9210
|
-
printKeyValue("Gas Station",
|
|
8231
|
+
printKeyValue("Gas Station", import_picocolors13.default.red("unreachable"));
|
|
9211
8232
|
const reason = status.status === "rejected" ? status.reason : "unknown";
|
|
9212
|
-
printLine(` ${
|
|
8233
|
+
printLine(` ${import_picocolors13.default.dim(reason instanceof Error ? reason.message : String(reason))}`);
|
|
9213
8234
|
}
|
|
9214
8235
|
printDivider();
|
|
9215
8236
|
if (balData) {
|
|
9216
8237
|
const suiBal = balData.gasReserve.sui;
|
|
9217
|
-
const suiColor = suiBal < 0.05 ?
|
|
8238
|
+
const suiColor = suiBal < 0.05 ? import_picocolors13.default.red : import_picocolors13.default.green;
|
|
9218
8239
|
printKeyValue("SUI (gas)", suiColor(`${suiBal.toFixed(4)} SUI`));
|
|
9219
8240
|
if (suiBal < 0.05) {
|
|
9220
|
-
printLine(` ${
|
|
8241
|
+
printLine(` ${import_picocolors13.default.yellow("\u26A0")} Below gas threshold (0.05 SUI) \u2014 transactions will need sponsorship`);
|
|
9221
8242
|
}
|
|
9222
8243
|
printKeyValue("Available", `$${balData.available.toFixed(2)}`);
|
|
9223
8244
|
} else {
|
|
9224
|
-
printKeyValue("Wallet",
|
|
8245
|
+
printKeyValue("Wallet", import_picocolors13.default.dim("could not fetch balances"));
|
|
9225
8246
|
}
|
|
9226
8247
|
printBlank();
|
|
9227
8248
|
if (gasStatus && !gasStatus.circuitBreaker && (balData?.gasReserve.sui ?? 0) >= 0.05) {
|
|
9228
|
-
printLine(` ${
|
|
8249
|
+
printLine(` ${import_picocolors13.default.green("\u2713")} Gas is healthy \u2014 transactions should succeed`);
|
|
9229
8250
|
} else if (gasStatus && !gasStatus.circuitBreaker) {
|
|
9230
|
-
printLine(` ${
|
|
8251
|
+
printLine(` ${import_picocolors13.default.yellow("\u26A0")} Low SUI but gas station is online \u2014 sponsorship available`);
|
|
9231
8252
|
} else {
|
|
9232
|
-
printLine(` ${
|
|
8253
|
+
printLine(` ${import_picocolors13.default.red("\u2717")} Gas station issues detected \u2014 fund wallet with SUI directly`);
|
|
9233
8254
|
printInfo("Send SUI to your address: t2000 address");
|
|
9234
8255
|
}
|
|
9235
8256
|
printBlank();
|
|
@@ -9255,9 +8276,6 @@ Examples:
|
|
|
9255
8276
|
$ t2000 send 50 to 0xabc... Send $50 USDC
|
|
9256
8277
|
$ t2000 borrow 200 Borrow $200 against savings
|
|
9257
8278
|
$ t2000 pay openai ... Pay for an API via MPP gateway
|
|
9258
|
-
$ t2000 buy 100 BTC Buy $100 of BTC
|
|
9259
|
-
$ t2000 sell 0.001 BTC Sell BTC for USDC
|
|
9260
|
-
$ t2000 swap 100 USDC SUI Swap between any tokens
|
|
9261
8279
|
$ t2000 mcp install Install MCP for AI platforms`);
|
|
9262
8280
|
registerInit(program3);
|
|
9263
8281
|
registerSend(program3);
|
|
@@ -9281,13 +8299,8 @@ Examples:
|
|
|
9281
8299
|
registerPay(program3);
|
|
9282
8300
|
registerLock(program3);
|
|
9283
8301
|
registerEarn(program3);
|
|
9284
|
-
registerRebalance(program3);
|
|
9285
|
-
registerSwap(program3);
|
|
9286
|
-
registerExchange(program3);
|
|
9287
8302
|
registerMcp(program3);
|
|
9288
8303
|
registerContacts(program3);
|
|
9289
|
-
registerInvest(program3);
|
|
9290
|
-
registerPortfolio(program3);
|
|
9291
8304
|
registerClaimRewards(program3);
|
|
9292
8305
|
registerGas(program3);
|
|
9293
8306
|
return program3;
|