@t2000/sdk 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -110,6 +110,8 @@ declare class T2000 extends EventEmitter<T2000Events> {
110
110
  }): Promise<WithdrawResult>;
111
111
  private withdrawAllProtocols;
112
112
  private _swapToUsdc;
113
+ private _swapFromUsdc;
114
+ private _convertWalletStablesToUsdc;
113
115
  maxWithdraw(): Promise<MaxWithdrawResult>;
114
116
  borrow(params: {
115
117
  amount: number;
@@ -119,6 +121,7 @@ declare class T2000 extends EventEmitter<T2000Events> {
119
121
  amount: number | 'all';
120
122
  protocol?: string;
121
123
  }): Promise<RepayResult>;
124
+ private _repayAllBorrows;
122
125
  maxBorrow(): Promise<MaxBorrowResult>;
123
126
  healthFactor(): Promise<HealthFactorResult>;
124
127
  private _swap;
package/dist/index.d.ts CHANGED
@@ -110,6 +110,8 @@ declare class T2000 extends EventEmitter<T2000Events> {
110
110
  }): Promise<WithdrawResult>;
111
111
  private withdrawAllProtocols;
112
112
  private _swapToUsdc;
113
+ private _swapFromUsdc;
114
+ private _convertWalletStablesToUsdc;
113
115
  maxWithdraw(): Promise<MaxWithdrawResult>;
114
116
  borrow(params: {
115
117
  amount: number;
@@ -119,6 +121,7 @@ declare class T2000 extends EventEmitter<T2000Events> {
119
121
  amount: number | 'all';
120
122
  protocol?: string;
121
123
  }): Promise<RepayResult>;
124
+ private _repayAllBorrows;
122
125
  maxBorrow(): Promise<MaxBorrowResult>;
123
126
  healthFactor(): Promise<HealthFactorResult>;
124
127
  private _swap;
package/dist/index.js CHANGED
@@ -2414,22 +2414,27 @@ var T2000 = class _T2000 extends EventEmitter {
2414
2414
  // -- Savings --
2415
2415
  async save(params) {
2416
2416
  const asset = "USDC";
2417
+ const bal = await queryBalance(this.client, this._address);
2418
+ const usdcBalance = bal.stables.USDC ?? 0;
2417
2419
  let amount;
2418
2420
  if (params.amount === "all") {
2419
- const bal = await queryBalance(this.client, this._address);
2420
- const assetBalance = bal.stables.USDC ?? 0;
2421
- amount = assetBalance - 1;
2421
+ await this._convertWalletStablesToUsdc(bal);
2422
+ const refreshedBal = await queryBalance(this.client, this._address);
2423
+ amount = (refreshedBal.stables.USDC ?? 0) - 1;
2422
2424
  if (amount <= 0) {
2423
2425
  throw new T2000Error("INSUFFICIENT_BALANCE", "Balance too low to save after $1 gas reserve", {
2424
2426
  reason: "gas_reserve_required",
2425
- available: assetBalance
2427
+ available: refreshedBal.stables.USDC ?? 0
2426
2428
  });
2427
2429
  }
2428
2430
  } else {
2429
2431
  amount = params.amount;
2430
- const bal = await queryBalance(this.client, this._address);
2431
- if (amount > (bal.stables.USDC ?? 0)) {
2432
- throw new T2000Error("INSUFFICIENT_BALANCE", `Insufficient USDC. Available: $${(bal.stables.USDC ?? 0).toFixed(2)}, requested: $${amount.toFixed(2)}`);
2432
+ if (amount > usdcBalance) {
2433
+ const totalStables = bal.available;
2434
+ if (amount > totalStables) {
2435
+ throw new T2000Error("INSUFFICIENT_BALANCE", `Insufficient balance. Available: $${totalStables.toFixed(2)}, requested: $${amount.toFixed(2)}`);
2436
+ }
2437
+ await this._convertWalletStablesToUsdc(bal, amount - usdcBalance);
2433
2438
  }
2434
2439
  }
2435
2440
  const fee = calculateFee("save", amount);
@@ -2606,6 +2611,39 @@ var T2000 = class _T2000 extends EventEmitter {
2606
2611
  const usdcReceived = estimatedOut / 10 ** toDecimals;
2607
2612
  return { usdcReceived, digest: gasResult.digest, gasCost: gasResult.gasCostSui };
2608
2613
  }
2614
+ async _swapFromUsdc(toAsset, amount) {
2615
+ const swapAdapter = this.registry.listSwap()[0];
2616
+ if (!swapAdapter) throw new T2000Error("PROTOCOL_UNAVAILABLE", "No swap adapter available");
2617
+ let estimatedOut = 0;
2618
+ let toDecimals = 6;
2619
+ const gasResult = await executeWithGas(this.client, this.keypair, async () => {
2620
+ const built = await swapAdapter.buildSwapTx(this._address, "USDC", toAsset, amount);
2621
+ estimatedOut = built.estimatedOut;
2622
+ toDecimals = built.toDecimals;
2623
+ return built.tx;
2624
+ });
2625
+ const received = estimatedOut / 10 ** toDecimals;
2626
+ return { received, digest: gasResult.digest, gasCost: gasResult.gasCostSui };
2627
+ }
2628
+ async _convertWalletStablesToUsdc(bal, amountNeeded) {
2629
+ const nonUsdcStables = [];
2630
+ for (const [asset, amount] of Object.entries(bal.stables)) {
2631
+ if (asset !== "USDC" && amount > 0.01) {
2632
+ nonUsdcStables.push({ asset, amount });
2633
+ }
2634
+ }
2635
+ if (nonUsdcStables.length === 0) return;
2636
+ nonUsdcStables.sort((a, b) => b.amount - a.amount);
2637
+ let converted = 0;
2638
+ for (const entry of nonUsdcStables) {
2639
+ if (amountNeeded !== void 0 && converted >= amountNeeded) break;
2640
+ try {
2641
+ await this._swapToUsdc(entry.asset, entry.amount);
2642
+ converted += entry.amount;
2643
+ } catch {
2644
+ }
2645
+ }
2646
+ }
2609
2647
  async maxWithdraw() {
2610
2648
  const adapter = await this.resolveLending(void 0, "USDC", "withdraw");
2611
2649
  return adapter.maxWithdraw(this._address, "USDC");
@@ -2644,34 +2682,93 @@ var T2000 = class _T2000 extends EventEmitter {
2644
2682
  };
2645
2683
  }
2646
2684
  async repay(params) {
2647
- const asset = "USDC";
2648
- const adapter = await this.resolveLending(params.protocol, asset, "repay");
2649
- let amount;
2650
- if (params.amount === "all") {
2651
- const hf2 = await adapter.getHealth(this._address);
2652
- amount = hf2.borrowed;
2653
- if (amount <= 0) {
2654
- throw new T2000Error("NO_COLLATERAL", "No outstanding borrow to repay");
2685
+ const allPositions = await this.registry.allPositions(this._address);
2686
+ const borrows = [];
2687
+ for (const pos of allPositions) {
2688
+ if (params.protocol && pos.protocolId !== params.protocol) continue;
2689
+ for (const b of pos.positions.borrows) {
2690
+ if (b.amount > 1e-3) borrows.push({ protocolId: pos.protocolId, asset: b.asset, amount: b.amount, apy: b.apy });
2655
2691
  }
2656
- } else {
2657
- amount = params.amount;
2658
2692
  }
2659
- const repayAmount = amount;
2693
+ if (borrows.length === 0) {
2694
+ throw new T2000Error("NO_COLLATERAL", "No outstanding borrow to repay");
2695
+ }
2696
+ if (params.amount === "all") {
2697
+ return this._repayAllBorrows(borrows);
2698
+ }
2699
+ borrows.sort((a, b) => b.apy - a.apy);
2700
+ const target = borrows[0];
2701
+ const adapter = this.registry.getLending(target.protocolId);
2702
+ if (!adapter) throw new T2000Error("PROTOCOL_UNAVAILABLE", `Protocol ${target.protocolId} not found`);
2703
+ const repayAmount = Math.min(params.amount, target.amount);
2704
+ let totalGasCost = 0;
2705
+ let lastDigest = "";
2706
+ if (target.asset !== "USDC") {
2707
+ const buffer = repayAmount * 1.005;
2708
+ const swapResult = await this._swapFromUsdc(target.asset, buffer);
2709
+ totalGasCost += swapResult.gasCost;
2710
+ lastDigest = swapResult.digest;
2711
+ }
2660
2712
  const gasResult = await executeWithGas(this.client, this.keypair, async () => {
2661
- const { tx } = await adapter.buildRepayTx(this._address, repayAmount, asset);
2713
+ const { tx } = await adapter.buildRepayTx(this._address, repayAmount, target.asset);
2662
2714
  return tx;
2663
2715
  });
2716
+ totalGasCost += gasResult.gasCostSui;
2717
+ lastDigest = gasResult.digest;
2664
2718
  const hf = await adapter.getHealth(this._address);
2665
- this.emitBalanceChange(asset, repayAmount, "repay", gasResult.digest);
2719
+ this.emitBalanceChange("USDC", repayAmount, "repay", lastDigest);
2666
2720
  return {
2667
2721
  success: true,
2668
- tx: gasResult.digest,
2722
+ tx: lastDigest,
2669
2723
  amount: repayAmount,
2670
2724
  remainingDebt: hf.borrowed,
2671
- gasCost: gasResult.gasCostSui,
2725
+ gasCost: totalGasCost,
2672
2726
  gasMethod: gasResult.gasMethod
2673
2727
  };
2674
2728
  }
2729
+ async _repayAllBorrows(borrows) {
2730
+ let totalRepaid = 0;
2731
+ let totalGasCost = 0;
2732
+ let lastDigest = "";
2733
+ let lastGasMethod = "self-funded";
2734
+ borrows.sort((a, b) => b.apy - a.apy);
2735
+ for (const borrow of borrows) {
2736
+ const adapter = this.registry.getLending(borrow.protocolId);
2737
+ if (!adapter) continue;
2738
+ if (borrow.asset !== "USDC") {
2739
+ try {
2740
+ const buffer = borrow.amount * 1.005;
2741
+ const swapResult = await this._swapFromUsdc(borrow.asset, buffer);
2742
+ totalGasCost += swapResult.gasCost;
2743
+ } catch (err) {
2744
+ throw new T2000Error(
2745
+ "SWAP_FAILED",
2746
+ `Could not convert USDC to ${borrow.asset} for repayment. Try again later.`,
2747
+ { originalError: err instanceof Error ? err.message : String(err) }
2748
+ );
2749
+ }
2750
+ }
2751
+ const gasResult = await executeWithGas(this.client, this.keypair, async () => {
2752
+ const { tx } = await adapter.buildRepayTx(this._address, borrow.amount, borrow.asset);
2753
+ return tx;
2754
+ });
2755
+ totalRepaid += borrow.amount;
2756
+ totalGasCost += gasResult.gasCostSui;
2757
+ lastDigest = gasResult.digest;
2758
+ lastGasMethod = gasResult.gasMethod;
2759
+ }
2760
+ const firstAdapter = this.registry.getLending(borrows[0].protocolId);
2761
+ const hf = firstAdapter ? await firstAdapter.getHealth(this._address) : { borrowed: 0 };
2762
+ this.emitBalanceChange("USDC", totalRepaid, "repay", lastDigest);
2763
+ return {
2764
+ success: true,
2765
+ tx: lastDigest,
2766
+ amount: totalRepaid,
2767
+ remainingDebt: hf.borrowed,
2768
+ gasCost: totalGasCost,
2769
+ gasMethod: lastGasMethod
2770
+ };
2771
+ }
2675
2772
  async maxBorrow() {
2676
2773
  const adapter = await this.resolveLending(void 0, "USDC", "borrow");
2677
2774
  return adapter.maxBorrow(this._address, "USDC");