@mento-protocol/mento-sdk 3.2.0 → 3.2.1

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.
@@ -8,16 +8,18 @@ import { BorrowTransactionService } from './internal/borrowTransactionService';
8
8
  * handle interest rates and batch managers, and query position data.
9
9
  *
10
10
  * All `build*` methods return `CallParams` ({ to, data, value }) that can be
11
- * executed with any wallet client. The `debtTokenSymbol` parameter (e.g., 'USDm')
11
+ * executed with any wallet client. The `debtTokenSymbol` parameter (e.g., 'GBPm')
12
12
  * identifies which borrowing deployment to interact with.
13
13
  *
14
14
  * @example
15
15
  * ```typescript
16
16
  * const mento = await Mento.create(ChainId.CELO)
17
17
  *
18
+ * const ownerIndex = await mento.borrow.findNextAvailableOwnerIndex('GBPm', '0x...', '0x...')
19
+ *
18
20
  * // Open a trove
19
- * const tx = await mento.borrow.buildOpenTroveTransaction('USDm', {
20
- * owner: '0x...', ownerIndex: 0,
21
+ * const tx = await mento.borrow.buildOpenTroveTransaction('GBPm', {
22
+ * owner: '0x...', ownerIndex,
21
23
  * collAmount: parseUnits('10', 18),
22
24
  * boldAmount: parseUnits('1000', 18),
23
25
  * annualInterestRate: parseUnits('0.05', 18),
@@ -37,7 +39,7 @@ export class BorrowService {
37
39
  * Builds a transaction to open a new trove (borrowing position).
38
40
  * Requires prior collateral approval via `buildCollateralApprovalParams`.
39
41
  *
40
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
42
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
41
43
  * @param params - Trove opening parameters including collateral, debt amount, and interest rate
42
44
  * @returns Transaction parameters ready to send
43
45
  */
@@ -47,7 +49,7 @@ export class BorrowService {
47
49
  /**
48
50
  * Builds a transaction to adjust an existing trove's collateral and/or debt.
49
51
  *
50
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
52
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
51
53
  * @param params - Adjustment parameters specifying collateral/debt changes
52
54
  * @returns Transaction parameters ready to send
53
55
  */
@@ -55,10 +57,13 @@ export class BorrowService {
55
57
  return this.withContext(debtTokenSymbol, (ctx) => this.txService.buildAdjustTroveTransaction(ctx, params));
56
58
  }
57
59
  /**
58
- * Builds a transaction to adjust a zombie trove (a trove that fell below minimum debt
59
- * after a liquidation). Same parameters as `buildAdjustTroveTransaction`.
60
+ * Builds a transaction to adjust a zombie trove. Zombie troves are still-open troves whose
61
+ * debt fell below the branch minimum debt, typically after a redemption.
62
+ *
63
+ * Use this when `getTroveData()` or `getUserTroves()` returns `status === 'zombie'`.
64
+ * Same parameters as `buildAdjustTroveTransaction`.
60
65
  *
61
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
66
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
62
67
  * @param params - Adjustment parameters specifying collateral/debt changes
63
68
  * @returns Transaction parameters ready to send
64
69
  */
@@ -68,7 +73,7 @@ export class BorrowService {
68
73
  /**
69
74
  * Builds a transaction to close a trove, repaying all debt and reclaiming collateral.
70
75
  *
71
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
76
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
72
77
  * @param troveId - The NFT token ID identifying the trove
73
78
  * @returns Transaction parameters ready to send
74
79
  */
@@ -79,7 +84,7 @@ export class BorrowService {
79
84
  * Builds a transaction to add collateral to an existing trove.
80
85
  * Requires prior collateral approval via `buildCollateralApprovalParams`.
81
86
  *
82
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
87
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
83
88
  * @param troveId - The NFT token ID identifying the trove
84
89
  * @param amount - Amount of collateral to add (in wei)
85
90
  * @returns Transaction parameters ready to send
@@ -90,7 +95,7 @@ export class BorrowService {
90
95
  /**
91
96
  * Builds a transaction to withdraw collateral from an existing trove.
92
97
  *
93
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
98
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
94
99
  * @param troveId - The NFT token ID identifying the trove
95
100
  * @param amount - Amount of collateral to withdraw (in wei)
96
101
  * @returns Transaction parameters ready to send
@@ -101,7 +106,7 @@ export class BorrowService {
101
106
  /**
102
107
  * Builds a transaction to borrow additional debt against an existing trove.
103
108
  *
104
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
109
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
105
110
  * @param troveId - The NFT token ID identifying the trove
106
111
  * @param amount - Additional debt amount to borrow (in wei)
107
112
  * @param maxFee - Maximum upfront fee the borrower is willing to pay (in wei)
@@ -113,7 +118,7 @@ export class BorrowService {
113
118
  /**
114
119
  * Builds a transaction to repay debt on an existing trove.
115
120
  *
116
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
121
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
117
122
  * @param troveId - The NFT token ID identifying the trove
118
123
  * @param amount - Amount of debt to repay (in wei)
119
124
  * @returns Transaction parameters ready to send
@@ -124,7 +129,7 @@ export class BorrowService {
124
129
  /**
125
130
  * Builds a transaction to change the annual interest rate on a trove.
126
131
  *
127
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
132
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
128
133
  * @param troveId - The NFT token ID identifying the trove
129
134
  * @param newRate - New annual interest rate (18-decimal fixed-point, e.g., parseUnits('0.05', 18) for 5%)
130
135
  * @param maxFee - Maximum upfront fee the borrower is willing to pay (in wei)
@@ -135,8 +140,10 @@ export class BorrowService {
135
140
  }
136
141
  /**
137
142
  * Builds a transaction to claim collateral surplus after a liquidation.
143
+ * This is for collateral held in the surplus pool after `closedByLiquidation`.
144
+ * Zombie troves with remaining collateral should usually be closed or adjusted instead.
138
145
  *
139
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
146
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
140
147
  * @returns Transaction parameters ready to send
141
148
  */
142
149
  buildClaimCollateralTransaction(debtTokenSymbol) {
@@ -145,7 +152,7 @@ export class BorrowService {
145
152
  /**
146
153
  * Builds a transaction to delegate interest rate management to a batch manager.
147
154
  *
148
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
155
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
149
156
  * @param troveId - The NFT token ID identifying the trove
150
157
  * @param manager - Address of the batch manager contract
151
158
  * @param maxFee - Maximum upfront fee the borrower is willing to pay (in wei)
@@ -157,7 +164,7 @@ export class BorrowService {
157
164
  /**
158
165
  * Builds a transaction to remove a trove from a batch manager, setting a new individual rate.
159
166
  *
160
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
167
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
161
168
  * @param troveId - The NFT token ID identifying the trove
162
169
  * @param newRate - New individual annual interest rate (18-decimal fixed-point)
163
170
  * @param maxFee - Maximum upfront fee the borrower is willing to pay (in wei)
@@ -169,7 +176,7 @@ export class BorrowService {
169
176
  /**
170
177
  * Builds a transaction to switch a trove to a different batch manager.
171
178
  *
172
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
179
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
173
180
  * @param troveId - The NFT token ID identifying the trove
174
181
  * @param newManager - Address of the new batch manager contract
175
182
  * @param maxFee - Maximum upfront fee the borrower is willing to pay (in wei)
@@ -182,7 +189,7 @@ export class BorrowService {
182
189
  * Builds a transaction to delegate interest rate management to another address
183
190
  * with bounded rate constraints.
184
191
  *
185
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
192
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
186
193
  * @param troveId - The NFT token ID identifying the trove
187
194
  * @param delegate - Address to delegate interest rate management to
188
195
  * @param minRate - Minimum allowed annual interest rate (18-decimal fixed-point)
@@ -198,7 +205,7 @@ export class BorrowService {
198
205
  /**
199
206
  * Builds a transaction to remove the interest rate delegate from a trove.
200
207
  *
201
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
208
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
202
209
  * @param troveId - The NFT token ID identifying the trove
203
210
  * @returns Transaction parameters ready to send
204
211
  */
@@ -209,7 +216,7 @@ export class BorrowService {
209
216
  * Builds approval params to allow BorrowerOperations to spend collateral tokens.
210
217
  * Must be executed before `buildOpenTroveTransaction` or `buildAddCollTransaction`.
211
218
  *
212
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
219
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
213
220
  * @param amount - Amount of collateral to approve (in wei)
214
221
  * @returns Transaction parameters for the ERC-20 approve call
215
222
  */
@@ -219,7 +226,7 @@ export class BorrowService {
219
226
  /**
220
227
  * Builds approval params for the debt token (e.g., for repayment or closing).
221
228
  *
222
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
229
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
223
230
  * @param spender - Address to approve as spender
224
231
  * @param amount - Amount of debt tokens to approve (in wei)
225
232
  * @returns Transaction parameters for the ERC-20 approve call
@@ -230,7 +237,7 @@ export class BorrowService {
230
237
  /**
231
238
  * Builds approval params for the gas compensation token (required when opening a trove).
232
239
  *
233
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
240
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
234
241
  * @param amount - Amount to approve (in wei). If omitted, approves the gas compensation amount.
235
242
  * @returns Transaction parameters for the ERC-20 approve call
236
243
  */
@@ -240,7 +247,7 @@ export class BorrowService {
240
247
  /**
241
248
  * Gets the current collateral token allowance for BorrowerOperations.
242
249
  *
243
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
250
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
244
251
  * @param owner - Address to check allowance for
245
252
  * @returns Current allowance in wei
246
253
  */
@@ -250,7 +257,7 @@ export class BorrowService {
250
257
  /**
251
258
  * Gets the current debt token allowance for a specific spender.
252
259
  *
253
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
260
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
254
261
  * @param owner - Address to check allowance for
255
262
  * @param spender - Address of the approved spender
256
263
  * @returns Current allowance in wei
@@ -261,7 +268,7 @@ export class BorrowService {
261
268
  /**
262
269
  * Gets the current gas compensation token allowance.
263
270
  *
264
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
271
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
265
272
  * @param owner - Address to check allowance for
266
273
  * @returns Current allowance in wei
267
274
  */
@@ -270,8 +277,10 @@ export class BorrowService {
270
277
  }
271
278
  /**
272
279
  * Fetches on-chain data for a specific trove.
280
+ * The returned position reflects the trove's current lifecycle status, including
281
+ * zombie troves that may still hold collateral even when their debt is zero.
273
282
  *
274
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
283
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
275
284
  * @param troveId - The NFT token ID identifying the trove
276
285
  * @returns Trove position data including collateral, debt, interest rate, and status
277
286
  */
@@ -279,11 +288,14 @@ export class BorrowService {
279
288
  return this.withContext(debtTokenSymbol, (ctx) => this.readService.getTroveData(ctx, troveId));
280
289
  }
281
290
  /**
282
- * Fetches all troves owned by an address.
291
+ * Fetches troves currently owned by an address via the Trove NFT.
292
+ * This includes zombie troves that have been removed from `SortedTroves` but are still owned
293
+ * by the address. Closed or liquidated troves are not returned once their Trove NFT is burned
294
+ * or transferred away.
283
295
  *
284
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
296
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
285
297
  * @param owner - Address to query troves for
286
- * @returns Array of trove positions owned by the address
298
+ * @returns Array of trove positions currently owned by the address
287
299
  */
288
300
  getUserTroves(debtTokenSymbol, owner) {
289
301
  return this.withContext(debtTokenSymbol, (ctx) => this.readService.getUserTroves(ctx, owner));
@@ -291,7 +303,7 @@ export class BorrowService {
291
303
  /**
292
304
  * Gets the current collateral token price from the price feed.
293
305
  *
294
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
306
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
295
307
  * @returns Collateral price in 18-decimal fixed-point format
296
308
  */
297
309
  getCollateralPrice(debtTokenSymbol) {
@@ -301,7 +313,7 @@ export class BorrowService {
301
313
  * Gets the system parameters for a borrowing deployment.
302
314
  * Returns MCR, CCR, SCR, BCR, minimum debt, gas compensation, and minimum interest rate.
303
315
  *
304
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
316
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
305
317
  * @returns System parameters (all values in 18-decimal fixed-point)
306
318
  */
307
319
  getSystemParams(debtTokenSymbol) {
@@ -310,7 +322,7 @@ export class BorrowService {
310
322
  /**
311
323
  * Checks whether the borrowing system has been shut down (e.g., during a crisis).
312
324
  *
313
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
325
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
314
326
  * @returns true if the system is shut down, false otherwise
315
327
  */
316
328
  isSystemShutDown(debtTokenSymbol) {
@@ -319,7 +331,7 @@ export class BorrowService {
319
331
  /**
320
332
  * Gets aggregate collateral and debt statistics for the borrowing branch.
321
333
  *
322
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
334
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
323
335
  * @returns Total collateral and total debt across all troves (in wei)
324
336
  */
325
337
  getBranchStats(debtTokenSymbol) {
@@ -328,7 +340,7 @@ export class BorrowService {
328
340
  /**
329
341
  * Gets the distribution of debt across interest rate brackets.
330
342
  *
331
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
343
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
332
344
  * @returns Array of brackets, each with a rate and total debt at that rate
333
345
  */
334
346
  getInterestRateBrackets(debtTokenSymbol) {
@@ -337,7 +349,7 @@ export class BorrowService {
337
349
  /**
338
350
  * Gets the weighted average interest rate across all active troves.
339
351
  *
340
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
352
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
341
353
  * @returns Average annual interest rate in 18-decimal fixed-point
342
354
  */
343
355
  getAverageInterestRate(debtTokenSymbol) {
@@ -346,7 +358,7 @@ export class BorrowService {
346
358
  /**
347
359
  * Gets information about a batch manager's configuration.
348
360
  *
349
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
361
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
350
362
  * @param address - Address of the batch manager
351
363
  * @returns Batch manager config (min/max rate, min change period), or null if not a valid manager
352
364
  */
@@ -356,7 +368,7 @@ export class BorrowService {
356
368
  /**
357
369
  * Estimates the upfront fee for opening a new trove.
358
370
  *
359
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
371
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
360
372
  * @param amount - Debt amount to borrow (in wei)
361
373
  * @param rate - Annual interest rate (18-decimal fixed-point)
362
374
  * @returns Estimated upfront fee in wei
@@ -367,7 +379,7 @@ export class BorrowService {
367
379
  /**
368
380
  * Estimates the upfront fee for increasing debt on an existing trove.
369
381
  *
370
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
382
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
371
383
  * @param troveId - The NFT token ID identifying the trove
372
384
  * @param debtIncrease - Amount of additional debt (in wei)
373
385
  * @returns Estimated upfront fee in wei
@@ -378,7 +390,7 @@ export class BorrowService {
378
390
  /**
379
391
  * Estimates the upfront fee for changing a trove's interest rate.
380
392
  *
381
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
393
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
382
394
  * @param troveId - The NFT token ID identifying the trove
383
395
  * @param newRate - New annual interest rate (18-decimal fixed-point)
384
396
  * @returns Estimated upfront fee in wei
@@ -389,7 +401,7 @@ export class BorrowService {
389
401
  /**
390
402
  * Estimates the upfront fee for joining a batch manager.
391
403
  *
392
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
404
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
393
405
  * @param troveId - The NFT token ID identifying the trove
394
406
  * @param batchAddress - Address of the batch manager to join
395
407
  * @returns Estimated upfront fee in wei
@@ -398,15 +410,40 @@ export class BorrowService {
398
410
  return this.withContext(debtTokenSymbol, (ctx) => this.readService.predictJoinBatchUpfrontFee(ctx, troveId, batchAddress));
399
411
  }
400
412
  /**
401
- * Gets the next available owner index for opening a new trove.
402
- * Each owner can have multiple troves, indexed starting from 0.
413
+ * Gets the current number of troves owned by an address via the Trove NFT.
414
+ *
415
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
416
+ * @param owner - Address of the trove owner
417
+ * @returns The number of troves currently owned by the address
418
+ */
419
+ getOwnedTroveCount(debtTokenSymbol, owner) {
420
+ return this.withContext(debtTokenSymbol, (ctx) => this.readService.getOwnedTroveCount(ctx, owner));
421
+ }
422
+ /**
423
+ * Finds the first safe owner index for opening a trove with the given transaction sender.
424
+ *
425
+ * The `opener` must be the address that will call BorrowerOperations on-chain.
426
+ * For smart accounts, pass the smart account address rather than the controlling EOA.
427
+ *
428
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
429
+ * @param owner - Address that will own the trove NFT
430
+ * @param opener - Address that will submit the open-trove transaction on-chain
431
+ * @returns The first owner index that does not already map to an existing trove
432
+ */
433
+ findNextAvailableOwnerIndex(debtTokenSymbol, owner, opener) {
434
+ return this.withContext(debtTokenSymbol, (ctx) => this.readService.findNextAvailableOwnerIndex(ctx, owner, opener));
435
+ }
436
+ /**
437
+ * Gets the current number of troves owned by an address via the Trove NFT.
438
+ *
439
+ * @deprecated Use `findNextAvailableOwnerIndex` when preparing an open-trove transaction.
403
440
  *
404
- * @param debtTokenSymbol - The debt token symbol (e.g., 'USDm')
441
+ * @param debtTokenSymbol - The debt token symbol (e.g., 'GBPm')
405
442
  * @param owner - Address of the trove owner
406
- * @returns The next available index (pass to OpenTroveParams.ownerIndex)
443
+ * @returns The number of troves currently owned by the address
407
444
  */
408
445
  getNextOwnerIndex(debtTokenSymbol, owner) {
409
- return this.withContext(debtTokenSymbol, (ctx) => this.readService.getNextOwnerIndex(ctx, owner));
446
+ return this.withContext(debtTokenSymbol, (ctx) => this.readService.getOwnedTroveCount(ctx, owner));
410
447
  }
411
448
  async withContext(debtTokenSymbol, callback) {
412
449
  const ctx = await this.ensureInitialized(debtTokenSymbol);
@@ -71,6 +71,14 @@ export function calculateDebtSuggestions(maxDebt, minDebt) {
71
71
  }
72
72
  return suggestions;
73
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
+ */
74
82
  export function getLoanDetails(collateral, debt, interestRate, collPrice, mcr) {
75
83
  // maxLtv = 1 / MCR (MCR is e.g. 1.1e18 meaning 110%)
76
84
  const maxLtv = (DECIMAL_PRECISION * DECIMAL_PRECISION) / mcr;
@@ -2,10 +2,11 @@ import { BORROWER_OPERATIONS_ABI, HINT_HELPERS_ABI, MULTI_TROVE_GETTER_ABI, PRIC
2
2
  import { COLL_INDEX } from '../../../core/constants';
3
3
  import { multicall } from '../../../utils/multicall';
4
4
  import { parseBorrowPosition } from './borrowPositionParser';
5
- import { formatTroveId, MAX_SAFE_INTEGER_BIGINT, parseTroveId, requireAddress, requireNonNegativeBigInt, } from './borrowValidation';
5
+ import { deriveTroveId, formatTroveId, MAX_SAFE_INTEGER_BIGINT, parseTroveId, requireAddress, requireNonNegativeBigInt, } from './borrowValidation';
6
6
  const TROVE_ID_BATCH_SIZE = 64;
7
7
  const TROVE_OWNER_BATCH_SIZE = 64;
8
8
  const TROVE_DETAIL_BATCH_SIZE = 64;
9
+ const OWNER_INDEX_PROBE_BATCH_SIZE = 64;
9
10
  export class BorrowReadService {
10
11
  constructor(publicClient) {
11
12
  this.publicClient = publicClient;
@@ -30,6 +31,10 @@ export class BorrowReadService {
30
31
  }
31
32
  async getUserTroves(ctx, owner) {
32
33
  const ownerAddress = requireAddress(owner, 'owner');
34
+ const ownedTroveCount = await this.getOwnedTroveCount(ctx, ownerAddress);
35
+ if (ownedTroveCount === 0) {
36
+ return [];
37
+ }
33
38
  const troveCount = (await this.publicClient.readContract({
34
39
  address: ctx.addresses.troveManager,
35
40
  abi: TROVE_MANAGER_ABI,
@@ -209,7 +214,7 @@ export class BorrowReadService {
209
214
  args: [COLL_INDEX, parsedTroveId, managerAddress],
210
215
  }));
211
216
  }
212
- async getNextOwnerIndex(ctx, owner) {
217
+ async getOwnedTroveCount(ctx, owner) {
213
218
  const ownerAddress = requireAddress(owner, 'owner');
214
219
  const ownerTroveCount = (await this.publicClient.readContract({
215
220
  address: ctx.addresses.troveNFT,
@@ -222,6 +227,30 @@ export class BorrowReadService {
222
227
  }
223
228
  return Number(ownerTroveCount);
224
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
+ }
225
254
  async readContractsInChunks(contracts, chunkSize) {
226
255
  if (contracts.length === 0) {
227
256
  return [];
@@ -1,7 +1,8 @@
1
- import { zeroAddress } from 'viem';
1
+ import { encodeAbiParameters, getAddress, keccak256, parseAbiParameters, zeroAddress } from 'viem';
2
2
  import { validateAddress } from '../../../utils/validation';
3
3
  const UINT128_MAX = (1n << 128n) - 1n;
4
4
  export const MAX_SAFE_INTEGER_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
5
+ const TROVE_ID_PARAMETERS = parseAbiParameters('address opener, address owner, uint256 ownerIndex');
5
6
  export function requireDebtTokenSymbol(symbol) {
6
7
  if (typeof symbol !== 'string' || symbol.trim().length === 0) {
7
8
  throw new Error('debtTokenSymbol must be a non-empty string');
@@ -36,6 +37,12 @@ export function parseTroveId(troveId) {
36
37
  export function formatTroveId(troveId) {
37
38
  return `0x${troveId.toString(16)}`;
38
39
  }
40
+ export function deriveTroveId(opener, owner, ownerIndex) {
41
+ const openerAddress = getAddress(requireAddress(opener, 'opener'));
42
+ const ownerAddress = getAddress(requireAddress(owner, 'owner'));
43
+ const validatedOwnerIndex = requireNonNegativeInteger(ownerIndex, 'ownerIndex');
44
+ return BigInt(keccak256(encodeAbiParameters(TROVE_ID_PARAMETERS, [openerAddress, ownerAddress, BigInt(validatedOwnerIndex)])));
45
+ }
39
46
  export function requireNonNegativeInteger(value, fieldName) {
40
47
  if (!Number.isSafeInteger(value) || value < 0) {
41
48
  throw new Error(`${fieldName} must be a non-negative safe integer`);