@t2000/sdk 0.8.7 → 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.
@@ -155,7 +155,6 @@ interface SaveResult {
155
155
  success: boolean;
156
156
  tx: string;
157
157
  amount: number;
158
- asset: string;
159
158
  apy: number;
160
159
  fee: number;
161
160
  gasCost: number;
@@ -359,8 +358,6 @@ declare class ProtocolRegistry {
359
358
  getSwap(id: string): SwapAdapter | undefined;
360
359
  listLending(): LendingAdapter[];
361
360
  listSwap(): SwapAdapter[];
362
- isSupportedAsset(asset: string, capability?: AdapterCapability): boolean;
363
- getSupportedAssets(capability?: AdapterCapability): string[];
364
361
  }
365
362
 
366
363
  declare const descriptor$3: ProtocolDescriptor;
@@ -478,4 +475,4 @@ declare function attack(client: SuiJsonRpcClient, signer: Ed25519Keypair, sentin
478
475
  /** All registered protocol descriptors — used by the indexer for event classification */
479
476
  declare const allDescriptors: ProtocolDescriptor[];
480
477
 
481
- export { type AdapterCapability as A, type BalanceResponse as B, CetusAdapter as C, type DepositInfo as D, type EarningsResult as E, type FundStatusResult as F, type GasMethod as G, type HealthFactorResult as H, listSentinels as I, descriptor$3 as J, requestAttack as K, type LendingAdapter as L, type MaxWithdrawResult as M, NaviAdapter as N, attack as O, type PositionsResult as P, descriptor as Q, type RepayResult as R, type SendResult as S, type T2000Options as T, settleAttack as U, submitPrompt as V, type WithdrawResult as W, descriptor$1 as X, type TransactionRecord as a, type SwapAdapter as b, type SaveResult as c, type BorrowResult as d, type MaxBorrowResult as e, type SwapResult as f, type RatesResult as g, type LendingRates as h, type RebalanceResult as i, type SentinelAgent as j, type SentinelAttackResult as k, type AdapterPositions as l, type AdapterTxResult as m, type AssetRates as n, type GasReserve as o, type HealthInfo as p, type PositionEntry as q, type ProtocolDescriptor as r, ProtocolRegistry as s, type RebalanceStep as t, type SentinelVerdict as u, SuilendAdapter as v, type SwapQuote as w, allDescriptors as x, descriptor$2 as y, getSentinelInfo as z };
478
+ export { type AdapterCapability as A, type BalanceResponse as B, CetusAdapter as C, type DepositInfo as D, type EarningsResult as E, type FundStatusResult as F, type GasMethod as G, type HealthFactorResult as H, listSentinels as I, descriptor$3 as J, requestAttack as K, type LendingAdapter as L, type MaxWithdrawResult as M, NaviAdapter as N, attack as O, type PositionsResult as P, descriptor as Q, type RepayResult as R, type SendResult as S, type T2000Options as T, settleAttack as U, submitPrompt as V, type WithdrawResult as W, descriptor$1 as X, type TransactionRecord as a, type SwapAdapter as b, type SaveResult as c, type BorrowResult as d, type MaxBorrowResult as e, type RatesResult as f, type LendingRates as g, type RebalanceResult as h, type SentinelAgent as i, type SentinelAttackResult as j, type AdapterPositions as k, type AdapterTxResult as l, type AssetRates as m, type GasReserve as n, type HealthInfo as o, type PositionEntry as p, type ProtocolDescriptor as q, ProtocolRegistry as r, type RebalanceStep as s, type SentinelVerdict as t, SuilendAdapter as u, type SwapQuote as v, type SwapResult as w, allDescriptors as x, descriptor$2 as y, getSentinelInfo as z };
@@ -155,7 +155,6 @@ interface SaveResult {
155
155
  success: boolean;
156
156
  tx: string;
157
157
  amount: number;
158
- asset: string;
159
158
  apy: number;
160
159
  fee: number;
161
160
  gasCost: number;
@@ -359,8 +358,6 @@ declare class ProtocolRegistry {
359
358
  getSwap(id: string): SwapAdapter | undefined;
360
359
  listLending(): LendingAdapter[];
361
360
  listSwap(): SwapAdapter[];
362
- isSupportedAsset(asset: string, capability?: AdapterCapability): boolean;
363
- getSupportedAssets(capability?: AdapterCapability): string[];
364
361
  }
365
362
 
366
363
  declare const descriptor$3: ProtocolDescriptor;
@@ -478,4 +475,4 @@ declare function attack(client: SuiJsonRpcClient, signer: Ed25519Keypair, sentin
478
475
  /** All registered protocol descriptors — used by the indexer for event classification */
479
476
  declare const allDescriptors: ProtocolDescriptor[];
480
477
 
481
- export { type AdapterCapability as A, type BalanceResponse as B, CetusAdapter as C, type DepositInfo as D, type EarningsResult as E, type FundStatusResult as F, type GasMethod as G, type HealthFactorResult as H, listSentinels as I, descriptor$3 as J, requestAttack as K, type LendingAdapter as L, type MaxWithdrawResult as M, NaviAdapter as N, attack as O, type PositionsResult as P, descriptor as Q, type RepayResult as R, type SendResult as S, type T2000Options as T, settleAttack as U, submitPrompt as V, type WithdrawResult as W, descriptor$1 as X, type TransactionRecord as a, type SwapAdapter as b, type SaveResult as c, type BorrowResult as d, type MaxBorrowResult as e, type SwapResult as f, type RatesResult as g, type LendingRates as h, type RebalanceResult as i, type SentinelAgent as j, type SentinelAttackResult as k, type AdapterPositions as l, type AdapterTxResult as m, type AssetRates as n, type GasReserve as o, type HealthInfo as p, type PositionEntry as q, type ProtocolDescriptor as r, ProtocolRegistry as s, type RebalanceStep as t, type SentinelVerdict as u, SuilendAdapter as v, type SwapQuote as w, allDescriptors as x, descriptor$2 as y, getSentinelInfo as z };
478
+ export { type AdapterCapability as A, type BalanceResponse as B, CetusAdapter as C, type DepositInfo as D, type EarningsResult as E, type FundStatusResult as F, type GasMethod as G, type HealthFactorResult as H, listSentinels as I, descriptor$3 as J, requestAttack as K, type LendingAdapter as L, type MaxWithdrawResult as M, NaviAdapter as N, attack as O, type PositionsResult as P, descriptor as Q, type RepayResult as R, type SendResult as S, type T2000Options as T, settleAttack as U, submitPrompt as V, type WithdrawResult as W, descriptor$1 as X, type TransactionRecord as a, type SwapAdapter as b, type SaveResult as c, type BorrowResult as d, type MaxBorrowResult as e, type RatesResult as f, type LendingRates as g, type RebalanceResult as h, type SentinelAgent as i, type SentinelAttackResult as j, type AdapterPositions as k, type AdapterTxResult as l, type AssetRates as m, type GasReserve as n, type HealthInfo as o, type PositionEntry as p, type ProtocolDescriptor as q, ProtocolRegistry as r, type RebalanceStep as s, type SentinelVerdict as t, SuilendAdapter as u, type SwapQuote as v, type SwapResult as w, allDescriptors as x, descriptor$2 as y, getSentinelInfo as z };
package/dist/index.cjs CHANGED
@@ -1317,24 +1317,6 @@ var ProtocolRegistry = class {
1317
1317
  listSwap() {
1318
1318
  return [...this.swap.values()];
1319
1319
  }
1320
- isSupportedAsset(asset, capability) {
1321
- for (const adapter of this.lending.values()) {
1322
- if (!adapter.supportedAssets.includes(asset)) continue;
1323
- if (capability && !adapter.capabilities.includes(capability)) continue;
1324
- return true;
1325
- }
1326
- return false;
1327
- }
1328
- getSupportedAssets(capability) {
1329
- const assets = /* @__PURE__ */ new Set();
1330
- for (const adapter of this.lending.values()) {
1331
- if (capability && !adapter.capabilities.includes(capability)) continue;
1332
- for (const a of adapter.supportedAssets) {
1333
- assets.add(a);
1334
- }
1335
- }
1336
- return [...assets];
1337
- }
1338
1320
  };
1339
1321
 
1340
1322
  // src/adapters/navi.ts
@@ -2433,43 +2415,39 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2433
2415
  }
2434
2416
  // -- Savings --
2435
2417
  async save(params) {
2436
- const asset = normalizeAsset(params.asset ?? "USDC");
2437
- if (!this.registry.isSupportedAsset(asset, "save")) {
2438
- const supported = this.registry.getSupportedAssets("save").join(", ");
2439
- throw new T2000Error("ASSET_NOT_SUPPORTED", `${asset} is not supported for save. Supported: ${supported}`);
2440
- }
2418
+ const asset = "USDC";
2419
+ const bal = await queryBalance(this.client, this._address);
2420
+ const usdcBalance = bal.stables.USDC ?? 0;
2441
2421
  let amount;
2442
2422
  if (params.amount === "all") {
2443
- const bal = await queryBalance(this.client, this._address);
2444
- const assetBalance = bal.stables[asset] ?? 0;
2445
- const reserve = asset === "USDC" ? 1 : 0;
2446
- amount = assetBalance - reserve;
2423
+ await this._convertWalletStablesToUsdc(bal);
2424
+ const refreshedBal = await queryBalance(this.client, this._address);
2425
+ amount = (refreshedBal.stables.USDC ?? 0) - 1;
2447
2426
  if (amount <= 0) {
2448
- throw new T2000Error("INSUFFICIENT_BALANCE", `Balance too low to save${asset === "USDC" ? " after $1 gas reserve" : ""}`, {
2449
- reason: asset === "USDC" ? "gas_reserve_required" : "zero_balance",
2450
- available: assetBalance
2427
+ throw new T2000Error("INSUFFICIENT_BALANCE", "Balance too low to save after $1 gas reserve", {
2428
+ reason: "gas_reserve_required",
2429
+ available: refreshedBal.stables.USDC ?? 0
2451
2430
  });
2452
2431
  }
2453
2432
  } else {
2454
2433
  amount = params.amount;
2455
- const bal = await queryBalance(this.client, this._address);
2456
- const assetBalance = bal.stables[asset] ?? 0;
2457
- if (amount > assetBalance) {
2458
- throw new T2000Error("INSUFFICIENT_BALANCE", `Insufficient ${asset}. Available: $${assetBalance.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);
2459
2440
  }
2460
2441
  }
2461
- const shouldCollectFee = asset === "USDC";
2462
- const fee = shouldCollectFee ? calculateFee("save", amount) : { amount: 0, rate: 0};
2442
+ const fee = calculateFee("save", amount);
2463
2443
  const saveAmount = amount;
2464
2444
  const adapter = await this.resolveLending(params.protocol, asset, "save");
2465
2445
  const gasResult = await executeWithGas(this.client, this.keypair, async () => {
2466
- const { tx } = await adapter.buildSaveTx(this._address, saveAmount, asset, { collectFee: shouldCollectFee });
2446
+ const { tx } = await adapter.buildSaveTx(this._address, saveAmount, asset, { collectFee: true });
2467
2447
  return tx;
2468
2448
  });
2469
2449
  const rates = await adapter.getRates(asset);
2470
- if (shouldCollectFee) {
2471
- reportFee(this._address, "save", fee.amount, fee.rate, gasResult.digest);
2472
- }
2450
+ reportFee(this._address, "save", fee.amount, fee.rate, gasResult.digest);
2473
2451
  this.emitBalanceChange(asset, saveAmount, "save", gasResult.digest);
2474
2452
  let savingsBalance = saveAmount;
2475
2453
  try {
@@ -2481,7 +2459,6 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2481
2459
  success: true,
2482
2460
  tx: gasResult.digest,
2483
2461
  amount: saveAmount,
2484
- asset,
2485
2462
  apy: rates.saveApy,
2486
2463
  fee: fee.amount,
2487
2464
  gasCost: gasResult.gasCostSui,
@@ -2490,14 +2467,27 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2490
2467
  };
2491
2468
  }
2492
2469
  async withdraw(params) {
2493
- const asset = normalizeAsset(params.asset ?? "USDC");
2494
2470
  if (params.amount === "all" && !params.protocol) {
2495
- return this.withdrawAllProtocols(asset);
2471
+ return this.withdrawAllProtocols();
2472
+ }
2473
+ const allPositions = await this.registry.allPositions(this._address);
2474
+ const supplies = [];
2475
+ for (const pos of allPositions) {
2476
+ if (params.protocol && pos.protocolId !== params.protocol) continue;
2477
+ for (const s of pos.positions.supplies) {
2478
+ if (s.amount > 1e-3) supplies.push({ protocolId: pos.protocolId, asset: s.asset, amount: s.amount, apy: s.apy });
2479
+ }
2496
2480
  }
2497
- const adapter = await this.resolveLending(params.protocol, asset, "withdraw");
2481
+ if (supplies.length === 0) {
2482
+ throw new T2000Error("NO_COLLATERAL", "No savings to withdraw");
2483
+ }
2484
+ supplies.sort((a, b) => a.apy - b.apy);
2485
+ const target = supplies[0];
2486
+ const adapter = this.registry.getLending(target.protocolId);
2487
+ if (!adapter) throw new T2000Error("PROTOCOL_UNAVAILABLE", `Protocol ${target.protocolId} not found`);
2498
2488
  let amount;
2499
2489
  if (params.amount === "all") {
2500
- const maxResult = await adapter.maxWithdraw(this._address, asset);
2490
+ const maxResult = await adapter.maxWithdraw(this._address, target.asset);
2501
2491
  amount = maxResult.maxAmount;
2502
2492
  if (amount <= 0) {
2503
2493
  throw new T2000Error("NO_COLLATERAL", "No savings to withdraw");
@@ -2506,7 +2496,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2506
2496
  amount = params.amount;
2507
2497
  const hf = await adapter.getHealth(this._address);
2508
2498
  if (hf.borrowed > 0) {
2509
- const maxResult = await adapter.maxWithdraw(this._address, asset);
2499
+ const maxResult = await adapter.maxWithdraw(this._address, target.asset);
2510
2500
  if (amount > maxResult.maxAmount) {
2511
2501
  throw new T2000Error(
2512
2502
  "WITHDRAW_WOULD_LIQUIDATE",
@@ -2523,20 +2513,37 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2523
2513
  const withdrawAmount = amount;
2524
2514
  let effectiveAmount = withdrawAmount;
2525
2515
  const gasResult = await executeWithGas(this.client, this.keypair, async () => {
2526
- const built = await adapter.buildWithdrawTx(this._address, withdrawAmount, asset);
2516
+ const built = await adapter.buildWithdrawTx(this._address, withdrawAmount, target.asset);
2527
2517
  effectiveAmount = built.effectiveAmount;
2528
2518
  return built.tx;
2529
2519
  });
2530
- this.emitBalanceChange(asset, effectiveAmount, "withdraw", gasResult.digest);
2520
+ let totalGasCost = gasResult.gasCostSui;
2521
+ let finalAmount = effectiveAmount;
2522
+ let lastDigest = gasResult.digest;
2523
+ if (target.asset !== "USDC") {
2524
+ try {
2525
+ const swapResult = await this._swapToUsdc(target.asset, effectiveAmount);
2526
+ finalAmount = swapResult.usdcReceived;
2527
+ lastDigest = swapResult.digest;
2528
+ totalGasCost += swapResult.gasCost;
2529
+ } catch (err) {
2530
+ throw new T2000Error(
2531
+ "SWAP_FAILED",
2532
+ `Withdrew $${effectiveAmount.toFixed(2)} ${target.asset} but swap to USDC failed. Your ${target.asset} is safe in your wallet.`,
2533
+ { withdrawDigest: gasResult.digest, originalError: err instanceof Error ? err.message : String(err) }
2534
+ );
2535
+ }
2536
+ }
2537
+ this.emitBalanceChange("USDC", finalAmount, "withdraw", lastDigest);
2531
2538
  return {
2532
2539
  success: true,
2533
- tx: gasResult.digest,
2534
- amount: effectiveAmount,
2535
- gasCost: gasResult.gasCostSui,
2540
+ tx: lastDigest,
2541
+ amount: finalAmount,
2542
+ gasCost: totalGasCost,
2536
2543
  gasMethod: gasResult.gasMethod
2537
2544
  };
2538
2545
  }
2539
- async withdrawAllProtocols(_asset) {
2546
+ async withdrawAllProtocols() {
2540
2547
  const allPositions = await this.registry.allPositions(this._address);
2541
2548
  const withdrawable = [];
2542
2549
  for (const pos of allPositions) {
@@ -2549,7 +2556,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2549
2556
  if (withdrawable.length === 0) {
2550
2557
  throw new T2000Error("NO_COLLATERAL", "No savings to withdraw across any protocol");
2551
2558
  }
2552
- let totalWithdrawn = 0;
2559
+ let totalUsdcReceived = 0;
2553
2560
  let lastDigest = "";
2554
2561
  let totalGasCost = 0;
2555
2562
  let lastGasMethod = "self-funded";
@@ -2564,30 +2571,88 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2564
2571
  effectiveAmount = built.effectiveAmount;
2565
2572
  return built.tx;
2566
2573
  });
2567
- totalWithdrawn += effectiveAmount;
2568
2574
  lastDigest = gasResult.digest;
2569
2575
  totalGasCost += gasResult.gasCostSui;
2570
2576
  lastGasMethod = gasResult.gasMethod;
2571
2577
  this.emitBalanceChange(entry.asset, effectiveAmount, "withdraw", gasResult.digest);
2578
+ if (entry.asset !== "USDC") {
2579
+ try {
2580
+ const swapResult = await this._swapToUsdc(entry.asset, effectiveAmount);
2581
+ totalUsdcReceived += swapResult.usdcReceived;
2582
+ lastDigest = swapResult.digest;
2583
+ totalGasCost += swapResult.gasCost;
2584
+ } catch {
2585
+ totalUsdcReceived += effectiveAmount;
2586
+ }
2587
+ } else {
2588
+ totalUsdcReceived += effectiveAmount;
2589
+ }
2572
2590
  }
2573
- if (totalWithdrawn <= 0) {
2591
+ if (totalUsdcReceived <= 0) {
2574
2592
  throw new T2000Error("NO_COLLATERAL", "No savings to withdraw across any protocol");
2575
2593
  }
2576
2594
  return {
2577
2595
  success: true,
2578
2596
  tx: lastDigest,
2579
- amount: totalWithdrawn,
2597
+ amount: totalUsdcReceived,
2580
2598
  gasCost: totalGasCost,
2581
2599
  gasMethod: lastGasMethod
2582
2600
  };
2583
2601
  }
2602
+ async _swapToUsdc(asset, amount) {
2603
+ const swapAdapter = this.registry.listSwap()[0];
2604
+ if (!swapAdapter) throw new T2000Error("PROTOCOL_UNAVAILABLE", "No swap adapter available");
2605
+ let estimatedOut = 0;
2606
+ let toDecimals = 6;
2607
+ const gasResult = await executeWithGas(this.client, this.keypair, async () => {
2608
+ const built = await swapAdapter.buildSwapTx(this._address, asset, "USDC", amount);
2609
+ estimatedOut = built.estimatedOut;
2610
+ toDecimals = built.toDecimals;
2611
+ return built.tx;
2612
+ });
2613
+ const usdcReceived = estimatedOut / 10 ** toDecimals;
2614
+ return { usdcReceived, digest: gasResult.digest, gasCost: gasResult.gasCostSui };
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
+ }
2584
2649
  async maxWithdraw() {
2585
2650
  const adapter = await this.resolveLending(void 0, "USDC", "withdraw");
2586
2651
  return adapter.maxWithdraw(this._address, "USDC");
2587
2652
  }
2588
2653
  // -- Borrowing --
2589
2654
  async borrow(params) {
2590
- const asset = normalizeAsset(params.asset ?? "USDC");
2655
+ const asset = "USDC";
2591
2656
  const adapter = await this.resolveLending(params.protocol, asset, "borrow");
2592
2657
  const maxResult = await adapter.maxBorrow(this._address, asset);
2593
2658
  if (maxResult.maxAmount <= 0) {
@@ -2599,17 +2664,14 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2599
2664
  currentHF: maxResult.currentHF
2600
2665
  });
2601
2666
  }
2602
- const shouldCollectFee = asset === "USDC";
2603
- const fee = shouldCollectFee ? calculateFee("borrow", params.amount) : { amount: 0, rate: 0};
2667
+ const fee = calculateFee("borrow", params.amount);
2604
2668
  const borrowAmount = params.amount;
2605
2669
  const gasResult = await executeWithGas(this.client, this.keypair, async () => {
2606
- const { tx } = await adapter.buildBorrowTx(this._address, borrowAmount, asset, { collectFee: shouldCollectFee });
2670
+ const { tx } = await adapter.buildBorrowTx(this._address, borrowAmount, asset, { collectFee: true });
2607
2671
  return tx;
2608
2672
  });
2609
2673
  const hf = await adapter.getHealth(this._address);
2610
- if (shouldCollectFee) {
2611
- reportFee(this._address, "borrow", fee.amount, fee.rate, gasResult.digest);
2612
- }
2674
+ reportFee(this._address, "borrow", fee.amount, fee.rate, gasResult.digest);
2613
2675
  this.emitBalanceChange(asset, borrowAmount, "borrow", gasResult.digest);
2614
2676
  return {
2615
2677
  success: true,
@@ -2622,34 +2684,93 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2622
2684
  };
2623
2685
  }
2624
2686
  async repay(params) {
2625
- const asset = normalizeAsset(params.asset ?? "USDC");
2626
- const adapter = await this.resolveLending(params.protocol, asset, "repay");
2627
- let amount;
2628
- if (params.amount === "all") {
2629
- const hf2 = await adapter.getHealth(this._address);
2630
- amount = hf2.borrowed;
2631
- if (amount <= 0) {
2632
- 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 });
2633
2693
  }
2634
- } else {
2635
- amount = params.amount;
2636
2694
  }
2637
- 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
+ }
2638
2714
  const gasResult = await executeWithGas(this.client, this.keypair, async () => {
2639
- const { tx } = await adapter.buildRepayTx(this._address, repayAmount, asset);
2715
+ const { tx } = await adapter.buildRepayTx(this._address, repayAmount, target.asset);
2640
2716
  return tx;
2641
2717
  });
2718
+ totalGasCost += gasResult.gasCostSui;
2719
+ lastDigest = gasResult.digest;
2642
2720
  const hf = await adapter.getHealth(this._address);
2643
- this.emitBalanceChange(asset, repayAmount, "repay", gasResult.digest);
2721
+ this.emitBalanceChange("USDC", repayAmount, "repay", lastDigest);
2644
2722
  return {
2645
2723
  success: true,
2646
- tx: gasResult.digest,
2724
+ tx: lastDigest,
2647
2725
  amount: repayAmount,
2648
2726
  remainingDebt: hf.borrowed,
2649
- gasCost: gasResult.gasCostSui,
2727
+ gasCost: totalGasCost,
2650
2728
  gasMethod: gasResult.gasMethod
2651
2729
  };
2652
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
+ }
2653
2774
  async maxBorrow() {
2654
2775
  const adapter = await this.resolveLending(void 0, "USDC", "borrow");
2655
2776
  return adapter.maxBorrow(this._address, "USDC");
@@ -2664,25 +2785,18 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2664
2785
  }
2665
2786
  return hf;
2666
2787
  }
2667
- // -- Swap --
2668
- async swap(params) {
2669
- const fromAsset = normalizeAsset(params.from);
2670
- const toAsset = normalizeAsset(params.to);
2788
+ // -- Swap (internal — used by rebalance and withdraw auto-swap) --
2789
+ async _swap(params) {
2790
+ const fromAsset = params.from;
2791
+ const toAsset = params.to;
2671
2792
  if (!(fromAsset in SUPPORTED_ASSETS) || !(toAsset in SUPPORTED_ASSETS)) {
2672
2793
  throw new T2000Error("ASSET_NOT_SUPPORTED", `Swap pair ${fromAsset}/${toAsset} is not supported`);
2673
2794
  }
2674
2795
  if (fromAsset === toAsset) {
2675
2796
  throw new T2000Error("INVALID_AMOUNT", "Cannot swap same asset");
2676
2797
  }
2677
- let adapter;
2678
- if (params.protocol) {
2679
- const found = this.registry.getSwap(params.protocol);
2680
- if (!found) throw new T2000Error("ASSET_NOT_SUPPORTED", `Swap adapter '${params.protocol}' not found`);
2681
- adapter = found;
2682
- } else {
2683
- const best = await this.registry.bestSwapQuote(fromAsset, toAsset, params.amount);
2684
- adapter = best.adapter;
2685
- }
2798
+ const best = await this.registry.bestSwapQuote(fromAsset, toAsset, params.amount);
2799
+ const adapter = best.adapter;
2686
2800
  const fee = calculateFee("swap", params.amount);
2687
2801
  const swapAmount = params.amount;
2688
2802
  const slippageBps = params.maxSlippage ? params.maxSlippage * 100 : void 0;
@@ -2725,9 +2839,9 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2725
2839
  gasMethod: gasResult.gasMethod
2726
2840
  };
2727
2841
  }
2728
- async swapQuote(params) {
2729
- const fromAsset = normalizeAsset(params.from);
2730
- const toAsset = normalizeAsset(params.to);
2842
+ async _swapQuote(params) {
2843
+ const fromAsset = params.from;
2844
+ const toAsset = params.to;
2731
2845
  const best = await this.registry.bestSwapQuote(fromAsset, toAsset, params.amount);
2732
2846
  const fee = calculateFee("swap", params.amount);
2733
2847
  return { ...best.quote, fee: { amount: fee.amount, rate: fee.rate } };
@@ -3172,7 +3286,6 @@ exports.MIST_PER_SUI = MIST_PER_SUI;
3172
3286
  exports.NaviAdapter = NaviAdapter;
3173
3287
  exports.ProtocolRegistry = ProtocolRegistry;
3174
3288
  exports.SENTINEL = SENTINEL;
3175
- exports.STABLE_ASSETS = STABLE_ASSETS;
3176
3289
  exports.SUI_DECIMALS = SUI_DECIMALS;
3177
3290
  exports.SUPPORTED_ASSETS = SUPPORTED_ASSETS;
3178
3291
  exports.SuilendAdapter = SuilendAdapter;
@@ -3195,7 +3308,6 @@ exports.getGasStatus = getGasStatus;
3195
3308
  exports.getPoolPrice = getPoolPrice;
3196
3309
  exports.getRates = getRates;
3197
3310
  exports.getSentinelInfo = getSentinelInfo;
3198
- exports.getSwapQuote = getSwapQuote;
3199
3311
  exports.keypairFromPrivateKey = keypairFromPrivateKey;
3200
3312
  exports.listSentinels = listSentinels;
3201
3313
  exports.loadKey = loadKey;
@@ -3203,7 +3315,6 @@ exports.mapMoveAbortCode = mapMoveAbortCode;
3203
3315
  exports.mapWalletError = mapWalletError;
3204
3316
  exports.mistToSui = mistToSui;
3205
3317
  exports.naviDescriptor = descriptor2;
3206
- exports.normalizeAsset = normalizeAsset;
3207
3318
  exports.rawToStable = rawToStable;
3208
3319
  exports.rawToUsdc = rawToUsdc;
3209
3320
  exports.requestAttack = requestAttack;