@scallop-io/sui-scallop-sdk 3.0.2 → 4.1.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/dist/Logger-Cg2iFdpH.d.ts +8 -0
- package/dist/builder.cjs +22 -0
- package/dist/builder.d.cts +28 -0
- package/dist/builder.d.ts +28 -0
- package/dist/builder.js +4 -0
- package/dist/chunk-33AK5RWM.js +15 -0
- package/dist/chunk-6UQ4NWEU.js +3 -0
- package/dist/chunk-DIC6P5HB.js +11 -0
- package/dist/chunk-S34VOEZN.js +3 -0
- package/dist/chunk-VTGQACWU.js +6 -0
- package/dist/client.cjs +21 -0
- package/dist/client.d.cts +401 -0
- package/dist/client.d.ts +401 -0
- package/dist/client.js +5 -0
- package/dist/errors.cjs +10 -0
- package/dist/errors.d.cts +32 -0
- package/dist/errors.d.ts +32 -0
- package/dist/errors.js +1 -0
- package/dist/index.cjs +51 -34
- package/dist/index.d.cts +130 -3145
- package/dist/index.d.ts +130 -3145
- package/dist/index.js +8 -12
- package/dist/logger.cjs +6 -0
- package/dist/logger.d.cts +7 -0
- package/dist/logger.d.ts +7 -0
- package/dist/logger.js +1 -0
- package/dist/query-DkitepDi.d.ts +3305 -0
- package/dist/query.cjs +19 -0
- package/dist/query.d.cts +9 -0
- package/dist/query.d.ts +9 -0
- package/dist/query.js +3 -0
- package/dist/types.cjs +2 -0
- package/dist/types.d.cts +90 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.js +1 -0
- package/package.json +98 -56
- package/src/constants/api.ts +2 -0
- package/src/constants/coinType.ts +2 -0
- package/src/constants/common.ts +0 -7
- package/src/constants/index.ts +2 -0
- package/src/constants/queryKeys.ts +14 -7
- package/src/constants/testAddress.ts +1 -0
- package/src/datasources/api.ts +34 -0
- package/src/datasources/indexer.ts +8 -0
- package/src/datasources/onchain.ts +121 -0
- package/src/entries/builder.ts +5 -0
- package/src/entries/client.ts +3 -0
- package/src/entries/errors.ts +2 -0
- package/src/entries/index.ts +17 -0
- package/src/entries/logger.ts +2 -0
- package/src/entries/query.ts +6 -0
- package/src/entries/types.ts +2 -0
- package/src/errors/ScallopConfigError.ts +8 -0
- package/src/errors/ScallopError.ts +30 -0
- package/src/errors/ScallopIndexerError.ts +8 -0
- package/src/errors/ScallopParseError.ts +8 -0
- package/src/errors/ScallopRpcError.ts +8 -0
- package/src/errors/ScallopTransactionBuildError.ts +8 -0
- package/src/errors/index.ts +6 -0
- package/src/logger/Logger.ts +6 -0
- package/src/logger/consoleLogger.ts +13 -0
- package/src/logger/index.ts +3 -0
- package/src/logger/noopLogger.ts +8 -0
- package/src/mappers/index.ts +1 -0
- package/src/mappers/moveTypeMapper.ts +48 -0
- package/src/models/index.ts +8 -10
- package/src/models/interface.ts +9 -9
- package/src/models/scallop.ts +9 -16
- package/src/models/scallopAddress/const.ts +416 -0
- package/src/models/scallopAddress/index.ts +188 -0
- package/src/models/scallopAddress/types.ts +212 -0
- package/src/models/{scallopBuilder.ts → scallopBuilder/index.ts} +65 -29
- package/src/models/scallopBuilder/types.ts +37 -0
- package/src/models/{scallopClient.ts → scallopClient/index.ts} +157 -598
- package/src/models/scallopClient/types.ts +25 -0
- package/src/models/scallopConstants/config/AddressConfigSource.ts +13 -0
- package/src/models/scallopConstants/config/ConfigValidator.ts +70 -0
- package/src/models/scallopConstants/config/PoolAddressConfigSource.ts +12 -0
- package/src/models/scallopConstants/config/ScallopConfig.ts +80 -0
- package/src/models/scallopConstants/config/ScallopConfigSnapshot.ts +46 -0
- package/src/models/scallopConstants/config/WhitelistConfigSource.ts +16 -0
- package/src/models/scallopConstants/config/index.ts +6 -0
- package/src/models/scallopConstants/const.ts +19 -0
- package/src/models/scallopConstants/constantsSource.ts +98 -0
- package/src/models/scallopConstants/deriveConstants.ts +150 -0
- package/src/models/scallopConstants/index.ts +257 -0
- package/src/models/scallopConstants/loadConstantsState.ts +135 -0
- package/src/models/scallopConstants/types.ts +77 -0
- package/src/models/scallopConstants/utils.ts +69 -0
- package/src/models/scallopQuery/index.ts +1279 -0
- package/src/models/scallopQuery/types.ts +9 -0
- package/src/models/scallopQuery/utils.ts +13 -0
- package/src/models/scallopUtils/index.ts +505 -0
- package/src/models/scallopUtils/types.ts +30 -0
- package/src/models/transactionExecutor.ts +122 -0
- package/src/repositories/addressApi/helpers.ts +14 -0
- package/src/repositories/addressApi/index.ts +24 -0
- package/src/repositories/addressApi/schema.ts +0 -0
- package/src/repositories/addressApi/types.ts +177 -0
- package/src/repositories/base.ts +38 -0
- package/src/repositories/borrowIncentive/bcs.ts +11 -0
- package/src/repositories/borrowIncentive/helpers.ts +396 -0
- package/src/repositories/borrowIncentive/index.ts +52 -0
- package/src/{types/query/borrowIncentive.ts → repositories/borrowIncentive/types.ts} +69 -23
- package/src/repositories/borrowIncentive/utils.ts +234 -0
- package/src/repositories/cache.ts +4 -0
- package/src/repositories/coinBalance/helpers.ts +394 -0
- package/src/repositories/coinBalance/index.ts +69 -0
- package/src/repositories/coinBalance/types.ts +33 -0
- package/src/repositories/flashloan/const.ts +3 -0
- package/src/repositories/flashloan/helpers.ts +107 -0
- package/src/repositories/flashloan/index.ts +34 -0
- package/src/repositories/flashloan/types.ts +16 -0
- package/src/repositories/isolatedAssets/bcs.ts +4 -0
- package/src/repositories/isolatedAssets/const.ts +2 -0
- package/src/repositories/isolatedAssets/helpers.ts +91 -0
- package/src/repositories/isolatedAssets/index.ts +42 -0
- package/src/repositories/isolatedAssets/types.ts +35 -0
- package/src/repositories/loyaltyProgram/bcs.ts +7 -0
- package/src/repositories/loyaltyProgram/helpers.ts +117 -0
- package/src/repositories/loyaltyProgram/index.ts +28 -0
- package/src/repositories/loyaltyProgram/schema.ts +18 -0
- package/src/repositories/loyaltyProgram/types.ts +37 -0
- package/src/repositories/market/const.ts +6 -0
- package/src/repositories/market/helpers.ts +740 -0
- package/src/repositories/market/index.ts +137 -0
- package/src/repositories/market/mapper.ts +30 -0
- package/src/repositories/market/types.ts +443 -0
- package/src/repositories/market/utils.ts +285 -0
- package/src/repositories/obligation/helpers.ts +228 -0
- package/src/repositories/obligation/index.ts +51 -0
- package/src/repositories/obligation/types.ts +71 -0
- package/src/repositories/obligation/utils.ts +57 -0
- package/src/repositories/poolAddresses/const.ts +5 -0
- package/src/repositories/poolAddresses/helpers.ts +422 -0
- package/src/repositories/poolAddresses/index.ts +56 -0
- package/src/repositories/poolAddresses/schema.ts +34 -0
- package/src/repositories/poolAddresses/types.ts +69 -0
- package/src/repositories/price/const.ts +1 -0
- package/src/repositories/price/helpers.ts +237 -0
- package/src/repositories/price/index.ts +91 -0
- package/src/repositories/price/schema.ts +24 -0
- package/src/repositories/price/types.ts +56 -0
- package/src/repositories/price/utils.ts +21 -0
- package/src/repositories/referral/bcs.ts +3 -0
- package/src/repositories/referral/helper.ts +24 -0
- package/src/repositories/referral/index.ts +31 -0
- package/src/repositories/referral/types.ts +33 -0
- package/src/repositories/spool/const.ts +1 -0
- package/src/repositories/spool/helpers.ts +471 -0
- package/src/repositories/spool/index.ts +135 -0
- package/src/{types/query/spool.ts → repositories/spool/types.ts} +67 -2
- package/src/repositories/spool/utils.ts +215 -0
- package/src/repositories/types.ts +39 -0
- package/src/repositories/utils.ts +137 -0
- package/src/repositories/veSca/bcs.ts +6 -0
- package/src/repositories/veSca/helpers.ts +341 -0
- package/src/repositories/veSca/index.ts +57 -0
- package/src/repositories/veSca/types.ts +79 -0
- package/src/repositories/veScaLoyaltyProgram/bcs.ts +3 -0
- package/src/repositories/veScaLoyaltyProgram/helpers.ts +135 -0
- package/src/repositories/veScaLoyaltyProgram/index.ts +28 -0
- package/src/repositories/veScaLoyaltyProgram/schema.ts +15 -0
- package/src/repositories/veScaLoyaltyProgram/types.ts +47 -0
- package/src/repositories/wiring/datasources.ts +42 -0
- package/src/repositories/wiring/metadata.ts +292 -0
- package/src/repositories/wiring/registry.ts +194 -0
- package/src/repositories/wiring/source.ts +63 -0
- package/src/repositories/xOracle/bcs.ts +7 -0
- package/src/repositories/xOracle/const.ts +1 -0
- package/src/repositories/xOracle/helpers.ts +304 -0
- package/src/repositories/xOracle/index.ts +45 -0
- package/src/repositories/xOracle/types.ts +96 -0
- package/src/repositories/xOracle/utils.ts +3 -0
- package/src/services/client/BorrowService.ts +229 -0
- package/src/services/client/CollateralService.ts +76 -0
- package/src/services/client/LendingService.ts +75 -0
- package/src/services/client/ReferralService.ts +71 -0
- package/src/services/client/SpoolService.ts +170 -0
- package/src/services/client/VeScaService.ts +142 -0
- package/src/services/client/types.ts +44 -0
- package/src/services/index.ts +8 -0
- package/src/services/query/portfolioCalculations.ts +1171 -0
- package/src/txBuilders/borrowIncentive/index.ts +80 -0
- package/src/txBuilders/borrowIncentive/moveCalls.ts +124 -0
- package/src/txBuilders/borrowIncentive/quick.ts +207 -0
- package/src/txBuilders/context.ts +62 -0
- package/src/txBuilders/core/index.ts +95 -0
- package/src/txBuilders/core/moveCalls.ts +241 -0
- package/src/txBuilders/core/quick.ts +306 -0
- package/src/{builders → txBuilders}/index.ts +35 -9
- package/src/txBuilders/loyaltyProgram/index.ts +78 -0
- package/src/txBuilders/loyaltyProgram/moveCalls.ts +44 -0
- package/src/txBuilders/loyaltyProgram/quick.ts +58 -0
- package/src/txBuilders/manifest.ts +160 -0
- package/src/txBuilders/modules.ts +70 -0
- package/src/{builders → txBuilders}/oracles/index.ts +3 -2
- package/src/{builders → txBuilders}/oracles/pyth.ts +4 -3
- package/src/txBuilders/referral/index.ts +72 -0
- package/src/txBuilders/referral/moveCalls.ts +93 -0
- package/src/txBuilders/referral/quick.ts +57 -0
- package/src/txBuilders/sCoin/index.ts +72 -0
- package/src/txBuilders/sCoin/moveCalls.ts +36 -0
- package/src/txBuilders/sCoin/quick.ts +34 -0
- package/src/txBuilders/spool/index.ts +82 -0
- package/src/txBuilders/spool/moveCalls.ts +79 -0
- package/src/{builders/spoolBuilder.ts → txBuilders/spool/quick.ts} +22 -162
- package/src/txBuilders/utils.ts +36 -0
- package/src/txBuilders/verify.ts +45 -0
- package/src/txBuilders/vesca/index.ts +75 -0
- package/src/txBuilders/vesca/moveCalls.ts +147 -0
- package/src/{builders/vescaBuilder.ts → txBuilders/vesca/quick.ts} +85 -274
- package/src/types/address.ts +4 -178
- package/src/types/builder/borrowIncentive.ts +4 -3
- package/src/types/builder/core.ts +6 -33
- package/src/types/builder/index.ts +5 -1
- package/src/types/builder/loyaltyProgram.ts +4 -3
- package/src/types/builder/modules.ts +49 -0
- package/src/types/builder/referral.ts +5 -4
- package/src/types/builder/sCoin.ts +20 -2
- package/src/types/builder/spool.ts +22 -2
- package/src/types/builder/vesca.ts +4 -12
- package/src/types/constant/index.ts +3 -1
- package/src/types/constant/queryKeys.ts +15 -8
- package/src/types/index.ts +5 -6
- package/src/types/internal/dto.ts +46 -0
- package/src/types/internal/index.ts +2 -0
- package/src/types/internal/move.ts +13 -0
- package/src/types/public/index.ts +21 -0
- package/src/types/query/core.ts +0 -317
- package/src/types/query/index.ts +3 -4
- package/src/types/query/portfolio.ts +2 -15
- package/src/types/repositories/borrowIncentive.ts +12 -0
- package/src/types/repositories/index.ts +9 -0
- package/src/types/repositories/loyaltyProgram.ts +2 -0
- package/src/types/repositories/market.ts +13 -0
- package/src/types/repositories/spool.ts +21 -0
- package/src/types/repositories/veSca.ts +5 -0
- package/src/types/repositories/veScaLoyaltyProgram.ts +2 -0
- package/src/types/sui.ts +0 -1
- package/src/utils/array.ts +7 -0
- package/src/utils/cache.ts +17 -0
- package/src/utils/object.ts +30 -12
- package/src/utils/query.ts +9 -190
- package/src/utils/vesca.ts +0 -8
- package/src/builders/borrowIncentiveBuilder.ts +0 -372
- package/src/builders/coreBuilder.ts +0 -636
- package/src/builders/loyaltyProgramBuilder.ts +0 -142
- package/src/builders/referralBuilder.ts +0 -192
- package/src/builders/sCoinBuilder.ts +0 -124
- package/src/index.ts +0 -13
- package/src/models/scallopAddress.ts +0 -841
- package/src/models/scallopAxios.ts +0 -185
- package/src/models/scallopConstants.ts +0 -380
- package/src/models/scallopIndexer.ts +0 -226
- package/src/models/scallopQuery.ts +0 -974
- package/src/models/scallopQueryClient.ts +0 -29
- package/src/models/scallopSuiKit.ts +0 -480
- package/src/models/scallopUtils.ts +0 -734
- package/src/queries/borrowIncentiveQuery.ts +0 -361
- package/src/queries/borrowLimitQuery.ts +0 -33
- package/src/queries/coreQuery.ts +0 -1173
- package/src/queries/flashloanFeeQuery.ts +0 -92
- package/src/queries/index.ts +0 -15
- package/src/queries/isolatedAssetQuery.ts +0 -103
- package/src/queries/loyaltyProgramQuery.ts +0 -178
- package/src/queries/ownerQuery.ts +0 -32
- package/src/queries/poolAddressesQuery.ts +0 -340
- package/src/queries/portfolioQuery.ts +0 -1149
- package/src/queries/priceQuery.ts +0 -37
- package/src/queries/referralQuery.ts +0 -42
- package/src/queries/sCoinQuery.ts +0 -195
- package/src/queries/spoolQuery.ts +0 -542
- package/src/queries/supplyLimitQuery.ts +0 -32
- package/src/queries/switchboardQuery.ts +0 -65
- package/src/queries/vescaQuery.ts +0 -312
- package/src/queries/xOracleQuery.ts +0 -149
- package/src/types/constant/common.ts +0 -55
- package/src/types/query/loyaltyProgram.ts +0 -12
- package/src/types/query/vesca.ts +0 -27
- package/src/utils/index.ts +0 -7
- package/src/utils/indexer.ts +0 -47
- /package/src/{models → datasources}/rateLimiter.ts +0 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { SuiClientTypes } from '@mysten/sui/client';
|
|
2
|
+
import {
|
|
3
|
+
PriceApiContext,
|
|
4
|
+
PriceIndexerContext,
|
|
5
|
+
PriceOnChainContext,
|
|
6
|
+
} from './types.js';
|
|
7
|
+
import { queryKeys } from 'src/constants/queryKeys.js';
|
|
8
|
+
import { PriceFeedObjectSchema } from './schema.js';
|
|
9
|
+
import { calculatePrice } from './utils.js';
|
|
10
|
+
import { logError, type OnChainReadContext } from '../utils.js';
|
|
11
|
+
import {
|
|
12
|
+
ScallopIndexerError,
|
|
13
|
+
ScallopParseError,
|
|
14
|
+
ScallopRpcError,
|
|
15
|
+
} from 'src/errors/index.js';
|
|
16
|
+
import { SuiPriceServiceConnection } from '@pythnetwork/pyth-sui-js';
|
|
17
|
+
import type { SuiObjectData } from 'src/types/index.js';
|
|
18
|
+
import { MarketCollateral, MarketPool } from '../market/types.js';
|
|
19
|
+
import { partitionArray } from 'src/utils/array.js';
|
|
20
|
+
|
|
21
|
+
export const getPythPricesFromApi = async (
|
|
22
|
+
ctx: PriceApiContext,
|
|
23
|
+
coinNames: string[]
|
|
24
|
+
) => {
|
|
25
|
+
const {
|
|
26
|
+
fetchWithCache,
|
|
27
|
+
metadata: { addresses },
|
|
28
|
+
pythPriceServiceConfig: { endpoint, config },
|
|
29
|
+
} = ctx;
|
|
30
|
+
|
|
31
|
+
// Multiple coins can share the same feed — dedupe before hitting the API.
|
|
32
|
+
// A coin without a configured pyth feed maps to `undefined` and defaults to 0.
|
|
33
|
+
const feedIdByCoin = new Map<string, string | undefined>(
|
|
34
|
+
coinNames.map((coinName) => [
|
|
35
|
+
coinName,
|
|
36
|
+
addresses.coins[coinName]?.oracle?.pyth?.feed,
|
|
37
|
+
])
|
|
38
|
+
);
|
|
39
|
+
const priceFeedIds = Array.from(new Set(feedIdByCoin.values())).filter(
|
|
40
|
+
(feedId): feedId is string => !!feedId
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const client = new SuiPriceServiceConnection(endpoint, config);
|
|
44
|
+
const feeds = await client.getLatestPriceFeeds(priceFeedIds);
|
|
45
|
+
if (!feeds) {
|
|
46
|
+
throw logError(
|
|
47
|
+
ctx.logger,
|
|
48
|
+
new ScallopIndexerError('Failed to fetch price feeds from Pyth API', {
|
|
49
|
+
context: { endpoint, priceFeedIds },
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return await fetchWithCache({
|
|
55
|
+
queryKey: queryKeys.oracle.getPythLatestPriceFeeds(endpoint, priceFeedIds),
|
|
56
|
+
queryFn: () => {
|
|
57
|
+
// Resolve each unique feed once, then fan back out to every coin.
|
|
58
|
+
const priceByFeedId = new Map<string, number>();
|
|
59
|
+
for (const feed of feeds) {
|
|
60
|
+
priceByFeedId.set(
|
|
61
|
+
feed.id,
|
|
62
|
+
feed.getPriceUnchecked().getPriceAsNumberUnchecked()
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// A coin with no configured feed, or a feed the API didn't return,
|
|
67
|
+
// defaults to a price of 0 rather than throwing.
|
|
68
|
+
const prices: Record<string, number> = {};
|
|
69
|
+
for (const [coinName, feedId] of feedIdByCoin) {
|
|
70
|
+
prices[coinName] =
|
|
71
|
+
(feedId !== undefined ? priceByFeedId.get(feedId) : undefined) ?? 0;
|
|
72
|
+
}
|
|
73
|
+
return prices;
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Fetch a single raw Pyth price-feed object by id (cached). Returns `null` when
|
|
80
|
+
* the object is absent. Parsing is left to the caller — this is the raw-object
|
|
81
|
+
* read the legacy `ScallopUtils.getPythPrice` did via `queryGetObject`.
|
|
82
|
+
*/
|
|
83
|
+
export const getPythFeedObjectFromOnChain = async (
|
|
84
|
+
ctx: OnChainReadContext,
|
|
85
|
+
feedObjectId: string
|
|
86
|
+
): Promise<SuiObjectData | null> => {
|
|
87
|
+
const { onchain, fetchWithCache } = ctx;
|
|
88
|
+
const options: SuiClientTypes.GetObjectOptions<{ json: true }> = {
|
|
89
|
+
objectId: feedObjectId,
|
|
90
|
+
include: { json: true },
|
|
91
|
+
};
|
|
92
|
+
const { object } = await fetchWithCache({
|
|
93
|
+
queryKey: queryKeys.rpc.getObject({ ...options, node: onchain.url }),
|
|
94
|
+
queryFn: () => onchain.getObject(options),
|
|
95
|
+
});
|
|
96
|
+
return object ?? null;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Batch-fetch raw Pyth price-feed objects in ONE getObjects call (cached). The
|
|
101
|
+
* caller keys results by `objectId`, so per-object failures are dropped rather
|
|
102
|
+
* than positioned — matching the legacy `queryGetObjects`, which returned only
|
|
103
|
+
* the successfully-fetched objects.
|
|
104
|
+
*/
|
|
105
|
+
export const getPythFeedObjectsFromOnChain = async (
|
|
106
|
+
ctx: OnChainReadContext,
|
|
107
|
+
feedObjectIds: string[]
|
|
108
|
+
): Promise<SuiObjectData[]> => {
|
|
109
|
+
if (feedObjectIds.length === 0) return [];
|
|
110
|
+
const { onchain, fetchWithCache } = ctx;
|
|
111
|
+
const options: SuiClientTypes.GetObjectsOptions<{ json: true }> = {
|
|
112
|
+
objectIds: feedObjectIds,
|
|
113
|
+
include: { json: true },
|
|
114
|
+
};
|
|
115
|
+
const { objects } = await fetchWithCache({
|
|
116
|
+
queryKey: queryKeys.rpc.getObjects({ ...options, node: onchain.url }),
|
|
117
|
+
queryFn: () => onchain.client.getObjects(options),
|
|
118
|
+
});
|
|
119
|
+
return objects.filter((o): o is SuiObjectData => !(o instanceof Error));
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const getPythPricesFromOnChain = async (
|
|
123
|
+
ctx: PriceOnChainContext,
|
|
124
|
+
coinNames: string[]
|
|
125
|
+
) => {
|
|
126
|
+
const {
|
|
127
|
+
onchain,
|
|
128
|
+
fetchWithCache,
|
|
129
|
+
metadata: { addresses },
|
|
130
|
+
} = ctx;
|
|
131
|
+
|
|
132
|
+
// Multiple coins can share the same feed object — dedupe before fetching.
|
|
133
|
+
// A coin without a configured pyth feed object maps to an empty/undefined
|
|
134
|
+
// value and defaults to 0 (filtered out of the fetch below).
|
|
135
|
+
const feedObjectByCoin = new Map<string, string | undefined>(
|
|
136
|
+
coinNames.map((coinName) => [
|
|
137
|
+
coinName,
|
|
138
|
+
addresses.coins[coinName]?.oracle?.pyth?.feedObject,
|
|
139
|
+
])
|
|
140
|
+
);
|
|
141
|
+
const feedObjectIds = Array.from(new Set(feedObjectByCoin.values())).filter(
|
|
142
|
+
(feedObject): feedObject is string => !!feedObject
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// Parse each unique feed object once, keyed by its object id. getObjects
|
|
146
|
+
// supports at most 50 ids per call — batch in chunks of 50.
|
|
147
|
+
const priceByFeedObject = new Map<string, number>();
|
|
148
|
+
for (const batch of partitionArray(feedObjectIds, 50)) {
|
|
149
|
+
const fetchOptions: SuiClientTypes.GetObjectsOptions<{ json: true }> = {
|
|
150
|
+
objectIds: batch,
|
|
151
|
+
include: {
|
|
152
|
+
json: true,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const { objects: priceFeedObjects } = await fetchWithCache({
|
|
157
|
+
queryKey: queryKeys.rpc.getObjects({
|
|
158
|
+
...fetchOptions,
|
|
159
|
+
node: onchain.url,
|
|
160
|
+
}),
|
|
161
|
+
queryFn: () => onchain.client.getObjects(fetchOptions),
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
for (const priceFeedObject of priceFeedObjects) {
|
|
165
|
+
if (priceFeedObject instanceof Error) {
|
|
166
|
+
throw logError(
|
|
167
|
+
ctx.logger,
|
|
168
|
+
new ScallopRpcError('Failed to fetch price feed object on chain', {
|
|
169
|
+
cause: priceFeedObject,
|
|
170
|
+
})
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const { data, success } = PriceFeedObjectSchema.safeParse(
|
|
175
|
+
priceFeedObject.json
|
|
176
|
+
);
|
|
177
|
+
if (!success) {
|
|
178
|
+
throw logError(
|
|
179
|
+
ctx.logger,
|
|
180
|
+
new ScallopParseError('Failed to parse price feed object', {
|
|
181
|
+
context: {
|
|
182
|
+
objectId: priceFeedObject.objectId,
|
|
183
|
+
json: JSON.stringify(priceFeedObject.json),
|
|
184
|
+
},
|
|
185
|
+
})
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const { price_feed } = data.price_info;
|
|
190
|
+
priceByFeedObject.set(
|
|
191
|
+
priceFeedObject.objectId,
|
|
192
|
+
calculatePrice(price_feed.price).toNumber()
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Fan the unique feed-object prices back out to every requested coin.
|
|
198
|
+
// A coin with no feed object, or one missing on chain, defaults to 0.
|
|
199
|
+
const prices: Record<string, number> = {};
|
|
200
|
+
for (const [coinName, feedObject] of feedObjectByCoin) {
|
|
201
|
+
prices[coinName] =
|
|
202
|
+
(feedObject !== undefined
|
|
203
|
+
? priceByFeedObject.get(feedObject)
|
|
204
|
+
: undefined) ?? 0;
|
|
205
|
+
}
|
|
206
|
+
return prices;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export const getPricesFromIndexer = async (
|
|
210
|
+
ctx: PriceIndexerContext,
|
|
211
|
+
{
|
|
212
|
+
coinNames,
|
|
213
|
+
}: {
|
|
214
|
+
coinNames: string[];
|
|
215
|
+
}
|
|
216
|
+
) => {
|
|
217
|
+
const { indexer, fetchWithCache } = ctx;
|
|
218
|
+
const path = '/api/market/migrate';
|
|
219
|
+
|
|
220
|
+
const resp = await fetchWithCache<{
|
|
221
|
+
pools: MarketPool[];
|
|
222
|
+
collaterals: MarketCollateral[];
|
|
223
|
+
}>({
|
|
224
|
+
queryKey: queryKeys.api.getMarkets(),
|
|
225
|
+
queryFn: () => indexer.get(path),
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
return resp.pools.reduce(
|
|
229
|
+
(acc, pool) => {
|
|
230
|
+
if (coinNames.includes(pool.coinName)) {
|
|
231
|
+
acc[pool.coinName] = pool.coinPrice;
|
|
232
|
+
}
|
|
233
|
+
return acc;
|
|
234
|
+
},
|
|
235
|
+
{} as Record<string, number>
|
|
236
|
+
);
|
|
237
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pyth prices
|
|
3
|
+
* indexer coin prices
|
|
4
|
+
* all coin prices
|
|
5
|
+
* price update policies
|
|
6
|
+
* asset oracle config
|
|
7
|
+
* switchboard aggregator ids
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { IndexerDataSource } from 'src/datasources/indexer.js';
|
|
11
|
+
import { OnChainDataSource } from 'src/datasources/onchain.js';
|
|
12
|
+
import { BaseRepository } from '../base.js';
|
|
13
|
+
import { QuerySource, runWithDataSourceFallback } from '../utils.js';
|
|
14
|
+
import { DEFAULT_PYTH_URL } from './const.js';
|
|
15
|
+
import {
|
|
16
|
+
getPricesFromIndexer,
|
|
17
|
+
getPythFeedObjectFromOnChain,
|
|
18
|
+
getPythFeedObjectsFromOnChain,
|
|
19
|
+
getPythPricesFromApi,
|
|
20
|
+
getPythPricesFromOnChain,
|
|
21
|
+
} from './helpers.js';
|
|
22
|
+
import {
|
|
23
|
+
PriceApiConfig,
|
|
24
|
+
PriceRepositoryParams,
|
|
25
|
+
PriceRepositoryContext,
|
|
26
|
+
PriceRepositoryMetadata,
|
|
27
|
+
} from './types.js';
|
|
28
|
+
|
|
29
|
+
export class PriceRepository extends BaseRepository<
|
|
30
|
+
PriceRepositoryContext,
|
|
31
|
+
PriceRepositoryMetadata
|
|
32
|
+
> {
|
|
33
|
+
private readonly config: PriceApiConfig;
|
|
34
|
+
private readonly indexer: IndexerDataSource;
|
|
35
|
+
private readonly onchain: OnChainDataSource;
|
|
36
|
+
|
|
37
|
+
constructor({
|
|
38
|
+
pythPriceServiceConfig,
|
|
39
|
+
indexer,
|
|
40
|
+
onchain,
|
|
41
|
+
...params
|
|
42
|
+
}: PriceRepositoryParams) {
|
|
43
|
+
super(params);
|
|
44
|
+
this.config = pythPriceServiceConfig ?? {
|
|
45
|
+
endpoint: DEFAULT_PYTH_URL,
|
|
46
|
+
config: {
|
|
47
|
+
timeout: 4_000,
|
|
48
|
+
httpRetries: 1,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
this.indexer = indexer;
|
|
52
|
+
this.onchain = onchain;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get context() {
|
|
56
|
+
return {
|
|
57
|
+
...this.baseContext,
|
|
58
|
+
onchain: this.onchain,
|
|
59
|
+
indexer: this.indexer,
|
|
60
|
+
pythPriceServiceConfig: this.config,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getPricesFromPyth({
|
|
65
|
+
coinNames,
|
|
66
|
+
source = 'api-first',
|
|
67
|
+
}: {
|
|
68
|
+
coinNames: string[];
|
|
69
|
+
source?: QuerySource;
|
|
70
|
+
}) {
|
|
71
|
+
return runWithDataSourceFallback({
|
|
72
|
+
source,
|
|
73
|
+
label: 'PriceRepository.getPriceFromPyth',
|
|
74
|
+
logger: this.logger,
|
|
75
|
+
api: () => getPythPricesFromApi(this.context, coinNames),
|
|
76
|
+
onchain: () => getPythPricesFromOnChain(this.context, coinNames),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getPythFeedObject(feedObjectId: string) {
|
|
81
|
+
return getPythFeedObjectFromOnChain(this.context, feedObjectId);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getPythFeedObjects(feedObjectIds: string[]) {
|
|
85
|
+
return getPythFeedObjectsFromOnChain(this.context, feedObjectIds);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
getPricesFromIndexer(args: { coinNames: string[] }) {
|
|
89
|
+
return getPricesFromIndexer(this.context, args);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const PriceFeedObjectSchema = z.object({
|
|
4
|
+
id: z.string(),
|
|
5
|
+
price_info: z.object({
|
|
6
|
+
arrival_time: z.string(),
|
|
7
|
+
attestation_time: z.string(),
|
|
8
|
+
price_feed: z.object({
|
|
9
|
+
ema_price: z.object({
|
|
10
|
+
conf: z.string(),
|
|
11
|
+
expo: z.object({ magnitude: z.string(), negative: z.boolean() }),
|
|
12
|
+
price: z.object({ magnitude: z.string(), negative: z.boolean() }),
|
|
13
|
+
timestamp: z.string(),
|
|
14
|
+
}),
|
|
15
|
+
price: z.object({
|
|
16
|
+
conf: z.string(),
|
|
17
|
+
expo: z.object({ magnitude: z.string(), negative: z.boolean() }),
|
|
18
|
+
price: z.object({ magnitude: z.string(), negative: z.boolean() }),
|
|
19
|
+
timestamp: z.string(),
|
|
20
|
+
}),
|
|
21
|
+
price_identifier: z.object({ bytes: z.string() }),
|
|
22
|
+
}),
|
|
23
|
+
}),
|
|
24
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { AddressesInterface } from 'src/types/address.js';
|
|
2
|
+
import { BaseContext, BaseRepoParams } from '../types.js';
|
|
3
|
+
import { PriceServiceConnectionConfig } from '@pythnetwork/pyth-sui-js';
|
|
4
|
+
import { IndexerDataSource } from 'src/datasources/indexer.js';
|
|
5
|
+
import { OnChainDataSource } from 'src/datasources/onchain.js';
|
|
6
|
+
|
|
7
|
+
// Derived from the canonical `core.coins` value shape (single source of truth for
|
|
8
|
+
// the per-coin oracle/treasury config). Kept as a dense `Record` (the schema's is
|
|
9
|
+
// `Partial`) via `NonNullable`, preserving the repo's "coin is present" contract.
|
|
10
|
+
export type CoinsAddresses = {
|
|
11
|
+
coins: Record<
|
|
12
|
+
string,
|
|
13
|
+
NonNullable<AddressesInterface['core']['coins'][string]>
|
|
14
|
+
>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type PriceRepositoryMetadata = {
|
|
18
|
+
addresses: CoinsAddresses;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type PriceApiConfig = {
|
|
22
|
+
endpoint: string;
|
|
23
|
+
config: PriceServiceConnectionConfig;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type PriceRepositoryContext = BaseContext & {
|
|
27
|
+
metadata: PriceRepositoryMetadata;
|
|
28
|
+
onchain: OnChainDataSource;
|
|
29
|
+
indexer: IndexerDataSource;
|
|
30
|
+
pythPriceServiceConfig: PriceApiConfig;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type PriceRepositoryParams = BaseRepoParams & {
|
|
34
|
+
onchain: OnChainDataSource;
|
|
35
|
+
indexer: IndexerDataSource;
|
|
36
|
+
metadata: PriceRepositoryMetadata;
|
|
37
|
+
pythPriceServiceConfig?: PriceApiConfig;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** Minimal context for the Pyth API price read. */
|
|
41
|
+
export type PriceApiContext = Pick<
|
|
42
|
+
PriceRepositoryContext,
|
|
43
|
+
'fetchWithCache' | 'metadata' | 'pythPriceServiceConfig' | 'logger'
|
|
44
|
+
>;
|
|
45
|
+
|
|
46
|
+
/** Minimal context for the on-chain Pyth feed-object price read. */
|
|
47
|
+
export type PriceOnChainContext = Pick<
|
|
48
|
+
PriceRepositoryContext,
|
|
49
|
+
'onchain' | 'fetchWithCache' | 'metadata' | 'logger'
|
|
50
|
+
>;
|
|
51
|
+
|
|
52
|
+
/** Minimal context for the indexer coin-price read. */
|
|
53
|
+
export type PriceIndexerContext = Pick<
|
|
54
|
+
PriceRepositoryContext,
|
|
55
|
+
'indexer' | 'fetchWithCache'
|
|
56
|
+
>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BigNumber } from 'bignumber.js';
|
|
2
|
+
|
|
3
|
+
export const calculatePrice = ({
|
|
4
|
+
price,
|
|
5
|
+
expo,
|
|
6
|
+
}: {
|
|
7
|
+
price: {
|
|
8
|
+
magnitude: string;
|
|
9
|
+
negative: boolean;
|
|
10
|
+
};
|
|
11
|
+
expo: {
|
|
12
|
+
magnitude: string;
|
|
13
|
+
negative: boolean;
|
|
14
|
+
};
|
|
15
|
+
}) => {
|
|
16
|
+
const expoSign = expo.negative ? -1 : 1;
|
|
17
|
+
const priceSign = price.negative ? -1 : 1;
|
|
18
|
+
return BigNumber(price.magnitude)
|
|
19
|
+
.multipliedBy(BigNumber(10).shiftedBy(expoSign * +expo.magnitude))
|
|
20
|
+
.multipliedBy(priceSign);
|
|
21
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { SuiClientTypes } from '@mysten/sui/client';
|
|
2
|
+
import { getDynamicFieldOrNull } from '../utils.js';
|
|
3
|
+
import { ReferralBindingContext } from './types.js';
|
|
4
|
+
import { encodeDynamicFieldNameForV2 } from 'src/utils/dynamicField.js';
|
|
5
|
+
import { ReferrerVeScaKeyBcs } from './bcs.js';
|
|
6
|
+
|
|
7
|
+
export const getVeScaKeyIdFromRefBindingsFromOnChain = async (
|
|
8
|
+
ctx: ReferralBindingContext,
|
|
9
|
+
refereeAddress: string
|
|
10
|
+
) => {
|
|
11
|
+
const {
|
|
12
|
+
metadata: { addresses },
|
|
13
|
+
} = ctx;
|
|
14
|
+
const fetchOptions: SuiClientTypes.GetDynamicFieldOptions = {
|
|
15
|
+
parentId: addresses.referral.bindingTableId,
|
|
16
|
+
name: encodeDynamicFieldNameForV2({
|
|
17
|
+
type: 'address',
|
|
18
|
+
value: refereeAddress,
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
const result = await getDynamicFieldOrNull(ctx, fetchOptions);
|
|
22
|
+
if (!result) return null;
|
|
23
|
+
return ReferrerVeScaKeyBcs.parse(result.dynamicField.value.bcs);
|
|
24
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { BaseRepository } from '../base.js';
|
|
2
|
+
import { OnChainDataSource } from 'src/datasources/onchain.js';
|
|
3
|
+
import { getVeScaKeyIdFromRefBindingsFromOnChain } from './helper.js';
|
|
4
|
+
import {
|
|
5
|
+
ReferralRepoParams,
|
|
6
|
+
ReferralRepoContext,
|
|
7
|
+
ReferralRepoMetadata,
|
|
8
|
+
} from './types.js';
|
|
9
|
+
|
|
10
|
+
export class ReferralRepository extends BaseRepository<
|
|
11
|
+
ReferralRepoContext,
|
|
12
|
+
ReferralRepoMetadata
|
|
13
|
+
> {
|
|
14
|
+
private readonly onchain: OnChainDataSource;
|
|
15
|
+
|
|
16
|
+
constructor({ onchain, ...params }: ReferralRepoParams) {
|
|
17
|
+
super(params);
|
|
18
|
+
this.onchain = onchain;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get context() {
|
|
22
|
+
return { ...this.baseContext, onchain: this.onchain };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getVeScaKeyIdFromReferralBindings(refereeAddress: string) {
|
|
26
|
+
return getVeScaKeyIdFromRefBindingsFromOnChain(
|
|
27
|
+
this.context,
|
|
28
|
+
refereeAddress
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { AddressesInterface } from 'src/types/address.js';
|
|
2
|
+
import { BaseContext, BaseRepoParams } from '../types.js';
|
|
3
|
+
import { OnChainDataSource } from 'src/datasources/onchain.js';
|
|
4
|
+
|
|
5
|
+
type ReferralAddresses<
|
|
6
|
+
T extends keyof AddressesInterface['referral'] =
|
|
7
|
+
keyof AddressesInterface['referral'],
|
|
8
|
+
> = {
|
|
9
|
+
referral: Pick<AddressesInterface['referral'], T>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type ReferralRepoMetadata = {
|
|
13
|
+
addresses: ReferralAddresses<'bindingTableId'>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type ReferralRepoContext = BaseContext & {
|
|
17
|
+
onchain: OnChainDataSource;
|
|
18
|
+
metadata: ReferralRepoMetadata;
|
|
19
|
+
};
|
|
20
|
+
export type ReferralRepoParams = BaseRepoParams & {
|
|
21
|
+
onchain: OnChainDataSource;
|
|
22
|
+
metadata: ReferralRepoMetadata;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Narrowed context for `getVeScaKeyIdFromRefBindingsFromOnChain`: it reads only
|
|
27
|
+
* `metadata.addresses.referral.bindingTableId`, plus the `onchain` /
|
|
28
|
+
* `fetchWithCache` fields it forwards to `getDynamicFieldOrNull`.
|
|
29
|
+
*/
|
|
30
|
+
export type ReferralBindingContext = BaseContext & {
|
|
31
|
+
onchain: OnChainDataSource;
|
|
32
|
+
metadata: { addresses: ReferralAddresses<'bindingTableId'> };
|
|
33
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const YEAR_IN_SECONDS = 365 * 24 * 60 * 60; // 1 year in seconds
|