@suilend/sdk 3.0.8 → 5.0.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.
Files changed (73) hide show
  1. package/_generated/_dependencies/source/0x1/ascii/structs.d.ts +4 -4
  2. package/_generated/_dependencies/source/0x1/ascii/structs.js +42 -41
  3. package/_generated/_dependencies/source/0x1/option/structs.js +22 -26
  4. package/_generated/_dependencies/source/0x1/type-name/structs.d.ts +3 -3
  5. package/_generated/_dependencies/source/0x1/type-name/structs.js +21 -25
  6. package/_generated/_dependencies/source/0x2/bag/structs.d.ts +5 -5
  7. package/_generated/_dependencies/source/0x2/bag/structs.js +22 -25
  8. package/_generated/_dependencies/source/0x2/balance/structs.d.ts +4 -4
  9. package/_generated/_dependencies/source/0x2/balance/structs.js +42 -41
  10. package/_generated/_dependencies/source/0x2/object/structs.d.ts +5 -5
  11. package/_generated/_dependencies/source/0x2/object/structs.js +42 -41
  12. package/_generated/_dependencies/source/0x2/object-table/structs.d.ts +5 -5
  13. package/_generated/_dependencies/source/0x2/object-table/structs.js +22 -25
  14. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/i64/structs.d.ts +3 -3
  15. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/i64/structs.js +22 -25
  16. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price/structs.d.ts +9 -9
  17. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price/structs.js +24 -25
  18. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-feed/structs.d.ts +21 -21
  19. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-feed/structs.js +23 -25
  20. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs.d.ts +2 -2
  21. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs.js +21 -25
  22. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-info/structs.d.ts +52 -52
  23. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-info/structs.js +45 -41
  24. package/_generated/_framework/vector.js +9 -6
  25. package/_generated/suilend/cell/structs.d.ts +1 -1
  26. package/_generated/suilend/cell/structs.js +21 -25
  27. package/_generated/suilend/decimal/structs.d.ts +2 -2
  28. package/_generated/suilend/decimal/structs.js +21 -25
  29. package/_generated/suilend/lending-market/structs.d.ts +129 -129
  30. package/_generated/suilend/lending-market/structs.js +391 -265
  31. package/_generated/suilend/liquidity-mining/structs.d.ts +44 -44
  32. package/_generated/suilend/liquidity-mining/structs.js +122 -89
  33. package/_generated/suilend/obligation/structs.d.ts +98 -98
  34. package/_generated/suilend/obligation/structs.js +194 -121
  35. package/_generated/suilend/rate-limiter/structs.d.ts +12 -12
  36. package/_generated/suilend/rate-limiter/structs.js +46 -41
  37. package/_generated/suilend/reserve/structs.d.ts +128 -128
  38. package/_generated/suilend/reserve/structs.js +243 -153
  39. package/_generated/suilend/reserve-config/structs.d.ts +30 -30
  40. package/_generated/suilend/reserve-config/structs.js +60 -41
  41. package/api/events.js +5 -14
  42. package/client.js +267 -319
  43. package/lib/initialize.d.ts +85 -3
  44. package/lib/initialize.js +119 -52
  45. package/lib/liquidityMining.js +15 -19
  46. package/lib/pyth.d.ts +16 -0
  47. package/lib/pyth.js +30 -15
  48. package/lib/pythAdapter.js +96 -112
  49. package/lib/strategyOwnerCap.js +8 -17
  50. package/margin/margin/admin_cap.d.ts +2 -2
  51. package/margin/margin/deps/std/type_name.d.ts +1 -1
  52. package/margin/margin/deps/suilend/lending_market.d.ts +2 -2
  53. package/margin/margin/market.d.ts +11 -11
  54. package/margin/margin/market.js +22 -22
  55. package/margin/margin/permissions.js +14 -14
  56. package/margin/margin/position.d.ts +47 -47
  57. package/margin/margin/position.js +10 -10
  58. package/margin/margin/router.d.ts +5 -5
  59. package/margin/margin/router.js +16 -16
  60. package/margin/margin/version.d.ts +1 -1
  61. package/margin/utils/index.js +22 -36
  62. package/package.json +1 -1
  63. package/parsers/apiReserveAssetDataEvent.js +4 -1
  64. package/parsers/lendingMarket.js +3 -4
  65. package/parsers/reserve.js +4 -1
  66. package/strategies.js +188 -204
  67. package/swap/quote.d.ts +1 -1
  68. package/swap/quote.js +28 -39
  69. package/swap/transaction.js +17 -26
  70. package/utils/events.js +20 -32
  71. package/utils/obligation.js +111 -135
  72. package/utils/simulate.d.ts +68 -1
  73. package/utils/simulate.js +149 -31
@@ -1,5 +1,6 @@
1
1
  import { SuiClientTypes } from "@mysten/sui/client";
2
2
  import { SuiGrpcClient } from "@mysten/sui/grpc";
3
+ import { SuiPriceServiceConnection } from "@pythnetwork/pyth-sui-js";
3
4
  import BigNumber from "bignumber.js";
4
5
  import { Reserve } from "../_generated/suilend/reserve/structs";
5
6
  import { SuilendClient } from "../client";
@@ -9,7 +10,87 @@ export declare const RESERVES_CUSTOM_ORDER: Record<string, string[]>;
9
10
  export declare const NORMALIZED_MAYA_COINTYPE: string;
10
11
  export declare const NORMALIZED_mPOINTS_COINTYPE: string;
11
12
  export declare const NORMALIZED_TREATS_COINTYPE: string;
12
- export declare const initializeSuilend: (suiGrpcClient: SuiGrpcClient, suilendClient: SuilendClient, lendingMarketMetadata?: LendingMarketMetadata, fallbackPythEndpoint?: string) => Promise<{
13
+ /**
14
+ * Price reserves for `initializeSuilend`, honouring the opt-in tolerance flag.
15
+ *
16
+ * Extracted so both branches are directly testable — the difference between
17
+ * them is a throw, which is the whole reason the flag exists and is exactly
18
+ * what a regression here would silently change.
19
+ */
20
+ export declare const refreshReservesForInitialize: (reserves: Reserve<string>[], pythConnection: SuiPriceServiceConnection, tolerateMissingPriceFeeds?: boolean) => Promise<{
21
+ reserves: Reserve<string>[];
22
+ unpricedCoinTypes: string[];
23
+ }>;
24
+ /**
25
+ * Price the temporary-Pyth-feed reserves from the cached-price service, in
26
+ * place, and report which ones ended up with a FABRICATED price.
27
+ *
28
+ * These reserves never reach the Pyth refresh at all, so nothing else can
29
+ * report them. When the cached lookup fails the price falls back to a
30
+ * hardcoded 0.0001 — a placeholder chosen to be non-zero, not a market price —
31
+ * and previously that was indistinguishable from a real quote: the reserve
32
+ * carried a plausible-looking number and appeared in no gap report.
33
+ *
34
+ * Extracted for the same reason as `refreshReservesForInitialize`:
35
+ * `initializeSuilend` needs a gRPC client and a SuilendClient, so logic left
36
+ * inline in it cannot be tested, and "is this price real" is not something to
37
+ * leave unverified.
38
+ */
39
+ export declare const priceTemporaryPythFeedReserves: (reserves: Reserve<string>[], fetchPrice?: (coinType: string) => Promise<number | undefined>) => Promise<string[]>;
40
+ export declare const initializeSuilend: (suiGrpcClient: SuiGrpcClient, suilendClient: SuilendClient, lendingMarketMetadata?: LendingMarketMetadata, fallbackPythEndpoint?: string, options?: {
41
+ /**
42
+ * Price connection to use instead of the one this function would build.
43
+ *
44
+ * Without this, the connection is constructed here and never exposed, so a
45
+ * caller cannot authenticate it, point it at a different Hermes deployment,
46
+ * or give it failover — it is unreachable from outside.
47
+ *
48
+ * SUPERSEDES `fallbackPythEndpoint`, which is ignored when this is set.
49
+ * That parameter is a hint for building a connection, and there is nothing
50
+ * left to build; it engages only when a `/live` probe fails, which tests
51
+ * whether Hermes is UP, not whether it still serves your feeds (a 401 or a
52
+ * per-feed 404 never trips it). Supply failover on the connection you
53
+ * inject — a multi-endpoint connection covers strictly more than the probe
54
+ * did.
55
+ *
56
+ * YOU ALSO OWN THE TIMEOUT. The connection this function builds for itself
57
+ * uses 30s; pyth-sui-js defaults to 5s, so an injected connection
58
+ * constructed with default options gets a 6x tighter deadline than
59
+ * initializeSuilend used to guarantee. Nothing here can impose a timeout on
60
+ * an object it did not construct — set it when you build the connection.
61
+ *
62
+ * Must be a `SuiPriceServiceConnection` (a subclass is fine, and is how you
63
+ * add `getLatestPriceFeedsPartial`). Not the structural
64
+ * `PartialPriceFeedSource` shape: with `tolerateMissingPriceFeeds` off this
65
+ * connection goes to `refreshReservePrice`, which needs the class's own
66
+ * `getLatestPriceFeeds`. The lower-level `refreshReservePriceTolerant` does
67
+ * accept the structural shape, since it only ever needs the partial fetch.
68
+ */
69
+ pythConnection?: SuiPriceServiceConnection;
70
+ /**
71
+ * Let a reserve whose feed no endpoint can serve keep the price already in
72
+ * its on-chain state, instead of failing the whole market. Off by default.
73
+ *
74
+ * OPT-IN because it trades a loud failure for a quiet one, and only the
75
+ * caller knows which it wants. A UI is usually better off failing visibly
76
+ * than rendering a stale price with no indication; a liquidator watching 45
77
+ * reserves is better off pricing the 41 it can than going blind over the 4
78
+ * it cannot.
79
+ *
80
+ * REQUIRES `pythConnection` to implement `getLatestPriceFeedsPartial` —
81
+ * throws otherwise, rather than degrading to the identical hard failure
82
+ * this flag exists to avoid. The connection this function builds for itself
83
+ * is all-or-nothing, so the flag cannot be used alone.
84
+ *
85
+ * Callers that enable this MUST read `unpricedCoinTypes` and decide per
86
+ * reserve what a kept price is allowed to mean — nothing here makes that
87
+ * judgement for them. An unpriced reserve stays in `refreshedRawReserves`
88
+ * carrying its on-chain price, which may be zero, so folding that array
89
+ * into collateral or borrow-limit math without excluding those coin types
90
+ * prices them at literal zero.
91
+ */
92
+ tolerateMissingPriceFeeds?: boolean;
93
+ }) => Promise<{
13
94
  lendingMarket: {
14
95
  version: bigint;
15
96
  reserves: {
@@ -148,6 +229,7 @@ export declare const initializeSuilend: (suiGrpcClient: SuiGrpcClient, suilendCl
148
229
  };
149
230
  coinMetadataMap: Record<string, SuiClientTypes.CoinMetadata>;
150
231
  refreshedRawReserves: Reserve<string>[];
232
+ unpricedCoinTypes: string[];
151
233
  reserveMap: Record<string, {
152
234
  config: {
153
235
  $typeName: string;
@@ -257,10 +339,10 @@ export declare const initializeSuilend: (suiGrpcClient: SuiGrpcClient, suilendCl
257
339
  totalDeposits: BigNumber;
258
340
  }>;
259
341
  reserveCoinTypes: string[];
260
- reserveCoinMetadataMap: Record<string, import("@suilend/sui-fe").Token>;
342
+ reserveCoinMetadataMap: Record<string, import("@suilend/sui-core").Token>;
261
343
  rewardCoinTypes: string[];
262
344
  activeRewardCoinTypes: string[];
263
- rewardCoinMetadataMap: Record<string, import("@suilend/sui-fe").Token>;
345
+ rewardCoinMetadataMap: Record<string, import("@suilend/sui-core").Token>;
264
346
  }>;
265
347
  export declare const initializeSuilendRewards: (reserveMap: Record<string, ParsedReserve>, activeRewardCoinTypes: string[]) => Promise<{
266
348
  rewardPriceMap: Record<string, BigNumber | undefined>;
package/lib/initialize.js CHANGED
@@ -1,21 +1,11 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { normalizeStructTag } from "@mysten/sui/utils";
11
- import { SuiPriceServiceConnection } from "@pythnetwork/pyth-sui-js";
12
2
  import BigNumber from "bignumber.js";
13
- import { NORMALIZED_ALKIMI_COINTYPE, NORMALIZED_AUSD_COINTYPE, NORMALIZED_BLUE_COINTYPE, NORMALIZED_BUCK_COINTYPE, NORMALIZED_DEEP_COINTYPE, NORMALIZED_DMC_COINTYPE, NORMALIZED_FUD_COINTYPE, NORMALIZED_HAEDAL_COINTYPE, NORMALIZED_HIPPO_COINTYPE, NORMALIZED_IKA_COINTYPE, NORMALIZED_KOBAN_COINTYPE, NORMALIZED_LBTC_COINTYPE, NORMALIZED_NS_COINTYPE, NORMALIZED_SEND_COINTYPE, NORMALIZED_SEND_POINTS_S1_COINTYPE, NORMALIZED_SEND_POINTS_S2_COINTYPE, NORMALIZED_SOL_COINTYPE, NORMALIZED_SUI_COINTYPE, NORMALIZED_UP_COINTYPE, NORMALIZED_USDB_COINTYPE, NORMALIZED_USDC_COINTYPE, NORMALIZED_USDsui_COINTYPE, NORMALIZED_WAL_COINTYPE, NORMALIZED_WBTC_COINTYPE, NORMALIZED_WETH_COINTYPE, NORMALIZED_XAUm_COINTYPE, NORMALIZED_eEARN_COINTYPE, NORMALIZED_eTHIRD_COINTYPE, NORMALIZED_flSUI_COINTYPE, NORMALIZED_fpSUI_COINTYPE, NORMALIZED_fudSUI_COINTYPE, NORMALIZED_iSUI_COINTYPE, NORMALIZED_jugSUI_COINTYPE, NORMALIZED_kSUI_COINTYPE, NORMALIZED_mSUI_COINTYPE, NORMALIZED_mUSD_COINTYPE, NORMALIZED_oshiSUI_COINTYPE, NORMALIZED_sSUI_COINTYPE, NORMALIZED_sdeUSD_COINTYPE, NORMALIZED_stratSUI_COINTYPE, NORMALIZED_suiETH_COINTYPE, NORMALIZED_suiUSDT_COINTYPE, NORMALIZED_suiUSDe_COINTYPE, NORMALIZED_suiWBTC_COINTYPE, NORMALIZED_trevinSUI_COINTYPE, NORMALIZED_upSUI_COINTYPE, NORMALIZED_wUSDC_COINTYPE, NORMALIZED_wUSDT_COINTYPE, NORMALIZED_xBTC_COINTYPE, NORMALIZED_yapSUI_COINTYPE, TEMPORARY_PYTH_PRICE_FEED_COINTYPES, getAllOwnedObjects, getCoinMetadataMap, getPrice, isSendPoints, isSteammPoints, } from "@suilend/sui-fe";
3
+ import { NORMALIZED_ALKIMI_COINTYPE, NORMALIZED_AUSD_COINTYPE, NORMALIZED_BLUE_COINTYPE, NORMALIZED_BUCK_COINTYPE, NORMALIZED_DEEP_COINTYPE, NORMALIZED_DMC_COINTYPE, NORMALIZED_FUD_COINTYPE, NORMALIZED_HAEDAL_COINTYPE, NORMALIZED_HIPPO_COINTYPE, NORMALIZED_IKA_COINTYPE, NORMALIZED_KOBAN_COINTYPE, NORMALIZED_LBTC_COINTYPE, NORMALIZED_NS_COINTYPE, NORMALIZED_SEND_COINTYPE, NORMALIZED_SEND_POINTS_S1_COINTYPE, NORMALIZED_SEND_POINTS_S2_COINTYPE, NORMALIZED_SOL_COINTYPE, NORMALIZED_SUI_COINTYPE, NORMALIZED_UP_COINTYPE, NORMALIZED_USDB_COINTYPE, NORMALIZED_USDC_COINTYPE, NORMALIZED_USDsui_COINTYPE, NORMALIZED_WAL_COINTYPE, NORMALIZED_WBTC_COINTYPE, NORMALIZED_WETH_COINTYPE, NORMALIZED_XAUm_COINTYPE, NORMALIZED_eEARN_COINTYPE, NORMALIZED_eTHIRD_COINTYPE, NORMALIZED_flSUI_COINTYPE, NORMALIZED_fpSUI_COINTYPE, NORMALIZED_fudSUI_COINTYPE, NORMALIZED_iSUI_COINTYPE, NORMALIZED_jugSUI_COINTYPE, NORMALIZED_kSUI_COINTYPE, NORMALIZED_mSUI_COINTYPE, NORMALIZED_mUSD_COINTYPE, NORMALIZED_oshiSUI_COINTYPE, NORMALIZED_sSUI_COINTYPE, NORMALIZED_sdeUSD_COINTYPE, NORMALIZED_stratSUI_COINTYPE, NORMALIZED_suiETH_COINTYPE, NORMALIZED_suiUSDT_COINTYPE, NORMALIZED_suiUSDe_COINTYPE, NORMALIZED_suiWBTC_COINTYPE, NORMALIZED_trevinSUI_COINTYPE, NORMALIZED_upSUI_COINTYPE, NORMALIZED_wUSDC_COINTYPE, NORMALIZED_wUSDT_COINTYPE, NORMALIZED_xBTC_COINTYPE, NORMALIZED_yapSUI_COINTYPE, TEMPORARY_PYTH_PRICE_FEED_COINTYPES, getAllOwnedObjects, getCoinMetadataMap, getPrice, isSendPoints, isSteammPoints, } from "@suilend/sui-core";
14
4
  import { LENDING_MARKET_ID, SuilendClient } from "../client.js";
15
5
  import { parseLendingMarket, parseObligation } from "../parsers/index.js";
16
6
  import * as simulate from "../utils/simulate.js";
17
7
  import { WAD } from "./constants.js";
18
- import { getWorkingPythEndpoint } from "./pyth.js";
8
+ import { resolvePythConnection } from "./pyth.js";
19
9
  import { STRATEGY_TYPE_INFO_MAP, STRATEGY_WRAPPER_PACKAGE_ID_V1, } from "./strategyOwnerCap.js";
20
10
  export const RESERVES_CUSTOM_ORDER = {
21
11
  [LENDING_MARKET_ID]: [
@@ -94,7 +84,79 @@ const TREATS_COINTYPE = "0x0dadb7fa2771c2952f96161fc1f0c105d1f22d53926b9ff2498a8
94
84
  export const NORMALIZED_MAYA_COINTYPE = normalizeStructTag(MAYA_COINTYPE);
95
85
  export const NORMALIZED_mPOINTS_COINTYPE = normalizeStructTag(mPOINTS_COINTYPE);
96
86
  export const NORMALIZED_TREATS_COINTYPE = normalizeStructTag(TREATS_COINTYPE);
97
- export const initializeSuilend = (suiGrpcClient, suilendClient, lendingMarketMetadata, fallbackPythEndpoint) => __awaiter(void 0, void 0, void 0, function* () {
87
+ /**
88
+ * Price reserves for `initializeSuilend`, honouring the opt-in tolerance flag.
89
+ *
90
+ * Extracted so both branches are directly testable — the difference between
91
+ * them is a throw, which is the whole reason the flag exists and is exactly
92
+ * what a regression here would silently change.
93
+ */
94
+ export const refreshReservesForInitialize = async (reserves, pythConnection, tolerateMissingPriceFeeds) => {
95
+ if (tolerateMissingPriceFeeds) {
96
+ // Tolerance is only expressible over a connection that can report WHICH
97
+ // feeds it could not serve. A stock SuiPriceServiceConnection cannot: the
98
+ // method does not exist in pyth-sui-js, so the tolerant path would fall
99
+ // back to the all-or-nothing fetch, and Hermes v2 404s the whole batch over
100
+ // one unknown id — the caller would get the same hard failure as before the
101
+ // flag existed, having explicitly asked not to.
102
+ //
103
+ // Rejected here rather than left to degrade quietly, because the pairing is
104
+ // otherwise invisible: nothing in the flag's name or type says it needs a
105
+ // connection the SDK cannot build for itself. Checked on CAPABILITY, not on
106
+ // whether a connection was injected — injecting a stock one is equally
107
+ // useless. Direct callers of refreshReservePriceTolerant keep the strict
108
+ // fallback, which is correct for an endpoint that omits unknown ids.
109
+ if (!simulate.canReportMissingPriceFeeds(pythConnection)) {
110
+ throw new Error(`tolerateMissingPriceFeeds requires a connection that can report per-feed gaps. The connection in use is all-or-nothing, so an endpoint that rejects the whole batch over one unknown feed would fail initialization regardless of this flag — ${simulate.PARTIAL_FETCH_REQUIRED_HINT}. Note that patching SuiPriceServiceConnection.prototype does NOT satisfy this: it can reroute getLatestPriceFeeds but cannot add a per-feed gap report.`);
111
+ }
112
+ return simulate.refreshReservePriceTolerant(reserves, pythConnection);
113
+ }
114
+ // Default path, behaviourally identical to what this function did before the
115
+ // flag existed: throws if any requested feed is absent from the response.
116
+ return {
117
+ reserves: await simulate.refreshReservePrice(reserves, pythConnection),
118
+ unpricedCoinTypes: [],
119
+ };
120
+ };
121
+ /**
122
+ * Price the temporary-Pyth-feed reserves from the cached-price service, in
123
+ * place, and report which ones ended up with a FABRICATED price.
124
+ *
125
+ * These reserves never reach the Pyth refresh at all, so nothing else can
126
+ * report them. When the cached lookup fails the price falls back to a
127
+ * hardcoded 0.0001 — a placeholder chosen to be non-zero, not a market price —
128
+ * and previously that was indistinguishable from a real quote: the reserve
129
+ * carried a plausible-looking number and appeared in no gap report.
130
+ *
131
+ * Extracted for the same reason as `refreshReservesForInitialize`:
132
+ * `initializeSuilend` needs a gRPC client and a SuilendClient, so logic left
133
+ * inline in it cannot be tested, and "is this price real" is not something to
134
+ * leave unverified.
135
+ */
136
+ export const priceTemporaryPythFeedReserves = async (reserves, fetchPrice = getPrice) => {
137
+ const fabricatedPriceCoinTypes = [];
138
+ await Promise.all(reserves.map(async (reserve) => {
139
+ const coinType = normalizeStructTag(reserve.coinType.name);
140
+ let cachedUsdPrice;
141
+ try {
142
+ cachedUsdPrice = await fetchPrice(coinType);
143
+ }
144
+ catch (err) {
145
+ console.error(err);
146
+ }
147
+ if (cachedUsdPrice === undefined) {
148
+ cachedUsdPrice = 0.0001; // Non-zero price override if no price
149
+ fabricatedPriceCoinTypes.push(coinType);
150
+ }
151
+ const parsedCachedUsdPrice = BigInt(+new BigNumber(cachedUsdPrice)
152
+ .times(WAD)
153
+ .integerValue(BigNumber.ROUND_DOWN));
154
+ reserve.price.value = parsedCachedUsdPrice;
155
+ reserve.smoothedPrice.value = parsedCachedUsdPrice;
156
+ }));
157
+ return fabricatedPriceCoinTypes;
158
+ };
159
+ export const initializeSuilend = async (suiGrpcClient, suilendClient, lendingMarketMetadata, fallbackPythEndpoint, options) => {
98
160
  const nowMs = Date.now();
99
161
  const nowS = Math.floor(nowMs / 1000);
100
162
  const interestCompoundedRawReserves = suilendClient.lendingMarket.reserves.map((r) => simulate.compoundReserveInterest(r, nowS));
@@ -109,35 +171,35 @@ export const initializeSuilend = (suiGrpcClient, suilendClient, lendingMarketMet
109
171
  reservesWithoutTemporaryPythPriceFeeds.push(reserve);
110
172
  }
111
173
  }
112
- // Get a working Pyth endpoint (try primary, fallback to fallbackPythEndpoint if provided)
113
- const pythEndpoint = yield getWorkingPythEndpoint(fallbackPythEndpoint);
114
- const pythConnection = new SuiPriceServiceConnection(pythEndpoint, {
115
- timeout: 30 * 1000,
116
- });
117
- const [refreshedReservesWithoutTemporaryPythPriceFeeds] = yield Promise.all([
118
- simulate.refreshReservePrice(reservesWithoutTemporaryPythPriceFeeds, pythConnection),
119
- Promise.all(reservesWithTemporaryPythPriceFeeds.map((reserve) => (() => __awaiter(void 0, void 0, void 0, function* () {
120
- let cachedUsdPrice;
121
- try {
122
- cachedUsdPrice = yield getPrice(normalizeStructTag(reserve.coinType.name));
123
- }
124
- catch (err) {
125
- console.error(err);
126
- }
127
- if (cachedUsdPrice === undefined)
128
- cachedUsdPrice = 0.0001; // Non-zero price override if no price
129
- const parsedCachedUsdPrice = BigInt(+new BigNumber(cachedUsdPrice)
130
- .times(WAD)
131
- .integerValue(BigNumber.ROUND_DOWN));
132
- reserve.price.value = parsedCachedUsdPrice;
133
- reserve.smoothedPrice.value = parsedCachedUsdPrice;
134
- }))())),
174
+ const pythConnection = await resolvePythConnection(options?.pythConnection, fallbackPythEndpoint);
175
+ const [refreshedWithoutTemporary, fabricatedPriceCoinTypes] = await Promise.all([
176
+ refreshReservesForInitialize(reservesWithoutTemporaryPythPriceFeeds, pythConnection, options?.tolerateMissingPriceFeeds),
177
+ priceTemporaryPythFeedReserves(reservesWithTemporaryPythPriceFeeds),
135
178
  ]);
136
179
  // Recombine reserves back into a single array
137
180
  const refreshedRawReserves = [
138
- ...refreshedReservesWithoutTemporaryPythPriceFeeds,
181
+ ...refreshedWithoutTemporary.reserves,
139
182
  ...reservesWithTemporaryPythPriceFeeds,
140
183
  ];
184
+ // Reserves NOT carrying a real refreshed price, from either cause: no endpoint
185
+ // served the Pyth feed (needs `tolerateMissingPriceFeeds`, else the refresh
186
+ // threw), or a temporary-feed reserve fell back to a fabricated placeholder
187
+ // (independent of that flag). Surfaced rather than logged-and-forgotten: only
188
+ // the caller knows whether a given reserve can safely hold such a price.
189
+ //
190
+ // Normalized, matching `reserveMap` / `reserveCoinTypes` keys. Entries in
191
+ // `refreshedRawReserves` carry the RAW on-chain `coinType.name`, which never
192
+ // compares equal to these (no `0x` prefix), so filtering that array means
193
+ // normalizing first:
194
+ //
195
+ // const unpriced = new Set(unpricedCoinTypes);
196
+ // refreshedRawReserves.filter(
197
+ // (r) => !unpriced.has(normalizeStructTag(r.coinType.name)),
198
+ // );
199
+ const unpricedCoinTypes = [
200
+ ...refreshedWithoutTemporary.unpricedCoinTypes,
201
+ ...fabricatedPriceCoinTypes,
202
+ ];
141
203
  const miscCoinTypes = [
142
204
  NORMALIZED_SEND_POINTS_S1_COINTYPE,
143
205
  NORMALIZED_SEND_POINTS_S2_COINTYPE,
@@ -164,12 +226,16 @@ export const initializeSuilend = (suiGrpcClient, suilendClient, lendingMarketMet
164
226
  const uniqueReserveCoinTypes = Array.from(new Set(reserveCoinTypes));
165
227
  const uniqueRewardCoinTypes = Array.from(new Set(rewardCoinTypes));
166
228
  const uniqueActiveRewardsCoinTypes = Array.from(new Set(activeRewardCoinTypes));
167
- const [miscCoinMetadataMap, reserveCoinMetadataMap, rewardCoinMetadataMap] = yield Promise.all([
229
+ const [miscCoinMetadataMap, reserveCoinMetadataMap, rewardCoinMetadataMap] = await Promise.all([
168
230
  getCoinMetadataMap(uniqueMiscCoinTypes),
169
231
  getCoinMetadataMap(uniqueReserveCoinTypes),
170
232
  getCoinMetadataMap(uniqueRewardCoinTypes),
171
233
  ]);
172
- const coinMetadataMap = Object.assign(Object.assign(Object.assign({}, miscCoinMetadataMap), reserveCoinMetadataMap), rewardCoinMetadataMap);
234
+ const coinMetadataMap = {
235
+ ...miscCoinMetadataMap,
236
+ ...reserveCoinMetadataMap,
237
+ ...rewardCoinMetadataMap,
238
+ };
173
239
  // const walReserve = refreshedRawReserves.find(
174
240
  // (r) => normalizeStructTag(r.coinType.name) === NORMALIZED_WAL_COINTYPE,
175
241
  // );
@@ -193,11 +259,12 @@ export const initializeSuilend = (suiGrpcClient, suilendClient, lendingMarketMet
193
259
  return aCustomOrderIndex > -1 ? -1 : 1;
194
260
  });
195
261
  }
196
- const reserveMap = lendingMarket.reserves.reduce((acc, reserve) => (Object.assign(Object.assign({}, acc), { [reserve.coinType]: reserve })), {});
262
+ const reserveMap = lendingMarket.reserves.reduce((acc, reserve) => ({ ...acc, [reserve.coinType]: reserve }), {});
197
263
  return {
198
264
  lendingMarket,
199
265
  coinMetadataMap,
200
266
  refreshedRawReserves,
267
+ unpricedCoinTypes,
201
268
  reserveMap,
202
269
  reserveCoinTypes: uniqueReserveCoinTypes,
203
270
  reserveCoinMetadataMap,
@@ -205,15 +272,15 @@ export const initializeSuilend = (suiGrpcClient, suilendClient, lendingMarketMet
205
272
  activeRewardCoinTypes: uniqueActiveRewardsCoinTypes,
206
273
  rewardCoinMetadataMap,
207
274
  };
208
- });
209
- export const initializeSuilendRewards = (reserveMap, activeRewardCoinTypes) => __awaiter(void 0, void 0, void 0, function* () {
210
- const rewardPriceMap = Object.entries(reserveMap).reduce((acc, [coinType, reserve]) => (Object.assign(Object.assign({}, acc), { [coinType]: reserve.price })), {});
275
+ };
276
+ export const initializeSuilendRewards = async (reserveMap, activeRewardCoinTypes) => {
277
+ const rewardPriceMap = Object.entries(reserveMap).reduce((acc, [coinType, reserve]) => ({ ...acc, [coinType]: reserve.price }), {});
211
278
  rewardPriceMap[NORMALIZED_TREATS_COINTYPE] = new BigNumber(0.1);
212
279
  const reservelessActiveRewardCoinTypes = activeRewardCoinTypes.filter((coinType) => !(isSendPoints(coinType) ||
213
280
  isSteammPoints(coinType) ||
214
281
  coinType === NORMALIZED_MAYA_COINTYPE ||
215
282
  coinType === NORMALIZED_mPOINTS_COINTYPE) && !rewardPriceMap[coinType]);
216
- const reservelessActiveRewardCachedUsdPrices = yield Promise.all(reservelessActiveRewardCoinTypes.map((coinType) => getPrice(coinType)));
283
+ const reservelessActiveRewardCachedUsdPrices = await Promise.all(reservelessActiveRewardCoinTypes.map((coinType) => getPrice(coinType)));
217
284
  for (let i = 0; i < reservelessActiveRewardCoinTypes.length; i++) {
218
285
  const cachedUsdPrice = reservelessActiveRewardCachedUsdPrices[i];
219
286
  if (cachedUsdPrice === undefined)
@@ -221,8 +288,8 @@ export const initializeSuilendRewards = (reserveMap, activeRewardCoinTypes) => _
221
288
  rewardPriceMap[reservelessActiveRewardCoinTypes[i]] = new BigNumber(cachedUsdPrice);
222
289
  }
223
290
  return { rewardPriceMap };
224
- });
225
- export const initializeObligations = (suiGrpcClient, suilendClient, refreshedRawReserves, reserveMap, address) => __awaiter(void 0, void 0, void 0, function* () {
291
+ };
292
+ export const initializeObligations = async (suiGrpcClient, suilendClient, refreshedRawReserves, reserveMap, address) => {
226
293
  if (!address)
227
294
  return {
228
295
  strategyOwnerCaps: [],
@@ -230,12 +297,12 @@ export const initializeObligations = (suiGrpcClient, suilendClient, refreshedRaw
230
297
  obligationOwnerCaps: [],
231
298
  obligations: [],
232
299
  };
233
- const [strategyOwnerCaps, obligationOwnerCaps] = yield Promise.all([
234
- (() => __awaiter(void 0, void 0, void 0, function* () {
300
+ const [strategyOwnerCaps, obligationOwnerCaps] = await Promise.all([
301
+ (async () => {
235
302
  const hasStrategies = Object.values(STRATEGY_TYPE_INFO_MAP).some((info) => info.lendingMarketId === suilendClient.lendingMarket.id);
236
303
  if (!hasStrategies)
237
304
  return [];
238
- const objects = yield getAllOwnedObjects(suiGrpcClient, address, `${STRATEGY_WRAPPER_PACKAGE_ID_V1}::strategy_wrapper::StrategyOwnerCap<${suilendClient.lendingMarket.$typeArgs[0]}>`);
305
+ const objects = await getAllOwnedObjects(suiGrpcClient, address, `${STRATEGY_WRAPPER_PACKAGE_ID_V1}::strategy_wrapper::StrategyOwnerCap<${suilendClient.lendingMarket.$typeArgs[0]}>`);
239
306
  return objects.map((obj) => {
240
307
  const json = obj.json;
241
308
  const id = json.id;
@@ -250,10 +317,10 @@ export const initializeObligations = (suiGrpcClient, suilendClient, refreshedRaw
250
317
  };
251
318
  return result;
252
319
  });
253
- }))(),
320
+ })(),
254
321
  SuilendClient.getObligationOwnerCaps(address, suilendClient.lendingMarket.$typeArgs, suiGrpcClient),
255
322
  ]);
256
- const obligations = (yield Promise.all([
323
+ const obligations = (await Promise.all([
257
324
  ...strategyOwnerCaps.map((soc) => soc.obligationId),
258
325
  ...obligationOwnerCaps.map((ownerCap) => ownerCap.obligationId),
259
326
  ].map((obligationId) => SuilendClient.getObligation(obligationId, suilendClient.lendingMarket.$typeArgs, suiGrpcClient))))
@@ -269,4 +336,4 @@ export const initializeObligations = (suiGrpcClient, suilendClient, refreshedRaw
269
336
  obligationOwnerCaps,
270
337
  obligations: nonStrategyObligations,
271
338
  };
272
- });
339
+ };
@@ -1,6 +1,6 @@
1
1
  import BigNumber from "bignumber.js";
2
2
  import cloneDeep from "lodash/cloneDeep.js";
3
- import { MS_PER_YEAR, NORMALIZED_eEARN_COINTYPE, NORMALIZED_eTHIRD_COINTYPE, NORMALIZED_sdeUSD_COINTYPE, isSendPoints, } from "@suilend/sui-fe";
3
+ import { MS_PER_YEAR, NORMALIZED_eEARN_COINTYPE, NORMALIZED_eTHIRD_COINTYPE, NORMALIZED_sdeUSD_COINTYPE, isSendPoints, } from "@suilend/sui-core";
4
4
  import { WAD } from "./constants.js";
5
5
  import { Side } from "./types.js";
6
6
  export const getDepositShare = (reserve, share) => share.div(10 ** reserve.mintDecimals).times(reserve.cTokenExchangeRate);
@@ -12,7 +12,7 @@ export const formatRewards = (parsedReserveMap, coinMetadataMap, priceMap, oblig
12
12
  const rewardMap = {};
13
13
  const getRewardSummary = (reserve, poolReward, side) => {
14
14
  const rewardCoinMetadata = coinMetadataMap[poolReward.coinType];
15
- const rewardPrice = priceMap === null || priceMap === void 0 ? void 0 : priceMap[poolReward.coinType];
15
+ const rewardPrice = priceMap?.[poolReward.coinType];
16
16
  const isActive = nowMs >= poolReward.startTimeMs && nowMs < poolReward.endTimeMs;
17
17
  const aprPercent = rewardPrice
18
18
  ? new BigNumber(reserve.depositsPoolRewardManager.totalShares.toString()).eq(0)
@@ -48,7 +48,7 @@ export const formatRewards = (parsedReserveMap, coinMetadataMap, priceMap, oblig
48
48
  perDay,
49
49
  side,
50
50
  },
51
- obligationClaims: Object.fromEntries((obligations !== null && obligations !== void 0 ? obligations : [])
51
+ obligationClaims: Object.fromEntries((obligations ?? [])
52
52
  .map((obligation) => {
53
53
  const claim = getObligationClaim(obligation, poolReward, side === Side.DEPOSIT
54
54
  ? reserve.depositsPoolRewardManager.id
@@ -96,7 +96,7 @@ const getObligationClaim = (obligation, poolReward, reservePoolManagerId, reserv
96
96
  if (!userReward)
97
97
  return;
98
98
  return {
99
- claimableAmount: (userReward === null || userReward === void 0 ? void 0 : userReward.earnedRewards)
99
+ claimableAmount: userReward?.earnedRewards
100
100
  ? new BigNumber(userReward.earnedRewards.value.toString())
101
101
  .div(WAD)
102
102
  .div(10 ** poolReward.mintDecimals)
@@ -132,26 +132,22 @@ export const getDedupedPerDayRewards = (filteredRewards) => {
132
132
  return result;
133
133
  };
134
134
  export const getRewardsAprPercent = (side, filteredRewards) => getDedupedAprRewards(filteredRewards).reduce((acc, reward) => acc.plus(reward.stats.aprPercent.times(side === Side.DEPOSIT ? 1 : -1)), new BigNumber(0));
135
- export const getStakingYieldAprPercent = (side, coinType, lstStatsMap, sdeUsdAprPercent, eThirdAprPercent, eEarnAprPercent) => {
136
- var _a;
137
- return side === Side.DEPOSIT
138
- ? coinType === NORMALIZED_sdeUSD_COINTYPE
139
- ? sdeUsdAprPercent
140
- : coinType === NORMALIZED_eTHIRD_COINTYPE
141
- ? eThirdAprPercent
142
- : coinType === NORMALIZED_eEARN_COINTYPE
143
- ? eEarnAprPercent
144
- : (_a = lstStatsMap[coinType]) === null || _a === void 0 ? void 0 : _a.aprPercent
145
- : undefined;
146
- };
135
+ export const getStakingYieldAprPercent = (side, coinType, lstStatsMap, sdeUsdAprPercent, eThirdAprPercent, eEarnAprPercent) => side === Side.DEPOSIT
136
+ ? coinType === NORMALIZED_sdeUSD_COINTYPE
137
+ ? sdeUsdAprPercent
138
+ : coinType === NORMALIZED_eTHIRD_COINTYPE
139
+ ? eThirdAprPercent
140
+ : coinType === NORMALIZED_eEARN_COINTYPE
141
+ ? eEarnAprPercent
142
+ : lstStatsMap[coinType]?.aprPercent
143
+ : undefined;
147
144
  export const getTotalAprPercent = (side, aprPercent, filteredRewards, stakingYieldAprPercent) => aprPercent
148
145
  .plus(getRewardsAprPercent(side, filteredRewards))
149
- .plus(stakingYieldAprPercent !== null && stakingYieldAprPercent !== void 0 ? stakingYieldAprPercent : 0);
146
+ .plus(stakingYieldAprPercent ?? 0);
150
147
  export const getNetAprPercent = (obligation, rewardMap, lstStatsMap, sdeUsdAprPercent, eThirdAprPercent, eEarnAprPercent, noShares) => {
151
148
  const weightedDepositedAmountUsd_aprPercent = obligation.deposits.reduce((acc, deposit) => {
152
- var _a;
153
149
  const weightedDepositedAmountUsd_baseAprPercent = deposit.reserve.depositAprPercent.times(deposit.depositedAmountUsd);
154
- const weightedDepositedAmountUsd_stakingYieldAprPercent = new BigNumber((_a = getStakingYieldAprPercent(Side.DEPOSIT, deposit.reserve.coinType, lstStatsMap, sdeUsdAprPercent, eThirdAprPercent, eEarnAprPercent)) !== null && _a !== void 0 ? _a : 0).times(deposit.depositedAmountUsd);
150
+ const weightedDepositedAmountUsd_stakingYieldAprPercent = new BigNumber(getStakingYieldAprPercent(Side.DEPOSIT, deposit.reserve.coinType, lstStatsMap, sdeUsdAprPercent, eThirdAprPercent, eEarnAprPercent) ?? 0).times(deposit.depositedAmountUsd);
155
151
  const weightedDepositedAmountUsd_rewardsAprPercent = getRewardsAprPercent(Side.DEPOSIT, getFilteredRewards(rewardMap[deposit.reserve.coinType].deposit)).times(!noShares
156
152
  ? getDepositShareUsd(deposit.reserve, new BigNumber(deposit.userRewardManager.share.toString()))
157
153
  : deposit.depositedAmountUsd);
package/lib/pyth.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { SuiPriceServiceConnection } from "@pythnetwork/pyth-sui-js";
1
2
  export declare const PRIMARY_PYTH_ENDPOINT = "https://hermes.pyth.network";
2
3
  /**
3
4
  * Tests if the primary Pyth connection endpoint is working by checking the /live endpoint
@@ -10,3 +11,18 @@ export declare const testPrimaryPythConnection: () => Promise<boolean>;
10
11
  * @returns The endpoint URL that is working, or the primary endpoint if fallback fails
11
12
  */
12
13
  export declare const getWorkingPythEndpoint: (fallbackPythEndpoint?: string) => Promise<string>;
14
+ /**
15
+ * Pick the price connection `initializeSuilend` will use.
16
+ *
17
+ * Lives here rather than in initialize.ts for two reasons: it belongs beside
18
+ * getWorkingPythEndpoint, the thing it decides whether to call; and this module
19
+ * imports nothing, so the seam is testable without resolving
20
+ * `@suilend/sui-core` (whose in-repo `exports` points at a .js file that does
21
+ * not exist, making initialize.ts unimportable from source).
22
+ *
23
+ * Skipping the probe for an injected connection is deliberate, not an
24
+ * optimisation. getWorkingPythEndpoint fetches hermes.pyth.network/live, which
25
+ * answers for whether Hermes is up — not for whether it still serves the
26
+ * caller's feeds, and not for whether the caller is even pointed at Hermes.
27
+ */
28
+ export declare const resolvePythConnection: (pythConnectionOverride?: SuiPriceServiceConnection, fallbackPythEndpoint?: string) => Promise<SuiPriceServiceConnection>;
package/lib/pyth.js CHANGED
@@ -1,20 +1,12 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
1
+ import { SuiPriceServiceConnection } from "@pythnetwork/pyth-sui-js";
10
2
  export const PRIMARY_PYTH_ENDPOINT = "https://hermes.pyth.network";
11
3
  /**
12
4
  * Tests if the primary Pyth connection endpoint is working by checking the /live endpoint
13
5
  * @returns true if the connection is working, false otherwise
14
6
  */
15
- export const testPrimaryPythConnection = () => __awaiter(void 0, void 0, void 0, function* () {
7
+ export const testPrimaryPythConnection = async () => {
16
8
  try {
17
- const res = yield fetch(`${PRIMARY_PYTH_ENDPOINT}/live`, {
9
+ const res = await fetch(`${PRIMARY_PYTH_ENDPOINT}/live`, {
18
10
  signal: AbortSignal.timeout(5 * 1000), // 5 second timeout
19
11
  });
20
12
  return res.ok;
@@ -23,15 +15,15 @@ export const testPrimaryPythConnection = () => __awaiter(void 0, void 0, void 0,
23
15
  console.warn(`[testPrimaryPythConnection] Pyth connection test failed for ${PRIMARY_PYTH_ENDPOINT}:`, err);
24
16
  return false;
25
17
  }
26
- });
18
+ };
27
19
  /**
28
20
  * Gets a working Pyth connection endpoint, trying primary first, then fallback if provided
29
21
  * @param fallbackPythEndpoint Optional fallback endpoint
30
22
  * @returns The endpoint URL that is working, or the primary endpoint if fallback fails
31
23
  */
32
- export const getWorkingPythEndpoint = (fallbackPythEndpoint) => __awaiter(void 0, void 0, void 0, function* () {
24
+ export const getWorkingPythEndpoint = async (fallbackPythEndpoint) => {
33
25
  // Test primary endpoint first
34
- const primaryWorking = yield testPrimaryPythConnection();
26
+ const primaryWorking = await testPrimaryPythConnection();
35
27
  if (primaryWorking) {
36
28
  console.log(`[getWorkingPythEndpoint] Using primary Pyth endpoint: ${PRIMARY_PYTH_ENDPOINT}`);
37
29
  return PRIMARY_PYTH_ENDPOINT;
@@ -45,4 +37,27 @@ export const getWorkingPythEndpoint = (fallbackPythEndpoint) => __awaiter(void 0
45
37
  console.error(`[getWorkingPythEndpoint] Primary Pyth endpoint failed, no fallback provided, using primary anyway`);
46
38
  return PRIMARY_PYTH_ENDPOINT;
47
39
  }
48
- });
40
+ };
41
+ /**
42
+ * Pick the price connection `initializeSuilend` will use.
43
+ *
44
+ * Lives here rather than in initialize.ts for two reasons: it belongs beside
45
+ * getWorkingPythEndpoint, the thing it decides whether to call; and this module
46
+ * imports nothing, so the seam is testable without resolving
47
+ * `@suilend/sui-core` (whose in-repo `exports` points at a .js file that does
48
+ * not exist, making initialize.ts unimportable from source).
49
+ *
50
+ * Skipping the probe for an injected connection is deliberate, not an
51
+ * optimisation. getWorkingPythEndpoint fetches hermes.pyth.network/live, which
52
+ * answers for whether Hermes is up — not for whether it still serves the
53
+ * caller's feeds, and not for whether the caller is even pointed at Hermes.
54
+ */
55
+ export const resolvePythConnection = async (pythConnectionOverride, fallbackPythEndpoint) => {
56
+ if (pythConnectionOverride)
57
+ return pythConnectionOverride;
58
+ // Get a working Pyth endpoint (try primary, fallback to fallbackPythEndpoint if provided)
59
+ const pythEndpoint = await getWorkingPythEndpoint(fallbackPythEndpoint);
60
+ return new SuiPriceServiceConnection(pythEndpoint, {
61
+ timeout: 30 * 1000,
62
+ });
63
+ };