@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.
Files changed (283) hide show
  1. package/dist/Logger-Cg2iFdpH.d.ts +8 -0
  2. package/dist/builder.cjs +22 -0
  3. package/dist/builder.d.cts +28 -0
  4. package/dist/builder.d.ts +28 -0
  5. package/dist/builder.js +4 -0
  6. package/dist/chunk-33AK5RWM.js +15 -0
  7. package/dist/chunk-6UQ4NWEU.js +3 -0
  8. package/dist/chunk-DIC6P5HB.js +11 -0
  9. package/dist/chunk-S34VOEZN.js +3 -0
  10. package/dist/chunk-VTGQACWU.js +6 -0
  11. package/dist/client.cjs +21 -0
  12. package/dist/client.d.cts +401 -0
  13. package/dist/client.d.ts +401 -0
  14. package/dist/client.js +5 -0
  15. package/dist/errors.cjs +10 -0
  16. package/dist/errors.d.cts +32 -0
  17. package/dist/errors.d.ts +32 -0
  18. package/dist/errors.js +1 -0
  19. package/dist/index.cjs +51 -34
  20. package/dist/index.d.cts +130 -3145
  21. package/dist/index.d.ts +130 -3145
  22. package/dist/index.js +8 -12
  23. package/dist/logger.cjs +6 -0
  24. package/dist/logger.d.cts +7 -0
  25. package/dist/logger.d.ts +7 -0
  26. package/dist/logger.js +1 -0
  27. package/dist/query-DkitepDi.d.ts +3305 -0
  28. package/dist/query.cjs +19 -0
  29. package/dist/query.d.cts +9 -0
  30. package/dist/query.d.ts +9 -0
  31. package/dist/query.js +3 -0
  32. package/dist/types.cjs +2 -0
  33. package/dist/types.d.cts +90 -0
  34. package/dist/types.d.ts +90 -0
  35. package/dist/types.js +1 -0
  36. package/package.json +98 -56
  37. package/src/constants/api.ts +2 -0
  38. package/src/constants/coinType.ts +2 -0
  39. package/src/constants/common.ts +0 -7
  40. package/src/constants/index.ts +2 -0
  41. package/src/constants/queryKeys.ts +14 -7
  42. package/src/constants/testAddress.ts +1 -0
  43. package/src/datasources/api.ts +34 -0
  44. package/src/datasources/indexer.ts +8 -0
  45. package/src/datasources/onchain.ts +121 -0
  46. package/src/entries/builder.ts +5 -0
  47. package/src/entries/client.ts +3 -0
  48. package/src/entries/errors.ts +2 -0
  49. package/src/entries/index.ts +17 -0
  50. package/src/entries/logger.ts +2 -0
  51. package/src/entries/query.ts +6 -0
  52. package/src/entries/types.ts +2 -0
  53. package/src/errors/ScallopConfigError.ts +8 -0
  54. package/src/errors/ScallopError.ts +30 -0
  55. package/src/errors/ScallopIndexerError.ts +8 -0
  56. package/src/errors/ScallopParseError.ts +8 -0
  57. package/src/errors/ScallopRpcError.ts +8 -0
  58. package/src/errors/ScallopTransactionBuildError.ts +8 -0
  59. package/src/errors/index.ts +6 -0
  60. package/src/logger/Logger.ts +6 -0
  61. package/src/logger/consoleLogger.ts +13 -0
  62. package/src/logger/index.ts +3 -0
  63. package/src/logger/noopLogger.ts +8 -0
  64. package/src/mappers/index.ts +1 -0
  65. package/src/mappers/moveTypeMapper.ts +48 -0
  66. package/src/models/index.ts +8 -10
  67. package/src/models/interface.ts +9 -9
  68. package/src/models/scallop.ts +9 -16
  69. package/src/models/scallopAddress/const.ts +416 -0
  70. package/src/models/scallopAddress/index.ts +188 -0
  71. package/src/models/scallopAddress/types.ts +212 -0
  72. package/src/models/{scallopBuilder.ts → scallopBuilder/index.ts} +65 -29
  73. package/src/models/scallopBuilder/types.ts +37 -0
  74. package/src/models/{scallopClient.ts → scallopClient/index.ts} +157 -598
  75. package/src/models/scallopClient/types.ts +25 -0
  76. package/src/models/scallopConstants/config/AddressConfigSource.ts +13 -0
  77. package/src/models/scallopConstants/config/ConfigValidator.ts +70 -0
  78. package/src/models/scallopConstants/config/PoolAddressConfigSource.ts +12 -0
  79. package/src/models/scallopConstants/config/ScallopConfig.ts +80 -0
  80. package/src/models/scallopConstants/config/ScallopConfigSnapshot.ts +46 -0
  81. package/src/models/scallopConstants/config/WhitelistConfigSource.ts +16 -0
  82. package/src/models/scallopConstants/config/index.ts +6 -0
  83. package/src/models/scallopConstants/const.ts +19 -0
  84. package/src/models/scallopConstants/constantsSource.ts +98 -0
  85. package/src/models/scallopConstants/deriveConstants.ts +150 -0
  86. package/src/models/scallopConstants/index.ts +257 -0
  87. package/src/models/scallopConstants/loadConstantsState.ts +135 -0
  88. package/src/models/scallopConstants/types.ts +77 -0
  89. package/src/models/scallopConstants/utils.ts +69 -0
  90. package/src/models/scallopQuery/index.ts +1279 -0
  91. package/src/models/scallopQuery/types.ts +9 -0
  92. package/src/models/scallopQuery/utils.ts +13 -0
  93. package/src/models/scallopUtils/index.ts +505 -0
  94. package/src/models/scallopUtils/types.ts +30 -0
  95. package/src/models/transactionExecutor.ts +122 -0
  96. package/src/repositories/addressApi/helpers.ts +14 -0
  97. package/src/repositories/addressApi/index.ts +24 -0
  98. package/src/repositories/addressApi/schema.ts +0 -0
  99. package/src/repositories/addressApi/types.ts +177 -0
  100. package/src/repositories/base.ts +38 -0
  101. package/src/repositories/borrowIncentive/bcs.ts +11 -0
  102. package/src/repositories/borrowIncentive/helpers.ts +396 -0
  103. package/src/repositories/borrowIncentive/index.ts +52 -0
  104. package/src/{types/query/borrowIncentive.ts → repositories/borrowIncentive/types.ts} +69 -23
  105. package/src/repositories/borrowIncentive/utils.ts +234 -0
  106. package/src/repositories/cache.ts +4 -0
  107. package/src/repositories/coinBalance/helpers.ts +394 -0
  108. package/src/repositories/coinBalance/index.ts +69 -0
  109. package/src/repositories/coinBalance/types.ts +33 -0
  110. package/src/repositories/flashloan/const.ts +3 -0
  111. package/src/repositories/flashloan/helpers.ts +107 -0
  112. package/src/repositories/flashloan/index.ts +34 -0
  113. package/src/repositories/flashloan/types.ts +16 -0
  114. package/src/repositories/isolatedAssets/bcs.ts +4 -0
  115. package/src/repositories/isolatedAssets/const.ts +2 -0
  116. package/src/repositories/isolatedAssets/helpers.ts +91 -0
  117. package/src/repositories/isolatedAssets/index.ts +42 -0
  118. package/src/repositories/isolatedAssets/types.ts +35 -0
  119. package/src/repositories/loyaltyProgram/bcs.ts +7 -0
  120. package/src/repositories/loyaltyProgram/helpers.ts +117 -0
  121. package/src/repositories/loyaltyProgram/index.ts +28 -0
  122. package/src/repositories/loyaltyProgram/schema.ts +18 -0
  123. package/src/repositories/loyaltyProgram/types.ts +37 -0
  124. package/src/repositories/market/const.ts +6 -0
  125. package/src/repositories/market/helpers.ts +740 -0
  126. package/src/repositories/market/index.ts +137 -0
  127. package/src/repositories/market/mapper.ts +30 -0
  128. package/src/repositories/market/types.ts +443 -0
  129. package/src/repositories/market/utils.ts +285 -0
  130. package/src/repositories/obligation/helpers.ts +228 -0
  131. package/src/repositories/obligation/index.ts +51 -0
  132. package/src/repositories/obligation/types.ts +71 -0
  133. package/src/repositories/obligation/utils.ts +57 -0
  134. package/src/repositories/poolAddresses/const.ts +5 -0
  135. package/src/repositories/poolAddresses/helpers.ts +422 -0
  136. package/src/repositories/poolAddresses/index.ts +56 -0
  137. package/src/repositories/poolAddresses/schema.ts +34 -0
  138. package/src/repositories/poolAddresses/types.ts +69 -0
  139. package/src/repositories/price/const.ts +1 -0
  140. package/src/repositories/price/helpers.ts +237 -0
  141. package/src/repositories/price/index.ts +91 -0
  142. package/src/repositories/price/schema.ts +24 -0
  143. package/src/repositories/price/types.ts +56 -0
  144. package/src/repositories/price/utils.ts +21 -0
  145. package/src/repositories/referral/bcs.ts +3 -0
  146. package/src/repositories/referral/helper.ts +24 -0
  147. package/src/repositories/referral/index.ts +31 -0
  148. package/src/repositories/referral/types.ts +33 -0
  149. package/src/repositories/spool/const.ts +1 -0
  150. package/src/repositories/spool/helpers.ts +471 -0
  151. package/src/repositories/spool/index.ts +135 -0
  152. package/src/{types/query/spool.ts → repositories/spool/types.ts} +67 -2
  153. package/src/repositories/spool/utils.ts +215 -0
  154. package/src/repositories/types.ts +39 -0
  155. package/src/repositories/utils.ts +137 -0
  156. package/src/repositories/veSca/bcs.ts +6 -0
  157. package/src/repositories/veSca/helpers.ts +341 -0
  158. package/src/repositories/veSca/index.ts +57 -0
  159. package/src/repositories/veSca/types.ts +79 -0
  160. package/src/repositories/veScaLoyaltyProgram/bcs.ts +3 -0
  161. package/src/repositories/veScaLoyaltyProgram/helpers.ts +135 -0
  162. package/src/repositories/veScaLoyaltyProgram/index.ts +28 -0
  163. package/src/repositories/veScaLoyaltyProgram/schema.ts +15 -0
  164. package/src/repositories/veScaLoyaltyProgram/types.ts +47 -0
  165. package/src/repositories/wiring/datasources.ts +42 -0
  166. package/src/repositories/wiring/metadata.ts +292 -0
  167. package/src/repositories/wiring/registry.ts +194 -0
  168. package/src/repositories/wiring/source.ts +63 -0
  169. package/src/repositories/xOracle/bcs.ts +7 -0
  170. package/src/repositories/xOracle/const.ts +1 -0
  171. package/src/repositories/xOracle/helpers.ts +304 -0
  172. package/src/repositories/xOracle/index.ts +45 -0
  173. package/src/repositories/xOracle/types.ts +96 -0
  174. package/src/repositories/xOracle/utils.ts +3 -0
  175. package/src/services/client/BorrowService.ts +229 -0
  176. package/src/services/client/CollateralService.ts +76 -0
  177. package/src/services/client/LendingService.ts +75 -0
  178. package/src/services/client/ReferralService.ts +71 -0
  179. package/src/services/client/SpoolService.ts +170 -0
  180. package/src/services/client/VeScaService.ts +142 -0
  181. package/src/services/client/types.ts +44 -0
  182. package/src/services/index.ts +8 -0
  183. package/src/services/query/portfolioCalculations.ts +1171 -0
  184. package/src/txBuilders/borrowIncentive/index.ts +80 -0
  185. package/src/txBuilders/borrowIncentive/moveCalls.ts +124 -0
  186. package/src/txBuilders/borrowIncentive/quick.ts +207 -0
  187. package/src/txBuilders/context.ts +62 -0
  188. package/src/txBuilders/core/index.ts +95 -0
  189. package/src/txBuilders/core/moveCalls.ts +241 -0
  190. package/src/txBuilders/core/quick.ts +306 -0
  191. package/src/{builders → txBuilders}/index.ts +35 -9
  192. package/src/txBuilders/loyaltyProgram/index.ts +78 -0
  193. package/src/txBuilders/loyaltyProgram/moveCalls.ts +44 -0
  194. package/src/txBuilders/loyaltyProgram/quick.ts +58 -0
  195. package/src/txBuilders/manifest.ts +160 -0
  196. package/src/txBuilders/modules.ts +70 -0
  197. package/src/{builders → txBuilders}/oracles/index.ts +3 -2
  198. package/src/{builders → txBuilders}/oracles/pyth.ts +4 -3
  199. package/src/txBuilders/referral/index.ts +72 -0
  200. package/src/txBuilders/referral/moveCalls.ts +93 -0
  201. package/src/txBuilders/referral/quick.ts +57 -0
  202. package/src/txBuilders/sCoin/index.ts +72 -0
  203. package/src/txBuilders/sCoin/moveCalls.ts +36 -0
  204. package/src/txBuilders/sCoin/quick.ts +34 -0
  205. package/src/txBuilders/spool/index.ts +82 -0
  206. package/src/txBuilders/spool/moveCalls.ts +79 -0
  207. package/src/{builders/spoolBuilder.ts → txBuilders/spool/quick.ts} +22 -162
  208. package/src/txBuilders/utils.ts +36 -0
  209. package/src/txBuilders/verify.ts +45 -0
  210. package/src/txBuilders/vesca/index.ts +75 -0
  211. package/src/txBuilders/vesca/moveCalls.ts +147 -0
  212. package/src/{builders/vescaBuilder.ts → txBuilders/vesca/quick.ts} +85 -274
  213. package/src/types/address.ts +4 -178
  214. package/src/types/builder/borrowIncentive.ts +4 -3
  215. package/src/types/builder/core.ts +6 -33
  216. package/src/types/builder/index.ts +5 -1
  217. package/src/types/builder/loyaltyProgram.ts +4 -3
  218. package/src/types/builder/modules.ts +49 -0
  219. package/src/types/builder/referral.ts +5 -4
  220. package/src/types/builder/sCoin.ts +20 -2
  221. package/src/types/builder/spool.ts +22 -2
  222. package/src/types/builder/vesca.ts +4 -12
  223. package/src/types/constant/index.ts +3 -1
  224. package/src/types/constant/queryKeys.ts +15 -8
  225. package/src/types/index.ts +5 -6
  226. package/src/types/internal/dto.ts +46 -0
  227. package/src/types/internal/index.ts +2 -0
  228. package/src/types/internal/move.ts +13 -0
  229. package/src/types/public/index.ts +21 -0
  230. package/src/types/query/core.ts +0 -317
  231. package/src/types/query/index.ts +3 -4
  232. package/src/types/query/portfolio.ts +2 -15
  233. package/src/types/repositories/borrowIncentive.ts +12 -0
  234. package/src/types/repositories/index.ts +9 -0
  235. package/src/types/repositories/loyaltyProgram.ts +2 -0
  236. package/src/types/repositories/market.ts +13 -0
  237. package/src/types/repositories/spool.ts +21 -0
  238. package/src/types/repositories/veSca.ts +5 -0
  239. package/src/types/repositories/veScaLoyaltyProgram.ts +2 -0
  240. package/src/types/sui.ts +0 -1
  241. package/src/utils/array.ts +7 -0
  242. package/src/utils/cache.ts +17 -0
  243. package/src/utils/object.ts +30 -12
  244. package/src/utils/query.ts +9 -190
  245. package/src/utils/vesca.ts +0 -8
  246. package/src/builders/borrowIncentiveBuilder.ts +0 -372
  247. package/src/builders/coreBuilder.ts +0 -636
  248. package/src/builders/loyaltyProgramBuilder.ts +0 -142
  249. package/src/builders/referralBuilder.ts +0 -192
  250. package/src/builders/sCoinBuilder.ts +0 -124
  251. package/src/index.ts +0 -13
  252. package/src/models/scallopAddress.ts +0 -841
  253. package/src/models/scallopAxios.ts +0 -185
  254. package/src/models/scallopConstants.ts +0 -380
  255. package/src/models/scallopIndexer.ts +0 -226
  256. package/src/models/scallopQuery.ts +0 -974
  257. package/src/models/scallopQueryClient.ts +0 -29
  258. package/src/models/scallopSuiKit.ts +0 -480
  259. package/src/models/scallopUtils.ts +0 -734
  260. package/src/queries/borrowIncentiveQuery.ts +0 -361
  261. package/src/queries/borrowLimitQuery.ts +0 -33
  262. package/src/queries/coreQuery.ts +0 -1173
  263. package/src/queries/flashloanFeeQuery.ts +0 -92
  264. package/src/queries/index.ts +0 -15
  265. package/src/queries/isolatedAssetQuery.ts +0 -103
  266. package/src/queries/loyaltyProgramQuery.ts +0 -178
  267. package/src/queries/ownerQuery.ts +0 -32
  268. package/src/queries/poolAddressesQuery.ts +0 -340
  269. package/src/queries/portfolioQuery.ts +0 -1149
  270. package/src/queries/priceQuery.ts +0 -37
  271. package/src/queries/referralQuery.ts +0 -42
  272. package/src/queries/sCoinQuery.ts +0 -195
  273. package/src/queries/spoolQuery.ts +0 -542
  274. package/src/queries/supplyLimitQuery.ts +0 -32
  275. package/src/queries/switchboardQuery.ts +0 -65
  276. package/src/queries/vescaQuery.ts +0 -312
  277. package/src/queries/xOracleQuery.ts +0 -149
  278. package/src/types/constant/common.ts +0 -55
  279. package/src/types/query/loyaltyProgram.ts +0 -12
  280. package/src/types/query/vesca.ts +0 -27
  281. package/src/utils/index.ts +0 -7
  282. package/src/utils/indexer.ts +0 -47
  283. /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,3 @@
1
+ import { bcs } from '@mysten/sui/bcs';
2
+
3
+ export const ReferrerVeScaKeyBcs = bcs.Address;
@@ -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