@t2000/sdk 0.19.24 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,15 +2,9 @@
2
2
 
3
3
  var transactions = require('@mysten/sui/transactions');
4
4
  var lending = require('@naviprotocol/lending');
5
- var aggregatorSdk = require('@cetusprotocol/aggregator-sdk');
6
- var utils = require('@mysten/sui/utils');
7
- var client = require('@suilend/sdk/client');
8
- var initialize = require('@suilend/sdk/lib/initialize');
9
- var types = require('@suilend/sdk/lib/types');
10
5
 
11
6
  // src/constants.ts
12
7
  var SAVE_FEE_BPS = 10n;
13
- var SWAP_FEE_BPS = 0n;
14
8
  var BORROW_FEE_BPS = 5n;
15
9
  var SUPPORTED_ASSETS = {
16
10
  USDC: {
@@ -42,39 +36,13 @@ var SUPPORTED_ASSETS = {
42
36
  decimals: 9,
43
37
  symbol: "SUI",
44
38
  displayName: "SUI"
45
- },
46
- BTC: {
47
- type: "0x0041f9f9344cac094454cd574e333c4fdb132d7bcc9379bcd4aab485b2a63942::wbtc::WBTC",
48
- decimals: 8,
49
- symbol: "BTC",
50
- displayName: "Bitcoin"
51
- },
52
- ETH: {
53
- type: "0xd0e89b2af5e4910726fbcd8b8dd37bb79b29e5f83f7491bca830e94f7f226d29::eth::ETH",
54
- decimals: 8,
55
- symbol: "ETH",
56
- displayName: "Ethereum"
57
- },
58
- GOLD: {
59
- type: "0x9d297676e7a4b771ab023291377b2adfaa4938fb9080b8d12430e4b108b836a9::xaum::XAUM",
60
- decimals: 9,
61
- symbol: "GOLD",
62
- displayName: "Gold"
63
39
  }
64
40
  };
65
- var STABLE_ASSETS = ["USDC", "USDT", "USDe", "USDsui"];
41
+ var STABLE_ASSETS = ["USDC"];
66
42
  var T2000_PACKAGE_ID = process.env.T2000_PACKAGE_ID ?? "0xab92e9f1fe549ad3d6a52924a73181b45791e76120b975138fac9ec9b75db9f3";
67
43
  var T2000_CONFIG_ID = process.env.T2000_CONFIG_ID ?? "0x408add9aa9322f93cfd87523d8f603006eb8713894f4c460283c58a6888dae8a";
68
44
  var T2000_TREASURY_ID = process.env.T2000_TREASURY_ID ?? "0x3bb501b8300125dca59019247941a42af6b292a150ce3cfcce9449456be2ec91";
69
45
  process.env.T2000_API_URL ?? "https://api.t2000.ai";
70
- var CETUS_USDC_SUI_POOL = "0x51e883ba7c0b566a26cbc8a94cd33eb0abd418a77cc1e60ad22fd9b1f29cd2ab";
71
- var CETUS_PACKAGE = "0x1eabed72c53feb3805120a081dc15963c204dc8d091542592abaf7a35689b2fb";
72
- var INVESTMENT_ASSETS = {
73
- SUI: SUPPORTED_ASSETS.SUI,
74
- BTC: SUPPORTED_ASSETS.BTC,
75
- ETH: SUPPORTED_ASSETS.ETH,
76
- GOLD: SUPPORTED_ASSETS.GOLD
77
- };
78
46
 
79
47
  // src/errors.ts
80
48
  var T2000Error = class extends Error {
@@ -101,13 +69,9 @@ var T2000Error = class extends Error {
101
69
  // src/adapters/registry.ts
102
70
  var ProtocolRegistry = class {
103
71
  lending = /* @__PURE__ */ new Map();
104
- swap = /* @__PURE__ */ new Map();
105
72
  registerLending(adapter) {
106
73
  this.lending.set(adapter.id, adapter);
107
74
  }
108
- registerSwap(adapter) {
109
- this.swap.set(adapter.id, adapter);
110
- }
111
75
  async bestSaveRate(asset) {
112
76
  const candidates = [];
113
77
  for (const adapter of this.lending.values()) {
@@ -143,23 +107,6 @@ var ProtocolRegistry = class {
143
107
  candidates.sort((a, b) => a.rate.borrowApy - b.rate.borrowApy);
144
108
  return candidates[0];
145
109
  }
146
- async bestSwapQuote(from, to, amount) {
147
- const candidates = [];
148
- for (const adapter of this.swap.values()) {
149
- const pairs = adapter.getSupportedPairs();
150
- if (!pairs.some((p) => p.from === from && p.to === to)) continue;
151
- try {
152
- const quote = await adapter.getQuote(from, to, amount);
153
- candidates.push({ adapter, quote });
154
- } catch {
155
- }
156
- }
157
- if (candidates.length === 0) {
158
- throw new T2000Error("ASSET_NOT_SUPPORTED", `No swap adapter supports ${from} \u2192 ${to}`);
159
- }
160
- candidates.sort((a, b) => b.quote.expectedOutput - a.quote.expectedOutput);
161
- return candidates[0];
162
- }
163
110
  async bestSaveRateAcrossAssets() {
164
111
  const candidates = [];
165
112
  for (const asset of STABLE_ASSETS) {
@@ -181,9 +128,8 @@ var ProtocolRegistry = class {
181
128
  }
182
129
  async allRatesAcrossAssets() {
183
130
  const results = [];
184
- const allAssets = [...STABLE_ASSETS, ...Object.keys(INVESTMENT_ASSETS)];
185
131
  const seen = /* @__PURE__ */ new Set();
186
- for (const asset of allAssets) {
132
+ for (const asset of STABLE_ASSETS) {
187
133
  if (seen.has(asset)) continue;
188
134
  seen.add(asset);
189
135
  for (const adapter of this.lending.values()) {
@@ -232,15 +178,9 @@ var ProtocolRegistry = class {
232
178
  getLending(id) {
233
179
  return this.lending.get(id);
234
180
  }
235
- getSwap(id) {
236
- return this.swap.get(id);
237
- }
238
181
  listLending() {
239
182
  return [...this.lending.values()];
240
183
  }
241
- listSwap() {
242
- return [...this.swap.values()];
243
- }
244
184
  };
245
185
 
246
186
  // src/utils/format.ts
@@ -261,12 +201,10 @@ function normalizeAsset(input) {
261
201
  // src/protocols/protocolFee.ts
262
202
  var FEE_RATES = {
263
203
  save: SAVE_FEE_BPS,
264
- swap: SWAP_FEE_BPS,
265
204
  borrow: BORROW_FEE_BPS
266
205
  };
267
206
  var OP_CODES = {
268
207
  save: 0,
269
- swap: 1,
270
208
  borrow: 2
271
209
  };
272
210
  function addCollectFeeToTx(tx, paymentCoin, operation) {
@@ -691,7 +629,6 @@ async function addClaimRewardsToTx(tx, client, address) {
691
629
  }
692
630
 
693
631
  // src/adapters/descriptors.ts
694
- var SUILEND_PACKAGE = "0xf95b06141ed4a174f239417323bde3f209b972f5930d8521ea38a52aff3a6ddf";
695
632
  var naviDescriptor = {
696
633
  id: "navi",
697
634
  name: "NAVI Protocol",
@@ -708,39 +645,8 @@ var naviDescriptor = {
708
645
  "incentive_v3::repay": "repay"
709
646
  }
710
647
  };
711
- var suilendDescriptor = {
712
- id: "suilend",
713
- name: "Suilend",
714
- packages: [SUILEND_PACKAGE],
715
- actionMap: {
716
- "lending_market::deposit_liquidity_and_mint_ctokens": "save",
717
- "lending_market::deposit_ctokens_into_obligation": "save",
718
- "lending_market::create_obligation": "save",
719
- "lending_market::withdraw_ctokens": "withdraw",
720
- "lending_market::redeem_ctokens_and_withdraw_liquidity": "withdraw",
721
- "lending_market::redeem_ctokens_and_withdraw_liquidity_request": "withdraw",
722
- "lending_market::fulfill_liquidity_request": "withdraw",
723
- "lending_market::unstake_sui_from_staker": "withdraw",
724
- "lending_market::borrow": "borrow",
725
- "lending_market::repay": "repay"
726
- }
727
- };
728
- var cetusDescriptor = {
729
- id: "cetus",
730
- name: "Cetus DEX",
731
- packages: [CETUS_PACKAGE],
732
- actionMap: {
733
- "router::swap": "swap",
734
- "router::swap_ab_bc": "swap",
735
- "router::swap_ab_cb": "swap",
736
- "router::swap_ba_bc": "swap",
737
- "router::swap_ba_cb": "swap"
738
- }
739
- };
740
648
  var allDescriptors = [
741
- naviDescriptor,
742
- suilendDescriptor,
743
- cetusDescriptor
649
+ naviDescriptor
744
650
  ];
745
651
 
746
652
  // src/adapters/navi.ts
@@ -824,658 +730,10 @@ var NaviAdapter = class {
824
730
  return addClaimRewardsToTx(tx, this.client, address);
825
731
  }
826
732
  };
827
- var DEFAULT_SLIPPAGE_BPS = 300;
828
- function createAggregatorClient(client, signer) {
829
- return new aggregatorSdk.AggregatorClient({
830
- client,
831
- signer,
832
- env: aggregatorSdk.Env.Mainnet
833
- });
834
- }
835
- async function buildSwapTx(params) {
836
- const { client, address, fromAsset, toAsset, amount, maxSlippageBps = DEFAULT_SLIPPAGE_BPS } = params;
837
- const fromInfo = SUPPORTED_ASSETS[fromAsset];
838
- const toInfo = SUPPORTED_ASSETS[toAsset];
839
- if (!fromInfo || !toInfo) {
840
- throw new T2000Error("ASSET_NOT_SUPPORTED", `Swap pair ${fromAsset}/${toAsset} is not supported`);
841
- }
842
- const rawAmount = BigInt(Math.floor(amount * 10 ** fromInfo.decimals));
843
- const aggClient = createAggregatorClient(client, address);
844
- const _origLog = console.log;
845
- console.log = () => {
846
- };
847
- let result;
848
- try {
849
- result = await aggClient.findRouters({
850
- from: fromInfo.type,
851
- target: toInfo.type,
852
- amount: rawAmount,
853
- byAmountIn: true
854
- });
855
- } finally {
856
- console.log = _origLog;
857
- }
858
- if (!result || result.insufficientLiquidity) {
859
- throw new T2000Error(
860
- "ASSET_NOT_SUPPORTED",
861
- `No swap route found for ${fromAsset} \u2192 ${toAsset}`
862
- );
863
- }
864
- const tx = new transactions.Transaction();
865
- const slippage = maxSlippageBps / 1e4;
866
- console.log = () => {
867
- };
868
- try {
869
- await aggClient.fastRouterSwap({
870
- router: result,
871
- txb: tx,
872
- slippage
873
- });
874
- } finally {
875
- console.log = _origLog;
876
- }
877
- const estimatedOut = Number(result.amountOut.toString());
878
- return {
879
- tx,
880
- estimatedOut,
881
- toDecimals: toInfo.decimals
882
- };
883
- }
884
- async function addSwapToTx(params) {
885
- const { tx, client, address, inputCoin, fromAsset, toAsset, amount, maxSlippageBps = DEFAULT_SLIPPAGE_BPS } = params;
886
- const fromInfo = SUPPORTED_ASSETS[fromAsset];
887
- const toInfo = SUPPORTED_ASSETS[toAsset];
888
- if (!fromInfo || !toInfo) {
889
- throw new T2000Error("ASSET_NOT_SUPPORTED", `Swap pair ${fromAsset}/${toAsset} is not supported`);
890
- }
891
- const rawAmount = BigInt(Math.floor(amount * 10 ** fromInfo.decimals));
892
- const aggClient = createAggregatorClient(client, address);
893
- const _origLog = console.log;
894
- console.log = () => {
895
- };
896
- let result;
897
- try {
898
- result = await aggClient.findRouters({
899
- from: fromInfo.type,
900
- target: toInfo.type,
901
- amount: rawAmount,
902
- byAmountIn: true
903
- });
904
- } finally {
905
- console.log = _origLog;
906
- }
907
- if (!result || result.insufficientLiquidity) {
908
- throw new T2000Error(
909
- "ASSET_NOT_SUPPORTED",
910
- `No swap route found for ${fromAsset} \u2192 ${toAsset}`
911
- );
912
- }
913
- const slippage = maxSlippageBps / 1e4;
914
- console.log = () => {
915
- };
916
- let outputCoin;
917
- try {
918
- outputCoin = await aggClient.routerSwap({
919
- router: result,
920
- txb: tx,
921
- inputCoin,
922
- slippage
923
- });
924
- } finally {
925
- console.log = _origLog;
926
- }
927
- const estimatedOut = Number(result.amountOut.toString());
928
- return {
929
- outputCoin,
930
- estimatedOut,
931
- toDecimals: toInfo.decimals
932
- };
933
- }
934
- async function getPoolPrice(client) {
935
- try {
936
- const pool = await client.getObject({
937
- id: CETUS_USDC_SUI_POOL,
938
- options: { showContent: true }
939
- });
940
- if (pool.data?.content?.dataType === "moveObject") {
941
- const fields = pool.data.content.fields;
942
- const currentSqrtPrice = BigInt(String(fields.current_sqrt_price ?? "0"));
943
- if (currentSqrtPrice > 0n) {
944
- const Q64 = 2n ** 64n;
945
- const sqrtPriceFloat = Number(currentSqrtPrice) / Number(Q64);
946
- const rawPrice = sqrtPriceFloat * sqrtPriceFloat;
947
- const suiPriceUsd = 1e3 / rawPrice;
948
- if (suiPriceUsd > 0.01 && suiPriceUsd < 1e3) return suiPriceUsd;
949
- }
950
- }
951
- } catch {
952
- }
953
- return 3.5;
954
- }
955
- async function getSwapQuote(client, fromAsset, toAsset, amount) {
956
- const fromInfo = SUPPORTED_ASSETS[fromAsset];
957
- const toInfo = SUPPORTED_ASSETS[toAsset];
958
- if (!fromInfo || !toInfo) {
959
- throw new T2000Error("ASSET_NOT_SUPPORTED", `Swap pair ${fromAsset}/${toAsset} is not supported`);
960
- }
961
- const rawAmount = BigInt(Math.floor(amount * 10 ** fromInfo.decimals));
962
- const poolPrice = await getPoolPrice(client);
963
- try {
964
- const aggClient = createAggregatorClient(client);
965
- const result = await aggClient.findRouters({
966
- from: fromInfo.type,
967
- target: toInfo.type,
968
- amount: rawAmount,
969
- byAmountIn: true
970
- });
971
- if (!result || result.insufficientLiquidity) {
972
- return fallbackQuote(fromAsset, amount, poolPrice);
973
- }
974
- const expectedOutput = Number(result.amountOut.toString()) / 10 ** toInfo.decimals;
975
- const priceImpact = result.deviationRatio ?? 0;
976
- return { expectedOutput, priceImpact, poolPrice };
977
- } catch {
978
- return fallbackQuote(fromAsset, amount, poolPrice);
979
- }
980
- }
981
- function fallbackQuote(fromAsset, amount, poolPrice) {
982
- const expectedOutput = fromAsset === "USDC" ? amount / poolPrice : amount * poolPrice;
983
- return { expectedOutput, priceImpact: 0, poolPrice };
984
- }
985
-
986
- // src/adapters/cetus.ts
987
- var CetusAdapter = class {
988
- id = "cetus";
989
- name = "Cetus";
990
- version = "1.0.0";
991
- capabilities = ["swap"];
992
- client;
993
- async init(client) {
994
- this.client = client;
995
- }
996
- initSync(client) {
997
- this.client = client;
998
- }
999
- async getQuote(from, to, amount) {
1000
- return getSwapQuote(this.client, from, to, amount);
1001
- }
1002
- async buildSwapTx(address, from, to, amount, maxSlippageBps) {
1003
- const result = await buildSwapTx({
1004
- client: this.client,
1005
- address,
1006
- fromAsset: from,
1007
- toAsset: to,
1008
- amount,
1009
- maxSlippageBps
1010
- });
1011
- return {
1012
- tx: result.tx,
1013
- estimatedOut: result.estimatedOut,
1014
- toDecimals: result.toDecimals
1015
- };
1016
- }
1017
- getSupportedPairs() {
1018
- const pairs = [];
1019
- for (const asset of Object.keys(INVESTMENT_ASSETS)) {
1020
- pairs.push({ from: "USDC", to: asset }, { from: asset, to: "USDC" });
1021
- }
1022
- for (const a of STABLE_ASSETS) {
1023
- for (const b of STABLE_ASSETS) {
1024
- if (a !== b) pairs.push({ from: a, to: b });
1025
- }
1026
- }
1027
- return pairs;
1028
- }
1029
- async getPoolPrice() {
1030
- return getPoolPrice(this.client);
1031
- }
1032
- async addSwapToTx(tx, address, inputCoin, from, to, amount, maxSlippageBps) {
1033
- return addSwapToTx({
1034
- tx,
1035
- client: this.client,
1036
- address,
1037
- inputCoin,
1038
- fromAsset: from,
1039
- toAsset: to,
1040
- amount,
1041
- maxSlippageBps
1042
- });
1043
- }
1044
- };
1045
- var MIN_HEALTH_FACTOR2 = 1.5;
1046
- async function quietSuilend(fn) {
1047
- const origLog = console.log;
1048
- const origWarn = console.warn;
1049
- const filter = (...args) => typeof args[0] === "string" && (args[0].includes("PythEndpoint") || args[0].includes("PythConnection"));
1050
- console.log = (...args) => {
1051
- if (!filter(...args)) origLog.apply(console, args);
1052
- };
1053
- console.warn = (...args) => {
1054
- if (!filter(...args)) origWarn.apply(console, args);
1055
- };
1056
- return fn().finally(() => {
1057
- console.log = origLog;
1058
- console.warn = origWarn;
1059
- });
1060
- }
1061
- var SuilendAdapter = class {
1062
- id = "suilend";
1063
- name = "Suilend";
1064
- version = "3.0.0";
1065
- capabilities = ["save", "withdraw", "borrow", "repay"];
1066
- supportedAssets = [...STABLE_ASSETS, "SUI", "ETH", "BTC", "GOLD"];
1067
- supportsSameAssetBorrow = false;
1068
- client;
1069
- sdkClient = null;
1070
- async init(client) {
1071
- this.client = client;
1072
- }
1073
- initSync(client) {
1074
- this.client = client;
1075
- }
1076
- async getSdkClient() {
1077
- if (!this.sdkClient) {
1078
- this.sdkClient = await client.SuilendClient.initialize(
1079
- client.LENDING_MARKET_ID,
1080
- client.LENDING_MARKET_TYPE,
1081
- this.client,
1082
- false
1083
- );
1084
- }
1085
- return this.sdkClient;
1086
- }
1087
- resolveSymbol(coinType) {
1088
- try {
1089
- const normalized = utils.normalizeStructTag(coinType);
1090
- for (const [key, info] of Object.entries(SUPPORTED_ASSETS)) {
1091
- try {
1092
- if (utils.normalizeStructTag(info.type) === normalized) return key;
1093
- } catch {
1094
- }
1095
- }
1096
- } catch {
1097
- }
1098
- const parts = coinType.split("::");
1099
- return parts[parts.length - 1] || "UNKNOWN";
1100
- }
1101
- async getRates(asset) {
1102
- try {
1103
- const sdk = await this.getSdkClient();
1104
- const { reserveMap } = await quietSuilend(() => initialize.initializeSuilend(this.client, sdk));
1105
- const assetInfo = SUPPORTED_ASSETS[asset];
1106
- if (!assetInfo) throw new T2000Error("ASSET_NOT_SUPPORTED", `Suilend does not support ${asset}`);
1107
- const normalized = utils.normalizeStructTag(assetInfo.type);
1108
- const reserve = Object.values(reserveMap).find((r) => {
1109
- try {
1110
- return utils.normalizeStructTag(r.coinType) === normalized;
1111
- } catch {
1112
- return false;
1113
- }
1114
- });
1115
- if (!reserve) throw new T2000Error("ASSET_NOT_SUPPORTED", `Suilend does not support ${asset}`);
1116
- return {
1117
- asset,
1118
- saveApy: reserve.depositAprPercent.toNumber(),
1119
- borrowApy: reserve.borrowAprPercent.toNumber()
1120
- };
1121
- } catch (err) {
1122
- if (err instanceof T2000Error) throw err;
1123
- const msg = err instanceof Error ? err.message : String(err);
1124
- throw new T2000Error("PROTOCOL_UNAVAILABLE", `Suilend getRates failed: ${msg}`);
1125
- }
1126
- }
1127
- async getPositions(address) {
1128
- const supplies = [];
1129
- const borrows = [];
1130
- try {
1131
- const sdk = await this.getSdkClient();
1132
- const { reserveMap, refreshedRawReserves } = await quietSuilend(() => initialize.initializeSuilend(this.client, sdk));
1133
- const { obligations, obligationOwnerCaps } = await initialize.initializeObligations(
1134
- this.client,
1135
- sdk,
1136
- refreshedRawReserves,
1137
- reserveMap,
1138
- address
1139
- );
1140
- if (obligationOwnerCaps.length === 0 || obligations.length === 0) {
1141
- return { supplies, borrows };
1142
- }
1143
- const obligation = obligations[0];
1144
- for (const dep of obligation.deposits) {
1145
- const symbol = this.resolveSymbol(dep.coinType);
1146
- const amount = dep.depositedAmount.toNumber();
1147
- const amountUsd = dep.depositedAmountUsd.toNumber();
1148
- const apy = dep.reserve.depositAprPercent.toNumber();
1149
- if (amountUsd > 0.01) {
1150
- supplies.push({ asset: symbol, amount, amountUsd, apy });
1151
- }
1152
- }
1153
- for (const bor of obligation.borrows) {
1154
- const symbol = this.resolveSymbol(bor.coinType);
1155
- const amount = bor.borrowedAmount.toNumber();
1156
- const amountUsd = bor.borrowedAmountUsd.toNumber();
1157
- const apy = bor.reserve.borrowAprPercent.toNumber();
1158
- if (amountUsd > 0.01) {
1159
- borrows.push({ asset: symbol, amount, amountUsd, apy });
1160
- }
1161
- }
1162
- } catch (err) {
1163
- if (err instanceof T2000Error) throw err;
1164
- const msg = err instanceof Error ? err.message : String(err);
1165
- throw new T2000Error("PROTOCOL_UNAVAILABLE", `Suilend getPositions failed: ${msg}`);
1166
- }
1167
- return { supplies, borrows };
1168
- }
1169
- async getHealth(address) {
1170
- try {
1171
- const sdk = await this.getSdkClient();
1172
- const { reserveMap, refreshedRawReserves } = await quietSuilend(() => initialize.initializeSuilend(this.client, sdk));
1173
- const { obligations, obligationOwnerCaps } = await initialize.initializeObligations(
1174
- this.client,
1175
- sdk,
1176
- refreshedRawReserves,
1177
- reserveMap,
1178
- address
1179
- );
1180
- if (obligationOwnerCaps.length === 0 || obligations.length === 0) {
1181
- return { healthFactor: Infinity, supplied: 0, borrowed: 0, maxBorrow: 0, liquidationThreshold: 0 };
1182
- }
1183
- const ob = obligations[0];
1184
- const supplied = ob.depositedAmountUsd.toNumber();
1185
- const borrowed = ob.borrowedAmountUsd.toNumber();
1186
- const borrowLimit = ob.borrowLimitUsd.toNumber();
1187
- const unhealthy = ob.unhealthyBorrowValueUsd.toNumber();
1188
- const liqThreshold = supplied > 0 ? unhealthy / supplied : 0.75;
1189
- const healthFactor = borrowed > 0 ? unhealthy / borrowed : Infinity;
1190
- const maxBorrow = Math.max(0, borrowLimit - borrowed);
1191
- return { healthFactor, supplied, borrowed, maxBorrow, liquidationThreshold: liqThreshold };
1192
- } catch {
1193
- return { healthFactor: Infinity, supplied: 0, borrowed: 0, maxBorrow: 0, liquidationThreshold: 0 };
1194
- }
1195
- }
1196
- async buildSaveTx(address, amount, asset, options) {
1197
- const assetKey = asset in SUPPORTED_ASSETS ? asset : "USDC";
1198
- const assetInfo = SUPPORTED_ASSETS[assetKey];
1199
- const sdk = await this.getSdkClient();
1200
- const caps = await client.SuilendClient.getObligationOwnerCaps(address, [client.LENDING_MARKET_TYPE], this.client);
1201
- const tx = new transactions.Transaction();
1202
- tx.setSender(address);
1203
- const rawValue = stableToRaw(amount, assetInfo.decimals).toString();
1204
- if (caps.length > 0) {
1205
- if (options?.collectFee) {
1206
- const allCoins = await this.fetchAllCoins(address, assetInfo.type);
1207
- if (allCoins.length === 0) throw new T2000Error("INSUFFICIENT_BALANCE", `No ${assetInfo.displayName} coins found`);
1208
- const primaryCoinId = allCoins[0].coinObjectId;
1209
- if (allCoins.length > 1) {
1210
- tx.mergeCoins(tx.object(primaryCoinId), allCoins.slice(1).map((c) => tx.object(c.coinObjectId)));
1211
- }
1212
- const [depositCoin] = tx.splitCoins(tx.object(primaryCoinId), [rawValue]);
1213
- addCollectFeeToTx(tx, depositCoin, "save");
1214
- }
1215
- await sdk.depositIntoObligation(address, assetInfo.type, rawValue, tx, caps[0].id);
1216
- } else {
1217
- const newCap = sdk.createObligation(tx);
1218
- let depositCoin;
1219
- if (assetKey === "SUI") {
1220
- [depositCoin] = tx.splitCoins(tx.gas, [rawValue]);
1221
- } else {
1222
- const allCoins = await this.fetchAllCoins(address, assetInfo.type);
1223
- if (allCoins.length === 0) throw new T2000Error("INSUFFICIENT_BALANCE", `No ${assetInfo.displayName} coins found`);
1224
- const primaryCoin = tx.object(allCoins[0].coinObjectId);
1225
- if (allCoins.length > 1) {
1226
- tx.mergeCoins(primaryCoin, allCoins.slice(1).map((c) => tx.object(c.coinObjectId)));
1227
- }
1228
- [depositCoin] = tx.splitCoins(primaryCoin, [rawValue]);
1229
- }
1230
- if (options?.collectFee) {
1231
- addCollectFeeToTx(tx, depositCoin, "save");
1232
- }
1233
- sdk.deposit(depositCoin, assetInfo.type, newCap, tx);
1234
- tx.transferObjects([newCap], address);
1235
- }
1236
- return { tx };
1237
- }
1238
- async buildWithdrawTx(address, amount, asset) {
1239
- const assetKey = asset in SUPPORTED_ASSETS ? asset : "USDC";
1240
- const assetInfo = SUPPORTED_ASSETS[assetKey];
1241
- const sdk = await this.getSdkClient();
1242
- const caps = await client.SuilendClient.getObligationOwnerCaps(address, [client.LENDING_MARKET_TYPE], this.client);
1243
- if (caps.length === 0) throw new T2000Error("NO_COLLATERAL", "No Suilend position found");
1244
- const { ctokenRaw, effectiveAmount, obligationIndex } = await this.resolveWithdrawCTokens(sdk, address, assetKey, assetInfo, amount);
1245
- const cap = caps[obligationIndex] ?? caps[0];
1246
- const tx = new transactions.Transaction();
1247
- tx.setSender(address);
1248
- await sdk.withdrawAndSendToUser(address, cap.id, cap.obligationId, assetInfo.type, ctokenRaw, tx);
1249
- return { tx, effectiveAmount };
1250
- }
1251
- async addWithdrawToTx(tx, address, amount, asset) {
1252
- const assetKey = asset in SUPPORTED_ASSETS ? asset : "USDC";
1253
- const assetInfo = SUPPORTED_ASSETS[assetKey];
1254
- const sdk = await this.getSdkClient();
1255
- const caps = await client.SuilendClient.getObligationOwnerCaps(address, [client.LENDING_MARKET_TYPE], this.client);
1256
- if (caps.length === 0) throw new T2000Error("NO_COLLATERAL", "No Suilend position found");
1257
- const { ctokenRaw, effectiveAmount, obligationIndex } = await this.resolveWithdrawCTokens(sdk, address, assetKey, assetInfo, amount);
1258
- const cap = caps[obligationIndex] ?? caps[0];
1259
- const coin = await sdk.withdraw(cap.id, cap.obligationId, assetInfo.type, ctokenRaw, tx);
1260
- return { coin, effectiveAmount };
1261
- }
1262
- async addSaveToTx(tx, address, coin, asset, options) {
1263
- const assetKey = asset in SUPPORTED_ASSETS ? asset : "USDC";
1264
- const assetInfo = SUPPORTED_ASSETS[assetKey];
1265
- const sdk = await this.getSdkClient();
1266
- const caps = await client.SuilendClient.getObligationOwnerCaps(address, [client.LENDING_MARKET_TYPE], this.client);
1267
- let capRef;
1268
- if (caps.length === 0) {
1269
- const newCap = sdk.createObligation(tx);
1270
- capRef = newCap;
1271
- tx.transferObjects([newCap], address);
1272
- } else {
1273
- capRef = caps[0].id;
1274
- }
1275
- if (options?.collectFee) {
1276
- addCollectFeeToTx(tx, coin, "save");
1277
- }
1278
- sdk.deposit(coin, assetInfo.type, capRef, tx);
1279
- }
1280
- async buildBorrowTx(address, amount, asset, options) {
1281
- const assetKey = asset in SUPPORTED_ASSETS ? asset : "USDC";
1282
- const assetInfo = SUPPORTED_ASSETS[assetKey];
1283
- const sdk = await this.getSdkClient();
1284
- const caps = await client.SuilendClient.getObligationOwnerCaps(address, [client.LENDING_MARKET_TYPE], this.client);
1285
- if (caps.length === 0) throw new T2000Error("NO_COLLATERAL", "No Suilend position found. Deposit collateral first with: t2000 save <amount>");
1286
- const rawValue = stableToRaw(amount, assetInfo.decimals).toString();
1287
- const tx = new transactions.Transaction();
1288
- tx.setSender(address);
1289
- if (options?.collectFee) {
1290
- const coin = await sdk.borrow(caps[0].id, caps[0].obligationId, assetInfo.type, rawValue, tx);
1291
- addCollectFeeToTx(tx, coin, "borrow");
1292
- tx.transferObjects([coin], address);
1293
- } else {
1294
- await sdk.borrowAndSendToUser(address, caps[0].id, caps[0].obligationId, assetInfo.type, rawValue, tx);
1295
- }
1296
- return { tx };
1297
- }
1298
- async buildRepayTx(address, amount, asset) {
1299
- const assetKey = asset in SUPPORTED_ASSETS ? asset : "USDC";
1300
- const assetInfo = SUPPORTED_ASSETS[assetKey];
1301
- const sdk = await this.getSdkClient();
1302
- const caps = await client.SuilendClient.getObligationOwnerCaps(address, [client.LENDING_MARKET_TYPE], this.client);
1303
- if (caps.length === 0) throw new T2000Error("NO_COLLATERAL", "No Suilend obligation found");
1304
- const rawValue = stableToRaw(amount, assetInfo.decimals).toString();
1305
- const tx = new transactions.Transaction();
1306
- tx.setSender(address);
1307
- await sdk.repayIntoObligation(address, caps[0].obligationId, assetInfo.type, rawValue, tx);
1308
- return { tx };
1309
- }
1310
- async addRepayToTx(tx, address, coin, asset) {
1311
- const assetKey = asset in SUPPORTED_ASSETS ? asset : "USDC";
1312
- const assetInfo = SUPPORTED_ASSETS[assetKey];
1313
- const sdk = await this.getSdkClient();
1314
- const caps = await client.SuilendClient.getObligationOwnerCaps(address, [client.LENDING_MARKET_TYPE], this.client);
1315
- if (caps.length === 0) throw new T2000Error("NO_COLLATERAL", "No Suilend obligation found");
1316
- sdk.repay(caps[0].obligationId, assetInfo.type, coin, tx);
1317
- }
1318
- async resolveWithdrawCTokens(sdk, address, assetKey, assetInfo, amount) {
1319
- const { reserveMap, refreshedRawReserves } = await quietSuilend(() => initialize.initializeSuilend(this.client, sdk));
1320
- const { obligations } = await initialize.initializeObligations(
1321
- this.client,
1322
- sdk,
1323
- refreshedRawReserves,
1324
- reserveMap,
1325
- address
1326
- );
1327
- if (obligations.length === 0) throw new T2000Error("NO_COLLATERAL", `Nothing to withdraw for ${assetInfo.displayName} on Suilend \u2014 no obligations found`);
1328
- let dep;
1329
- let matchedObligation = 0;
1330
- for (let oi = 0; oi < obligations.length; oi++) {
1331
- dep = obligations[oi].deposits.find((d) => {
1332
- const resolved = this.resolveSymbol(d.coinType);
1333
- if (resolved === assetKey) return true;
1334
- try {
1335
- return utils.normalizeStructTag(d.coinType) === utils.normalizeStructTag(assetInfo.type);
1336
- } catch {
1337
- return false;
1338
- }
1339
- });
1340
- if (dep && dep.depositedAmount.toNumber() > 1e-10) {
1341
- matchedObligation = oi;
1342
- break;
1343
- }
1344
- dep = void 0;
1345
- }
1346
- if (!dep) {
1347
- const foundDeposits = obligations.flatMap(
1348
- (o, i) => o.deposits.map((d) => `ob${i}:${this.resolveSymbol(d.coinType)}=${d.depositedAmount.toFixed(8)}`)
1349
- );
1350
- throw new T2000Error(
1351
- "NO_COLLATERAL",
1352
- `Nothing to withdraw for ${assetInfo.displayName} (${assetKey}) on Suilend. Found deposits: [${foundDeposits.join(", ")}]`
1353
- );
1354
- }
1355
- const deposited = dep.depositedAmount.toNumber();
1356
- const effectiveAmount = Math.min(amount, deposited);
1357
- const proportion = effectiveAmount / deposited;
1358
- const ctokenRaw = proportion >= 0.999 ? dep.depositedCtokenAmount.toFixed(0) : dep.depositedCtokenAmount.times(proportion).integerValue(1).toFixed(0);
1359
- return { ctokenRaw, effectiveAmount, obligationIndex: matchedObligation };
1360
- }
1361
- async maxWithdraw(address, _asset) {
1362
- const health = await this.getHealth(address);
1363
- let maxAmount;
1364
- if (health.borrowed === 0) {
1365
- maxAmount = health.supplied;
1366
- } else {
1367
- maxAmount = Math.max(0, health.supplied - health.borrowed * MIN_HEALTH_FACTOR2 / health.liquidationThreshold);
1368
- }
1369
- const remainingSupply = health.supplied - maxAmount;
1370
- const hfAfter = health.borrowed > 0 ? remainingSupply * health.liquidationThreshold / health.borrowed : Infinity;
1371
- return { maxAmount, healthFactorAfter: hfAfter, currentHF: health.healthFactor };
1372
- }
1373
- async maxBorrow(address, _asset) {
1374
- const health = await this.getHealth(address);
1375
- return { maxAmount: health.maxBorrow, healthFactorAfter: MIN_HEALTH_FACTOR2, currentHF: health.healthFactor };
1376
- }
1377
- async fetchAllCoins(owner, coinType) {
1378
- const all = [];
1379
- let cursor = null;
1380
- let hasNext = true;
1381
- while (hasNext) {
1382
- const page = await this.client.getCoins({ owner, coinType, cursor: cursor ?? void 0 });
1383
- all.push(...page.data.map((c) => ({ coinObjectId: c.coinObjectId, balance: c.balance })));
1384
- cursor = page.nextCursor;
1385
- hasNext = page.hasNextPage;
1386
- }
1387
- return all;
1388
- }
1389
- async getPendingRewards(address) {
1390
- try {
1391
- const sdk = await this.getSdkClient();
1392
- const { reserveMap, refreshedRawReserves } = await quietSuilend(() => initialize.initializeSuilend(this.client, sdk));
1393
- const { obligations, obligationOwnerCaps } = await initialize.initializeObligations(
1394
- this.client,
1395
- sdk,
1396
- refreshedRawReserves,
1397
- reserveMap,
1398
- address
1399
- );
1400
- if (obligationOwnerCaps.length === 0 || obligations.length === 0) return [];
1401
- const ob = obligations[0];
1402
- const rewards = [];
1403
- const WAD = 1e18;
1404
- for (const dep of ob.deposits) {
1405
- const urm = dep.userRewardManager;
1406
- for (const rw of dep.reserve.depositsPoolRewardManager?.poolRewards ?? []) {
1407
- if (rw.endTimeMs <= Date.now()) continue;
1408
- let claimableAmount = 0;
1409
- const userReward = urm?.rewards?.[rw.rewardIndex];
1410
- if (userReward?.earnedRewards) {
1411
- claimableAmount = Number(BigInt(userReward.earnedRewards.value.toString())) / WAD / 10 ** rw.mintDecimals;
1412
- }
1413
- const symbol = rw.symbol || rw.coinType.split("::").pop() || "UNKNOWN";
1414
- rewards.push({
1415
- protocol: "suilend",
1416
- asset: this.resolveSymbol(dep.coinType),
1417
- coinType: rw.coinType,
1418
- symbol,
1419
- amount: claimableAmount,
1420
- estimatedValueUsd: 0
1421
- });
1422
- }
1423
- }
1424
- return rewards;
1425
- } catch {
1426
- return [];
1427
- }
1428
- }
1429
- async addClaimRewardsToTx(tx, address) {
1430
- try {
1431
- const sdk = await this.getSdkClient();
1432
- const caps = await client.SuilendClient.getObligationOwnerCaps(address, [client.LENDING_MARKET_TYPE], this.client);
1433
- if (caps.length === 0) return [];
1434
- const { reserveMap, refreshedRawReserves } = await quietSuilend(() => initialize.initializeSuilend(this.client, sdk));
1435
- const { obligations } = await initialize.initializeObligations(
1436
- this.client,
1437
- sdk,
1438
- refreshedRawReserves,
1439
- reserveMap,
1440
- address
1441
- );
1442
- if (obligations.length === 0) return [];
1443
- const ob = obligations[0];
1444
- const claimRewards = [];
1445
- for (const dep of ob.deposits) {
1446
- for (const rw of dep.reserve.depositsPoolRewardManager.poolRewards) {
1447
- if (rw.endTimeMs <= Date.now()) continue;
1448
- claimRewards.push({
1449
- reserveArrayIndex: dep.reserveArrayIndex,
1450
- rewardIndex: BigInt(rw.rewardIndex),
1451
- rewardCoinType: rw.coinType,
1452
- side: types.Side.DEPOSIT
1453
- });
1454
- }
1455
- }
1456
- if (claimRewards.length === 0) return [];
1457
- sdk.claimRewardsAndSendToUser(address, caps[0].id, claimRewards, tx);
1458
- return claimRewards.map((r) => ({
1459
- protocol: "suilend",
1460
- asset: "",
1461
- coinType: r.rewardCoinType,
1462
- symbol: r.rewardCoinType.split("::").pop() ?? "UNKNOWN",
1463
- amount: 0,
1464
- estimatedValueUsd: 0
1465
- }));
1466
- } catch {
1467
- return [];
1468
- }
1469
- }
1470
- };
1471
733
 
1472
- exports.CetusAdapter = CetusAdapter;
1473
734
  exports.NaviAdapter = NaviAdapter;
1474
735
  exports.ProtocolRegistry = ProtocolRegistry;
1475
- exports.SuilendAdapter = SuilendAdapter;
1476
736
  exports.allDescriptors = allDescriptors;
1477
- exports.cetusDescriptor = cetusDescriptor;
1478
737
  exports.naviDescriptor = naviDescriptor;
1479
- exports.suilendDescriptor = suilendDescriptor;
1480
738
  //# sourceMappingURL=index.cjs.map
1481
739
  //# sourceMappingURL=index.cjs.map