@t2000/sdk 0.3.0 → 0.4.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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { EventEmitter } from 'eventemitter3';
2
2
  import { SuiClient } from '@mysten/sui/client';
3
- import { normalizeSuiAddress, isValidSuiAddress } from '@mysten/sui/utils';
3
+ import { normalizeSuiAddress, isValidSuiAddress, normalizeStructTag } from '@mysten/sui/utils';
4
4
  import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
5
5
  import { decodeSuiPrivateKey } from '@mysten/sui/cryptography';
6
6
  import { createHash, randomBytes, createCipheriv, createDecipheriv, scryptSync } from 'crypto';
@@ -8,9 +8,9 @@ import { access, mkdir, writeFile, readFile } from 'fs/promises';
8
8
  import { dirname, resolve } from 'path';
9
9
  import { homedir } from 'os';
10
10
  import { Transaction } from '@mysten/sui/transactions';
11
- import { getPool, getCoins, mergeCoinsPTB, depositCoinPTB, getLendingState, withdrawCoinPTB, borrowCoinPTB, getHealthFactor as getHealthFactor$1, repayCoinPTB, getPriceFeeds, filterPriceFeeds, updateOraclePricesPTB } from '@naviprotocol/lending';
12
- import { CetusClmmSDK } from '@cetusprotocol/sui-clmm-sdk';
11
+ import { getPool, getLendingState, getHealthFactor as getHealthFactor$1, getCoins, mergeCoinsPTB, depositCoinPTB, withdrawCoinPTB, borrowCoinPTB, repayCoinPTB, getPriceFeeds, filterPriceFeeds, updateOraclePricesPTB } from '@naviprotocol/lending';
13
12
  import { bcs } from '@mysten/sui/bcs';
13
+ import { AggregatorClient, Env } from '@cetusprotocol/aggregator-sdk';
14
14
 
15
15
  // src/t2000.ts
16
16
 
@@ -522,8 +522,8 @@ async function buildRepayTx(client, address, amount) {
522
522
  await repayCoinPTB(tx, USDC_TYPE, coinObj, { ...ENV, amount: rawAmount });
523
523
  return tx;
524
524
  }
525
- async function getHealthFactor(client, keypair) {
526
- const address = keypair.getPublicKey().toSuiAddress();
525
+ async function getHealthFactor(client, addressOrKeypair) {
526
+ const address = typeof addressOrKeypair === "string" ? addressOrKeypair : addressOrKeypair.getPublicKey().toSuiAddress();
527
527
  const [healthFactor, state, pool] = await Promise.all([
528
528
  getHealthFactor$1(address, clientOpt(client, true)),
529
529
  getLendingState(address, clientOpt(client, true)),
@@ -555,8 +555,8 @@ async function getRates(client) {
555
555
  return { USDC: { saveApy: 4, borrowApy: 6 } };
556
556
  }
557
557
  }
558
- async function getPositions(client, keypair) {
559
- const address = keypair.getPublicKey().toSuiAddress();
558
+ async function getPositions(client, addressOrKeypair) {
559
+ const address = typeof addressOrKeypair === "string" ? addressOrKeypair : addressOrKeypair.getPublicKey().toSuiAddress();
560
560
  const state = await getLendingState(address, clientOpt(client, true));
561
561
  const positions = [];
562
562
  for (const pos of state) {
@@ -584,8 +584,8 @@ async function getPositions(client, keypair) {
584
584
  }
585
585
  return { positions };
586
586
  }
587
- async function maxWithdrawAmount(client, keypair) {
588
- const hf = await getHealthFactor(client, keypair);
587
+ async function maxWithdrawAmount(client, addressOrKeypair) {
588
+ const hf = await getHealthFactor(client, addressOrKeypair);
589
589
  const ltv = hf.liquidationThreshold > 0 ? hf.liquidationThreshold : 0.75;
590
590
  let maxAmount;
591
591
  if (hf.borrowed === 0) {
@@ -601,8 +601,8 @@ async function maxWithdrawAmount(client, keypair) {
601
601
  currentHF: hf.healthFactor
602
602
  };
603
603
  }
604
- async function maxBorrowAmount(client, keypair) {
605
- const hf = await getHealthFactor(client, keypair);
604
+ async function maxBorrowAmount(client, addressOrKeypair) {
605
+ const hf = await getHealthFactor(client, addressOrKeypair);
606
606
  const ltv = hf.liquidationThreshold > 0 ? hf.liquidationThreshold : 0.75;
607
607
  const maxAmount = Math.max(0, hf.supplied * ltv / MIN_HEALTH_FACTOR - hf.borrowed);
608
608
  return {
@@ -611,108 +611,6 @@ async function maxBorrowAmount(client, keypair) {
611
611
  currentHF: hf.healthFactor
612
612
  };
613
613
  }
614
- var DEFAULT_SLIPPAGE_BPS = 300;
615
- function isA2B(from) {
616
- return from === "USDC";
617
- }
618
- var _cetusSDK = null;
619
- function getCetusSDK() {
620
- if (!_cetusSDK) {
621
- _cetusSDK = CetusClmmSDK.createSDK({ env: "mainnet" });
622
- }
623
- return _cetusSDK;
624
- }
625
- async function buildSwapTx(params) {
626
- const { client, address, fromAsset, toAsset, amount, maxSlippageBps = DEFAULT_SLIPPAGE_BPS } = params;
627
- const a2b = isA2B(fromAsset);
628
- const fromInfo = SUPPORTED_ASSETS[fromAsset];
629
- const toInfo = SUPPORTED_ASSETS[toAsset];
630
- const rawAmount = BigInt(Math.floor(amount * 10 ** fromInfo.decimals));
631
- const sdk = getCetusSDK();
632
- sdk.setSenderAddress(address);
633
- const pool = await sdk.Pool.getPool(CETUS_USDC_SUI_POOL);
634
- const preSwapResult = await sdk.Swap.preSwap({
635
- pool,
636
- current_sqrt_price: pool.current_sqrt_price,
637
- coin_type_a: pool.coin_type_a,
638
- coin_type_b: pool.coin_type_b,
639
- decimals_a: 6,
640
- decimals_b: 9,
641
- a2b,
642
- by_amount_in: true,
643
- amount: rawAmount.toString()
644
- });
645
- const estimatedOut = Number(preSwapResult.estimated_amount_out);
646
- const slippageFactor = (1e4 - maxSlippageBps) / 1e4;
647
- const amountLimit = Math.floor(estimatedOut * slippageFactor);
648
- const swapPayload = await sdk.Swap.createSwapPayload({
649
- pool_id: pool.id,
650
- coin_type_a: pool.coin_type_a,
651
- coin_type_b: pool.coin_type_b,
652
- a2b,
653
- by_amount_in: true,
654
- amount: preSwapResult.amount.toString(),
655
- amount_limit: amountLimit.toString()
656
- });
657
- return {
658
- tx: swapPayload,
659
- estimatedOut,
660
- toDecimals: toInfo.decimals
661
- };
662
- }
663
- async function getPoolPrice(client) {
664
- try {
665
- const pool = await client.getObject({
666
- id: CETUS_USDC_SUI_POOL,
667
- options: { showContent: true }
668
- });
669
- if (pool.data?.content?.dataType === "moveObject") {
670
- const fields = pool.data.content.fields;
671
- const currentSqrtPrice = BigInt(String(fields.current_sqrt_price ?? "0"));
672
- if (currentSqrtPrice > 0n) {
673
- const Q64 = 2n ** 64n;
674
- const sqrtPriceFloat = Number(currentSqrtPrice) / Number(Q64);
675
- const rawPrice = sqrtPriceFloat * sqrtPriceFloat;
676
- const suiPriceUsd = 1e3 / rawPrice;
677
- if (suiPriceUsd > 0.01 && suiPriceUsd < 1e3) return suiPriceUsd;
678
- }
679
- }
680
- } catch {
681
- }
682
- return 3.5;
683
- }
684
- async function getSwapQuote(client, fromAsset, toAsset, amount) {
685
- const a2b = isA2B(fromAsset);
686
- const fromInfo = SUPPORTED_ASSETS[fromAsset];
687
- const toInfo = SUPPORTED_ASSETS[toAsset];
688
- const rawAmount = BigInt(Math.floor(amount * 10 ** fromInfo.decimals));
689
- const poolPrice = await getPoolPrice(client);
690
- try {
691
- const sdk = getCetusSDK();
692
- const pool = await sdk.Pool.getPool(CETUS_USDC_SUI_POOL);
693
- const preSwapResult = await sdk.Swap.preSwap({
694
- pool,
695
- current_sqrt_price: pool.current_sqrt_price,
696
- coin_type_a: pool.coin_type_a,
697
- coin_type_b: pool.coin_type_b,
698
- decimals_a: 6,
699
- decimals_b: 9,
700
- a2b,
701
- by_amount_in: true,
702
- amount: rawAmount.toString()
703
- });
704
- const expectedOutput = Number(preSwapResult.estimated_amount_out) / 10 ** toInfo.decimals;
705
- return { expectedOutput, priceImpact: 0, poolPrice };
706
- } catch {
707
- let expectedOutput;
708
- if (fromAsset === "USDC") {
709
- expectedOutput = amount / poolPrice;
710
- } else {
711
- expectedOutput = amount * poolPrice;
712
- }
713
- return { expectedOutput, priceImpact: 0, poolPrice };
714
- }
715
- }
716
614
 
717
615
  // src/protocols/yieldTracker.ts
718
616
  async function getEarnings(client, keypair) {
@@ -924,6 +822,298 @@ async function attack(client, signer, sentinelId, prompt, feeMist) {
924
822
  feePaid: Number(fee) / Number(MIST_PER_SUI)
925
823
  };
926
824
  }
825
+
826
+ // src/adapters/registry.ts
827
+ var ProtocolRegistry = class {
828
+ lending = /* @__PURE__ */ new Map();
829
+ swap = /* @__PURE__ */ new Map();
830
+ registerLending(adapter) {
831
+ this.lending.set(adapter.id, adapter);
832
+ }
833
+ registerSwap(adapter) {
834
+ this.swap.set(adapter.id, adapter);
835
+ }
836
+ async bestSaveRate(asset) {
837
+ const candidates = [];
838
+ for (const adapter of this.lending.values()) {
839
+ if (!adapter.supportedAssets.includes(asset)) continue;
840
+ if (!adapter.capabilities.includes("save")) continue;
841
+ try {
842
+ const rate = await adapter.getRates(asset);
843
+ candidates.push({ adapter, rate });
844
+ } catch {
845
+ }
846
+ }
847
+ if (candidates.length === 0) {
848
+ throw new Error(`No lending adapter supports saving ${asset}`);
849
+ }
850
+ candidates.sort((a, b) => b.rate.saveApy - a.rate.saveApy);
851
+ return candidates[0];
852
+ }
853
+ async bestBorrowRate(asset, opts) {
854
+ const candidates = [];
855
+ for (const adapter of this.lending.values()) {
856
+ if (!adapter.supportedAssets.includes(asset)) continue;
857
+ if (!adapter.capabilities.includes("borrow")) continue;
858
+ if (opts?.requireSameAssetBorrow && !adapter.supportsSameAssetBorrow) continue;
859
+ try {
860
+ const rate = await adapter.getRates(asset);
861
+ candidates.push({ adapter, rate });
862
+ } catch {
863
+ }
864
+ }
865
+ if (candidates.length === 0) {
866
+ throw new Error(`No lending adapter supports borrowing ${asset}`);
867
+ }
868
+ candidates.sort((a, b) => a.rate.borrowApy - b.rate.borrowApy);
869
+ return candidates[0];
870
+ }
871
+ async bestSwapQuote(from, to, amount) {
872
+ const candidates = [];
873
+ for (const adapter of this.swap.values()) {
874
+ const pairs = adapter.getSupportedPairs();
875
+ if (!pairs.some((p) => p.from === from && p.to === to)) continue;
876
+ try {
877
+ const quote = await adapter.getQuote(from, to, amount);
878
+ candidates.push({ adapter, quote });
879
+ } catch {
880
+ }
881
+ }
882
+ if (candidates.length === 0) {
883
+ throw new Error(`No swap adapter supports ${from} \u2192 ${to}`);
884
+ }
885
+ candidates.sort((a, b) => b.quote.expectedOutput - a.quote.expectedOutput);
886
+ return candidates[0];
887
+ }
888
+ async allRates(asset) {
889
+ const results = [];
890
+ for (const adapter of this.lending.values()) {
891
+ if (!adapter.supportedAssets.includes(asset)) continue;
892
+ try {
893
+ const rates = await adapter.getRates(asset);
894
+ results.push({ protocol: adapter.name, protocolId: adapter.id, rates });
895
+ } catch {
896
+ }
897
+ }
898
+ return results;
899
+ }
900
+ async allPositions(address) {
901
+ const results = [];
902
+ for (const adapter of this.lending.values()) {
903
+ try {
904
+ const positions = await adapter.getPositions(address);
905
+ if (positions.supplies.length > 0 || positions.borrows.length > 0) {
906
+ results.push({ protocol: adapter.name, protocolId: adapter.id, positions });
907
+ }
908
+ } catch {
909
+ }
910
+ }
911
+ return results;
912
+ }
913
+ getLending(id) {
914
+ return this.lending.get(id);
915
+ }
916
+ getSwap(id) {
917
+ return this.swap.get(id);
918
+ }
919
+ listLending() {
920
+ return [...this.lending.values()];
921
+ }
922
+ listSwap() {
923
+ return [...this.swap.values()];
924
+ }
925
+ };
926
+
927
+ // src/adapters/navi.ts
928
+ var NaviAdapter = class {
929
+ id = "navi";
930
+ name = "NAVI Protocol";
931
+ version = "1.0.0";
932
+ capabilities = ["save", "withdraw", "borrow", "repay"];
933
+ supportedAssets = ["USDC"];
934
+ supportsSameAssetBorrow = true;
935
+ client;
936
+ async init(client) {
937
+ this.client = client;
938
+ }
939
+ initSync(client) {
940
+ this.client = client;
941
+ }
942
+ async getRates(asset) {
943
+ const rates = await getRates(this.client);
944
+ const key = asset.toUpperCase();
945
+ const r = rates[key];
946
+ if (!r) throw new Error(`NAVI does not support ${asset}`);
947
+ return { asset, saveApy: r.saveApy, borrowApy: r.borrowApy };
948
+ }
949
+ async getPositions(address) {
950
+ const result = await getPositions(this.client, address);
951
+ return {
952
+ supplies: result.positions.filter((p) => p.type === "save").map((p) => ({ asset: p.asset, amount: p.amount, apy: p.apy })),
953
+ borrows: result.positions.filter((p) => p.type === "borrow").map((p) => ({ asset: p.asset, amount: p.amount, apy: p.apy }))
954
+ };
955
+ }
956
+ async getHealth(address) {
957
+ return getHealthFactor(this.client, address);
958
+ }
959
+ async buildSaveTx(address, amount, _asset, options) {
960
+ const tx = await buildSaveTx(this.client, address, amount, options);
961
+ return { tx };
962
+ }
963
+ async buildWithdrawTx(address, amount, _asset) {
964
+ const result = await buildWithdrawTx(this.client, address, amount);
965
+ return { tx: result.tx, effectiveAmount: result.effectiveAmount };
966
+ }
967
+ async buildBorrowTx(address, amount, _asset, options) {
968
+ const tx = await buildBorrowTx(this.client, address, amount, options);
969
+ return { tx };
970
+ }
971
+ async buildRepayTx(address, amount, _asset) {
972
+ const tx = await buildRepayTx(this.client, address, amount);
973
+ return { tx };
974
+ }
975
+ async maxWithdraw(address, _asset) {
976
+ return maxWithdrawAmount(this.client, address);
977
+ }
978
+ async maxBorrow(address, _asset) {
979
+ return maxBorrowAmount(this.client, address);
980
+ }
981
+ };
982
+ var DEFAULT_SLIPPAGE_BPS = 300;
983
+ function createAggregatorClient(client, signer) {
984
+ return new AggregatorClient({
985
+ client,
986
+ signer,
987
+ env: Env.Mainnet
988
+ });
989
+ }
990
+ async function buildSwapTx(params) {
991
+ const { client, address, fromAsset, toAsset, amount, maxSlippageBps = DEFAULT_SLIPPAGE_BPS } = params;
992
+ const fromInfo = SUPPORTED_ASSETS[fromAsset];
993
+ const toInfo = SUPPORTED_ASSETS[toAsset];
994
+ const rawAmount = BigInt(Math.floor(amount * 10 ** fromInfo.decimals));
995
+ const aggClient = createAggregatorClient(client, address);
996
+ const result = await aggClient.findRouters({
997
+ from: fromInfo.type,
998
+ target: toInfo.type,
999
+ amount: rawAmount,
1000
+ byAmountIn: true
1001
+ });
1002
+ if (!result || result.insufficientLiquidity) {
1003
+ throw new T2000Error(
1004
+ "ASSET_NOT_SUPPORTED",
1005
+ `No swap route found for ${fromAsset} \u2192 ${toAsset}`
1006
+ );
1007
+ }
1008
+ const tx = new Transaction();
1009
+ const slippage = maxSlippageBps / 1e4;
1010
+ await aggClient.fastRouterSwap({
1011
+ router: result,
1012
+ txb: tx,
1013
+ slippage
1014
+ });
1015
+ const estimatedOut = Number(result.amountOut.toString());
1016
+ return {
1017
+ tx,
1018
+ estimatedOut,
1019
+ toDecimals: toInfo.decimals
1020
+ };
1021
+ }
1022
+ async function getPoolPrice(client) {
1023
+ try {
1024
+ const pool = await client.getObject({
1025
+ id: CETUS_USDC_SUI_POOL,
1026
+ options: { showContent: true }
1027
+ });
1028
+ if (pool.data?.content?.dataType === "moveObject") {
1029
+ const fields = pool.data.content.fields;
1030
+ const currentSqrtPrice = BigInt(String(fields.current_sqrt_price ?? "0"));
1031
+ if (currentSqrtPrice > 0n) {
1032
+ const Q64 = 2n ** 64n;
1033
+ const sqrtPriceFloat = Number(currentSqrtPrice) / Number(Q64);
1034
+ const rawPrice = sqrtPriceFloat * sqrtPriceFloat;
1035
+ const suiPriceUsd = 1e3 / rawPrice;
1036
+ if (suiPriceUsd > 0.01 && suiPriceUsd < 1e3) return suiPriceUsd;
1037
+ }
1038
+ }
1039
+ } catch {
1040
+ }
1041
+ return 3.5;
1042
+ }
1043
+ async function getSwapQuote(client, fromAsset, toAsset, amount) {
1044
+ const fromInfo = SUPPORTED_ASSETS[fromAsset];
1045
+ const toInfo = SUPPORTED_ASSETS[toAsset];
1046
+ const rawAmount = BigInt(Math.floor(amount * 10 ** fromInfo.decimals));
1047
+ const poolPrice = await getPoolPrice(client);
1048
+ try {
1049
+ const aggClient = createAggregatorClient(client);
1050
+ const result = await aggClient.findRouters({
1051
+ from: fromInfo.type,
1052
+ target: toInfo.type,
1053
+ amount: rawAmount,
1054
+ byAmountIn: true
1055
+ });
1056
+ if (!result || result.insufficientLiquidity) {
1057
+ return fallbackQuote(fromAsset, amount, poolPrice);
1058
+ }
1059
+ const expectedOutput = Number(result.amountOut.toString()) / 10 ** toInfo.decimals;
1060
+ const priceImpact = result.deviationRatio ?? 0;
1061
+ return { expectedOutput, priceImpact, poolPrice };
1062
+ } catch {
1063
+ return fallbackQuote(fromAsset, amount, poolPrice);
1064
+ }
1065
+ }
1066
+ function fallbackQuote(fromAsset, amount, poolPrice) {
1067
+ const expectedOutput = fromAsset === "USDC" ? amount / poolPrice : amount * poolPrice;
1068
+ return { expectedOutput, priceImpact: 0, poolPrice };
1069
+ }
1070
+
1071
+ // src/adapters/cetus.ts
1072
+ var CetusAdapter = class {
1073
+ id = "cetus";
1074
+ name = "Cetus";
1075
+ version = "1.0.0";
1076
+ capabilities = ["swap"];
1077
+ client;
1078
+ async init(client) {
1079
+ this.client = client;
1080
+ }
1081
+ initSync(client) {
1082
+ this.client = client;
1083
+ }
1084
+ async getQuote(from, to, amount) {
1085
+ return getSwapQuote(
1086
+ this.client,
1087
+ from.toUpperCase(),
1088
+ to.toUpperCase(),
1089
+ amount
1090
+ );
1091
+ }
1092
+ async buildSwapTx(address, from, to, amount, maxSlippageBps) {
1093
+ const result = await buildSwapTx({
1094
+ client: this.client,
1095
+ address,
1096
+ fromAsset: from.toUpperCase(),
1097
+ toAsset: to.toUpperCase(),
1098
+ amount,
1099
+ maxSlippageBps
1100
+ });
1101
+ return {
1102
+ tx: result.tx,
1103
+ estimatedOut: result.estimatedOut,
1104
+ toDecimals: result.toDecimals
1105
+ };
1106
+ }
1107
+ getSupportedPairs() {
1108
+ return [
1109
+ { from: "USDC", to: "SUI" },
1110
+ { from: "SUI", to: "USDC" }
1111
+ ];
1112
+ }
1113
+ async getPoolPrice() {
1114
+ return getPoolPrice(this.client);
1115
+ }
1116
+ };
927
1117
  function hasLeadingZeroBits(hash, bits) {
928
1118
  const fullBytes = Math.floor(bits / 8);
929
1119
  const remainingBits = bits % 8;
@@ -1156,11 +1346,23 @@ var T2000 = class _T2000 extends EventEmitter {
1156
1346
  keypair;
1157
1347
  client;
1158
1348
  _address;
1159
- constructor(keypair, client) {
1349
+ registry;
1350
+ constructor(keypair, client, registry) {
1160
1351
  super();
1161
1352
  this.keypair = keypair;
1162
1353
  this.client = client;
1163
1354
  this._address = getAddress(keypair);
1355
+ this.registry = registry ?? _T2000.createDefaultRegistry(client);
1356
+ }
1357
+ static createDefaultRegistry(client) {
1358
+ const registry = new ProtocolRegistry();
1359
+ const naviAdapter = new NaviAdapter();
1360
+ naviAdapter.initSync(client);
1361
+ registry.registerLending(naviAdapter);
1362
+ const cetusAdapter = new CetusAdapter();
1363
+ cetusAdapter.initSync(client);
1364
+ registry.registerSwap(cetusAdapter);
1365
+ return registry;
1164
1366
  }
1165
1367
  static async create(options = {}) {
1166
1368
  const { keyPath, pin, passphrase, network = DEFAULT_NETWORK, rpcUrl, sponsored, name } = options;
@@ -1281,6 +1483,11 @@ var T2000 = class _T2000 extends EventEmitter {
1281
1483
  exportKey() {
1282
1484
  return exportPrivateKey(this.keypair);
1283
1485
  }
1486
+ async registerAdapter(adapter) {
1487
+ await adapter.init(this.client);
1488
+ if ("buildSaveTx" in adapter) this.registry.registerLending(adapter);
1489
+ if ("buildSwapTx" in adapter) this.registry.registerSwap(adapter);
1490
+ }
1284
1491
  // -- Savings --
1285
1492
  async save(params) {
1286
1493
  const asset = (params.asset ?? "USDC").toUpperCase();
@@ -1303,12 +1510,12 @@ var T2000 = class _T2000 extends EventEmitter {
1303
1510
  }
1304
1511
  const fee = calculateFee("save", amount);
1305
1512
  const saveAmount = amount;
1306
- const gasResult = await executeWithGas(
1307
- this.client,
1308
- this.keypair,
1309
- () => buildSaveTx(this.client, this._address, saveAmount, { collectFee: true })
1310
- );
1311
- const rates = await getRates(this.client);
1513
+ const adapter = await this.resolveLending(params.protocol, asset, "save");
1514
+ const gasResult = await executeWithGas(this.client, this.keypair, async () => {
1515
+ const { tx } = await adapter.buildSaveTx(this._address, saveAmount, asset, { collectFee: true });
1516
+ return tx;
1517
+ });
1518
+ const rates = await adapter.getRates(asset);
1312
1519
  reportFee(this._address, "save", fee.amount, fee.rate, gasResult.digest);
1313
1520
  this.emitBalanceChange("USDC", saveAmount, "save", gasResult.digest);
1314
1521
  let savingsBalance = saveAmount;
@@ -1321,7 +1528,7 @@ var T2000 = class _T2000 extends EventEmitter {
1321
1528
  success: true,
1322
1529
  tx: gasResult.digest,
1323
1530
  amount: saveAmount,
1324
- apy: rates.USDC.saveApy,
1531
+ apy: rates.saveApy,
1325
1532
  fee: fee.amount,
1326
1533
  gasCost: gasResult.gasCostSui,
1327
1534
  gasMethod: gasResult.gasMethod,
@@ -1333,18 +1540,19 @@ var T2000 = class _T2000 extends EventEmitter {
1333
1540
  if (asset !== "USDC") {
1334
1541
  throw new T2000Error("ASSET_NOT_SUPPORTED", `Only USDC is supported for withdraw. Got: ${asset}`);
1335
1542
  }
1543
+ const adapter = await this.resolveLending(params.protocol, asset, "withdraw");
1336
1544
  let amount;
1337
1545
  if (params.amount === "all") {
1338
- const maxResult = await this.maxWithdraw();
1546
+ const maxResult = await adapter.maxWithdraw(this._address, asset);
1339
1547
  amount = maxResult.maxAmount;
1340
1548
  if (amount <= 0) {
1341
1549
  throw new T2000Error("NO_COLLATERAL", "No savings to withdraw");
1342
1550
  }
1343
1551
  } else {
1344
1552
  amount = params.amount;
1345
- const hf = await this.healthFactor();
1553
+ const hf = await adapter.getHealth(this._address);
1346
1554
  if (hf.borrowed > 0) {
1347
- const maxResult = await this.maxWithdraw();
1555
+ const maxResult = await adapter.maxWithdraw(this._address, asset);
1348
1556
  if (amount > maxResult.maxAmount) {
1349
1557
  throw new T2000Error(
1350
1558
  "WITHDRAW_WOULD_LIQUIDATE",
@@ -1361,7 +1569,7 @@ var T2000 = class _T2000 extends EventEmitter {
1361
1569
  const withdrawAmount = amount;
1362
1570
  let effectiveAmount = withdrawAmount;
1363
1571
  const gasResult = await executeWithGas(this.client, this.keypair, async () => {
1364
- const built = await buildWithdrawTx(this.client, this._address, withdrawAmount);
1572
+ const built = await adapter.buildWithdrawTx(this._address, withdrawAmount, asset);
1365
1573
  effectiveAmount = built.effectiveAmount;
1366
1574
  return built.tx;
1367
1575
  });
@@ -1375,7 +1583,8 @@ var T2000 = class _T2000 extends EventEmitter {
1375
1583
  };
1376
1584
  }
1377
1585
  async maxWithdraw() {
1378
- return maxWithdrawAmount(this.client, this.keypair);
1586
+ const adapter = await this.resolveLending(void 0, "USDC", "withdraw");
1587
+ return adapter.maxWithdraw(this._address, "USDC");
1379
1588
  }
1380
1589
  // -- Borrowing --
1381
1590
  async borrow(params) {
@@ -1383,7 +1592,8 @@ var T2000 = class _T2000 extends EventEmitter {
1383
1592
  if (asset !== "USDC") {
1384
1593
  throw new T2000Error("ASSET_NOT_SUPPORTED", `Only USDC is supported for borrow. Got: ${asset}`);
1385
1594
  }
1386
- const maxResult = await this.maxBorrow();
1595
+ const adapter = await this.resolveLending(params.protocol, asset, "borrow");
1596
+ const maxResult = await adapter.maxBorrow(this._address, asset);
1387
1597
  if (params.amount > maxResult.maxAmount) {
1388
1598
  throw new T2000Error("HEALTH_FACTOR_TOO_LOW", `Max safe borrow: $${maxResult.maxAmount.toFixed(2)}`, {
1389
1599
  maxBorrow: maxResult.maxAmount,
@@ -1392,12 +1602,11 @@ var T2000 = class _T2000 extends EventEmitter {
1392
1602
  }
1393
1603
  const fee = calculateFee("borrow", params.amount);
1394
1604
  const borrowAmount = params.amount;
1395
- const gasResult = await executeWithGas(
1396
- this.client,
1397
- this.keypair,
1398
- () => buildBorrowTx(this.client, this._address, borrowAmount, { collectFee: true })
1399
- );
1400
- const hf = await getHealthFactor(this.client, this.keypair);
1605
+ const gasResult = await executeWithGas(this.client, this.keypair, async () => {
1606
+ const { tx } = await adapter.buildBorrowTx(this._address, borrowAmount, asset, { collectFee: true });
1607
+ return tx;
1608
+ });
1609
+ const hf = await adapter.getHealth(this._address);
1401
1610
  reportFee(this._address, "borrow", fee.amount, fee.rate, gasResult.digest);
1402
1611
  this.emitBalanceChange("USDC", borrowAmount, "borrow", gasResult.digest);
1403
1612
  return {
@@ -1415,9 +1624,10 @@ var T2000 = class _T2000 extends EventEmitter {
1415
1624
  if (asset !== "USDC") {
1416
1625
  throw new T2000Error("ASSET_NOT_SUPPORTED", `Only USDC is supported for repay. Got: ${asset}`);
1417
1626
  }
1627
+ const adapter = await this.resolveLending(params.protocol, asset, "repay");
1418
1628
  let amount;
1419
1629
  if (params.amount === "all") {
1420
- const hf2 = await this.healthFactor();
1630
+ const hf2 = await adapter.getHealth(this._address);
1421
1631
  amount = hf2.borrowed;
1422
1632
  if (amount <= 0) {
1423
1633
  throw new T2000Error("NO_COLLATERAL", "No outstanding borrow to repay");
@@ -1426,12 +1636,11 @@ var T2000 = class _T2000 extends EventEmitter {
1426
1636
  amount = params.amount;
1427
1637
  }
1428
1638
  const repayAmount = amount;
1429
- const gasResult = await executeWithGas(
1430
- this.client,
1431
- this.keypair,
1432
- () => buildRepayTx(this.client, this._address, repayAmount)
1433
- );
1434
- const hf = await getHealthFactor(this.client, this.keypair);
1639
+ const gasResult = await executeWithGas(this.client, this.keypair, async () => {
1640
+ const { tx } = await adapter.buildRepayTx(this._address, repayAmount, asset);
1641
+ return tx;
1642
+ });
1643
+ const hf = await adapter.getHealth(this._address);
1435
1644
  this.emitBalanceChange("USDC", repayAmount, "repay", gasResult.digest);
1436
1645
  return {
1437
1646
  success: true,
@@ -1443,10 +1652,12 @@ var T2000 = class _T2000 extends EventEmitter {
1443
1652
  };
1444
1653
  }
1445
1654
  async maxBorrow() {
1446
- return maxBorrowAmount(this.client, this.keypair);
1655
+ const adapter = await this.resolveLending(void 0, "USDC", "borrow");
1656
+ return adapter.maxBorrow(this._address, "USDC");
1447
1657
  }
1448
1658
  async healthFactor() {
1449
- const hf = await getHealthFactor(this.client, this.keypair);
1659
+ const adapter = await this.resolveLending(void 0, "USDC", "save");
1660
+ const hf = await adapter.getHealth(this._address);
1450
1661
  if (hf.healthFactor < 1.2) {
1451
1662
  this.emit("healthCritical", { healthFactor: hf.healthFactor, threshold: 1.5, severity: "critical" });
1452
1663
  } else if (hf.healthFactor < 2) {
@@ -1464,23 +1675,26 @@ var T2000 = class _T2000 extends EventEmitter {
1464
1675
  if (fromAsset === toAsset) {
1465
1676
  throw new T2000Error("INVALID_AMOUNT", "Cannot swap same asset");
1466
1677
  }
1678
+ let adapter;
1679
+ if (params.protocol) {
1680
+ const found = this.registry.getSwap(params.protocol);
1681
+ if (!found) throw new T2000Error("ASSET_NOT_SUPPORTED", `Swap adapter '${params.protocol}' not found`);
1682
+ adapter = found;
1683
+ } else {
1684
+ const best = await this.registry.bestSwapQuote(fromAsset, toAsset, params.amount);
1685
+ adapter = best.adapter;
1686
+ }
1467
1687
  const fee = calculateFee("swap", params.amount);
1468
1688
  const swapAmount = params.amount;
1469
1689
  const slippageBps = params.maxSlippage ? params.maxSlippage * 100 : void 0;
1470
1690
  let swapMeta = { estimatedOut: 0, toDecimals: 0 };
1471
1691
  const gasResult = await executeWithGas(this.client, this.keypair, async () => {
1472
- const built = await buildSwapTx({
1473
- client: this.client,
1474
- address: this._address,
1475
- fromAsset,
1476
- toAsset,
1477
- amount: swapAmount,
1478
- maxSlippageBps: slippageBps
1479
- });
1692
+ const built = await adapter.buildSwapTx(this._address, fromAsset, toAsset, swapAmount, slippageBps);
1480
1693
  swapMeta = { estimatedOut: built.estimatedOut, toDecimals: built.toDecimals };
1481
1694
  return built.tx;
1482
1695
  });
1483
1696
  const toInfo = SUPPORTED_ASSETS[toAsset];
1697
+ await this.client.waitForTransaction({ digest: gasResult.digest });
1484
1698
  const txDetail = await this.client.getTransactionBlock({
1485
1699
  digest: gasResult.digest,
1486
1700
  options: { showBalanceChanges: true }
@@ -1515,17 +1729,39 @@ var T2000 = class _T2000 extends EventEmitter {
1515
1729
  async swapQuote(params) {
1516
1730
  const fromAsset = params.from.toUpperCase();
1517
1731
  const toAsset = params.to.toUpperCase();
1518
- const quote = await getSwapQuote(this.client, fromAsset, toAsset, params.amount);
1732
+ const best = await this.registry.bestSwapQuote(fromAsset, toAsset, params.amount);
1519
1733
  const fee = calculateFee("swap", params.amount);
1520
- return { ...quote, fee: { amount: fee.amount, rate: fee.rate } };
1734
+ return { ...best.quote, fee: { amount: fee.amount, rate: fee.rate } };
1521
1735
  }
1522
1736
  // -- Info --
1523
1737
  async positions() {
1524
- return getPositions(this.client, this.keypair);
1738
+ const allPositions = await this.registry.allPositions(this._address);
1739
+ const positions = allPositions.flatMap(
1740
+ (p) => [
1741
+ ...p.positions.supplies.map((s) => ({
1742
+ protocol: p.protocolId,
1743
+ asset: s.asset,
1744
+ type: "save",
1745
+ amount: s.amount,
1746
+ apy: s.apy
1747
+ })),
1748
+ ...p.positions.borrows.map((b) => ({
1749
+ protocol: p.protocolId,
1750
+ asset: b.asset,
1751
+ type: "borrow",
1752
+ amount: b.amount,
1753
+ apy: b.apy
1754
+ }))
1755
+ ]
1756
+ );
1757
+ return { positions };
1525
1758
  }
1526
1759
  async rates() {
1527
1760
  return getRates(this.client);
1528
1761
  }
1762
+ async allRates(asset = "USDC") {
1763
+ return this.registry.allRates(asset);
1764
+ }
1529
1765
  async earnings() {
1530
1766
  const result = await getEarnings(this.client, this.keypair);
1531
1767
  if (result.totalYieldEarned > 0) {
@@ -1552,6 +1788,29 @@ var T2000 = class _T2000 extends EventEmitter {
1552
1788
  return attack(this.client, this.keypair, id, prompt, fee);
1553
1789
  }
1554
1790
  // -- Helpers --
1791
+ async resolveLending(protocol, asset, capability) {
1792
+ if (protocol) {
1793
+ const adapter = this.registry.getLending(protocol);
1794
+ if (!adapter) throw new T2000Error("ASSET_NOT_SUPPORTED", `Lending adapter '${protocol}' not found`);
1795
+ return adapter;
1796
+ }
1797
+ if (capability === "save") {
1798
+ const { adapter } = await this.registry.bestSaveRate(asset);
1799
+ return adapter;
1800
+ }
1801
+ if (capability === "borrow" || capability === "repay") {
1802
+ const adapters2 = this.registry.listLending().filter(
1803
+ (a) => a.supportedAssets.includes(asset) && a.capabilities.includes(capability) && (capability !== "borrow" || a.supportsSameAssetBorrow)
1804
+ );
1805
+ if (adapters2.length === 0) throw new T2000Error("ASSET_NOT_SUPPORTED", `No adapter supports ${capability} ${asset}`);
1806
+ return adapters2[0];
1807
+ }
1808
+ const adapters = this.registry.listLending().filter(
1809
+ (a) => a.supportedAssets.includes(asset) && a.capabilities.includes(capability)
1810
+ );
1811
+ if (adapters.length === 0) throw new T2000Error("ASSET_NOT_SUPPORTED", `No adapter supports ${capability} ${asset}`);
1812
+ return adapters[0];
1813
+ }
1555
1814
  emitBalanceChange(asset, amount, cause, tx) {
1556
1815
  this.emit("balanceChange", { asset, previous: 0, current: 0, cause, tx });
1557
1816
  }
@@ -1649,7 +1908,297 @@ function parseMoveAbort(errorStr) {
1649
1908
  }
1650
1909
  return { reason: errorStr };
1651
1910
  }
1911
+ var USDC_TYPE2 = SUPPORTED_ASSETS.USDC.type;
1912
+ SUPPORTED_ASSETS.USDC.decimals;
1913
+ var WAD = 1e18;
1914
+ var MIN_HEALTH_FACTOR2 = 1.5;
1915
+ function interpolateRate(utilBreakpoints, aprBreakpoints, utilizationPct) {
1916
+ if (utilBreakpoints.length === 0) return 0;
1917
+ if (utilizationPct <= utilBreakpoints[0]) return aprBreakpoints[0];
1918
+ if (utilizationPct >= utilBreakpoints[utilBreakpoints.length - 1]) {
1919
+ return aprBreakpoints[aprBreakpoints.length - 1];
1920
+ }
1921
+ for (let i = 1; i < utilBreakpoints.length; i++) {
1922
+ if (utilizationPct <= utilBreakpoints[i]) {
1923
+ const t = (utilizationPct - utilBreakpoints[i - 1]) / (utilBreakpoints[i] - utilBreakpoints[i - 1]);
1924
+ return aprBreakpoints[i - 1] + t * (aprBreakpoints[i] - aprBreakpoints[i - 1]);
1925
+ }
1926
+ }
1927
+ return aprBreakpoints[aprBreakpoints.length - 1];
1928
+ }
1929
+ function computeRatesFromReserve(reserve) {
1930
+ const decimals = reserve.mintDecimals;
1931
+ const available = Number(reserve.availableAmount) / 10 ** decimals;
1932
+ const borrowed = Number(reserve.borrowedAmount.value) / WAD / 10 ** decimals;
1933
+ const totalDeposited = available + borrowed;
1934
+ const utilizationPct = totalDeposited > 0 ? borrowed / totalDeposited * 100 : 0;
1935
+ const config = reserve.config.element;
1936
+ if (!config) return { borrowAprPct: 0, depositAprPct: 0, utilizationPct: 0 };
1937
+ const utils = config.interestRateUtils.map(Number);
1938
+ const aprs = config.interestRateAprs.map((a) => Number(a) / 100);
1939
+ const borrowAprPct = interpolateRate(utils, aprs, utilizationPct);
1940
+ const spreadFeeBps = Number(config.spreadFeeBps);
1941
+ const depositAprPct = utilizationPct / 100 * (borrowAprPct / 100) * (1 - spreadFeeBps / 1e4) * 100;
1942
+ return { borrowAprPct, depositAprPct, utilizationPct };
1943
+ }
1944
+ function cTokenRatio(reserve) {
1945
+ if (reserve.ctokenSupply === 0n) return 1;
1946
+ const available = Number(reserve.availableAmount);
1947
+ const borrowed = Number(reserve.borrowedAmount.value) / WAD;
1948
+ const spreadFees = Number(reserve.unclaimedSpreadFees.value) / WAD;
1949
+ const totalSupply = available + borrowed - spreadFees;
1950
+ return totalSupply / Number(reserve.ctokenSupply);
1951
+ }
1952
+ var SuilendAdapter = class {
1953
+ id = "suilend";
1954
+ name = "Suilend";
1955
+ version = "1.0.0";
1956
+ capabilities = ["save", "withdraw"];
1957
+ supportedAssets = ["USDC"];
1958
+ supportsSameAssetBorrow = false;
1959
+ client;
1960
+ suilend;
1961
+ lendingMarketType;
1962
+ initialized = false;
1963
+ async init(client) {
1964
+ let sdk;
1965
+ try {
1966
+ sdk = await import('@suilend/sdk');
1967
+ } catch {
1968
+ throw new T2000Error(
1969
+ "PROTOCOL_UNAVAILABLE",
1970
+ "Suilend SDK not installed. Run: npm install @suilend/sdk@^1"
1971
+ );
1972
+ }
1973
+ this.client = client;
1974
+ this.lendingMarketType = sdk.LENDING_MARKET_TYPE;
1975
+ try {
1976
+ this.suilend = await sdk.SuilendClient.initialize(
1977
+ sdk.LENDING_MARKET_ID,
1978
+ sdk.LENDING_MARKET_TYPE,
1979
+ client
1980
+ );
1981
+ } catch (err) {
1982
+ throw new T2000Error(
1983
+ "PROTOCOL_UNAVAILABLE",
1984
+ `Failed to initialize Suilend: ${err instanceof Error ? err.message : String(err)}`
1985
+ );
1986
+ }
1987
+ this.initialized = true;
1988
+ }
1989
+ ensureInit() {
1990
+ if (!this.initialized) {
1991
+ throw new T2000Error(
1992
+ "PROTOCOL_UNAVAILABLE",
1993
+ "SuilendAdapter not initialized. Call init() first."
1994
+ );
1995
+ }
1996
+ }
1997
+ findReserve(asset) {
1998
+ const upper = asset.toUpperCase();
1999
+ let coinType;
2000
+ if (upper === "USDC") coinType = USDC_TYPE2;
2001
+ else if (upper === "SUI") coinType = "0x2::sui::SUI";
2002
+ else if (asset.includes("::")) coinType = asset;
2003
+ else return void 0;
2004
+ try {
2005
+ const normalized = normalizeStructTag(coinType);
2006
+ return this.suilend.lendingMarket.reserves.find(
2007
+ (r) => normalizeStructTag(r.coinType.name) === normalized
2008
+ );
2009
+ } catch {
2010
+ return void 0;
2011
+ }
2012
+ }
2013
+ async getObligationCaps(address) {
2014
+ const SuilendClientStatic = (await import('@suilend/sdk')).SuilendClient;
2015
+ return SuilendClientStatic.getObligationOwnerCaps(
2016
+ address,
2017
+ [this.lendingMarketType],
2018
+ this.client
2019
+ );
2020
+ }
2021
+ resolveSymbol(coinType) {
2022
+ const normalized = normalizeStructTag(coinType);
2023
+ if (normalized === normalizeStructTag(USDC_TYPE2)) return "USDC";
2024
+ if (normalized === normalizeStructTag("0x2::sui::SUI")) return "SUI";
2025
+ const parts = coinType.split("::");
2026
+ return parts[parts.length - 1] || "UNKNOWN";
2027
+ }
2028
+ async getRates(asset) {
2029
+ this.ensureInit();
2030
+ const reserve = this.findReserve(asset);
2031
+ if (!reserve) {
2032
+ throw new T2000Error("ASSET_NOT_SUPPORTED", `Suilend does not support ${asset}`);
2033
+ }
2034
+ const { borrowAprPct, depositAprPct } = computeRatesFromReserve(reserve);
2035
+ return {
2036
+ asset,
2037
+ saveApy: depositAprPct,
2038
+ borrowApy: borrowAprPct
2039
+ };
2040
+ }
2041
+ async getPositions(address) {
2042
+ this.ensureInit();
2043
+ const supplies = [];
2044
+ const borrows = [];
2045
+ const caps = await this.getObligationCaps(address);
2046
+ if (caps.length === 0) return { supplies, borrows };
2047
+ const obligation = await this.suilend.getObligation(caps[0].obligationId);
2048
+ for (const deposit of obligation.deposits) {
2049
+ const coinType = normalizeStructTag(deposit.coinType.name);
2050
+ const reserve = this.suilend.lendingMarket.reserves.find(
2051
+ (r) => normalizeStructTag(r.coinType.name) === coinType
2052
+ );
2053
+ if (!reserve) continue;
2054
+ const ctokenAmount = Number(deposit.depositedCtokenAmount.toString());
2055
+ const ratio = cTokenRatio(reserve);
2056
+ const amount = ctokenAmount * ratio / 10 ** reserve.mintDecimals;
2057
+ const { depositAprPct } = computeRatesFromReserve(reserve);
2058
+ supplies.push({ asset: this.resolveSymbol(coinType), amount, apy: depositAprPct });
2059
+ }
2060
+ for (const borrow of obligation.borrows) {
2061
+ const coinType = normalizeStructTag(borrow.coinType.name);
2062
+ const reserve = this.suilend.lendingMarket.reserves.find(
2063
+ (r) => normalizeStructTag(r.coinType.name) === coinType
2064
+ );
2065
+ if (!reserve) continue;
2066
+ const rawBorrowed = Number(borrow.borrowedAmount.value.toString()) / WAD;
2067
+ const amount = rawBorrowed / 10 ** reserve.mintDecimals;
2068
+ const reserveRate = Number(reserve.cumulativeBorrowRate.value.toString()) / WAD;
2069
+ const posRate = Number(borrow.cumulativeBorrowRate.value.toString()) / WAD;
2070
+ const compounded = posRate > 0 ? amount * (reserveRate / posRate) : amount;
2071
+ const { borrowAprPct } = computeRatesFromReserve(reserve);
2072
+ borrows.push({ asset: this.resolveSymbol(coinType), amount: compounded, apy: borrowAprPct });
2073
+ }
2074
+ return { supplies, borrows };
2075
+ }
2076
+ async getHealth(address) {
2077
+ this.ensureInit();
2078
+ const caps = await this.getObligationCaps(address);
2079
+ if (caps.length === 0) {
2080
+ return { healthFactor: Infinity, supplied: 0, borrowed: 0, maxBorrow: 0, liquidationThreshold: 0 };
2081
+ }
2082
+ const positions = await this.getPositions(address);
2083
+ const supplied = positions.supplies.reduce((s, p) => s + p.amount, 0);
2084
+ const borrowed = positions.borrows.reduce((s, p) => s + p.amount, 0);
2085
+ const reserve = this.findReserve("USDC");
2086
+ const closeLtv = reserve?.config?.element?.closeLtvPct ?? 75;
2087
+ const openLtv = reserve?.config?.element?.openLtvPct ?? 70;
2088
+ const liqThreshold = closeLtv / 100;
2089
+ const healthFactor = borrowed > 0 ? supplied * liqThreshold / borrowed : Infinity;
2090
+ const maxBorrow = Math.max(0, supplied * (openLtv / 100) - borrowed);
2091
+ return { healthFactor, supplied, borrowed, maxBorrow, liquidationThreshold: liqThreshold };
2092
+ }
2093
+ async buildSaveTx(address, amount, _asset, options) {
2094
+ this.ensureInit();
2095
+ const rawAmount = usdcToRaw(amount).toString();
2096
+ const tx = new Transaction();
2097
+ tx.setSender(address);
2098
+ const caps = await this.getObligationCaps(address);
2099
+ let capRef;
2100
+ if (caps.length === 0) {
2101
+ const [newCap] = this.suilend.createObligation(tx);
2102
+ capRef = newCap;
2103
+ } else {
2104
+ capRef = caps[0].id;
2105
+ }
2106
+ const allCoins = await this.fetchAllCoins(address, USDC_TYPE2);
2107
+ if (allCoins.length === 0) {
2108
+ throw new T2000Error("INSUFFICIENT_BALANCE", "No USDC coins found");
2109
+ }
2110
+ const primaryCoinId = allCoins[0].coinObjectId;
2111
+ if (allCoins.length > 1) {
2112
+ tx.mergeCoins(
2113
+ tx.object(primaryCoinId),
2114
+ allCoins.slice(1).map((c) => tx.object(c.coinObjectId))
2115
+ );
2116
+ }
2117
+ const [depositCoin] = tx.splitCoins(tx.object(primaryCoinId), [rawAmount]);
2118
+ if (options?.collectFee) {
2119
+ addCollectFeeToTx(tx, depositCoin, "save");
2120
+ }
2121
+ this.suilend.deposit(depositCoin, USDC_TYPE2, capRef, tx);
2122
+ return { tx };
2123
+ }
2124
+ async buildWithdrawTx(address, amount, _asset) {
2125
+ this.ensureInit();
2126
+ const caps = await this.getObligationCaps(address);
2127
+ if (caps.length === 0) {
2128
+ throw new T2000Error("NO_COLLATERAL", "No Suilend position found");
2129
+ }
2130
+ const positions = await this.getPositions(address);
2131
+ const usdcSupply = positions.supplies.find((s) => s.asset === "USDC");
2132
+ const deposited = usdcSupply?.amount ?? 0;
2133
+ const effectiveAmount = Math.min(amount, deposited);
2134
+ if (effectiveAmount <= 0) {
2135
+ throw new T2000Error("NO_COLLATERAL", "Nothing to withdraw from Suilend");
2136
+ }
2137
+ const rawAmount = usdcToRaw(effectiveAmount).toString();
2138
+ const tx = new Transaction();
2139
+ tx.setSender(address);
2140
+ await this.suilend.withdrawAndSendToUser(
2141
+ address,
2142
+ caps[0].id,
2143
+ caps[0].obligationId,
2144
+ USDC_TYPE2,
2145
+ rawAmount,
2146
+ tx
2147
+ );
2148
+ return { tx, effectiveAmount };
2149
+ }
2150
+ async buildBorrowTx(_address, _amount, _asset, _options) {
2151
+ throw new T2000Error(
2152
+ "ASSET_NOT_SUPPORTED",
2153
+ "SuilendAdapter.buildBorrowTx() not available \u2014 Suilend requires different collateral/borrow assets. Deferred to Phase 10."
2154
+ );
2155
+ }
2156
+ async buildRepayTx(_address, _amount, _asset) {
2157
+ throw new T2000Error(
2158
+ "ASSET_NOT_SUPPORTED",
2159
+ "SuilendAdapter.buildRepayTx() not available \u2014 deferred to Phase 10."
2160
+ );
2161
+ }
2162
+ async maxWithdraw(address, _asset) {
2163
+ this.ensureInit();
2164
+ const health = await this.getHealth(address);
2165
+ let maxAmount;
2166
+ if (health.borrowed === 0) {
2167
+ maxAmount = health.supplied;
2168
+ } else {
2169
+ maxAmount = Math.max(
2170
+ 0,
2171
+ health.supplied - health.borrowed * MIN_HEALTH_FACTOR2 / health.liquidationThreshold
2172
+ );
2173
+ }
2174
+ const remainingSupply = health.supplied - maxAmount;
2175
+ const hfAfter = health.borrowed > 0 ? remainingSupply * health.liquidationThreshold / health.borrowed : Infinity;
2176
+ return {
2177
+ maxAmount,
2178
+ healthFactorAfter: hfAfter,
2179
+ currentHF: health.healthFactor
2180
+ };
2181
+ }
2182
+ async maxBorrow(_address, _asset) {
2183
+ throw new T2000Error(
2184
+ "ASSET_NOT_SUPPORTED",
2185
+ "SuilendAdapter.maxBorrow() not available \u2014 deferred to Phase 10."
2186
+ );
2187
+ }
2188
+ async fetchAllCoins(owner, coinType) {
2189
+ const all = [];
2190
+ let cursor = null;
2191
+ let hasNext = true;
2192
+ while (hasNext) {
2193
+ const page = await this.client.getCoins({ owner, coinType, cursor: cursor ?? void 0 });
2194
+ all.push(...page.data.map((c) => ({ coinObjectId: c.coinObjectId, balance: c.balance })));
2195
+ cursor = page.nextCursor;
2196
+ hasNext = page.hasNextPage;
2197
+ }
2198
+ return all;
2199
+ }
2200
+ };
1652
2201
 
1653
- export { BPS_DENOMINATOR, CLOCK_ID, DEFAULT_NETWORK, MIST_PER_SUI, SENTINEL, SUI_DECIMALS, SUPPORTED_ASSETS, T2000, T2000Error, USDC_DECIMALS, addCollectFeeToTx, calculateFee, executeAutoTopUp, executeWithGas, exportPrivateKey, formatSui, formatUsd, generateKeypair, getAddress, getGasStatus, getPoolPrice, getRates, getSentinelInfo, getSwapQuote, keypairFromPrivateKey, listSentinels, loadKey, mapMoveAbortCode, mapWalletError, mistToSui, rawToUsdc, requestAttack, saveKey, attack as sentinelAttack, settleAttack, shouldAutoTopUp, simulateTransaction, solveHashcash, submitPrompt, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, walletExists };
2202
+ export { BPS_DENOMINATOR, CLOCK_ID, CetusAdapter, DEFAULT_NETWORK, MIST_PER_SUI, NaviAdapter, ProtocolRegistry, SENTINEL, SUI_DECIMALS, SUPPORTED_ASSETS, SuilendAdapter, T2000, T2000Error, USDC_DECIMALS, addCollectFeeToTx, calculateFee, executeAutoTopUp, executeWithGas, exportPrivateKey, formatSui, formatUsd, generateKeypair, getAddress, getGasStatus, getPoolPrice, getRates, getSentinelInfo, getSwapQuote, keypairFromPrivateKey, listSentinels, loadKey, mapMoveAbortCode, mapWalletError, mistToSui, rawToUsdc, requestAttack, saveKey, attack as sentinelAttack, settleAttack, shouldAutoTopUp, simulateTransaction, solveHashcash, submitPrompt, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, walletExists };
1654
2203
  //# sourceMappingURL=index.js.map
1655
2204
  //# sourceMappingURL=index.js.map