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