@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,394 @@
1
+ import { CoinBalanceContext } from './types.js';
2
+ import { logError } from '../utils.js';
3
+ import { ScallopRpcError, ScallopParseError } from 'src/errors/index.js';
4
+ import { normalizeStructTag } from '@mysten/sui/utils';
5
+ import { SuiClientTypes } from '@mysten/sui/client';
6
+ import { queryKeys } from 'src/constants/queryKeys.js';
7
+ import type { QueryClient } from '@tanstack/query-core';
8
+ import { getSharedObjectData } from 'src/utils/object.js';
9
+ import { SuiTxBlock } from '@scallop-io/sui-kit';
10
+ import { bcs } from '@mysten/sui/bcs';
11
+ import { BigNumber } from 'bignumber.js';
12
+
13
+ const getUserBalanceFromOnChain = async (
14
+ ctx: Pick<CoinBalanceContext, 'onchain'>,
15
+ { address, coinType }: { address: string; coinType: string }
16
+ ) => {
17
+ const { onchain } = ctx;
18
+ const result = await onchain.client.getBalance({
19
+ owner: address,
20
+ coinType,
21
+ });
22
+
23
+ return result.balance;
24
+ };
25
+
26
+ const updateCachedBalance = (
27
+ queryClient: QueryClient,
28
+ {
29
+ balance,
30
+ coinType,
31
+ address,
32
+ node,
33
+ }: {
34
+ balance: SuiClientTypes.Balance;
35
+ coinType: string;
36
+ address: string;
37
+ node: string;
38
+ }
39
+ ) => {
40
+ const key = queryKeys.rpc.getCoinBalance({
41
+ node,
42
+ address,
43
+ coinType,
44
+ });
45
+
46
+ queryClient.setQueryData<SuiClientTypes.Balance>(key, balance, {
47
+ updatedAt: Date.now(),
48
+ });
49
+ };
50
+
51
+ const getUserBalancesFromOnChain = async (
52
+ ctx: Pick<CoinBalanceContext, 'onchain'> & {
53
+ queryClient: QueryClient;
54
+ },
55
+ address: string
56
+ ) => {
57
+ const { onchain, queryClient } = ctx;
58
+ const allBalances: SuiClientTypes.Balance[] = [];
59
+ let cursor: string | null = null;
60
+ let hasNextPage = true;
61
+
62
+ while (hasNextPage) {
63
+ const result = await onchain.client.listBalances({
64
+ owner: address,
65
+ cursor,
66
+ limit: 50,
67
+ });
68
+ allBalances.push(...(result.balances ?? []));
69
+ hasNextPage = result.hasNextPage;
70
+ cursor = result.cursor;
71
+ }
72
+
73
+ if (!allBalances.length) return {};
74
+
75
+ const balances = allBalances.reduce(
76
+ (acc, curr) => {
77
+ if (curr.balance !== '0') {
78
+ const coinType = normalizeStructTag(curr.coinType);
79
+ acc[coinType] = curr;
80
+ updateCachedBalance(queryClient, {
81
+ balance: curr,
82
+ coinType,
83
+ address,
84
+ node: onchain.url,
85
+ });
86
+ }
87
+ return acc;
88
+ },
89
+ {} as Record<string, SuiClientTypes.Balance>
90
+ );
91
+
92
+ return balances;
93
+ };
94
+
95
+ export const getCoinAmountsFromOnChain = async (
96
+ ctx: Pick<
97
+ CoinBalanceContext,
98
+ 'onchain' | 'fetchWithCache' | 'metadata' | 'queryClient'
99
+ >,
100
+ readArgs: {
101
+ coinNames?: string[];
102
+ address: string;
103
+ }
104
+ ) => {
105
+ const { fetchWithCache, onchain, metadata, queryClient } = ctx;
106
+ const { address, coinNames = [...metadata.whitelist.lending.values()] } =
107
+ readArgs;
108
+
109
+ const balances = await fetchWithCache({
110
+ queryKey: queryKeys.rpc.getAllCoinBalances({
111
+ node: onchain.url,
112
+ activeAddress: address,
113
+ }),
114
+ queryFn: () =>
115
+ getUserBalancesFromOnChain({ onchain, queryClient }, address),
116
+ });
117
+
118
+ // Dense map: every requested coin is present, defaulting to 0 (matches the
119
+ // legacy getCoinAmounts contract — number values, no dropped keys).
120
+ const filteredBalances = coinNames.reduce(
121
+ (acc, coinName) => {
122
+ const coinType = metadata.parseCoinType(coinName);
123
+ acc[coinName] = coinType ? Number(balances[coinType]?.balance ?? 0) : 0;
124
+ return acc;
125
+ },
126
+ {} as Record<string, number>
127
+ );
128
+
129
+ return filteredBalances;
130
+ };
131
+
132
+ export const getCoinAmountFromOnChain = async (
133
+ ctx: Pick<CoinBalanceContext, 'onchain' | 'fetchWithCache' | 'metadata'>,
134
+ readArgs: {
135
+ coinName: string;
136
+ address: string;
137
+ }
138
+ ): Promise<number> => {
139
+ const { fetchWithCache, onchain, metadata } = ctx;
140
+ const { address, coinName } = readArgs;
141
+
142
+ const coinType = metadata.parseCoinType(coinName);
143
+ if (!coinType) return 0;
144
+
145
+ const balance = await fetchWithCache({
146
+ queryKey: queryKeys.rpc.getCoinBalance({
147
+ node: onchain.url,
148
+ address,
149
+ coinType,
150
+ }),
151
+ queryFn: () =>
152
+ getUserBalanceFromOnChain({ onchain }, { address, coinType }),
153
+ });
154
+
155
+ return Number(balance?.balance ?? 0);
156
+ };
157
+
158
+ export const getSCoinAmountsFromOnChain = async (
159
+ ctx: Pick<
160
+ CoinBalanceContext,
161
+ 'onchain' | 'fetchWithCache' | 'metadata' | 'queryClient'
162
+ >,
163
+ readArgs: {
164
+ sCoinNames?: string[];
165
+ address: string;
166
+ }
167
+ ) => {
168
+ const { fetchWithCache, onchain, metadata, queryClient } = ctx;
169
+ const { address, sCoinNames = [...metadata.whitelist.scoin.values()] } =
170
+ readArgs;
171
+
172
+ const balances = await fetchWithCache({
173
+ queryKey: queryKeys.rpc.getAllCoinBalances({
174
+ node: onchain.url,
175
+ activeAddress: address,
176
+ }),
177
+ queryFn: () =>
178
+ getUserBalancesFromOnChain({ onchain, queryClient }, address),
179
+ });
180
+
181
+ const filteredBalances = sCoinNames.reduce(
182
+ (acc, coinName) => {
183
+ const sCoinType = metadata.parseSCoinType(coinName);
184
+ acc[coinName] = sCoinType ? Number(balances[sCoinType]?.balance ?? 0) : 0;
185
+ return acc;
186
+ },
187
+ {} as Record<string, number>
188
+ );
189
+
190
+ return filteredBalances;
191
+ };
192
+
193
+ export const getSCoinAmountFromOnChain = async (
194
+ ctx: Pick<CoinBalanceContext, 'onchain' | 'fetchWithCache' | 'metadata'>,
195
+ readArgs: {
196
+ sCoinName: string;
197
+ address: string;
198
+ }
199
+ ) => {
200
+ const { fetchWithCache, onchain, metadata } = ctx;
201
+ const { address, sCoinName } = readArgs;
202
+
203
+ const sCoinType = metadata.parseSCoinType(sCoinName);
204
+ if (!sCoinType) return 0;
205
+
206
+ const balance = await fetchWithCache({
207
+ queryKey: queryKeys.rpc.getCoinBalance({
208
+ node: onchain.url,
209
+ address,
210
+ coinType: sCoinType,
211
+ }),
212
+ queryFn: () =>
213
+ getUserBalanceFromOnChain({ onchain }, { address, coinType: sCoinType }),
214
+ });
215
+
216
+ return Number(balance?.balance ?? 0);
217
+ };
218
+
219
+ export const getMarketCoinAmountsFromOnChain = async (
220
+ ctx: Pick<
221
+ CoinBalanceContext,
222
+ 'onchain' | 'fetchWithCache' | 'metadata' | 'queryClient'
223
+ >,
224
+ readArgs: {
225
+ marketCoinNames?: string[];
226
+ address: string;
227
+ }
228
+ ) => {
229
+ const { fetchWithCache, onchain, metadata, queryClient } = ctx;
230
+ const { address, marketCoinNames = [...metadata.whitelist.scoin.values()] } =
231
+ readArgs;
232
+
233
+ const balances = await fetchWithCache({
234
+ queryKey: queryKeys.rpc.getAllCoinBalances({
235
+ node: onchain.url,
236
+ activeAddress: address,
237
+ }),
238
+ queryFn: () =>
239
+ getUserBalancesFromOnChain({ onchain, queryClient }, address),
240
+ });
241
+
242
+ const filteredBalances = marketCoinNames.reduce(
243
+ (acc, coinName) => {
244
+ const marketCoinType = metadata.parseMarketCoinType(coinName);
245
+ acc[coinName] = marketCoinType
246
+ ? Number(balances[marketCoinType]?.balance ?? 0)
247
+ : 0;
248
+ return acc;
249
+ },
250
+ {} as Record<string, number>
251
+ );
252
+
253
+ return filteredBalances;
254
+ };
255
+
256
+ export const getMarketCoinAmountFromOnChain = async (
257
+ ctx: Pick<CoinBalanceContext, 'onchain' | 'fetchWithCache' | 'metadata'>,
258
+ readArgs: {
259
+ marketCoinName: string;
260
+ address: string;
261
+ }
262
+ ) => {
263
+ const { fetchWithCache, onchain, metadata } = ctx;
264
+ const { address, marketCoinName } = readArgs;
265
+
266
+ const marketCoinType = metadata.parseMarketCoinType(marketCoinName);
267
+ if (!marketCoinType) return 0;
268
+
269
+ const balance = await fetchWithCache({
270
+ queryKey: queryKeys.rpc.getCoinBalance({
271
+ node: onchain.url,
272
+ address,
273
+ coinType: marketCoinType,
274
+ }),
275
+ queryFn: () =>
276
+ getUserBalanceFromOnChain(
277
+ { onchain },
278
+ { address, coinType: marketCoinType }
279
+ ),
280
+ });
281
+
282
+ return Number(balance?.balance ?? 0);
283
+ };
284
+
285
+ export const querySCoinTotalSupplyFromOnChain = async (
286
+ ctx: Pick<
287
+ CoinBalanceContext,
288
+ 'onchain' | 'fetchWithCache' | 'metadata' | 'logger'
289
+ >,
290
+ sCoinName: string
291
+ ) => {
292
+ const { onchain, metadata, fetchWithCache } = ctx;
293
+ const {
294
+ getCoinDecimal,
295
+ parseSCoinType,
296
+ parseUnderlyingSCoinType,
297
+ parseCoinName,
298
+ addresses: { scoin },
299
+ } = metadata;
300
+
301
+ const queryTarget = `${scoin.id}::s_coin_converter::total_supply`;
302
+ const treasury = scoin.coins[sCoinName]?.treasury;
303
+ if (!treasury) {
304
+ throw logError(
305
+ ctx.logger,
306
+ new ScallopParseError(
307
+ `Treasury address not found for sCoin: ${sCoinName}`,
308
+ {
309
+ context: { sCoinName },
310
+ }
311
+ )
312
+ );
313
+ }
314
+
315
+ const tx = new SuiTxBlock();
316
+ const treasuryObject = await fetchWithCache({
317
+ queryKey: queryKeys.rpc.getSharedObject({
318
+ objectId: treasury,
319
+ node: onchain.url,
320
+ }),
321
+ queryFn: () => onchain.getObject({ objectId: treasury }),
322
+ });
323
+ if (!treasuryObject.object) {
324
+ throw logError(
325
+ ctx.logger,
326
+ new ScallopRpcError(`Failed to fetch treasury object ${treasury}`, {
327
+ context: { treasury },
328
+ })
329
+ );
330
+ }
331
+ const args = [
332
+ await getSharedObjectData(
333
+ { onchain, fetchWithCache },
334
+ {
335
+ tx,
336
+ objectId: treasuryObject.object,
337
+ }
338
+ ),
339
+ ];
340
+ const typeArgs = [
341
+ parseSCoinType(sCoinName),
342
+ parseUnderlyingSCoinType(sCoinName),
343
+ ].filter((t): t is string => !!t);
344
+ tx.moveCall(queryTarget, args, typeArgs);
345
+
346
+ const include: Omit<
347
+ SuiClientTypes.SimulateTransactionOptions<{
348
+ commandResults: true;
349
+ }>,
350
+ 'transaction'
351
+ > = {
352
+ include: {
353
+ commandResults: true,
354
+ },
355
+ };
356
+
357
+ const queryResults = await fetchWithCache({
358
+ queryKey: queryKeys.rpc.getInspectTxn({
359
+ queryTarget,
360
+ args,
361
+ typeArgs,
362
+ node: onchain.url,
363
+ ...include,
364
+ }),
365
+ queryFn: () =>
366
+ onchain.client.simulateTransaction<{ commandResults: true }>({
367
+ ...include,
368
+ transaction: tx.txBlock,
369
+ }),
370
+ });
371
+
372
+ const commandResults = queryResults?.commandResults;
373
+ if (!commandResults) {
374
+ throw logError(
375
+ ctx.logger,
376
+ new ScallopRpcError(
377
+ `Failed to query total supply for ${sCoinName}: ${queryResults[queryResults.$kind]?.status.error?.message}`,
378
+ { context: { sCoinName } }
379
+ )
380
+ );
381
+ }
382
+
383
+ if (
384
+ commandResults &&
385
+ commandResults[0]?.returnValues &&
386
+ commandResults[0].returnValues[0]
387
+ ) {
388
+ const value = commandResults[0].returnValues[0].bcs;
389
+ const decimal = getCoinDecimal(parseCoinName(sCoinName) ?? '') ?? 0;
390
+ return BigNumber(bcs.u64().parse(value)).shiftedBy(-decimal).toNumber();
391
+ }
392
+
393
+ return 0;
394
+ };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * coin balance by address
3
+ * total supply of scoin
4
+ */
5
+
6
+ import { BaseRepository } from '../base.js';
7
+ import { OnChainDataSource } from 'src/datasources/onchain.js';
8
+ import {
9
+ getCoinAmountFromOnChain,
10
+ getCoinAmountsFromOnChain,
11
+ getMarketCoinAmountFromOnChain,
12
+ getMarketCoinAmountsFromOnChain,
13
+ getSCoinAmountFromOnChain,
14
+ getSCoinAmountsFromOnChain,
15
+ querySCoinTotalSupplyFromOnChain,
16
+ } from './helpers.js';
17
+ import {
18
+ CoinBalanceContext,
19
+ CoinBalanceMetadata,
20
+ CoinBalanceRepoParams,
21
+ } from './types.js';
22
+
23
+ export class CoinBalanceRepository extends BaseRepository<
24
+ CoinBalanceContext,
25
+ CoinBalanceMetadata
26
+ > {
27
+ private readonly onchain: OnChainDataSource;
28
+
29
+ constructor({ onchain, ...params }: CoinBalanceRepoParams) {
30
+ super(params);
31
+ this.onchain = onchain;
32
+ }
33
+
34
+ get context() {
35
+ return {
36
+ ...this.baseContext,
37
+ onchain: this.onchain,
38
+ queryClient: this.queryClient,
39
+ };
40
+ }
41
+
42
+ getCoinAmounts(args: { coinNames?: string[]; address: string }) {
43
+ return getCoinAmountsFromOnChain(this.context, args);
44
+ }
45
+
46
+ getCoinAmount(args: { coinName: string; address: string }) {
47
+ return getCoinAmountFromOnChain(this.context, args);
48
+ }
49
+
50
+ getSCoinAmounts(args: { sCoinNames?: string[]; address: string }) {
51
+ return getSCoinAmountsFromOnChain(this.context, args);
52
+ }
53
+
54
+ getSCoinAmount(args: { sCoinName: string; address: string }) {
55
+ return getSCoinAmountFromOnChain(this.context, args);
56
+ }
57
+
58
+ getSCoinTotalSupply(sCoinName: string) {
59
+ return querySCoinTotalSupplyFromOnChain(this.context, sCoinName);
60
+ }
61
+
62
+ getMarketCoinAmounts(args: { marketCoinNames?: string[]; address: string }) {
63
+ return getMarketCoinAmountsFromOnChain(this.context, args);
64
+ }
65
+
66
+ getMarketCoinAmount(args: { marketCoinName: string; address: string }) {
67
+ return getMarketCoinAmountFromOnChain(this.context, args);
68
+ }
69
+ }
@@ -0,0 +1,33 @@
1
+ import type { QueryClient } from '@tanstack/query-core';
2
+ import { BaseContext, BaseRepoParams } from '../types.js';
3
+ import { AddressesInterface } from 'src/types/address.js';
4
+ import { OnChainDataSource } from 'src/datasources/onchain.js';
5
+
6
+ export type CoinBalanceMetadata = {
7
+ whitelist: {
8
+ lending: ReadonlySet<string>;
9
+ scoin: ReadonlySet<string>;
10
+ };
11
+ addresses: {
12
+ scoin: AddressesInterface['scoin'];
13
+ };
14
+ parseCoinType: (coinName: string) => string | undefined;
15
+ parseSCoinType: (sCoinName: string) => string | undefined;
16
+ parseMarketCoinType: (coinName: string) => string | undefined;
17
+ parseSCoinNameFromType: (sCoinType: string) => string | undefined;
18
+ parseUnderlyingSCoinType: (sCoinName: string) => string | undefined;
19
+ getSCoinTreasury: (sCoinName: string) => string | undefined;
20
+ getCoinDecimal: (coinName: string) => number | undefined;
21
+ parseCoinName: (marketCoinName: string) => string | undefined;
22
+ };
23
+
24
+ export type CoinBalanceContext = BaseContext & {
25
+ onchain: OnChainDataSource;
26
+ queryClient: QueryClient;
27
+ metadata: CoinBalanceMetadata;
28
+ };
29
+
30
+ export type CoinBalanceRepoParams = BaseRepoParams & {
31
+ onchain: OnChainDataSource;
32
+ metadata: CoinBalanceMetadata;
33
+ };
@@ -0,0 +1,3 @@
1
+ export const FEE_DENOMINATOR = 1e4;
2
+ export const FLASHLOAN_FEES_TABLE_ID =
3
+ '0x00481a93b819d744a7d79ecdc6c62c74f2f7cb4779316c4df640415817ac61bb' as const;
@@ -0,0 +1,107 @@
1
+ import { SuiClientTypes } from '@mysten/sui/client';
2
+ import { queryKeys } from 'src/constants/queryKeys.js';
3
+ import { SuiObjectData } from 'src/types/sui.js';
4
+ import { getDfObjectIdAndName, parseObjectAs } from 'src/utils/object.js';
5
+ import { FlashloanRepoContext } from './types.js';
6
+ import type { OnChainReadContext } from '../utils.js';
7
+ import { bcs } from '@mysten/sui/bcs';
8
+ import { FEE_DENOMINATOR, FLASHLOAN_FEES_TABLE_ID } from './const.js';
9
+
10
+ const queryFlashloanFees = async (
11
+ ctx: OnChainReadContext,
12
+ {
13
+ assetTypeMap,
14
+ }: {
15
+ assetTypeMap: Record<string, string>;
16
+ }
17
+ ) => {
18
+ const { onchain, fetchWithCache } = ctx;
19
+
20
+ let cursor: string | null | undefined = null;
21
+ let nextPage: boolean = false;
22
+ const ids: string[] = [];
23
+
24
+ do {
25
+ const inputs: SuiClientTypes.ListDynamicFieldsOptions = {
26
+ parentId: FLASHLOAN_FEES_TABLE_ID,
27
+ limit: 50,
28
+ cursor,
29
+ };
30
+ const resp = await fetchWithCache({
31
+ queryKey: queryKeys.rpc.getDynamicFields(inputs),
32
+ queryFn: () => onchain.client.listDynamicFields(inputs),
33
+ });
34
+
35
+ if (!resp) break;
36
+
37
+ // get the dynamic object ids
38
+ const dynamicFieldObjectIds = resp.dynamicFields
39
+ .filter((field) => {
40
+ const assetType = `0x${bcs.string().parse(field.name.bcs)}`;
41
+ return !!assetTypeMap[assetType];
42
+ })
43
+ .map((field) => field.fieldId);
44
+
45
+ ids.push(...dynamicFieldObjectIds);
46
+ nextPage = resp.hasNextPage;
47
+ cursor = resp.cursor;
48
+ } while (nextPage);
49
+
50
+ if (ids.length === 0) {
51
+ return [];
52
+ }
53
+
54
+ // fetch the dynamic objects in batch
55
+ const include = {
56
+ json: true,
57
+ };
58
+
59
+ const { objects: flashloanFeeObjects } = await fetchWithCache({
60
+ queryKey: queryKeys.rpc.getObjects({
61
+ node: onchain.url,
62
+ objectIds: ids,
63
+ }),
64
+ queryFn: () =>
65
+ onchain.client.getObjects({
66
+ objectIds: ids,
67
+ include,
68
+ }),
69
+ });
70
+
71
+ return flashloanFeeObjects;
72
+ };
73
+
74
+ export const getFlashloanFeesFromOnChain = async (
75
+ ctx: FlashloanRepoContext,
76
+ { assetNames }: { assetNames: string[] }
77
+ ) => {
78
+ const { metadata } = ctx;
79
+ const assetNamesSet = new Set(assetNames);
80
+ const assetTypeMap = Object.fromEntries(
81
+ [...metadata.coinTypeToCoinNameMap.entries()].filter(([, coinName]) =>
82
+ assetNamesSet.has(coinName)
83
+ )
84
+ );
85
+
86
+ const flashloanFeeObjects = await queryFlashloanFees(ctx, {
87
+ assetTypeMap,
88
+ });
89
+
90
+ return flashloanFeeObjects
91
+ .filter((object): object is SuiObjectData => !(object instanceof Error))
92
+ .reduce(
93
+ (prev, curr) => {
94
+ if (curr) {
95
+ const { name } = getDfObjectIdAndName(curr);
96
+ const assetType = `0x${name}`;
97
+ const assetName = assetTypeMap[assetType];
98
+ if (!assetName) return prev;
99
+
100
+ const feeNumerator = +parseObjectAs<string>(curr);
101
+ prev[assetName] = feeNumerator / FEE_DENOMINATOR;
102
+ }
103
+ return prev;
104
+ },
105
+ {} as Record<string, number>
106
+ );
107
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Flashloan Fees
3
+ */
4
+
5
+ import { BaseRepository } from '../base.js';
6
+ import { OnChainDataSource } from '../../datasources/onchain.js';
7
+ import { getFlashloanFeesFromOnChain } from './helpers.js';
8
+ import {
9
+ FlashloanMetadata,
10
+ FlashloanRepoParams,
11
+ FlashloanRepoContext,
12
+ } from './types.js';
13
+
14
+ export class FlashloanRepository extends BaseRepository<
15
+ FlashloanRepoContext,
16
+ FlashloanMetadata
17
+ > {
18
+ private readonly onchain: OnChainDataSource;
19
+
20
+ constructor({ onchain, ...params }: FlashloanRepoParams) {
21
+ super(params);
22
+ this.onchain = onchain;
23
+ }
24
+
25
+ get context() {
26
+ return { ...this.baseContext, onchain: this.onchain };
27
+ }
28
+
29
+ getFlashloanFees(assetNames: string[]) {
30
+ return getFlashloanFeesFromOnChain(this.context, {
31
+ assetNames,
32
+ });
33
+ }
34
+ }
@@ -0,0 +1,16 @@
1
+ import { BaseContext, BaseRepoParams } from '../types.js';
2
+ import { OnChainDataSource } from '../../datasources/onchain.js';
3
+
4
+ export type FlashloanRepoContext = BaseContext & {
5
+ onchain: OnChainDataSource;
6
+ metadata: FlashloanMetadata;
7
+ };
8
+
9
+ export type FlashloanRepoParams = BaseRepoParams & {
10
+ onchain: OnChainDataSource;
11
+ metadata: FlashloanMetadata;
12
+ };
13
+
14
+ export type FlashloanMetadata = {
15
+ coinTypeToCoinNameMap: ReadonlyMap<string, string>;
16
+ };
@@ -0,0 +1,4 @@
1
+ import { bcs } from '@mysten/sui/bcs';
2
+
3
+ export const IsolatedAssetBcs = bcs.bool();
4
+ export const IsolatedAssetTypeBcs = bcs.string();
@@ -0,0 +1,2 @@
1
+ export const ISOLATED_ASSET_KEY_TYPE = `0xe7dbb371a9595631f7964b7ece42255ad0e738cc85fe6da26c7221b220f01af6::market_dynamic_keys::IsolatedAssetKey`; // prod
2
+ // const isolatedAssetKeyType = `0x6c23585e940a989588432509107e98bae06dbca4e333f26d0635d401b3c7c76d::market_dynamic_keys::IsolatedAssetKey`;