@mento-protocol/mento-sdk 3.2.7 → 3.2.8

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 (302) hide show
  1. package/dist/cache/routes.d.ts +13 -0
  2. package/dist/cache/routes.js +14649 -0
  3. package/dist/cache/tokens.d.ts +68 -0
  4. package/dist/cache/tokens.js +488 -0
  5. package/dist/core/abis/activePool.d.ts +2 -0
  6. package/dist/core/abis/activePool.js +14 -0
  7. package/dist/core/abis/addressesRegistry.d.ts +2 -0
  8. package/dist/core/abis/addressesRegistry.js +26 -0
  9. package/dist/core/abis/bipoolmanager.d.ts +34 -0
  10. package/dist/core/abis/bipoolmanager.js +72 -0
  11. package/dist/core/abis/borrowerOperations.d.ts +9 -0
  12. package/dist/core/abis/borrowerOperations.js +89 -0
  13. package/dist/core/abis/breakerbox.d.ts +13 -0
  14. package/dist/core/abis/breakerbox.js +8 -0
  15. package/dist/core/abis/broker.d.ts +2 -0
  16. package/dist/core/abis/broker.js +9 -0
  17. package/dist/core/abis/erc20.d.ts +9 -0
  18. package/dist/core/abis/erc20.js +21 -0
  19. package/dist/core/abis/fpmm.d.ts +270 -0
  20. package/dist/core/abis/fpmm.js +49 -0
  21. package/dist/core/abis/fpmmFactory.d.ts +85 -0
  22. package/dist/core/abis/fpmmFactory.js +26 -0
  23. package/dist/core/abis/hintHelpers.d.ts +2 -0
  24. package/dist/core/abis/hintHelpers.js +14 -0
  25. package/dist/core/abis/index.d.ts +22 -0
  26. package/dist/core/abis/index.js +38 -0
  27. package/dist/core/abis/liquidityStrategy.d.ts +132 -0
  28. package/dist/core/abis/liquidityStrategy.js +10 -0
  29. package/dist/core/abis/multiTroveGetter.d.ts +8 -0
  30. package/dist/core/abis/multiTroveGetter.js +15 -0
  31. package/dist/core/abis/priceFeed.d.ts +7 -0
  32. package/dist/core/abis/priceFeed.js +16 -0
  33. package/dist/core/abis/pricingmodule.d.ts +2 -0
  34. package/dist/core/abis/pricingmodule.js +6 -0
  35. package/dist/core/abis/reserve.d.ts +3 -0
  36. package/dist/core/abis/reserve.js +18 -0
  37. package/dist/core/abis/router.d.ts +521 -0
  38. package/dist/core/abis/router.js +45 -0
  39. package/dist/core/abis/sortedTroves.d.ts +2 -0
  40. package/dist/core/abis/sortedTroves.js +15 -0
  41. package/dist/core/abis/systemParams.d.ts +2 -0
  42. package/dist/core/abis/systemParams.js +14 -0
  43. package/dist/core/abis/troveManager.d.ts +2 -0
  44. package/dist/core/abis/troveManager.js +27 -0
  45. package/dist/core/abis/troveNFT.d.ts +2 -0
  46. package/dist/core/abis/troveNFT.js +9 -0
  47. package/dist/core/abis/virtualPool.d.ts +50 -0
  48. package/dist/core/abis/virtualPool.js +11 -0
  49. package/dist/core/abis/virtualPoolFactory.d.ts +59 -0
  50. package/dist/core/abis/virtualPoolFactory.js +17 -0
  51. package/dist/core/constants/addresses.d.ts +18 -0
  52. package/dist/core/constants/addresses.js +113 -0
  53. package/dist/core/constants/borrowConstants.d.ts +10 -0
  54. package/dist/core/constants/borrowConstants.js +16 -0
  55. package/dist/core/constants/borrowRegistries.d.ts +7 -0
  56. package/dist/core/constants/borrowRegistries.js +34 -0
  57. package/dist/core/constants/chainId.d.ts +7 -0
  58. package/dist/core/constants/chainId.js +11 -0
  59. package/dist/core/constants/contractNames.d.ts +21 -0
  60. package/dist/core/constants/contractNames.js +24 -0
  61. package/dist/core/constants/index.d.ts +6 -0
  62. package/dist/core/constants/index.js +22 -0
  63. package/dist/core/errors/base.d.ts +8 -0
  64. package/dist/core/errors/base.js +17 -0
  65. package/dist/core/errors/index.d.ts +4 -0
  66. package/dist/core/errors/index.js +20 -0
  67. package/dist/core/errors/oracle.d.ts +9 -0
  68. package/dist/core/errors/oracle.js +15 -0
  69. package/dist/core/errors/router.d.ts +14 -0
  70. package/dist/core/errors/router.js +24 -0
  71. package/dist/core/types/borrow.d.ts +87 -0
  72. package/dist/core/types/borrow.js +3 -0
  73. package/dist/core/types/contractAddresses.d.ts +42 -0
  74. package/dist/core/types/contractAddresses.js +3 -0
  75. package/dist/core/types/index.d.ts +10 -0
  76. package/dist/core/types/index.js +26 -0
  77. package/dist/core/types/liquidity.d.ts +194 -0
  78. package/dist/core/types/liquidity.js +3 -0
  79. package/dist/core/types/pool.d.ts +208 -0
  80. package/dist/core/types/pool.js +14 -0
  81. package/dist/core/types/provider.d.ts +45 -0
  82. package/dist/core/types/provider.js +3 -0
  83. package/dist/core/types/route.d.ts +62 -0
  84. package/dist/core/types/route.js +3 -0
  85. package/dist/core/types/token.d.ts +21 -0
  86. package/dist/core/types/token.js +3 -0
  87. package/dist/core/types/tradingLimits.d.ts +91 -0
  88. package/dist/core/types/tradingLimits.js +3 -0
  89. package/dist/core/types/tradingMode.d.ts +24 -0
  90. package/dist/core/types/tradingMode.js +31 -0
  91. package/dist/core/types/transaction.d.ts +45 -0
  92. package/dist/core/types/transaction.js +3 -0
  93. package/dist/esm/cache/routes.js +14644 -0
  94. package/dist/esm/cache/tokens.js +480 -0
  95. package/dist/esm/core/abis/activePool.js +10 -0
  96. package/dist/esm/core/abis/addressesRegistry.js +22 -0
  97. package/dist/esm/core/abis/bipoolmanager.js +68 -0
  98. package/dist/esm/core/abis/borrowerOperations.js +85 -0
  99. package/dist/esm/core/abis/breakerbox.js +4 -0
  100. package/dist/esm/core/abis/broker.js +5 -0
  101. package/dist/esm/core/abis/erc20.js +17 -0
  102. package/dist/esm/core/abis/fpmm.js +45 -0
  103. package/dist/esm/core/abis/fpmmFactory.js +22 -0
  104. package/dist/esm/core/abis/hintHelpers.js +10 -0
  105. package/dist/esm/core/abis/index.js +21 -0
  106. package/dist/esm/core/abis/liquidityStrategy.js +6 -0
  107. package/dist/esm/core/abis/multiTroveGetter.js +11 -0
  108. package/dist/esm/core/abis/priceFeed.js +12 -0
  109. package/dist/esm/core/abis/pricingmodule.js +2 -0
  110. package/dist/esm/core/abis/reserve.js +14 -0
  111. package/dist/esm/core/abis/router.js +41 -0
  112. package/dist/esm/core/abis/sortedTroves.js +11 -0
  113. package/dist/esm/core/abis/systemParams.js +10 -0
  114. package/dist/esm/core/abis/troveManager.js +23 -0
  115. package/dist/esm/core/abis/troveNFT.js +5 -0
  116. package/dist/esm/core/abis/virtualPool.js +7 -0
  117. package/dist/esm/core/abis/virtualPoolFactory.js +13 -0
  118. package/dist/esm/core/constants/addresses.js +107 -0
  119. package/dist/esm/core/constants/borrowConstants.js +12 -0
  120. package/dist/esm/core/constants/borrowRegistries.js +29 -0
  121. package/dist/esm/core/constants/chainId.js +7 -0
  122. package/dist/esm/core/constants/contractNames.js +20 -0
  123. package/dist/esm/core/constants/index.js +5 -0
  124. package/dist/esm/core/errors/base.js +12 -0
  125. package/dist/esm/core/errors/index.js +3 -0
  126. package/dist/esm/core/errors/oracle.js +10 -0
  127. package/dist/esm/core/errors/router.js +18 -0
  128. package/dist/esm/core/types/borrow.js +1 -0
  129. package/dist/esm/core/types/contractAddresses.js +1 -0
  130. package/dist/esm/core/types/index.js +9 -0
  131. package/dist/esm/core/types/liquidity.js +1 -0
  132. package/dist/esm/core/types/pool.js +10 -0
  133. package/dist/esm/core/types/provider.js +1 -0
  134. package/dist/esm/core/types/route.js +1 -0
  135. package/dist/esm/core/types/token.js +1 -0
  136. package/dist/esm/core/types/tradingLimits.js +1 -0
  137. package/dist/esm/core/types/tradingMode.js +26 -0
  138. package/dist/esm/core/types/transaction.js +1 -0
  139. package/dist/esm/index.js +139 -0
  140. package/dist/esm/package.json +1 -0
  141. package/dist/esm/services/borrow/BorrowService.js +455 -0
  142. package/dist/esm/services/borrow/borrowHelpers.js +3 -0
  143. package/dist/esm/services/borrow/borrowMath.js +127 -0
  144. package/dist/esm/services/borrow/index.js +3 -0
  145. package/dist/esm/services/borrow/internal/borrowApprovalService.js +48 -0
  146. package/dist/esm/services/borrow/internal/borrowContextStore.js +35 -0
  147. package/dist/esm/services/borrow/internal/borrowErc20.js +38 -0
  148. package/dist/esm/services/borrow/internal/borrowHints.js +27 -0
  149. package/dist/esm/services/borrow/internal/borrowPositionParser.js +82 -0
  150. package/dist/esm/services/borrow/internal/borrowReadService.js +271 -0
  151. package/dist/esm/services/borrow/internal/borrowRegistryReader.js +108 -0
  152. package/dist/esm/services/borrow/internal/borrowTransactionService.js +271 -0
  153. package/dist/esm/services/borrow/internal/borrowTypes.js +1 -0
  154. package/dist/esm/services/borrow/internal/borrowValidation.js +89 -0
  155. package/dist/esm/services/index.js +8 -0
  156. package/dist/esm/services/liquidity/LiquidityService.js +163 -0
  157. package/dist/esm/services/liquidity/basicLiquidity.js +162 -0
  158. package/dist/esm/services/liquidity/index.js +1 -0
  159. package/dist/esm/services/liquidity/liquidityHelpers.js +95 -0
  160. package/dist/esm/services/liquidity/rebalance.js +59 -0
  161. package/dist/esm/services/liquidity/zapHelpers.js +181 -0
  162. package/dist/esm/services/liquidity/zapIn.js +131 -0
  163. package/dist/esm/services/liquidity/zapOut.js +248 -0
  164. package/dist/esm/services/pools/PoolService.js +204 -0
  165. package/dist/esm/services/pools/index.js +1 -0
  166. package/dist/esm/services/pools/poolDetails.js +209 -0
  167. package/dist/esm/services/pools/poolDiscovery.js +112 -0
  168. package/dist/esm/services/pools/rebalancePreview.js +181 -0
  169. package/dist/esm/services/quotes/QuoteService.js +85 -0
  170. package/dist/esm/services/quotes/index.js +1 -0
  171. package/dist/esm/services/routes/RouteService.js +268 -0
  172. package/dist/esm/services/routes/index.js +1 -0
  173. package/dist/esm/services/swap/SwapService.js +247 -0
  174. package/dist/esm/services/swap/index.js +1 -0
  175. package/dist/esm/services/tokens/index.js +1 -0
  176. package/dist/esm/services/tokens/tokenService.js +285 -0
  177. package/dist/esm/services/trading/TradingLimitsService.js +154 -0
  178. package/dist/esm/services/trading/TradingService.js +222 -0
  179. package/dist/esm/services/trading/index.js +2 -0
  180. package/dist/esm/utils/chainConfig.js +118 -0
  181. package/dist/esm/utils/costUtils.js +56 -0
  182. package/dist/esm/utils/deadline.js +22 -0
  183. package/dist/esm/utils/index.js +9 -0
  184. package/dist/esm/utils/multicall.js +47 -0
  185. package/dist/esm/utils/pathEncoder.js +69 -0
  186. package/dist/esm/utils/rateFeed.js +23 -0
  187. package/dist/esm/utils/retry.js +24 -0
  188. package/dist/esm/utils/routeUtils.js +361 -0
  189. package/dist/esm/utils/routes.js +2 -0
  190. package/dist/esm/utils/sortUtils.js +33 -0
  191. package/dist/esm/utils/tokens.js +2 -0
  192. package/dist/esm/utils/tradingLimits.js +163 -0
  193. package/dist/esm/utils/validation.js +30 -0
  194. package/dist/index.d.ts +101 -0
  195. package/dist/index.js +158 -0
  196. package/dist/services/borrow/BorrowService.d.ts +381 -0
  197. package/dist/services/borrow/BorrowService.js +460 -0
  198. package/dist/services/borrow/borrowHelpers.d.ts +4 -0
  199. package/dist/services/borrow/borrowHelpers.js +13 -0
  200. package/dist/services/borrow/borrowMath.d.ts +21 -0
  201. package/dist/services/borrow/borrowMath.js +137 -0
  202. package/dist/services/borrow/index.d.ts +4 -0
  203. package/dist/services/borrow/index.js +20 -0
  204. package/dist/services/borrow/internal/borrowApprovalService.d.ts +14 -0
  205. package/dist/services/borrow/internal/borrowApprovalService.js +53 -0
  206. package/dist/services/borrow/internal/borrowContextStore.d.ts +11 -0
  207. package/dist/services/borrow/internal/borrowContextStore.js +40 -0
  208. package/dist/services/borrow/internal/borrowErc20.d.ts +5 -0
  209. package/dist/services/borrow/internal/borrowErc20.js +43 -0
  210. package/dist/services/borrow/internal/borrowHints.d.ts +7 -0
  211. package/dist/services/borrow/internal/borrowHints.js +31 -0
  212. package/dist/services/borrow/internal/borrowPositionParser.d.ts +4 -0
  213. package/dist/services/borrow/internal/borrowPositionParser.js +87 -0
  214. package/dist/services/borrow/internal/borrowReadService.d.ts +31 -0
  215. package/dist/services/borrow/internal/borrowReadService.js +276 -0
  216. package/dist/services/borrow/internal/borrowRegistryReader.d.ts +5 -0
  217. package/dist/services/borrow/internal/borrowRegistryReader.js +113 -0
  218. package/dist/services/borrow/internal/borrowTransactionService.d.ts +23 -0
  219. package/dist/services/borrow/internal/borrowTransactionService.js +276 -0
  220. package/dist/services/borrow/internal/borrowTypes.d.ts +15 -0
  221. package/dist/services/borrow/internal/borrowTypes.js +3 -0
  222. package/dist/services/borrow/internal/borrowValidation.d.ts +14 -0
  223. package/dist/services/borrow/internal/borrowValidation.js +104 -0
  224. package/dist/services/index.d.ts +9 -0
  225. package/dist/services/index.js +25 -0
  226. package/dist/services/liquidity/LiquidityService.d.ts +139 -0
  227. package/dist/services/liquidity/LiquidityService.js +168 -0
  228. package/dist/services/liquidity/basicLiquidity.d.ts +11 -0
  229. package/dist/services/liquidity/basicLiquidity.js +172 -0
  230. package/dist/services/liquidity/index.d.ts +2 -0
  231. package/dist/services/liquidity/index.js +18 -0
  232. package/dist/services/liquidity/liquidityHelpers.d.ts +19 -0
  233. package/dist/services/liquidity/liquidityHelpers.js +104 -0
  234. package/dist/services/liquidity/rebalance.d.ts +6 -0
  235. package/dist/services/liquidity/rebalance.js +64 -0
  236. package/dist/services/liquidity/zapHelpers.d.ts +100 -0
  237. package/dist/services/liquidity/zapHelpers.js +192 -0
  238. package/dist/services/liquidity/zapIn.d.ts +18 -0
  239. package/dist/services/liquidity/zapIn.js +138 -0
  240. package/dist/services/liquidity/zapOut.d.ts +9 -0
  241. package/dist/services/liquidity/zapOut.js +255 -0
  242. package/dist/services/pools/PoolService.d.ts +69 -0
  243. package/dist/services/pools/PoolService.js +209 -0
  244. package/dist/services/pools/index.d.ts +2 -0
  245. package/dist/services/pools/index.js +18 -0
  246. package/dist/services/pools/poolDetails.d.ts +13 -0
  247. package/dist/services/pools/poolDetails.js +216 -0
  248. package/dist/services/pools/poolDiscovery.d.ts +12 -0
  249. package/dist/services/pools/poolDiscovery.js +117 -0
  250. package/dist/services/pools/rebalancePreview.d.ts +5 -0
  251. package/dist/services/pools/rebalancePreview.js +186 -0
  252. package/dist/services/quotes/QuoteService.d.ts +51 -0
  253. package/dist/services/quotes/QuoteService.js +91 -0
  254. package/dist/services/quotes/index.d.ts +2 -0
  255. package/dist/services/quotes/index.js +18 -0
  256. package/dist/services/routes/RouteService.d.ts +117 -0
  257. package/dist/services/routes/RouteService.js +306 -0
  258. package/dist/services/routes/index.d.ts +2 -0
  259. package/dist/services/routes/index.js +18 -0
  260. package/dist/services/swap/SwapService.d.ts +198 -0
  261. package/dist/services/swap/SwapService.js +252 -0
  262. package/dist/services/swap/index.d.ts +2 -0
  263. package/dist/services/swap/index.js +18 -0
  264. package/dist/services/tokens/index.d.ts +2 -0
  265. package/dist/services/tokens/index.js +18 -0
  266. package/dist/services/tokens/tokenService.d.ts +55 -0
  267. package/dist/services/tokens/tokenService.js +290 -0
  268. package/dist/services/trading/TradingLimitsService.d.ts +38 -0
  269. package/dist/services/trading/TradingLimitsService.js +159 -0
  270. package/dist/services/trading/TradingService.d.ts +115 -0
  271. package/dist/services/trading/TradingService.js +227 -0
  272. package/dist/services/trading/index.d.ts +3 -0
  273. package/dist/services/trading/index.js +19 -0
  274. package/dist/utils/chainConfig.d.ts +16 -0
  275. package/dist/utils/chainConfig.js +123 -0
  276. package/dist/utils/costUtils.d.ts +12 -0
  277. package/dist/utils/costUtils.js +60 -0
  278. package/dist/utils/deadline.d.ts +21 -0
  279. package/dist/utils/deadline.js +26 -0
  280. package/dist/utils/index.d.ts +10 -0
  281. package/dist/utils/index.js +26 -0
  282. package/dist/utils/multicall.d.ts +30 -0
  283. package/dist/utils/multicall.js +52 -0
  284. package/dist/utils/pathEncoder.d.ts +34 -0
  285. package/dist/utils/pathEncoder.js +73 -0
  286. package/dist/utils/rateFeed.d.ts +18 -0
  287. package/dist/utils/rateFeed.js +27 -0
  288. package/dist/utils/retry.d.ts +12 -0
  289. package/dist/utils/retry.js +28 -0
  290. package/dist/utils/routeUtils.d.ts +295 -0
  291. package/dist/utils/routeUtils.js +371 -0
  292. package/dist/utils/routes.d.ts +3 -0
  293. package/dist/utils/routes.js +8 -0
  294. package/dist/utils/sortUtils.d.ts +24 -0
  295. package/dist/utils/sortUtils.js +39 -0
  296. package/dist/utils/tokens.d.ts +2 -0
  297. package/dist/utils/tokens.js +13 -0
  298. package/dist/utils/tradingLimits.d.ts +41 -0
  299. package/dist/utils/tradingLimits.js +171 -0
  300. package/dist/utils/validation.d.ts +19 -0
  301. package/dist/utils/validation.js +34 -0
  302. package/package.json +1 -1
@@ -0,0 +1,127 @@
1
+ import { MAX_LTV_ALLOWED_RATIO, LTV_RISK_MEDIUM, LTV_RISK_HIGH, REDEMPTION_RISK_MEDIUM, REDEMPTION_RISK_LOW, } from '../../core/constants';
2
+ const DECIMAL_PRECISION = 10n ** 18n;
3
+ const THOUSAND = 1000n;
4
+ const NORMALIZED_MAX_LTV = DECIMAL_PRECISION;
5
+ const DEBT_SUGGESTION_RATIOS = [300n, 600n, 800n];
6
+ export function getLtv(collateral, debt, collPrice) {
7
+ const collateralUsd = (collateral * collPrice) / DECIMAL_PRECISION;
8
+ if (collateralUsd === 0n)
9
+ return null;
10
+ return (debt * DECIMAL_PRECISION) / collateralUsd;
11
+ }
12
+ export function getLiquidationPrice(collateral, debt, mcr) {
13
+ if (collateral <= 0n || debt <= 0n)
14
+ return null;
15
+ if (mcr <= DECIMAL_PRECISION)
16
+ return null;
17
+ // liquidationPrice = (debt * mcr) / collateral
18
+ return (debt * mcr) / collateral;
19
+ }
20
+ export function getLiquidationRisk(ltv, maxLtv) {
21
+ if (maxLtv <= 0n)
22
+ return 'low';
23
+ // Compare using cross-multiplication to avoid floor-division drift.
24
+ if (ltv * THOUSAND > maxLtv * LTV_RISK_HIGH)
25
+ return 'high';
26
+ if (ltv * THOUSAND > maxLtv * LTV_RISK_MEDIUM)
27
+ return 'medium';
28
+ return 'low';
29
+ }
30
+ export function getRedemptionRisk(debtInFront, totalDebt) {
31
+ if (totalDebt === 0n)
32
+ return null;
33
+ // Compare using cross-multiplication to avoid floor-division drift.
34
+ if (debtInFront * THOUSAND > totalDebt * REDEMPTION_RISK_LOW)
35
+ return 'low';
36
+ if (debtInFront * THOUSAND > totalDebt * REDEMPTION_RISK_MEDIUM)
37
+ return 'medium';
38
+ return 'high';
39
+ }
40
+ export function calculateMaxDebt(collateralUsd, maxLtv) {
41
+ return (collateralUsd * maxLtv) / DECIMAL_PRECISION;
42
+ }
43
+ export function calculateDebtSuggestions(maxDebt, minDebt) {
44
+ if (maxDebt <= 0n)
45
+ return [];
46
+ const suggestions = [];
47
+ for (let i = 0; i < DEBT_SUGGESTION_RATIOS.length; i++) {
48
+ let amount = (maxDebt * DEBT_SUGGESTION_RATIOS[i]) / THOUSAND;
49
+ // Mirror frontend behavior:
50
+ // - First suggestion is clamped up to minDebt.
51
+ // - Later suggestions below minDebt are omitted.
52
+ if (amount < minDebt) {
53
+ if (i === 0) {
54
+ amount = minDebt;
55
+ }
56
+ else {
57
+ continue;
58
+ }
59
+ }
60
+ // ltv relative to maxLtv (normalized to 1e18 where 1e18 === maxLtv)
61
+ const ltv = (amount * DECIMAL_PRECISION) / maxDebt;
62
+ // Hide suggestions that exceed maxLtv.
63
+ if (ltv > NORMALIZED_MAX_LTV) {
64
+ continue;
65
+ }
66
+ suggestions.push({
67
+ amount,
68
+ ltv,
69
+ risk: getLiquidationRisk(ltv, NORMALIZED_MAX_LTV),
70
+ });
71
+ }
72
+ return suggestions;
73
+ }
74
+ /**
75
+ * Computes collateralization metrics from the supplied collateral, debt, price, and MCR.
76
+ *
77
+ * This helper is lifecycle-agnostic: it does not inspect trove status. For example, a zombie
78
+ * trove with `debt === 0` may still hold collateral on-chain, but this function only reports
79
+ * the math implied by the inputs. Combine its output with `BorrowPosition.status` when building
80
+ * UI for open, zombie, closed, or liquidated troves.
81
+ */
82
+ export function getLoanDetails(collateral, debt, interestRate, collPrice, mcr) {
83
+ // maxLtv = 1 / MCR (MCR is e.g. 1.1e18 meaning 110%)
84
+ const maxLtv = (DECIMAL_PRECISION * DECIMAL_PRECISION) / mcr;
85
+ // maxLtvAllowed = maxLtv * MAX_LTV_ALLOWED_RATIO / 1000
86
+ const maxLtvAllowed = (maxLtv * MAX_LTV_ALLOWED_RATIO) / THOUSAND;
87
+ const collateralUsd = collateral !== null && collPrice !== null
88
+ ? (collateral * collPrice) / DECIMAL_PRECISION
89
+ : null;
90
+ const ltv = debt !== null && collateralUsd !== null && collateralUsd > 0n
91
+ ? (debt * DECIMAL_PRECISION) / collateralUsd
92
+ : collateral !== null && collateral < 0n
93
+ ? DECIMAL_PRECISION
94
+ : null;
95
+ const status = ltv === null
96
+ ? null
97
+ : collateral !== null && (collateral < 0n || ltv > DECIMAL_PRECISION)
98
+ ? 'underwater'
99
+ : ltv > maxLtv
100
+ ? 'liquidatable'
101
+ : ltv > maxLtvAllowed
102
+ ? 'at-risk'
103
+ : 'healthy';
104
+ const maxDebt = collateralUsd !== null ? calculateMaxDebt(collateralUsd, maxLtv) : null;
105
+ const maxDebtAllowed = collateralUsd !== null && collateralUsd > 0n
106
+ ? (collateralUsd * maxLtvAllowed) / DECIMAL_PRECISION
107
+ : null;
108
+ const liquidationRisk = ltv !== null ? getLiquidationRisk(ltv, maxLtv) : null;
109
+ const liquidationPrice = collateral !== null && debt !== null && collateral > 0n
110
+ ? getLiquidationPrice(collateral, debt, mcr)
111
+ : null;
112
+ return {
113
+ collateral,
114
+ collateralUsd,
115
+ collPrice,
116
+ debt,
117
+ interestRate,
118
+ ltv,
119
+ maxLtv,
120
+ maxLtvAllowed,
121
+ liquidationPrice,
122
+ liquidationRisk,
123
+ maxDebt,
124
+ maxDebtAllowed,
125
+ status,
126
+ };
127
+ }
@@ -0,0 +1,3 @@
1
+ export * from './BorrowService';
2
+ export * from './borrowHelpers';
3
+ export * from './borrowMath';
@@ -0,0 +1,48 @@
1
+ import { encodeFunctionData } from 'viem';
2
+ import { ERC20_ABI } from '../../../core/abis';
3
+ import { buildErc20ApprovalParams, readErc20Allowance } from './borrowErc20';
4
+ import { requireAddress, requireNonNegativeBigInt } from './borrowValidation';
5
+ const ZERO_VALUE = '0';
6
+ export class BorrowApprovalService {
7
+ constructor(publicClient) {
8
+ this.publicClient = publicClient;
9
+ }
10
+ buildCollateralApprovalParams(ctx, amount) {
11
+ const approvalAmount = requireNonNegativeBigInt(amount, 'amount');
12
+ return buildErc20ApprovalParams(ctx.addresses.collToken, ctx.addresses.borrowerOperations, approvalAmount);
13
+ }
14
+ buildDebtApprovalParams(ctx, spender, amount) {
15
+ const spenderAddress = requireAddress(spender, 'spender');
16
+ const approvalAmount = requireNonNegativeBigInt(amount, 'amount');
17
+ const data = encodeFunctionData({
18
+ abi: ERC20_ABI,
19
+ functionName: 'approve',
20
+ args: [spenderAddress, approvalAmount],
21
+ });
22
+ return { to: ctx.addresses.debtToken, data, value: ZERO_VALUE };
23
+ }
24
+ buildGasCompensationApprovalParams(ctx, amount) {
25
+ const approvalAmount = amount === undefined
26
+ ? ctx.systemParams.ethGasCompensation
27
+ : requireNonNegativeBigInt(amount, 'amount');
28
+ return buildErc20ApprovalParams(ctx.addresses.gasToken, ctx.addresses.borrowerOperations, approvalAmount);
29
+ }
30
+ async getCollateralAllowance(ctx, owner) {
31
+ const ownerAddress = requireAddress(owner, 'owner');
32
+ return readErc20Allowance(this.publicClient, ctx.addresses.collToken, ownerAddress, ctx.addresses.borrowerOperations);
33
+ }
34
+ async getDebtAllowance(ctx, owner, spender) {
35
+ const ownerAddress = requireAddress(owner, 'owner');
36
+ const spenderAddress = requireAddress(spender, 'spender');
37
+ return (await this.publicClient.readContract({
38
+ address: ctx.addresses.debtToken,
39
+ abi: ERC20_ABI,
40
+ functionName: 'allowance',
41
+ args: [ownerAddress, spenderAddress],
42
+ }));
43
+ }
44
+ async getGasTokenAllowance(ctx, owner) {
45
+ const ownerAddress = requireAddress(owner, 'owner');
46
+ return readErc20Allowance(this.publicClient, ctx.addresses.gasToken, ownerAddress, ctx.addresses.borrowerOperations);
47
+ }
48
+ }
@@ -0,0 +1,35 @@
1
+ import { getBorrowRegistry } from '../../../core/constants';
2
+ import { readSystemParams, resolveAddressesFromRegistry } from './borrowRegistryReader';
3
+ import { requireDebtTokenSymbol } from './borrowValidation';
4
+ export class BorrowContextStore {
5
+ constructor(publicClient, chainId) {
6
+ this.publicClient = publicClient;
7
+ this.chainId = chainId;
8
+ this.deployments = new Map();
9
+ this.initializing = new Map();
10
+ }
11
+ async ensureInitialized(debtTokenSymbol) {
12
+ const symbol = requireDebtTokenSymbol(debtTokenSymbol);
13
+ const cached = this.deployments.get(symbol);
14
+ if (cached)
15
+ return cached;
16
+ const inFlight = this.initializing.get(symbol);
17
+ if (inFlight)
18
+ return inFlight;
19
+ const initPromise = (async () => {
20
+ const registryAddress = getBorrowRegistry(this.chainId, symbol);
21
+ const addresses = await resolveAddressesFromRegistry(this.publicClient, registryAddress);
22
+ const systemParams = await readSystemParams(this.publicClient, addresses.borrowerOperations);
23
+ const ctx = { addresses, systemParams };
24
+ this.deployments.set(symbol, ctx);
25
+ return ctx;
26
+ })();
27
+ this.initializing.set(symbol, initPromise);
28
+ try {
29
+ return await initPromise;
30
+ }
31
+ finally {
32
+ this.initializing.delete(symbol);
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,38 @@
1
+ import { encodeFunctionData } from 'viem';
2
+ import { ERC20_ABI } from '../../../core/abis';
3
+ import { validateAddress } from '../../../utils/validation';
4
+ const ZERO_VALUE = '0';
5
+ function requireBigInt(value, fieldName) {
6
+ if (typeof value !== 'bigint') {
7
+ throw new Error(`${fieldName} must be a bigint`);
8
+ }
9
+ if (value < 0n) {
10
+ throw new Error(`${fieldName} cannot be negative`);
11
+ }
12
+ return value;
13
+ }
14
+ export function buildErc20ApprovalParams(token, spender, amount) {
15
+ validateAddress(token, 'token');
16
+ validateAddress(spender, 'spender');
17
+ if (amount < 0n) {
18
+ throw new Error('Approval amount cannot be negative');
19
+ }
20
+ const data = encodeFunctionData({
21
+ abi: ERC20_ABI,
22
+ functionName: 'approve',
23
+ args: [spender, amount],
24
+ });
25
+ return { to: token, data, value: ZERO_VALUE };
26
+ }
27
+ export async function readErc20Allowance(publicClient, token, owner, spender) {
28
+ validateAddress(token, 'token');
29
+ validateAddress(owner, 'owner');
30
+ validateAddress(spender, 'spender');
31
+ const allowanceRaw = await publicClient.readContract({
32
+ address: token,
33
+ abi: ERC20_ABI,
34
+ functionName: 'allowance',
35
+ args: [owner, spender],
36
+ });
37
+ return requireBigInt(allowanceRaw, 'allowance');
38
+ }
@@ -0,0 +1,27 @@
1
+ import { HINT_HELPERS_ABI, SORTED_TROVES_ABI } from '../../../core/abis';
2
+ import { COLL_INDEX } from '../../../core/constants';
3
+ import { ceilSqrt, requireNonNegativeBigInt } from './borrowValidation';
4
+ export async function getTroveOperationHints(publicClient, ctx, interestRate) {
5
+ const annualInterestRate = requireNonNegativeBigInt(interestRate, 'interestRate');
6
+ const troveCount = (await publicClient.readContract({
7
+ address: ctx.addresses.sortedTroves,
8
+ abi: SORTED_TROVES_ABI,
9
+ functionName: 'getSize',
10
+ args: [],
11
+ }));
12
+ const sqrtCount = ceilSqrt(troveCount);
13
+ const numTrials = 10n * (sqrtCount > 0n ? sqrtCount : 1n);
14
+ const [approxHint] = (await publicClient.readContract({
15
+ address: ctx.addresses.hintHelpers,
16
+ abi: HINT_HELPERS_ABI,
17
+ functionName: 'getApproxHint',
18
+ args: [COLL_INDEX, annualInterestRate, numTrials, 42n],
19
+ }));
20
+ const [upperHint, lowerHint] = (await publicClient.readContract({
21
+ address: ctx.addresses.sortedTroves,
22
+ abi: SORTED_TROVES_ABI,
23
+ functionName: 'findInsertPosition',
24
+ args: [annualInterestRate, approxHint, approxHint],
25
+ }));
26
+ return { upper: upperHint, lower: lowerHint };
27
+ }
@@ -0,0 +1,82 @@
1
+ import { zeroAddress } from 'viem';
2
+ import { validateAddress } from '../../../utils/validation';
3
+ const MAX_SAFE_INTEGER_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
4
+ function requireAddress(value, fieldName) {
5
+ if (typeof value !== 'string') {
6
+ throw new Error(`${fieldName} must be a string address`);
7
+ }
8
+ validateAddress(value, fieldName);
9
+ return value;
10
+ }
11
+ function requireBigInt(value, fieldName) {
12
+ if (typeof value !== 'bigint') {
13
+ throw new Error(`${fieldName} must be a bigint`);
14
+ }
15
+ if (value < 0n) {
16
+ throw new Error(`${fieldName} cannot be negative`);
17
+ }
18
+ return value;
19
+ }
20
+ function requireNonNegativeInteger(value, fieldName) {
21
+ if (typeof value === 'bigint') {
22
+ if (value < 0n) {
23
+ throw new Error(`${fieldName} cannot be negative`);
24
+ }
25
+ if (value > MAX_SAFE_INTEGER_BIGINT) {
26
+ throw new Error(`${fieldName} exceeds Number.MAX_SAFE_INTEGER`);
27
+ }
28
+ return Number(value);
29
+ }
30
+ if (typeof value === 'number') {
31
+ if (!Number.isSafeInteger(value)) {
32
+ throw new Error(`${fieldName} must be a safe integer`);
33
+ }
34
+ if (value < 0) {
35
+ throw new Error(`${fieldName} cannot be negative`);
36
+ }
37
+ return value;
38
+ }
39
+ throw new Error(`${fieldName} must be a number or bigint`);
40
+ }
41
+ export function mapTroveStatus(statusNum) {
42
+ if (!Number.isSafeInteger(statusNum) || statusNum < 0) {
43
+ throw new Error(`Invalid trove status: ${statusNum}`);
44
+ }
45
+ switch (statusNum) {
46
+ case 0:
47
+ return 'nonExistent';
48
+ case 1:
49
+ return 'active';
50
+ case 2:
51
+ return 'closedByOwner';
52
+ case 3:
53
+ return 'closedByLiquidation';
54
+ case 4:
55
+ return 'zombie';
56
+ default:
57
+ throw new Error(`Unknown trove status: ${statusNum}`);
58
+ }
59
+ }
60
+ export function parseBorrowPosition(troveId, latestData, trovesData) {
61
+ if (typeof troveId !== 'string' || troveId.length === 0) {
62
+ throw new Error('troveId must be a non-empty string');
63
+ }
64
+ const latest = latestData;
65
+ const trove = trovesData;
66
+ const interestBatchManager = requireAddress(trove.interestBatchManager, 'trovesData.interestBatchManager');
67
+ return {
68
+ troveId,
69
+ collateral: requireBigInt(latest.entireColl, 'latestData.entireColl'),
70
+ debt: requireBigInt(latest.entireDebt, 'latestData.entireDebt'),
71
+ annualInterestRate: requireBigInt(latest.annualInterestRate, 'latestData.annualInterestRate'),
72
+ status: mapTroveStatus(requireNonNegativeInteger(trove.status, 'trovesData.status')),
73
+ interestBatchManager: interestBatchManager.toLowerCase() === zeroAddress ? null : interestBatchManager,
74
+ lastDebtUpdateTime: requireNonNegativeInteger(trove.lastDebtUpdateTime, 'trovesData.lastDebtUpdateTime'),
75
+ lastInterestRateAdjTime: requireNonNegativeInteger(latest.lastInterestRateAdjTime, 'latestData.lastInterestRateAdjTime'),
76
+ redistBoldDebtGain: requireBigInt(latest.redistBoldDebtGain, 'latestData.redistBoldDebtGain'),
77
+ redistCollGain: requireBigInt(latest.redistCollGain, 'latestData.redistCollGain'),
78
+ accruedInterest: requireBigInt(latest.accruedInterest, 'latestData.accruedInterest'),
79
+ recordedDebt: requireBigInt(latest.recordedDebt, 'latestData.recordedDebt'),
80
+ accruedBatchManagementFee: requireBigInt(latest.accruedBatchManagementFee, 'latestData.accruedBatchManagementFee'),
81
+ };
82
+ }
@@ -0,0 +1,271 @@
1
+ import { BORROWER_OPERATIONS_ABI, HINT_HELPERS_ABI, MULTI_TROVE_GETTER_ABI, PRICE_FEED_ABI, TROVE_MANAGER_ABI, TROVE_NFT_ABI, } from '../../../core/abis';
2
+ import { COLL_INDEX } from '../../../core/constants';
3
+ import { multicall } from '../../../utils/multicall';
4
+ import { parseBorrowPosition } from './borrowPositionParser';
5
+ import { deriveTroveId, formatTroveId, MAX_SAFE_INTEGER_BIGINT, parseTroveId, requireAddress, requireNonNegativeBigInt, } from './borrowValidation';
6
+ const TROVE_ID_BATCH_SIZE = 64;
7
+ const TROVE_OWNER_BATCH_SIZE = 64;
8
+ const TROVE_DETAIL_BATCH_SIZE = 64;
9
+ const OWNER_INDEX_PROBE_BATCH_SIZE = 64;
10
+ export class BorrowReadService {
11
+ constructor(publicClient) {
12
+ this.publicClient = publicClient;
13
+ }
14
+ async getTroveData(ctx, troveId) {
15
+ const parsedTroveId = parseTroveId(troveId);
16
+ const [latestData, trovesData] = await this.readContractsInChunks([
17
+ {
18
+ address: ctx.addresses.troveManager,
19
+ abi: TROVE_MANAGER_ABI,
20
+ functionName: 'getLatestTroveData',
21
+ args: [parsedTroveId],
22
+ },
23
+ {
24
+ address: ctx.addresses.troveManager,
25
+ abi: TROVE_MANAGER_ABI,
26
+ functionName: 'Troves',
27
+ args: [parsedTroveId],
28
+ },
29
+ ], 2);
30
+ return parseBorrowPosition(formatTroveId(parsedTroveId), latestData, trovesData);
31
+ }
32
+ async getUserTroves(ctx, owner) {
33
+ const ownerAddress = requireAddress(owner, 'owner');
34
+ const ownedTroveCount = await this.getOwnedTroveCount(ctx, ownerAddress);
35
+ if (ownedTroveCount === 0) {
36
+ return [];
37
+ }
38
+ const troveCount = (await this.publicClient.readContract({
39
+ address: ctx.addresses.troveManager,
40
+ abi: TROVE_MANAGER_ABI,
41
+ functionName: 'getTroveIdsCount',
42
+ args: [],
43
+ }));
44
+ if (troveCount === 0n) {
45
+ return [];
46
+ }
47
+ const troveIdContracts = [];
48
+ for (let i = 0n; i < troveCount; i++) {
49
+ troveIdContracts.push({
50
+ address: ctx.addresses.troveManager,
51
+ abi: TROVE_MANAGER_ABI,
52
+ functionName: 'getTroveFromTroveIdsArray',
53
+ args: [i],
54
+ });
55
+ }
56
+ const troveIds = (await this.readContractsInChunks(troveIdContracts, TROVE_ID_BATCH_SIZE)).map((troveId) => troveId);
57
+ const ownerContracts = troveIds.map((troveId) => ({
58
+ address: ctx.addresses.troveNFT,
59
+ abi: TROVE_NFT_ABI,
60
+ functionName: 'ownerOf',
61
+ args: [troveId],
62
+ }));
63
+ const troveOwners = await this.readContractsInChunks(ownerContracts, TROVE_OWNER_BATCH_SIZE);
64
+ const matchedTroveIds = troveIds.filter((troveId, index) => troveOwners[index].toLowerCase() === ownerAddress.toLowerCase());
65
+ if (matchedTroveIds.length === 0) {
66
+ return [];
67
+ }
68
+ const troveDetailContracts = matchedTroveIds.flatMap((troveId) => ([
69
+ {
70
+ address: ctx.addresses.troveManager,
71
+ abi: TROVE_MANAGER_ABI,
72
+ functionName: 'getLatestTroveData',
73
+ args: [troveId],
74
+ },
75
+ {
76
+ address: ctx.addresses.troveManager,
77
+ abi: TROVE_MANAGER_ABI,
78
+ functionName: 'Troves',
79
+ args: [troveId],
80
+ },
81
+ ]));
82
+ const detailResults = await this.readContractsInChunks(troveDetailContracts, TROVE_DETAIL_BATCH_SIZE * 2);
83
+ return matchedTroveIds.map((troveId, index) => {
84
+ const offset = index * 2;
85
+ return parseBorrowPosition(formatTroveId(troveId), detailResults[offset], detailResults[offset + 1]);
86
+ });
87
+ }
88
+ async getCollateralPrice(ctx) {
89
+ return (await this.publicClient.readContract({
90
+ address: ctx.addresses.priceFeed,
91
+ abi: PRICE_FEED_ABI,
92
+ functionName: 'fetchPrice',
93
+ args: [],
94
+ }));
95
+ }
96
+ async isSystemShutDown(ctx) {
97
+ return (await this.publicClient.readContract({
98
+ address: ctx.addresses.borrowerOperations,
99
+ abi: BORROWER_OPERATIONS_ABI,
100
+ functionName: 'hasBeenShutDown',
101
+ args: [],
102
+ }));
103
+ }
104
+ async getBranchStats(ctx) {
105
+ const [totalColl, totalDebt] = await Promise.all([
106
+ this.publicClient.readContract({
107
+ address: ctx.addresses.borrowerOperations,
108
+ abi: BORROWER_OPERATIONS_ABI,
109
+ functionName: 'getEntireBranchColl',
110
+ args: [],
111
+ }),
112
+ this.publicClient.readContract({
113
+ address: ctx.addresses.borrowerOperations,
114
+ abi: BORROWER_OPERATIONS_ABI,
115
+ functionName: 'getEntireBranchDebt',
116
+ args: [],
117
+ }),
118
+ ]);
119
+ return { totalColl: totalColl, totalDebt: totalDebt };
120
+ }
121
+ async getInterestRateBrackets(ctx) {
122
+ const result = (await this.publicClient.readContract({
123
+ address: ctx.addresses.multiTroveGetter,
124
+ abi: MULTI_TROVE_GETTER_ABI,
125
+ functionName: 'getDebtPerInterestRateAscending',
126
+ args: [COLL_INDEX, 0n, 500n],
127
+ }));
128
+ const entries = result[0] ?? [];
129
+ const grouped = new Map();
130
+ for (const item of entries) {
131
+ const rate = requireNonNegativeBigInt(item.interestRate, 'interestRate');
132
+ const debt = requireNonNegativeBigInt(item.debt, 'debt');
133
+ const key = rate.toString();
134
+ grouped.set(key, (grouped.get(key) ?? 0n) + debt);
135
+ }
136
+ return Array.from(grouped.entries())
137
+ .map(([rate, totalDebt]) => ({
138
+ rate: BigInt(rate),
139
+ totalDebt,
140
+ }))
141
+ .sort((a, b) => (a.rate < b.rate ? -1 : a.rate > b.rate ? 1 : 0));
142
+ }
143
+ async getAverageInterestRate(ctx) {
144
+ const brackets = await this.getInterestRateBrackets(ctx);
145
+ if (brackets.length === 0)
146
+ return 0n;
147
+ let weightedRateSum = 0n;
148
+ let totalDebt = 0n;
149
+ for (const bracket of brackets) {
150
+ weightedRateSum += bracket.rate * bracket.totalDebt;
151
+ totalDebt += bracket.totalDebt;
152
+ }
153
+ return totalDebt === 0n ? 0n : weightedRateSum / totalDebt;
154
+ }
155
+ async getBatchManagerInfo(ctx, address) {
156
+ const batchManagerAddress = requireAddress(address, 'address');
157
+ const exists = (await this.publicClient.readContract({
158
+ address: ctx.addresses.borrowerOperations,
159
+ abi: BORROWER_OPERATIONS_ABI,
160
+ functionName: 'checkBatchManagerExists',
161
+ args: [batchManagerAddress],
162
+ }));
163
+ if (!exists)
164
+ return null;
165
+ const manager = (await this.publicClient.readContract({
166
+ address: ctx.addresses.borrowerOperations,
167
+ abi: BORROWER_OPERATIONS_ABI,
168
+ functionName: 'getInterestBatchManager',
169
+ args: [batchManagerAddress],
170
+ }));
171
+ return {
172
+ minRate: requireNonNegativeBigInt(manager.minInterestRate, 'minInterestRate'),
173
+ maxRate: requireNonNegativeBigInt(manager.maxInterestRate, 'maxInterestRate'),
174
+ minChangePeriod: requireNonNegativeBigInt(manager.minInterestRateChangePeriod, 'minInterestRateChangePeriod'),
175
+ };
176
+ }
177
+ async predictOpenTroveUpfrontFee(ctx, amount, rate) {
178
+ const borrowedAmount = requireNonNegativeBigInt(amount, 'amount');
179
+ const annualInterestRate = requireNonNegativeBigInt(rate, 'rate');
180
+ return (await this.publicClient.readContract({
181
+ address: ctx.addresses.hintHelpers,
182
+ abi: HINT_HELPERS_ABI,
183
+ functionName: 'predictOpenTroveUpfrontFee',
184
+ args: [COLL_INDEX, borrowedAmount, annualInterestRate],
185
+ }));
186
+ }
187
+ async predictAdjustUpfrontFee(ctx, troveId, debtIncrease) {
188
+ const parsedTroveId = parseTroveId(troveId);
189
+ const debtIncreaseAmount = requireNonNegativeBigInt(debtIncrease, 'debtIncrease');
190
+ return (await this.publicClient.readContract({
191
+ address: ctx.addresses.hintHelpers,
192
+ abi: HINT_HELPERS_ABI,
193
+ functionName: 'predictAdjustTroveUpfrontFee',
194
+ args: [COLL_INDEX, parsedTroveId, debtIncreaseAmount],
195
+ }));
196
+ }
197
+ async predictAdjustInterestRateUpfrontFee(ctx, troveId, newRate) {
198
+ const parsedTroveId = parseTroveId(troveId);
199
+ const newAnnualInterestRate = requireNonNegativeBigInt(newRate, 'newRate');
200
+ return (await this.publicClient.readContract({
201
+ address: ctx.addresses.hintHelpers,
202
+ abi: HINT_HELPERS_ABI,
203
+ functionName: 'predictAdjustInterestRateUpfrontFee',
204
+ args: [COLL_INDEX, parsedTroveId, newAnnualInterestRate],
205
+ }));
206
+ }
207
+ async predictJoinBatchUpfrontFee(ctx, troveId, batchAddress) {
208
+ const parsedTroveId = parseTroveId(troveId);
209
+ const managerAddress = requireAddress(batchAddress, 'batchAddress');
210
+ return (await this.publicClient.readContract({
211
+ address: ctx.addresses.hintHelpers,
212
+ abi: HINT_HELPERS_ABI,
213
+ functionName: 'predictJoinBatchInterestRateUpfrontFee',
214
+ args: [COLL_INDEX, parsedTroveId, managerAddress],
215
+ }));
216
+ }
217
+ async getOwnedTroveCount(ctx, owner) {
218
+ const ownerAddress = requireAddress(owner, 'owner');
219
+ const ownerTroveCount = (await this.publicClient.readContract({
220
+ address: ctx.addresses.troveNFT,
221
+ abi: TROVE_NFT_ABI,
222
+ functionName: 'balanceOf',
223
+ args: [ownerAddress],
224
+ }));
225
+ if (ownerTroveCount > MAX_SAFE_INTEGER_BIGINT) {
226
+ throw new Error('Owner trove count exceeds Number.MAX_SAFE_INTEGER');
227
+ }
228
+ return Number(ownerTroveCount);
229
+ }
230
+ async findNextAvailableOwnerIndex(ctx, owner, opener) {
231
+ const ownerAddress = requireAddress(owner, 'owner');
232
+ const openerAddress = requireAddress(opener, 'opener');
233
+ let candidateIndex = await this.getOwnedTroveCount(ctx, ownerAddress);
234
+ while (candidateIndex <= Number.MAX_SAFE_INTEGER) {
235
+ const batchIndices = Array.from({ length: Math.min(OWNER_INDEX_PROBE_BATCH_SIZE, Number.MAX_SAFE_INTEGER - candidateIndex + 1) }, (_, offset) => candidateIndex + offset);
236
+ const statusContracts = batchIndices.map((ownerIndex) => ({
237
+ address: ctx.addresses.troveManager,
238
+ abi: TROVE_MANAGER_ABI,
239
+ functionName: 'getTroveStatus',
240
+ args: [deriveTroveId(openerAddress, ownerAddress, ownerIndex)],
241
+ }));
242
+ const statuses = await this.readContractsInChunks(statusContracts, OWNER_INDEX_PROBE_BATCH_SIZE);
243
+ const availableOffset = statuses.findIndex((status) => status === 0 || status === 0n);
244
+ if (availableOffset !== -1) {
245
+ return batchIndices[availableOffset];
246
+ }
247
+ candidateIndex += batchIndices.length;
248
+ }
249
+ throw new Error('Next available owner index exceeds Number.MAX_SAFE_INTEGER');
250
+ }
251
+ async getNextOwnerIndex(ctx, owner) {
252
+ return this.getOwnedTroveCount(ctx, owner);
253
+ }
254
+ async readContractsInChunks(contracts, chunkSize) {
255
+ if (contracts.length === 0) {
256
+ return [];
257
+ }
258
+ const results = [];
259
+ for (let index = 0; index < contracts.length; index += chunkSize) {
260
+ const chunk = contracts.slice(index, index + chunkSize);
261
+ const chunkResults = await multicall(this.publicClient, chunk, { allowFailure: false });
262
+ for (const result of chunkResults) {
263
+ if (result.status === 'failure') {
264
+ throw result.error;
265
+ }
266
+ results.push(result.result);
267
+ }
268
+ }
269
+ return results;
270
+ }
271
+ }