@t2000/cli 0.22.26 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +12 -98
  2. package/dist/{ccip-JEEJV65M.js → ccip-XP27NGI7.js} +3 -3
  3. package/dist/{chunk-A5X4KG7U.js → chunk-3I6VJOM6.js} +210 -970
  4. package/dist/chunk-3I6VJOM6.js.map +1 -0
  5. package/dist/chunk-EI3GHTKX.js +968 -0
  6. package/dist/chunk-EI3GHTKX.js.map +1 -0
  7. package/dist/{chunk-3WKGZRWT.js → chunk-JJCGJTSI.js} +44787 -55564
  8. package/dist/chunk-JJCGJTSI.js.map +1 -0
  9. package/dist/{chunk-XOAZJ42V.js → chunk-Q2LY5BHK.js} +589 -584
  10. package/dist/{chunk-XOAZJ42V.js.map → chunk-Q2LY5BHK.js.map} +1 -1
  11. package/dist/{chunk-EEPD7SHV.js → chunk-TYYJRUQI.js} +15755 -15918
  12. package/dist/chunk-TYYJRUQI.js.map +1 -0
  13. package/dist/{client-R3NRAXMD.js → client-IXUBQ3HM.js} +334 -638
  14. package/dist/client-IXUBQ3HM.js.map +1 -0
  15. package/dist/client-YNWQPUC5.js +84 -0
  16. package/dist/client-YNWQPUC5.js.map +1 -0
  17. package/dist/{dist-KJM2NT74.js → dist-22NK522A.js} +124 -511
  18. package/dist/{dist-KJM2NT74.js.map → dist-22NK522A.js.map} +1 -1
  19. package/dist/{dist-NBWIWHHS.js → dist-2P6AEWCE.js} +5 -29
  20. package/dist/index.js +179 -1034
  21. package/dist/index.js.map +1 -1
  22. package/package.json +5 -6
  23. package/dist/chunk-3WKGZRWT.js.map +0 -1
  24. package/dist/chunk-77SWBATH.js +0 -204
  25. package/dist/chunk-77SWBATH.js.map +0 -1
  26. package/dist/chunk-A5X4KG7U.js.map +0 -1
  27. package/dist/chunk-EEPD7SHV.js.map +0 -1
  28. package/dist/client-CK5OR2TP.js +0 -746
  29. package/dist/client-CK5OR2TP.js.map +0 -1
  30. package/dist/client-R3NRAXMD.js.map +0 -1
  31. /package/dist/{ccip-JEEJV65M.js.map → ccip-XP27NGI7.js.map} +0 -0
  32. /package/dist/{dist-NBWIWHHS.js.map → dist-2P6AEWCE.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-3WKGZRWT.js";
20
+ } from "./chunk-JJCGJTSI.js";
22
21
  import "./chunk-V7PXDEKG.js";
23
- import "./chunk-XOAZJ42V.js";
22
+ import "./chunk-Q2LY5BHK.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 ${import_picocolors2.default.green("\u2713")} Swap ${import_picocolors2.default.green("\u2713")} Investment`
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 buy 100 BTC")} \u2502`);
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) {
@@ -4128,13 +4090,14 @@ function registerSave(program3) {
4128
4090
  if (amount !== "all" && (isNaN(amount) || amount <= 0)) {
4129
4091
  throw new Error('Amount must be a positive number or "all"');
4130
4092
  }
4093
+ const asset = opts.asset?.toUpperCase() ?? "USDC";
4131
4094
  const pin = await resolvePin();
4132
4095
  const agent = await T2000.create({ pin, keyPath: opts.key });
4133
4096
  let gasManagerUsdc = 0;
4134
4097
  agent.on("gasAutoTopUp", (data) => {
4135
4098
  gasManagerUsdc = data.usdcSpent;
4136
4099
  });
4137
- const result = await agent.save({ amount, protocol: opts.protocol });
4100
+ const result = await agent.save({ amount, asset, protocol: opts.protocol });
4138
4101
  if (isJsonMode()) {
4139
4102
  printJson(result);
4140
4103
  return;
@@ -4144,26 +4107,26 @@ function registerSave(program3) {
4144
4107
  printSuccess(`Gas manager: ${import_picocolors5.default.yellow(formatUsd(gasManagerUsdc))} USDC \u2192 SUI`);
4145
4108
  }
4146
4109
  const protocolName = opts.protocol ?? "best rate";
4147
- printSuccess(`Saved ${import_picocolors5.default.yellow(formatUsd(result.amount))} USDC to ${protocolName}`);
4110
+ printSuccess(`Saved ${import_picocolors5.default.yellow(formatUsd(result.amount))} ${asset} to ${protocolName}`);
4148
4111
  if (result.fee > 0) {
4149
4112
  const feeRate = (result.fee / result.amount * 100).toFixed(1);
4150
- printSuccess(`Protocol fee: ${import_picocolors5.default.dim(`${formatUsd(result.fee)} USDC (${feeRate}%)`)}`);
4113
+ printSuccess(`Protocol fee: ${import_picocolors5.default.dim(`${formatUsd(result.fee)} ${asset} (${feeRate}%)`)}`);
4151
4114
  }
4152
4115
  printSuccess(`Current APY: ${import_picocolors5.default.green(`${result.apy.toFixed(2)}%`)}`);
4153
- printSuccess(`Savings balance: ${import_picocolors5.default.yellow(formatUsd(result.savingsBalance))} USDC`);
4116
+ printSuccess(`Savings balance: ${import_picocolors5.default.yellow(formatUsd(result.savingsBalance))} ${asset}`);
4154
4117
  printKeyValue("Tx", explorerUrl(result.tx));
4155
4118
  printBlank();
4156
4119
  } catch (error) {
4157
4120
  handleError(error);
4158
4121
  }
4159
4122
  };
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, suilend)").action(action);
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, suilend)").action(action);
4123
+ program3.command("save").description("Deposit into NAVI lending to earn yield (USDC, USDT, SUI, USDe, USDsui)").argument("<amount>", 'Amount to save (or "all")').option("--key <path>", "Key file path").option("--protocol <name>", "Protocol to use (e.g. navi)").option("--asset <token>", "Asset to deposit (default: USDC)").action(action);
4124
+ program3.command("supply").description("Deposit into NAVI lending (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)").option("--asset <token>", "Asset to deposit (default: USDC)").action(action);
4162
4125
  }
4163
4126
 
4164
4127
  // src/commands/withdraw.ts
4165
4128
  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, suilend)").action(async (amountStr, opts) => {
4129
+ program3.command("withdraw").description("Withdraw from NAVI lending (USDC, USDT, SUI, USDe, USDsui)").argument("<amount>", 'Amount to withdraw (or "all")').option("--key <path>", "Key file path").option("--protocol <name>", "Protocol to use (e.g. navi)").option("--asset <token>", "Asset to withdraw (default: auto-detect)").action(async (amountStr, opts) => {
4167
4130
  try {
4168
4131
  const amount = amountStr === "all" ? "all" : parseFloat(amountStr);
4169
4132
  if (amount !== "all" && (isNaN(amount) || amount <= 0)) {
@@ -4171,13 +4134,14 @@ function registerWithdraw(program3) {
4171
4134
  }
4172
4135
  const pin = await resolvePin();
4173
4136
  const agent = await T2000.create({ pin, keyPath: opts.key });
4174
- const result = await agent.withdraw({ amount, protocol: opts.protocol });
4137
+ const result = await agent.withdraw({ amount, asset: opts.asset, protocol: opts.protocol });
4175
4138
  if (isJsonMode()) {
4176
4139
  printJson(result);
4177
4140
  return;
4178
4141
  }
4142
+ const assetLabel = opts.asset ?? "USDC";
4179
4143
  printBlank();
4180
- printSuccess(`Withdrew ${formatUsd(result.amount)} USDC`);
4144
+ printSuccess(`Withdrew ${formatUsd(result.amount)} ${assetLabel}`);
4181
4145
  printKeyValue("Tx", explorerUrl(result.tx));
4182
4146
  printBlank();
4183
4147
  } catch (error) {
@@ -4279,7 +4243,6 @@ function registerHealth(program3) {
4279
4243
 
4280
4244
  // src/commands/rates.ts
4281
4245
  var import_picocolors6 = __toESM(require_picocolors(), 1);
4282
- var INVEST_ASSETS = Object.keys(INVESTMENT_ASSETS);
4283
4246
  function registerRates(program3) {
4284
4247
  program3.command("rates").description("Show current APY rates across protocols and stablecoins").option("--key <path>", "Key file path").action(async (opts) => {
4285
4248
  try {
@@ -4308,20 +4271,6 @@ function registerRates(program3) {
4308
4271
  }
4309
4272
  printBlank();
4310
4273
  }
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
4274
  if (allRates.length === 0) {
4326
4275
  printInfo("No protocol rates available");
4327
4276
  printBlank();
@@ -7887,7 +7836,7 @@ function registerLock(program3) {
7887
7836
  });
7888
7837
  program3.command("unlock").description("Unlock agent \u2014 resume operations").action(async () => {
7889
7838
  try {
7890
- const { T2000: T20003 } = await import("./dist-NBWIWHHS.js");
7839
+ const { T2000: T20003 } = await import("./dist-2P6AEWCE.js");
7891
7840
  const MAX_ATTEMPTS2 = 3;
7892
7841
  let pin;
7893
7842
  for (let attempt = 1; attempt <= MAX_ATTEMPTS2; attempt++) {
@@ -7941,21 +7890,18 @@ function registerLock(program3) {
7941
7890
  // src/commands/earn.ts
7942
7891
  var import_picocolors11 = __toESM(require_picocolors(), 1);
7943
7892
  function registerEarn(program3) {
7944
- program3.command("earn").description("Show all earning opportunities \u2014 savings yield + investment yield").option("--key <path>", "Key file path").action(async (opts) => {
7893
+ program3.command("earn").description("Show all earning opportunities \u2014 savings yield").option("--key <path>", "Key file path").action(async (opts) => {
7945
7894
  try {
7946
7895
  const pin = await resolvePin();
7947
7896
  const agent = await T2000.create({ pin, keyPath: opts.key });
7948
- const [positionsResult, portfolioResult, ratesResult] = await Promise.allSettled([
7897
+ const [positionsResult, ratesResult] = await Promise.allSettled([
7949
7898
  agent.positions(),
7950
- agent.getPortfolio(),
7951
7899
  agent.allRates("USDC")
7952
7900
  ]);
7953
7901
  const posData = positionsResult.status === "fulfilled" ? positionsResult.value : null;
7954
- const portfolio = portfolioResult.status === "fulfilled" ? portfolioResult.value : null;
7955
7902
  const ratesData = ratesResult.status === "fulfilled" ? ratesResult.value : null;
7956
7903
  const savePositions = posData?.positions.filter((p) => p.type === "save") ?? [];
7957
7904
  const totalSaved = savePositions.reduce((s, p) => s + p.amount, 0);
7958
- const earningInvestments = portfolio?.positions.filter((p) => p.earning && p.currentValue > 0) ?? [];
7959
7905
  const bestSaveApy = ratesData?.length ? Math.max(...ratesData.map((r) => r.rates.saveApy)) : 0;
7960
7906
  if (isJsonMode()) {
7961
7907
  printJson({
@@ -7970,14 +7916,7 @@ function registerEarn(program3) {
7970
7916
  protocol: r.protocol,
7971
7917
  asset: "USDC",
7972
7918
  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
- }))
7919
+ })) ?? []
7981
7920
  });
7982
7921
  return;
7983
7922
  }
@@ -8014,298 +7953,16 @@ function registerEarn(program3) {
8014
7953
  } else {
8015
7954
  printInfo("Savings data unavailable");
8016
7955
  }
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
7956
  printBlank();
8042
7957
  printLine(import_picocolors11.default.bold("Quick Actions"));
8043
7958
  printDivider();
8044
7959
  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
7960
  printBlank();
8138
7961
  } catch (error) {
8139
7962
  handleError(error);
8140
7963
  }
8141
7964
  });
8142
7965
  }
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
7966
 
8310
7967
  // src/commands/mcp.ts
8311
7968
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
@@ -8353,7 +8010,7 @@ function registerMcp(program3) {
8353
8010
  mcp.command("start", { isDefault: true }).description("Start MCP server (stdio transport)").option("--key <path>", "Key file path").action(async (opts) => {
8354
8011
  let mod;
8355
8012
  try {
8356
- mod = await import("./dist-KJM2NT74.js");
8013
+ mod = await import("./dist-22NK522A.js");
8357
8014
  } catch {
8358
8015
  console.error(
8359
8016
  "MCP server not installed. Run:\n npm install -g @t2000/mcp"
@@ -8503,735 +8160,227 @@ function registerContacts(program3) {
8503
8160
  });
8504
8161
  }
8505
8162
 
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) => {
8163
+ // src/commands/claimRewards.ts
8164
+ var import_picocolors12 = __toESM(require_picocolors(), 1);
8165
+ function registerClaimRewards(program3) {
8166
+ program3.command("claim-rewards").description("Claim pending protocol rewards").option("--key <path>", "Key file path").action(async (opts) => {
8511
8167
  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
8168
  const pin = await resolvePin();
8520
8169
  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
- });
8170
+ const result = await agent.claimRewards();
8526
8171
  if (isJsonMode()) {
8527
8172
  printJson(result);
8528
8173
  return;
8529
8174
  }
8530
8175
  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));
8176
+ if (result.rewards.length === 0) {
8177
+ printLine(` ${import_picocolors12.default.dim("No rewards to claim")}`);
8178
+ printBlank();
8179
+ return;
8180
+ }
8181
+ const protocols = [...new Set(result.rewards.map((r) => r.protocol))];
8182
+ printLine(` ${import_picocolors12.default.green("\u2713")} Claimed rewards`);
8183
+ printSeparator();
8184
+ const received = result.totalValueUsd;
8185
+ if (received >= 0.01) {
8186
+ printKeyValue("Value", `${import_picocolors12.default.green(formatUsd(received))}`);
8187
+ } else if (received > 0) {
8188
+ printKeyValue("Value", `${import_picocolors12.default.green("< $0.01")}`);
8189
+ } else {
8190
+ printKeyValue("Value", `${import_picocolors12.default.dim("< $0.01 (rewards are still accruing)")}`);
8191
+ }
8192
+ printKeyValue("Source", protocols.join(", "));
8193
+ if (result.tx) {
8194
+ printKeyValue("Tx", `https://suiscan.xyz/mainnet/tx/${result.tx}`);
8195
+ }
8535
8196
  printBlank();
8536
8197
  } catch (error) {
8537
8198
  handleError(error);
8538
8199
  }
8539
8200
  });
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) => {
8201
+ }
8202
+
8203
+ // src/commands/gas.ts
8204
+ var import_picocolors13 = __toESM(require_picocolors(), 1);
8205
+ function registerGas(program3) {
8206
+ program3.command("gas").description("Check gas station status and wallet gas balance").option("--key <path>", "Key file path").action(async (opts) => {
8541
8207
  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;
8208
+ const pin = await resolvePin();
8209
+ const agent = await T2000.create({ pin, keyPath: opts.key });
8210
+ const address = agent.address();
8211
+ const [status, bal] = await Promise.allSettled([
8212
+ getGasStatus(address),
8213
+ agent.balance()
8214
+ ]);
8215
+ const gasStatus = status.status === "fulfilled" ? status.value : null;
8216
+ const balData = bal.status === "fulfilled" ? bal.value : null;
8217
+ if (isJsonMode()) {
8218
+ printJson({
8219
+ gasStation: gasStatus ?? { error: status.status === "rejected" ? String(status.reason) : "unavailable" },
8220
+ wallet: balData ? { sui: balData.gasReserve.sui, available: balData.available } : null
8221
+ });
8222
+ return;
8223
+ }
8224
+ printHeader("Gas Status");
8225
+ if (gasStatus) {
8226
+ const cbStatus = gasStatus.circuitBreaker ? import_picocolors13.default.red("TRIPPED \u2014 sponsorship paused") : import_picocolors13.default.green("OK");
8227
+ printKeyValue("Gas Station", cbStatus);
8228
+ printKeyValue("SUI Price (TWAP)", `$${gasStatus.suiPrice.toFixed(4)}`);
8229
+ if (gasStatus.bootstrapRemaining !== void 0) {
8230
+ printKeyValue("Bootstrap", `${gasStatus.bootstrapUsed}/10 used (${gasStatus.bootstrapRemaining} remaining)`);
8231
+ }
8232
+ } else {
8233
+ printKeyValue("Gas Station", import_picocolors13.default.red("unreachable"));
8234
+ const reason = status.status === "rejected" ? status.reason : "unknown";
8235
+ printLine(` ${import_picocolors13.default.dim(reason instanceof Error ? reason.message : String(reason))}`);
8236
+ }
8237
+ printDivider();
8238
+ if (balData) {
8239
+ const suiBal = balData.gasReserve.sui;
8240
+ const suiColor = suiBal < 0.05 ? import_picocolors13.default.red : import_picocolors13.default.green;
8241
+ printKeyValue("SUI (gas)", suiColor(`${suiBal.toFixed(4)} SUI`));
8242
+ if (suiBal < 0.05) {
8243
+ printLine(` ${import_picocolors13.default.yellow("\u26A0")} Below gas threshold (0.05 SUI) \u2014 transactions will need sponsorship`);
8550
8244
  }
8245
+ printKeyValue("Available", `$${balData.available.toFixed(2)}`);
8246
+ } else {
8247
+ printKeyValue("Wallet", import_picocolors13.default.dim("could not fetch balances"));
8248
+ }
8249
+ printBlank();
8250
+ if (gasStatus && !gasStatus.circuitBreaker && (balData?.gasReserve.sui ?? 0) >= 0.05) {
8251
+ printLine(` ${import_picocolors13.default.green("\u2713")} Gas is healthy \u2014 transactions should succeed`);
8252
+ } else if (gasStatus && !gasStatus.circuitBreaker) {
8253
+ printLine(` ${import_picocolors13.default.yellow("\u26A0")} Low SUI but gas station is online \u2014 sponsorship available`);
8254
+ } else {
8255
+ printLine(` ${import_picocolors13.default.red("\u2717")} Gas station issues detected \u2014 fund wallet with SUI directly`);
8256
+ printInfo("Send SUI to your address: t2000 address");
8257
+ }
8258
+ printBlank();
8259
+ } catch (error) {
8260
+ handleError(error);
8261
+ }
8262
+ });
8263
+ }
8264
+
8265
+ // src/commands/swap.ts
8266
+ var import_picocolors14 = __toESM(require_picocolors(), 1);
8267
+ function registerSwap(program3) {
8268
+ program3.command("swap").description("Swap tokens via Cetus Aggregator (20+ DEXs)").argument("<amount>", "Amount to swap").argument("<from>", "Source token (e.g. SUI, USDC, CETUS)").argument("[to_keyword]", '"for" keyword (optional)').argument("<to>", "Target token (e.g. USDC, SUI, DEEP)").option("--slippage <pct>", "Max slippage percentage (default: 1)", "1").option("--key <path>", "Key file path").action(async (amountStr, from, toKeywordOrTo, to, opts) => {
8269
+ try {
8270
+ const amount = parseFloat(amountStr);
8271
+ if (isNaN(amount) || amount <= 0) {
8272
+ throw new Error("Amount must be a positive number");
8551
8273
  }
8274
+ if (toKeywordOrTo?.toLowerCase() === "for" && !to) {
8275
+ throw new Error("Usage: t2000 swap <amount> <from> [for] <to>");
8276
+ }
8277
+ const actualTo = to ?? toKeywordOrTo;
8278
+ const slippage = Math.min(parseFloat(opts.slippage ?? "1") / 100, 0.05);
8552
8279
  const pin = await resolvePin();
8553
8280
  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
- });
8281
+ const result = await agent.swap({ from, to: actualTo, amount, slippage });
8561
8282
  if (isJsonMode()) {
8562
8283
  printJson(result);
8563
8284
  return;
8564
8285
  }
8565
8286
  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)}`));
8287
+ printSuccess(`Swapped ${import_picocolors14.default.yellow(String(result.fromAmount))} ${result.fromToken} for ${import_picocolors14.default.green(result.toAmount.toFixed(4))} ${result.toToken}`);
8288
+ if (result.priceImpact > 5e-3) {
8289
+ printKeyValue("Price Impact", import_picocolors14.default.yellow(`${(result.priceImpact * 100).toFixed(2)}%`));
8572
8290
  }
8291
+ printKeyValue("Route", `${result.fromToken} \u2192 ${result.toToken} (${result.route})`);
8292
+ printKeyValue("Gas", `${result.gasCost.toFixed(4)} SUI (${result.gasMethod})`);
8573
8293
  printKeyValue("Tx", explorerUrl(result.tx));
8574
8294
  printBlank();
8575
8295
  } catch (error) {
8576
8296
  handleError(error);
8577
8297
  }
8578
8298
  });
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) => {
8299
+ }
8300
+
8301
+ // src/commands/swapQuote.ts
8302
+ var import_picocolors15 = __toESM(require_picocolors(), 1);
8303
+ function registerSwapQuote(program3) {
8304
+ program3.command("swap-quote").description("Preview a swap quote without executing (shows output, price impact, route)").argument("<amount>", "Amount to swap").argument("<from>", "Source token (e.g. SUI, USDC)").argument("[to_keyword]", '"for" keyword (optional)').argument("<to>", "Target token (e.g. USDC, SUI)").option("--key <path>", "Key file path").action(async (amountStr, from, toKeywordOrTo, to, opts) => {
8580
8305
  try {
8306
+ const amount = parseFloat(amountStr);
8307
+ if (isNaN(amount) || amount <= 0) {
8308
+ throw new Error("Amount must be a positive number");
8309
+ }
8310
+ if (toKeywordOrTo?.toLowerCase() === "for" && !to) {
8311
+ throw new Error("Usage: t2000 swap-quote <amount> <from> [for] <to>");
8312
+ }
8313
+ const actualTo = to ?? toKeywordOrTo;
8581
8314
  const pin = await resolvePin();
8582
8315
  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
- });
8316
+ const result = await agent.swapQuote({ from, to: actualTo, amount });
8587
8317
  if (isJsonMode()) {
8588
8318
  printJson(result);
8589
8319
  return;
8590
8320
  }
8591
8321
  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));
8322
+ printKeyValue("Input", `${result.fromAmount} ${result.fromToken}`);
8323
+ printKeyValue("Output", import_picocolors15.default.green(`${result.toAmount.toFixed(6)} ${result.toToken}`));
8324
+ if (result.priceImpact > 1e-3) {
8325
+ printKeyValue("Price Impact", import_picocolors15.default.yellow(`${(result.priceImpact * 100).toFixed(2)}%`));
8601
8326
  }
8327
+ printKeyValue("Route", `${result.fromToken} \u2192 ${result.toToken} (${result.route})`);
8602
8328
  printBlank();
8603
8329
  } catch (error) {
8604
8330
  handleError(error);
8605
8331
  }
8606
8332
  });
8607
- investCmd.command("unearn <asset>").description("Withdraw invested asset from lending (keeps in portfolio)").option("--key <path>", "Key file path").action(async (asset, opts) => {
8333
+ }
8334
+
8335
+ // src/commands/stake.ts
8336
+ var import_picocolors16 = __toESM(require_picocolors(), 1);
8337
+ function registerStake(program3) {
8338
+ program3.command("stake").description("Stake SUI for vSUI via VOLO liquid staking (earn ~3-5% APY)").argument("<amount>", "Amount of SUI to stake (minimum 1)").option("--key <path>", "Key file path").action(async (amountStr, opts) => {
8608
8339
  try {
8340
+ const amount = parseFloat(amountStr);
8341
+ if (isNaN(amount) || amount < 1) {
8342
+ throw new Error("Amount must be at least 1 SUI");
8343
+ }
8609
8344
  const pin = await resolvePin();
8610
8345
  const agent = await T2000.create({ pin, keyPath: opts.key });
8611
- const result = await agent.investUnearn({
8612
- asset: asset.toUpperCase()
8613
- });
8346
+ const result = await agent.stakeVSui({ amount });
8614
8347
  if (isJsonMode()) {
8615
8348
  printJson(result);
8616
8349
  return;
8617
8350
  }
8618
8351
  printBlank();
8619
- const sym = asset.toUpperCase();
8620
- printSuccess(`Withdrew ${formatAssetAmount(result.amount, sym)} ${sym} from ${result.protocol}`);
8621
- printKeyValue("Status", `${sym} withdrawn to wallet`);
8352
+ printSuccess(`Staked ${import_picocolors16.default.yellow(String(result.amountSui))} SUI for ${import_picocolors16.default.green(result.vSuiReceived.toFixed(4))} vSUI`);
8353
+ printSuccess(`APY: ${import_picocolors16.default.green(`${(result.apy * 100).toFixed(2)}%`)}`);
8354
+ printKeyValue("Gas", `${result.gasCost.toFixed(4)} SUI (${result.gasMethod})`);
8622
8355
  printKeyValue("Tx", explorerUrl(result.tx));
8623
8356
  printBlank();
8624
8357
  } catch (error) {
8625
8358
  handleError(error);
8626
8359
  }
8627
8360
  });
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) => {
8361
+ }
8362
+
8363
+ // src/commands/unstake.ts
8364
+ var import_picocolors17 = __toESM(require_picocolors(), 1);
8365
+ function registerUnstake(program3) {
8366
+ program3.command("unstake").description("Unstake vSUI back to SUI (returns SUI including accumulated yield)").argument("<amount>", 'Amount of vSUI to unstake (or "all")').option("--key <path>", "Key file path").action(async (amountStr, opts) => {
8629
8367
  try {
8368
+ const amount = amountStr === "all" ? "all" : parseFloat(amountStr);
8369
+ if (amount !== "all" && (isNaN(amount) || amount <= 0)) {
8370
+ throw new Error('Amount must be a positive number or "all"');
8371
+ }
8630
8372
  const pin = await resolvePin();
8631
8373
  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
- });
8374
+ const result = await agent.unstakeVSui({ amount });
8636
8375
  if (isJsonMode()) {
8637
8376
  printJson(result);
8638
8377
  return;
8639
8378
  }
8640
8379
  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
- // src/commands/claimRewards.ts
9141
- var import_picocolors16 = __toESM(require_picocolors(), 1);
9142
- function registerClaimRewards(program3) {
9143
- program3.command("claim-rewards").description("Claim pending protocol rewards").option("--key <path>", "Key file path").action(async (opts) => {
9144
- try {
9145
- const pin = await resolvePin();
9146
- const agent = await T2000.create({ pin, keyPath: opts.key });
9147
- const result = await agent.claimRewards();
9148
- if (isJsonMode()) {
9149
- printJson(result);
9150
- return;
9151
- }
9152
- printBlank();
9153
- if (result.rewards.length === 0) {
9154
- printLine(` ${import_picocolors16.default.dim("No rewards to claim")}`);
9155
- printBlank();
9156
- return;
9157
- }
9158
- const protocols = [...new Set(result.rewards.map((r) => r.protocol))];
9159
- printLine(` ${import_picocolors16.default.green("\u2713")} Claimed and converted rewards to USDC`);
9160
- printSeparator();
9161
- const received = result.usdcReceived;
9162
- if (received >= 0.01) {
9163
- printKeyValue("Received", `${import_picocolors16.default.green(formatUsd(received))} USDC`);
9164
- } else if (received > 0) {
9165
- printKeyValue("Received", `${import_picocolors16.default.green("< $0.01")} USDC`);
9166
- } else {
9167
- printKeyValue("Received", `${import_picocolors16.default.dim("< $0.01 USDC (rewards are still accruing)")}`);
9168
- }
9169
- printKeyValue("Source", protocols.join(", "));
9170
- if (result.tx) {
9171
- printKeyValue("Tx", `https://suiscan.xyz/mainnet/tx/${result.tx}`);
9172
- }
9173
- printBlank();
9174
- } catch (error) {
9175
- handleError(error);
9176
- }
9177
- });
9178
- }
9179
-
9180
- // src/commands/gas.ts
9181
- var import_picocolors17 = __toESM(require_picocolors(), 1);
9182
- function registerGas(program3) {
9183
- program3.command("gas").description("Check gas station status and wallet gas balance").option("--key <path>", "Key file path").action(async (opts) => {
9184
- try {
9185
- const pin = await resolvePin();
9186
- const agent = await T2000.create({ pin, keyPath: opts.key });
9187
- const address = agent.address();
9188
- const [status, bal] = await Promise.allSettled([
9189
- getGasStatus(address),
9190
- agent.balance()
9191
- ]);
9192
- const gasStatus = status.status === "fulfilled" ? status.value : null;
9193
- const balData = bal.status === "fulfilled" ? bal.value : null;
9194
- if (isJsonMode()) {
9195
- printJson({
9196
- gasStation: gasStatus ?? { error: status.status === "rejected" ? String(status.reason) : "unavailable" },
9197
- wallet: balData ? { sui: balData.gasReserve.sui, available: balData.available } : null
9198
- });
9199
- return;
9200
- }
9201
- printHeader("Gas Status");
9202
- if (gasStatus) {
9203
- const cbStatus = gasStatus.circuitBreaker ? import_picocolors17.default.red("TRIPPED \u2014 sponsorship paused") : import_picocolors17.default.green("OK");
9204
- printKeyValue("Gas Station", cbStatus);
9205
- printKeyValue("SUI Price (TWAP)", `$${gasStatus.suiPrice.toFixed(4)}`);
9206
- if (gasStatus.bootstrapRemaining !== void 0) {
9207
- printKeyValue("Bootstrap", `${gasStatus.bootstrapUsed}/10 used (${gasStatus.bootstrapRemaining} remaining)`);
9208
- }
9209
- } else {
9210
- printKeyValue("Gas Station", import_picocolors17.default.red("unreachable"));
9211
- const reason = status.status === "rejected" ? status.reason : "unknown";
9212
- printLine(` ${import_picocolors17.default.dim(reason instanceof Error ? reason.message : String(reason))}`);
9213
- }
9214
- printDivider();
9215
- if (balData) {
9216
- const suiBal = balData.gasReserve.sui;
9217
- const suiColor = suiBal < 0.05 ? import_picocolors17.default.red : import_picocolors17.default.green;
9218
- printKeyValue("SUI (gas)", suiColor(`${suiBal.toFixed(4)} SUI`));
9219
- if (suiBal < 0.05) {
9220
- printLine(` ${import_picocolors17.default.yellow("\u26A0")} Below gas threshold (0.05 SUI) \u2014 transactions will need sponsorship`);
9221
- }
9222
- printKeyValue("Available", `$${balData.available.toFixed(2)}`);
9223
- } else {
9224
- printKeyValue("Wallet", import_picocolors17.default.dim("could not fetch balances"));
9225
- }
9226
- printBlank();
9227
- if (gasStatus && !gasStatus.circuitBreaker && (balData?.gasReserve.sui ?? 0) >= 0.05) {
9228
- printLine(` ${import_picocolors17.default.green("\u2713")} Gas is healthy \u2014 transactions should succeed`);
9229
- } else if (gasStatus && !gasStatus.circuitBreaker) {
9230
- printLine(` ${import_picocolors17.default.yellow("\u26A0")} Low SUI but gas station is online \u2014 sponsorship available`);
9231
- } else {
9232
- printLine(` ${import_picocolors17.default.red("\u2717")} Gas station issues detected \u2014 fund wallet with SUI directly`);
9233
- printInfo("Send SUI to your address: t2000 address");
9234
- }
8380
+ printSuccess(`Unstaked ${import_picocolors17.default.yellow(result.vSuiAmount.toFixed(4))} vSUI`);
8381
+ printSuccess(`Received ${import_picocolors17.default.green(result.suiReceived.toFixed(4))} SUI`);
8382
+ printKeyValue("Gas", `${result.gasCost.toFixed(4)} SUI (${result.gasMethod})`);
8383
+ printKeyValue("Tx", explorerUrl(result.tx));
9235
8384
  printBlank();
9236
8385
  } catch (error) {
9237
8386
  handleError(error);
@@ -9255,9 +8404,6 @@ Examples:
9255
8404
  $ t2000 send 50 to 0xabc... Send $50 USDC
9256
8405
  $ t2000 borrow 200 Borrow $200 against savings
9257
8406
  $ 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
8407
  $ t2000 mcp install Install MCP for AI platforms`);
9262
8408
  registerInit(program3);
9263
8409
  registerSend(program3);
@@ -9281,15 +8427,14 @@ Examples:
9281
8427
  registerPay(program3);
9282
8428
  registerLock(program3);
9283
8429
  registerEarn(program3);
9284
- registerRebalance(program3);
9285
- registerSwap(program3);
9286
- registerExchange(program3);
9287
8430
  registerMcp(program3);
9288
8431
  registerContacts(program3);
9289
- registerInvest(program3);
9290
- registerPortfolio(program3);
9291
8432
  registerClaimRewards(program3);
9292
8433
  registerGas(program3);
8434
+ registerSwap(program3);
8435
+ registerSwapQuote(program3);
8436
+ registerStake(program3);
8437
+ registerUnstake(program3);
9293
8438
  return program3;
9294
8439
  }
9295
8440