@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,471 @@
1
+ import { normalizeStructTag } from '@mysten/sui/utils';
2
+ import { queryKeys } from 'src/constants/queryKeys.js';
3
+ import { mapSpoolData } from './utils.js';
4
+ import type { SuiObjectData } from 'src/types/index.js';
5
+ import type { CoinPrices } from 'src/types/utils.js';
6
+ import { parseObjectAs } from 'src/utils/object.js';
7
+ import { partitionArray } from 'src/utils/array.js';
8
+ import type {
9
+ RequiredSpoolObjects,
10
+ Spool,
11
+ SpoolIndexerContext,
12
+ SpoolOnChainContext,
13
+ SpoolReadArgs,
14
+ Spools,
15
+ StakeAccounts,
16
+ StakePool,
17
+ StakeRewardPool,
18
+ } from './types.js';
19
+ import type { OnChainReadContext } from '../utils.js';
20
+ import {
21
+ calculateSpoolData,
22
+ calculateSpoolRewardPoolData,
23
+ parseOriginSpoolData,
24
+ parseOriginSpoolRewardPoolData,
25
+ parseSpoolObjects,
26
+ } from './utils.js';
27
+ import type { SuiClientTypes } from '@mysten/sui/client';
28
+
29
+ export const getSpoolsFromIndexer = async (
30
+ ctx: SpoolIndexerContext,
31
+ { coinPrices, stakeCoinNames }: SpoolReadArgs = {}
32
+ ): Promise<Spools> => {
33
+ const { indexer, fetchWithCache, metadata } = ctx;
34
+ coinPrices ??= {};
35
+
36
+ const { spools: rawSpools } = await fetchWithCache<{ spools: Spool[] }>({
37
+ queryKey: queryKeys.api.getSpools(),
38
+ queryFn: () => indexer.get<{ spools: Spool[] }>('/api/spools/migrate'),
39
+ });
40
+
41
+ const allowedNames = stakeCoinNames
42
+ ? new Set(stakeCoinNames)
43
+ : metadata.whitelist.spool;
44
+
45
+ const rewardCoinName = metadata.getSpoolRewardCoinName();
46
+
47
+ return rawSpools.reduce<Spools>((spools, spool) => {
48
+ if (!allowedNames.has(spool.marketCoinName)) return spools;
49
+
50
+ const coinName = metadata.parseCoinName(spool.marketCoinName);
51
+
52
+ spools[spool.marketCoinName] = {
53
+ ...spool,
54
+ coinPrice: coinPrices[coinName] ?? spool.coinPrice,
55
+ marketCoinPrice:
56
+ coinPrices[spool.marketCoinName] ?? spool.marketCoinPrice,
57
+ rewardCoinPrice: coinPrices[rewardCoinName] ?? spool.rewardCoinPrice,
58
+ };
59
+
60
+ return spools;
61
+ }, {});
62
+ };
63
+
64
+ export const getSpoolFromIndexer = async (
65
+ ctx: SpoolIndexerContext,
66
+ {
67
+ coinPrices,
68
+ stakeCoinName,
69
+ }: {
70
+ coinPrices: CoinPrices;
71
+ stakeCoinName: string;
72
+ }
73
+ ): Promise<Spool | null> => {
74
+ const spools = await getSpoolsFromIndexer(ctx, {
75
+ coinPrices,
76
+ stakeCoinNames: [stakeCoinName],
77
+ });
78
+
79
+ return spools[stakeCoinName] ?? null;
80
+ };
81
+
82
+ export const getSpoolsFromOnChain = async (
83
+ ctx: SpoolOnChainContext,
84
+ { coinPrices = {}, stakeCoinNames }: SpoolReadArgs = {}
85
+ ): Promise<Spools> => {
86
+ const { metadata } = ctx;
87
+ const marketCoinNames = stakeCoinNames ?? [
88
+ ...metadata.whitelist.spool.values(),
89
+ ];
90
+ const coinNames = marketCoinNames.map((marketCoinName) =>
91
+ metadata.parseCoinName(marketCoinName)
92
+ );
93
+ const requiredObjects = await queryRequiredSpoolObjects(ctx, coinNames);
94
+
95
+ const results = await Promise.allSettled(
96
+ marketCoinNames.map((marketCoinName, index) =>
97
+ getSpoolFromOnChain(ctx, {
98
+ coinPrices,
99
+ stakeCoinName: marketCoinName,
100
+ requiredObjects: requiredObjects[coinNames[index]],
101
+ })
102
+ )
103
+ );
104
+
105
+ return results.reduce<Spools>((spools, result, index) => {
106
+ if (result.status === 'fulfilled' && result.value) {
107
+ spools[marketCoinNames[index]] = result.value;
108
+ }
109
+ return spools;
110
+ }, {});
111
+ };
112
+
113
+ export const getSpoolFromOnChain = async (
114
+ ctx: SpoolOnChainContext,
115
+ {
116
+ coinPrices,
117
+ stakeCoinName,
118
+ requiredObjects,
119
+ }: {
120
+ coinPrices: CoinPrices;
121
+ stakeCoinName: string;
122
+ requiredObjects?: RequiredSpoolObjects[string];
123
+ }
124
+ ): Promise<Spool> => {
125
+ const { metadata } = ctx;
126
+ const coinName = metadata.parseCoinName(stakeCoinName);
127
+ const rewardCoinName = metadata.getSpoolRewardCoinName();
128
+ const parsedSpoolObjects = mapSpoolData(
129
+ parseSpoolObjects(
130
+ requiredObjects ??
131
+ (await queryRequiredSpoolObjects(ctx, [coinName]))[coinName]
132
+ )
133
+ );
134
+ const parsedSpoolData = parseOriginSpoolData(parsedSpoolObjects);
135
+
136
+ const marketCoinPrice = coinPrices[stakeCoinName] ?? 0;
137
+ const calculatedSpoolData = calculateSpoolData(
138
+ parsedSpoolData,
139
+ marketCoinPrice,
140
+ metadata.getCoinDecimal(stakeCoinName)
141
+ );
142
+
143
+ const parsedSpoolRewardPoolData =
144
+ parseOriginSpoolRewardPoolData(parsedSpoolObjects);
145
+ const rewardCoinPrice = coinPrices[rewardCoinName] ?? 0;
146
+
147
+ return {
148
+ marketCoinName: stakeCoinName,
149
+ symbol: metadata.parseSymbol(stakeCoinName),
150
+ coinType: metadata.parseCoinType(coinName),
151
+ marketCoinType: metadata.parseMarketCoinType(coinName),
152
+ rewardCoinType: metadata.isMarketCoin(rewardCoinName)
153
+ ? metadata.parseMarketCoinType(rewardCoinName)
154
+ : metadata.parseCoinType(rewardCoinName),
155
+ sCoinType: metadata.parseSCoinType(stakeCoinName) ?? '',
156
+ coinDecimal: metadata.getCoinDecimal(coinName),
157
+ rewardCoinDecimal: metadata.getCoinDecimal(rewardCoinName),
158
+ coinPrice: coinPrices[coinName] ?? 0,
159
+ marketCoinPrice,
160
+ rewardCoinPrice,
161
+ maxPoint: parsedSpoolData.maxPoint,
162
+ distributedPoint: parsedSpoolData.distributedPoint,
163
+ maxStake: parsedSpoolData.maxStake,
164
+ ...calculatedSpoolData,
165
+ exchangeRateNumerator: parsedSpoolRewardPoolData.exchangeRateNumerator,
166
+ exchangeRateDenominator: parsedSpoolRewardPoolData.exchangeRateDenominator,
167
+ ...calculateSpoolRewardPoolData(
168
+ parsedSpoolData,
169
+ parsedSpoolRewardPoolData,
170
+ calculatedSpoolData,
171
+ rewardCoinPrice,
172
+ metadata.getCoinDecimal(rewardCoinName)
173
+ ),
174
+ };
175
+ };
176
+
177
+ export const getStakeAccountsFromOnChain = async (
178
+ ctx: SpoolOnChainContext,
179
+ {
180
+ address,
181
+ stakeCoinNames,
182
+ }: {
183
+ address: string;
184
+ stakeCoinNames?: readonly string[];
185
+ }
186
+ ): Promise<StakeAccounts> => {
187
+ const { metadata } = ctx;
188
+ const stakeAccountType = `${metadata.addresses.spoolObjectId}::spool_account::SpoolAccount`;
189
+
190
+ const stakeAccountObjects = await queryStakeAccounts(ctx, {
191
+ address,
192
+ stakeAccountType,
193
+ });
194
+
195
+ const stakeAccounts = (
196
+ stakeCoinNames ?? [...metadata.whitelist.spool.values()]
197
+ ).reduce<StakeAccounts>((accounts, stakeName) => {
198
+ accounts[stakeName] = [];
199
+ return accounts;
200
+ }, {});
201
+
202
+ const stakeMarketCoinTypes = Object.keys(stakeAccounts).reduce<
203
+ Record<string, string>
204
+ >((types, stakeCoinName) => {
205
+ const coinName = metadata.parseCoinName(stakeCoinName);
206
+ const marketCoinType = metadata.parseMarketCoinType(coinName);
207
+ types[stakeCoinName] =
208
+ `${metadata.addresses.spoolObjectId}::spool_account::SpoolAccount<${marketCoinType}>`;
209
+ return types;
210
+ }, {});
211
+
212
+ const stakeNameByType = Object.entries(stakeMarketCoinTypes).reduce<
213
+ Record<string, string>
214
+ >((types, [stakeCoinName, stakeType]) => {
215
+ types[stakeType] = stakeCoinName;
216
+ return types;
217
+ }, {});
218
+
219
+ for (const stakeObject of stakeAccountObjects) {
220
+ const { objectId: id, type, json } = stakeObject;
221
+ if (!id || !type || !json) continue;
222
+
223
+ const fields = parseObjectAs<{
224
+ spool_id: string;
225
+ stake_type: string;
226
+ stakes: string;
227
+ index: string;
228
+ points: string;
229
+ total_points: string;
230
+ }>(stakeObject);
231
+ const normalizedType = normalizeStructTag(type);
232
+ const stakeCoinName = stakeNameByType[normalizedType];
233
+ const stakeAccountArray = stakeAccounts[stakeCoinName];
234
+ if (!stakeAccountArray) continue;
235
+
236
+ stakeAccountArray.push({
237
+ id,
238
+ type: normalizedType,
239
+ stakePoolId: String(fields.spool_id),
240
+ stakeType: normalizeStructTag(String(fields.stake_type)),
241
+ staked: Number(fields.stakes),
242
+ index: Number(fields.index),
243
+ points: Number(fields.points),
244
+ totalPoints: Number(fields.total_points),
245
+ });
246
+ }
247
+
248
+ return stakeAccounts;
249
+ };
250
+
251
+ export const getSpoolRewardPoolsFromOnChain = async (
252
+ ctx: SpoolOnChainContext,
253
+ {
254
+ stakeCoinNames,
255
+ }: {
256
+ stakeCoinNames: string[];
257
+ }
258
+ ) => {
259
+ const stakeRewardPools = await Promise.allSettled(
260
+ stakeCoinNames.map((stakeCoinName) =>
261
+ queryStakeRewardPool(ctx, { stakeCoinName })
262
+ )
263
+ );
264
+
265
+ return stakeRewardPools.reduce<Record<string, StakeRewardPool | undefined>>(
266
+ (pools, result, index) => {
267
+ if (result.status === 'fulfilled') {
268
+ pools[stakeCoinNames[index]] = result.value;
269
+ }
270
+ return pools;
271
+ },
272
+ {}
273
+ );
274
+ };
275
+
276
+ const queryStakeAccounts = async (
277
+ ctx: OnChainReadContext,
278
+ { address, stakeAccountType }: { address: string; stakeAccountType: string }
279
+ ) => {
280
+ const { onchain, fetchWithCache } = ctx;
281
+
282
+ let hasNextPage = false;
283
+ let nextCursor: string | null | undefined = null;
284
+
285
+ const stakeObjectsResponse: SuiObjectData[] = [];
286
+
287
+ do {
288
+ const input: SuiClientTypes.ListOwnedObjectsOptions = {
289
+ owner: address,
290
+ type: stakeAccountType,
291
+ include: {
292
+ json: true,
293
+ },
294
+ cursor: nextCursor,
295
+ limit: 50,
296
+ };
297
+ const response =
298
+ await fetchWithCache<SuiClientTypes.ListOwnedObjectsResponse>({
299
+ queryKey: queryKeys.rpc.getOwnedObjects(input),
300
+ queryFn: () => onchain.client.listOwnedObjects(input),
301
+ });
302
+
303
+ if (response.objects) {
304
+ stakeObjectsResponse.push(...response.objects);
305
+ }
306
+
307
+ if (response.hasNextPage && response.cursor) {
308
+ hasNextPage = true;
309
+ nextCursor = response.cursor;
310
+ } else {
311
+ hasNextPage = false;
312
+ }
313
+ } while (hasNextPage);
314
+
315
+ return stakeObjectsResponse;
316
+ };
317
+
318
+ const queryRequiredSpoolObjects = async (
319
+ { onchain, metadata, fetchWithCache }: SpoolOnChainContext,
320
+ stakeCoinNames: string[]
321
+ ): Promise<RequiredSpoolObjects> => {
322
+ const keyTypes = ['spool', 'spoolReward', 'sCoinTreasury'] as const;
323
+ const objectIds = stakeCoinNames.flatMap((coinName) => {
324
+ const poolData = metadata.poolAddresses[coinName];
325
+ return keyTypes.flatMap((keyType) => {
326
+ const objectId = poolData?.[keyType];
327
+ return objectId ? [objectId] : [];
328
+ });
329
+ });
330
+
331
+ const objectDatas: SuiObjectData[] = [];
332
+ const batches = partitionArray([...new Set(objectIds)], 50);
333
+
334
+ for (const batch of batches) {
335
+ const response = await fetchWithCache({
336
+ queryKey: queryKeys.rpc.getObjects({
337
+ objectIds: batch,
338
+ node: onchain.url,
339
+ }),
340
+ queryFn: () =>
341
+ onchain.client.getObjects({
342
+ objectIds: batch,
343
+ include: { json: true },
344
+ }),
345
+ });
346
+
347
+ objectDatas.push(
348
+ ...response.objects.filter(
349
+ (object): object is SuiObjectData => !(object instanceof Error)
350
+ )
351
+ );
352
+ }
353
+
354
+ const objectDataMap = objectDatas.reduce(
355
+ (acc, obj) => {
356
+ acc[obj.objectId] = obj;
357
+ return acc;
358
+ },
359
+ {} as Record<string, SuiObjectData>
360
+ );
361
+
362
+ return stakeCoinNames.reduce<RequiredSpoolObjects>((objects, coinName) => {
363
+ const poolData = metadata.poolAddresses[coinName];
364
+ objects[coinName] = {
365
+ spool: objectDataMap[poolData?.spool ?? ''],
366
+ spoolReward: objectDataMap[poolData?.spoolReward ?? ''],
367
+ };
368
+ return objects;
369
+ }, {});
370
+ };
371
+
372
+ export const queryStakeRewardPool = async (
373
+ ctx: SpoolOnChainContext,
374
+ {
375
+ stakeCoinName,
376
+ }: {
377
+ stakeCoinName: string;
378
+ }
379
+ ): Promise<StakeRewardPool | undefined> => {
380
+ const { metadata, onchain, fetchWithCache } = ctx;
381
+
382
+ const poolId = metadata.addresses.spools[stakeCoinName]?.rewardPoolId;
383
+ if (!poolId) return undefined;
384
+
385
+ const fetchOptions = {
386
+ json: true,
387
+ };
388
+ const stakeRewardPoolObjectResponse = await fetchWithCache({
389
+ queryKey: queryKeys.rpc.getObject({
390
+ objectId: poolId,
391
+ node: onchain.url,
392
+ include: fetchOptions,
393
+ }),
394
+ queryFn: () =>
395
+ onchain.getObject({
396
+ objectId: poolId,
397
+ include: fetchOptions,
398
+ }),
399
+ });
400
+
401
+ const stakeRewardPoolObject = stakeRewardPoolObjectResponse.object;
402
+ if (!stakeRewardPoolObject?.json) return undefined;
403
+
404
+ const rewardPoolFields = parseObjectAs<{
405
+ spool_id: string;
406
+ exchange_rate_numerator: string;
407
+ exchange_rate_denominator: string;
408
+ rewards: string;
409
+ claimed_rewards: string;
410
+ }>(stakeRewardPoolObject);
411
+
412
+ return {
413
+ id: stakeRewardPoolObject.objectId,
414
+ type: normalizeStructTag(stakeRewardPoolObject.type ?? ''),
415
+ stakePoolId: String(rewardPoolFields.spool_id),
416
+ ratioNumerator: Number(rewardPoolFields.exchange_rate_numerator),
417
+ ratioDenominator: Number(rewardPoolFields.exchange_rate_denominator),
418
+ rewards: Number(rewardPoolFields.rewards),
419
+ claimedRewards: Number(rewardPoolFields.claimed_rewards),
420
+ };
421
+ };
422
+
423
+ export const getStakePoolFromOnChain = async (
424
+ ctx: SpoolOnChainContext,
425
+ stakeCoinName: string
426
+ ): Promise<StakePool | undefined> => {
427
+ const { onchain, fetchWithCache, metadata } = ctx;
428
+ const poolId = metadata.addresses.spools[stakeCoinName]?.id;
429
+ if (!poolId) return undefined;
430
+
431
+ const include = { json: true };
432
+ const response = await fetchWithCache({
433
+ queryKey: queryKeys.rpc.getObject({
434
+ objectId: poolId,
435
+ node: onchain.url,
436
+ include,
437
+ }),
438
+ queryFn: () => onchain.getObject({ objectId: poolId, include }),
439
+ });
440
+
441
+ const object = response.object;
442
+ if (!object?.json) return undefined;
443
+
444
+ const fields = parseObjectAs<{
445
+ max_distributed_point: string;
446
+ distributed_point: string;
447
+ distributed_point_per_period: string;
448
+ point_distribution_time: string;
449
+ max_stakes: string;
450
+ stake_type: string;
451
+ stakes: string;
452
+ index: string;
453
+ created_at: string;
454
+ last_update: string;
455
+ }>(object);
456
+
457
+ return {
458
+ id: object.objectId,
459
+ type: normalizeStructTag(object.type ?? ''),
460
+ maxPoint: Number(fields.max_distributed_point),
461
+ distributedPoint: Number(fields.distributed_point),
462
+ pointPerPeriod: Number(fields.distributed_point_per_period),
463
+ period: Number(fields.point_distribution_time),
464
+ maxStake: Number(fields.max_stakes),
465
+ stakeType: normalizeStructTag(String(fields.stake_type)),
466
+ totalStaked: Number(fields.stakes),
467
+ index: Number(fields.index),
468
+ createdAt: Number(fields.created_at),
469
+ lastUpdate: Number(fields.last_update),
470
+ };
471
+ };
@@ -0,0 +1,135 @@
1
+ /**
2
+ * spools
3
+ * spool reward pools
4
+ * stake accounts
5
+ */
6
+
7
+ import type { IndexerDataSource } from 'src/datasources/indexer.js';
8
+ import type { OnChainDataSource } from 'src/datasources/onchain.js';
9
+ import type { CoinPrices } from 'src/types/utils.js';
10
+ import { BaseRepository } from '../base.js';
11
+ import type { QuerySource } from '../utils.js';
12
+ import { runWithDataSourceFallback } from '../utils.js';
13
+ import type {
14
+ SpoolReadArgs,
15
+ SpoolRepoParams,
16
+ SpoolRepoContext,
17
+ SpoolMetadata,
18
+ } from './types.js';
19
+ import {
20
+ getSpoolFromIndexer,
21
+ getSpoolFromOnChain,
22
+ getSpoolRewardPoolsFromOnChain,
23
+ getSpoolsFromIndexer,
24
+ getSpoolsFromOnChain,
25
+ getStakeAccountsFromOnChain,
26
+ getStakePoolFromOnChain,
27
+ } from './helpers.js';
28
+
29
+ export class SpoolRepository extends BaseRepository<
30
+ SpoolRepoContext,
31
+ SpoolMetadata
32
+ > {
33
+ private readonly indexer: IndexerDataSource;
34
+ private readonly onchain: OnChainDataSource;
35
+
36
+ constructor({ indexer, onchain, ...params }: SpoolRepoParams) {
37
+ super(params);
38
+ this.indexer = indexer;
39
+ this.onchain = onchain;
40
+ }
41
+
42
+ get context() {
43
+ return {
44
+ ...this.baseContext,
45
+ indexer: this.indexer,
46
+ onchain: this.onchain,
47
+ };
48
+ }
49
+
50
+ getSpools({
51
+ stakeCoinNames,
52
+ coinPrices,
53
+ source = 'api-first',
54
+ }: SpoolReadArgs & {
55
+ source?: QuerySource;
56
+ }) {
57
+ return runWithDataSourceFallback({
58
+ source,
59
+ label: 'SpoolRepository.getSpools',
60
+ logger: this.logger,
61
+ api: () =>
62
+ getSpoolsFromIndexer(this.context, {
63
+ stakeCoinNames,
64
+ coinPrices,
65
+ }),
66
+ onchain: () =>
67
+ getSpoolsFromOnChain(this.context, {
68
+ stakeCoinNames,
69
+ coinPrices,
70
+ }),
71
+ });
72
+ }
73
+
74
+ getSpool({
75
+ stakeCoinName,
76
+ coinPrices,
77
+ source = 'api-first',
78
+ }: {
79
+ stakeCoinName: string;
80
+ coinPrices: CoinPrices;
81
+ source?: QuerySource;
82
+ }) {
83
+ return runWithDataSourceFallback({
84
+ source,
85
+ label: 'SpoolRepository.getSpool',
86
+ logger: this.logger,
87
+ api: () =>
88
+ getSpoolFromIndexer(this.context, {
89
+ stakeCoinName,
90
+ coinPrices,
91
+ }),
92
+ onchain: () =>
93
+ getSpoolFromOnChain(this.context, {
94
+ stakeCoinName,
95
+ coinPrices,
96
+ }),
97
+ });
98
+ }
99
+
100
+ /**
101
+ * Get stake accounts for all pools
102
+ */
103
+ getStakeAccounts(args: {
104
+ address: string;
105
+ stakeCoinNames?: readonly string[];
106
+ }) {
107
+ return getStakeAccountsFromOnChain(this.context, args);
108
+ }
109
+
110
+ /**
111
+ * Get stake accounts for a specific pool
112
+ */
113
+ async getStakeAccountsByPool(address: string, poolName: string) {
114
+ const allStakeAccounts = await this.getStakeAccounts({
115
+ address,
116
+ stakeCoinNames: [poolName],
117
+ });
118
+ return allStakeAccounts[poolName] || [];
119
+ }
120
+
121
+ getSpoolRewardPools(stakeCoinNames: string[]) {
122
+ return getSpoolRewardPoolsFromOnChain(this.context, { stakeCoinNames });
123
+ }
124
+
125
+ getSpoolRewardPool(stakeCoinName: string) {
126
+ return getSpoolRewardPoolsFromOnChain(this.context, {
127
+ stakeCoinNames: [stakeCoinName],
128
+ }).then((rewardPools) => rewardPools[stakeCoinName]);
129
+ }
130
+
131
+ /** The raw staking pool (`spool`) object for a stake market coin. */
132
+ getStakePool(stakeCoinName: string) {
133
+ return getStakePoolFromOnChain(this.context, stakeCoinName);
134
+ }
135
+ }
@@ -1,7 +1,72 @@
1
- type OptionalKeys<T> = {
2
- [K in keyof T]?: T[K];
1
+ import type { IndexerDataSource } from 'src/datasources/indexer.js';
2
+ import type { OnChainDataSource } from 'src/datasources/onchain.js';
3
+ import type { PoolAddress } from 'src/types/constant/index.js';
4
+ import type { SuiObjectData } from 'src/types/index.js';
5
+ import type { CoinPrices, OptionalKeys } from 'src/types/utils.js';
6
+ import type { BaseContext, BaseRepoParams } from '../types.js';
7
+
8
+ export type SpoolMetadata = {
9
+ whitelist: {
10
+ spool: ReadonlySet<string>;
11
+ };
12
+ addresses: {
13
+ spoolObjectId: string;
14
+ spools: Record<
15
+ string,
16
+ {
17
+ id: string;
18
+ rewardPoolId: string;
19
+ }
20
+ >;
21
+ };
22
+ poolAddresses: Readonly<Record<string, PoolAddress | undefined>>;
23
+ parseCoinName: (marketCoinName: string) => string;
24
+ parseSymbol: (coinName: string) => string;
25
+ parseCoinType: (coinName: string) => string;
26
+ parseMarketCoinType: (coinName: string) => string;
27
+ parseSCoinType: (sCoinName: string) => string | undefined;
28
+ isMarketCoin: (coinName: string) => boolean;
29
+ getCoinDecimal: (coinName: string) => number;
30
+ getSpoolRewardCoinName: () => string;
31
+ };
32
+
33
+ export type SpoolRepoContext = BaseContext & {
34
+ indexer: IndexerDataSource;
35
+ onchain: OnChainDataSource;
36
+ metadata: SpoolMetadata;
37
+ };
38
+
39
+ export type SpoolRepoParams = BaseRepoParams & {
40
+ indexer: IndexerDataSource;
41
+ onchain: OnChainDataSource;
42
+ metadata: SpoolMetadata;
43
+ };
44
+
45
+ /** Minimal context for indexer-sourced spool reads (no on-chain client). */
46
+ export type SpoolIndexerContext = Pick<
47
+ SpoolRepoContext,
48
+ 'indexer' | 'fetchWithCache' | 'metadata'
49
+ >;
50
+
51
+ /** Minimal context for on-chain spool reads (no indexer). */
52
+ export type SpoolOnChainContext = Pick<
53
+ SpoolRepoContext,
54
+ 'onchain' | 'fetchWithCache' | 'metadata'
55
+ >;
56
+
57
+ export type SpoolReadArgs = {
58
+ coinPrices?: CoinPrices;
59
+ stakeCoinNames?: readonly string[];
3
60
  };
4
61
 
62
+ export type RequiredSpoolObjects = Record<
63
+ string,
64
+ {
65
+ spool?: SuiObjectData;
66
+ spoolReward?: SuiObjectData;
67
+ }
68
+ >;
69
+
5
70
  export type Spools = OptionalKeys<Record<string, Spool>>;
6
71
 
7
72
  export type Spool = {