@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.
- package/_generated/_dependencies/source/0x1/ascii/structs.d.ts +4 -4
- package/_generated/_dependencies/source/0x1/ascii/structs.js +42 -41
- package/_generated/_dependencies/source/0x1/option/structs.js +22 -26
- package/_generated/_dependencies/source/0x1/type-name/structs.d.ts +3 -3
- package/_generated/_dependencies/source/0x1/type-name/structs.js +21 -25
- package/_generated/_dependencies/source/0x2/bag/structs.d.ts +5 -5
- package/_generated/_dependencies/source/0x2/bag/structs.js +22 -25
- package/_generated/_dependencies/source/0x2/balance/structs.d.ts +4 -4
- package/_generated/_dependencies/source/0x2/balance/structs.js +42 -41
- package/_generated/_dependencies/source/0x2/object/structs.d.ts +5 -5
- package/_generated/_dependencies/source/0x2/object/structs.js +42 -41
- package/_generated/_dependencies/source/0x2/object-table/structs.d.ts +5 -5
- package/_generated/_dependencies/source/0x2/object-table/structs.js +22 -25
- package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/i64/structs.d.ts +3 -3
- package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/i64/structs.js +22 -25
- package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price/structs.d.ts +9 -9
- package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price/structs.js +24 -25
- package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-feed/structs.d.ts +21 -21
- package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-feed/structs.js +23 -25
- package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs.d.ts +2 -2
- package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs.js +21 -25
- package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-info/structs.d.ts +52 -52
- package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-info/structs.js +45 -41
- package/_generated/_framework/vector.js +9 -6
- package/_generated/suilend/cell/structs.d.ts +1 -1
- package/_generated/suilend/cell/structs.js +21 -25
- package/_generated/suilend/decimal/structs.d.ts +2 -2
- package/_generated/suilend/decimal/structs.js +21 -25
- package/_generated/suilend/lending-market/structs.d.ts +129 -129
- package/_generated/suilend/lending-market/structs.js +391 -265
- package/_generated/suilend/liquidity-mining/structs.d.ts +44 -44
- package/_generated/suilend/liquidity-mining/structs.js +122 -89
- package/_generated/suilend/obligation/structs.d.ts +98 -98
- package/_generated/suilend/obligation/structs.js +194 -121
- package/_generated/suilend/rate-limiter/structs.d.ts +12 -12
- package/_generated/suilend/rate-limiter/structs.js +46 -41
- package/_generated/suilend/reserve/structs.d.ts +128 -128
- package/_generated/suilend/reserve/structs.js +243 -153
- package/_generated/suilend/reserve-config/structs.d.ts +30 -30
- package/_generated/suilend/reserve-config/structs.js +60 -41
- package/api/events.js +5 -14
- package/client.js +267 -319
- package/lib/initialize.d.ts +85 -3
- package/lib/initialize.js +119 -52
- package/lib/liquidityMining.js +15 -19
- package/lib/pyth.d.ts +16 -0
- package/lib/pyth.js +30 -15
- package/lib/pythAdapter.js +96 -112
- package/lib/strategyOwnerCap.js +8 -17
- package/margin/margin/admin_cap.d.ts +2 -2
- package/margin/margin/deps/std/type_name.d.ts +1 -1
- package/margin/margin/deps/suilend/lending_market.d.ts +2 -2
- package/margin/margin/market.d.ts +11 -11
- package/margin/margin/market.js +22 -22
- package/margin/margin/permissions.js +14 -14
- package/margin/margin/position.d.ts +47 -47
- package/margin/margin/position.js +10 -10
- package/margin/margin/router.d.ts +5 -5
- package/margin/margin/router.js +16 -16
- package/margin/margin/version.d.ts +1 -1
- package/margin/utils/index.js +22 -36
- package/package.json +1 -1
- package/parsers/apiReserveAssetDataEvent.js +4 -1
- package/parsers/lendingMarket.js +3 -4
- package/parsers/reserve.js +4 -1
- package/strategies.js +188 -204
- package/swap/quote.d.ts +1 -1
- package/swap/quote.js +28 -39
- package/swap/transaction.js +17 -26
- package/utils/events.js +20 -32
- package/utils/obligation.js +111 -135
- package/utils/simulate.d.ts +68 -1
- package/utils/simulate.js +149 -31
package/lib/initialize.d.ts
CHANGED
|
@@ -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
|
-
|
|
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-
|
|
342
|
+
reserveCoinMetadataMap: Record<string, import("@suilend/sui-core").Token>;
|
|
261
343
|
rewardCoinTypes: string[];
|
|
262
344
|
activeRewardCoinTypes: string[];
|
|
263
|
-
rewardCoinMetadataMap: Record<string, import("@suilend/sui-
|
|
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-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
...
|
|
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] =
|
|
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 =
|
|
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) => (
|
|
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) =>
|
|
210
|
-
const rewardPriceMap = Object.entries(reserveMap).reduce((acc, [coinType, reserve]) => (
|
|
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 =
|
|
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) =>
|
|
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] =
|
|
234
|
-
(() =>
|
|
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 =
|
|
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 = (
|
|
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
|
+
};
|
package/lib/liquidityMining.js
CHANGED
|
@@ -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-
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
?
|
|
140
|
-
: coinType ===
|
|
141
|
-
?
|
|
142
|
-
: coinType
|
|
143
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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 = () =>
|
|
7
|
+
export const testPrimaryPythConnection = async () => {
|
|
16
8
|
try {
|
|
17
|
-
const res =
|
|
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) =>
|
|
24
|
+
export const getWorkingPythEndpoint = async (fallbackPythEndpoint) => {
|
|
33
25
|
// Test primary endpoint first
|
|
34
|
-
const primaryWorking =
|
|
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
|
+
};
|