@gearbox-protocol/periphery-v3 1.7.0-next.5 → 1.7.0-next.6

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.
@@ -4,6 +4,7 @@
4
4
  pragma solidity ^0.8.17;
5
5
 
6
6
  import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
7
+ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
7
8
 
8
9
  import {IContractsRegister} from "@gearbox-protocol/core-v3/contracts/interfaces/IContractsRegister.sol";
9
10
  import {ICreditAccountV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditAccountV3.sol";
@@ -14,40 +15,43 @@ import {
14
15
  } from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditManagerV3.sol";
15
16
  import {IPoolQuotaKeeperV3} from "@gearbox-protocol/core-v3/contracts/interfaces/IPoolQuotaKeeperV3.sol";
16
17
  import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
18
+ import {PERCENTAGE_FACTOR} from "@gearbox-protocol/core-v3/contracts/libraries/Constants.sol";
17
19
  import {SanityCheckTrait} from "@gearbox-protocol/core-v3/contracts/traits/SanityCheckTrait.sol";
18
20
 
19
21
  import {IAddressProviderV3} from "@gearbox-protocol/governance/contracts/interfaces/IAddressProviderV3.sol";
20
22
  import {IMarketConfiguratorV3} from "@gearbox-protocol/governance/contracts/interfaces/IMarketConfiguratorV3.sol";
21
23
 
22
- import {CreditAccountData, CreditAccountFilter, CreditManagerFilter, TokenInfo} from "./Types.sol";
24
+ import {CreditAccountData, CreditAccountFilter, CreditManagerFilter, Pagination, TokenInfo} from "./Types.sol";
23
25
 
24
26
  /// @title Credit account compressor
25
27
  /// @notice Allows to fetch data on all credit accounts matching certain criteria in an efficient manner
26
28
  /// @dev The contract is not gas optimized and is thus not recommended for on-chain use
27
- /// @dev The querying functions exist in two modes, with one of them trying to YOLO-load everything and the other
28
- /// one loading as many accounts as fit in the call gas limit to implement the tightest pagination possible
29
+ /// @dev Querying functions try to process as many accounts as possible and stop when they get close to gas limit,
30
+ /// but additional pagination options are available, see `Pagination`
29
31
  contract CreditAccountCompressor is IVersion, SanityCheckTrait {
30
32
  /// @notice Contract version
31
33
  uint256 public constant override version = 3_10;
32
34
 
33
35
  /// @notice Address provider contract address
34
- address public immutable addressProvider;
36
+ address public immutable ADDRESS_PROVIDER;
35
37
 
36
- /// @dev Maximum number of accounts to consider in a single step to avoid excessive memory expansion
37
- uint256 internal constant STEP_SIZE = 1000;
38
+ /// @dev Amount of gas that covers filter matching and data preparation for a single account
39
+ /// @dev After processing a large number of accounts, gas costs might eventually exceed this number due to
40
+ /// quadratic memory expansion cost and memory always growing in Solidity
41
+ uint256 internal constant GAS_THRESHOLD = 2e6;
38
42
 
39
43
  /// @notice Thrown when address provider is not a contract or does not implement `marketConfigurators()`
40
44
  error InvalidAddressProviderException();
41
45
 
42
46
  /// @notice Constructor
43
- /// @param addressProvider_ Address provider contract address
44
- constructor(address addressProvider_) nonZeroAddress(addressProvider_) {
45
- if (addressProvider_.code.length == 0) revert InvalidAddressProviderException();
46
- try IAddressProviderV3(addressProvider_).marketConfigurators() {}
47
+ /// @param addressProvider Address provider contract address
48
+ constructor(address addressProvider) nonZeroAddress(addressProvider) {
49
+ if (addressProvider.code.length == 0) revert InvalidAddressProviderException();
50
+ try IAddressProviderV3(addressProvider).marketConfigurators() {}
47
51
  catch {
48
52
  revert InvalidAddressProviderException();
49
53
  }
50
- addressProvider = addressProvider_;
54
+ ADDRESS_PROVIDER = addressProvider;
51
55
  }
52
56
 
53
57
  // -------- //
@@ -61,168 +65,114 @@ contract CreditAccountCompressor is IVersion, SanityCheckTrait {
61
65
  }
62
66
 
63
67
  /// @notice Returns data for credit accounts that match `caFilter` in credit managers matching `cmFilter`
64
- /// @dev The `offset` parameter specifies the position to start scanning from in the list of all
65
- /// credit accounts opened in credit managers matching `cmFilter`
66
- /// @dev The `false` value of `finished` return variable indicates that gas supplied with a call
67
- /// was insufficient to process all the accounts and next iteration is needed which should
68
- /// start from `nextOffset` position
69
- function getCreditAccounts(CreditManagerFilter memory cmFilter, CreditAccountFilter memory caFilter, uint256 offset)
70
- external
71
- view
72
- returns (CreditAccountData[] memory data, bool finished, uint256 nextOffset)
73
- {
74
- uint256 num = countCreditAccounts(cmFilter, caFilter, offset);
75
- nextOffset = offset;
76
-
77
- // FIXME: If the number of accounts matching the filter is much larger than the number of accounts that
78
- // can realistically be processed with most providers, pre-allocation burns unnecessarily large amount
79
- // of gas due to quadratic memory expansion cost.
80
- // An idea for the mitigation is to calculate the maximum memory pointer that can possibly be reached
81
- // during calculations, and write resulting data after it, only expanding memory when actually needed.
82
- data = new CreditAccountData[](num);
83
- uint256 dataOffset;
68
+ /// @dev The `false` value of `finished` return variable indicates either that return limit was reached or
69
+ /// that gas supplied with a call was insufficient to process all the accounts and next iteration is
70
+ /// needed which should start from the `nextOffset` position
71
+ function getCreditAccounts(
72
+ CreditManagerFilter memory cmFilter,
73
+ CreditAccountFilter memory caFilter,
74
+ Pagination memory pagination
75
+ ) external view returns (CreditAccountData[] memory data, bool finished, uint256 nextOffset) {
76
+ uint256 num = countCreditAccounts(cmFilter, caFilter, pagination);
77
+ if (num == 0) return (data, true, 0);
78
+
79
+ // create a second object to avoid modifying `pagination` which can have side-effects in the calling code
80
+ Pagination memory _p = Pagination({
81
+ offset: pagination.offset,
82
+ scanLimit: pagination.scanLimit == 0 ? type(uint256).max : pagination.scanLimit,
83
+ returnLimit: pagination.returnLimit == 0 ? type(uint256).max : pagination.returnLimit
84
+ });
84
85
 
85
- // FIXME: This should be proven to be enough to properly return the data, and possibly made tighter
86
- uint256 gasThreshold = gasleft() / 64;
86
+ data = new CreditAccountData[](Math.min(_p.returnLimit, num));
87
+ uint256 dataOffset;
87
88
 
89
+ nextOffset = _p.offset;
88
90
  address[] memory creditManagers = _getCreditManagers(cmFilter);
89
91
  for (uint256 i; i < creditManagers.length; ++i) {
90
92
  uint256 len = ICreditManagerV3(creditManagers[i]).creditAccountsLen();
91
93
 
92
94
  // first, we need to get to the `offset` position
93
- if (len <= offset) {
94
- offset -= len;
95
+ if (len <= _p.offset) {
96
+ _p.offset -= len;
95
97
  continue;
96
98
  }
97
99
 
98
- while (offset < len) {
99
- bool outOfGas;
100
- uint256 processed;
101
-
102
- // circumvent the "Stack too deep." error
103
- uint256 offset_ = offset;
104
-
105
- (dataOffset, outOfGas, processed) = _getCreditAccounts({
106
- creditManager: creditManagers[i],
107
- filter: caFilter,
108
- data: data,
109
- dataOffset: dataOffset,
110
- offset: offset_,
111
- limit: STEP_SIZE,
112
- gasThreshold: gasThreshold
113
- });
114
- offset += processed;
115
- nextOffset += processed;
116
-
117
- if (outOfGas) {
118
- // trim the array to its actual size
119
- assembly {
120
- mstore(data, dataOffset)
121
- }
122
- return (data, dataOffset == num, nextOffset);
123
- }
124
- }
125
- offset = 0;
126
- }
127
- return (data, true, nextOffset);
128
- }
100
+ uint256 limit = Math.min(len - _p.offset, _p.scanLimit);
129
101
 
130
- /// @dev Same as above but with no gas limit control
131
- function getCreditAccounts(CreditManagerFilter memory cmFilter, CreditAccountFilter memory caFilter)
132
- external
133
- view
134
- returns (CreditAccountData[] memory data)
135
- {
136
- uint256 num = countCreditAccounts(cmFilter, caFilter, 0);
137
- data = new CreditAccountData[](num);
138
- uint256 dataOffset;
139
-
140
- address[] memory creditManagers = _getCreditManagers(cmFilter);
141
- for (uint256 i; i < creditManagers.length; ++i) {
142
- uint256 len = ICreditManagerV3(creditManagers[i]).creditAccountsLen();
143
- (dataOffset,,) = _getCreditAccounts({
102
+ uint256 scanned;
103
+ (dataOffset, scanned) = _getCreditAccounts({
144
104
  creditManager: creditManagers[i],
145
105
  filter: caFilter,
146
106
  data: data,
147
107
  dataOffset: dataOffset,
148
- offset: 0,
149
- limit: len,
150
- gasThreshold: 0
108
+ offset: _p.offset,
109
+ limit: limit
151
110
  });
152
- }
153
- }
111
+ nextOffset += scanned;
154
112
 
155
- /// @notice Returns data for credit accounts that match `caFilter` in a given `creditManager`
156
- /// @dev The `offset` parameter specifies the position to start scanning from in the list of all
157
- /// credit accounts opened in `creditManager`
158
- /// @dev The `false` value of `finished` return variable indicates that gas supplied with a call
159
- /// was insufficient to process all the accounts and next iteration is needed which should
160
- /// start from `nextOffset` position
161
- function getCreditAccounts(address creditManager, CreditAccountFilter memory caFilter, uint256 offset)
162
- external
163
- view
164
- returns (CreditAccountData[] memory data, bool finished, uint256 nextOffset)
165
- {
166
- uint256 num = countCreditAccounts(creditManager, caFilter, offset);
167
- nextOffset = offset;
168
-
169
- // FIXME: If the number of accounts matching the filter is much larger than the number of accounts that
170
- // can realistically be processed with most providers, pre-allocation burns unnecessarily large amount
171
- // of gas due to quadratic memory expansion cost.
172
- // An idea for the mitigation is to calculate the maximum memory pointer that can possibly be reached
173
- // during calculations, and write resulting data after it, only expanding memory when actually needed.
174
- data = new CreditAccountData[](num);
175
- uint256 dataOffset;
176
-
177
- // FIXME: This should be proven to be enough to properly return the data, and possibly made tighter
178
- uint256 gasThreshold = gasleft() / 64;
113
+ // either finished or reached one of return size or gas limits
114
+ if (dataOffset == data.length || scanned < limit) break;
179
115
 
180
- uint256 len = ICreditManagerV3(creditManager).creditAccountsLen();
181
- while (offset < len) {
182
- bool outOfGas;
183
- uint256 processed;
184
- (dataOffset, outOfGas, processed) = _getCreditAccounts({
185
- creditManager: creditManager,
186
- filter: caFilter,
187
- data: data,
188
- dataOffset: dataOffset,
189
- offset: offset,
190
- limit: STEP_SIZE,
191
- gasThreshold: gasThreshold
192
- });
193
- offset += processed;
194
- nextOffset += processed;
116
+ _p.scanLimit -= limit;
117
+ if (_p.scanLimit == 0) break;
118
+ _p.offset = 0;
119
+ }
195
120
 
196
- if (outOfGas) {
197
- // trim the array to its actual size
198
- assembly {
199
- mstore(data, dataOffset)
200
- }
201
- return (data, dataOffset == num, nextOffset);
121
+ if (dataOffset < data.length) {
122
+ // trim the array to its actual size
123
+ assembly {
124
+ mstore(data, dataOffset)
202
125
  }
203
126
  }
204
- return (data, true, nextOffset);
127
+
128
+ return (data, dataOffset == num, nextOffset);
205
129
  }
206
130
 
207
- /// @dev Same as above but with no gas limit control
208
- function getCreditAccounts(address creditManager, CreditAccountFilter memory caFilter)
131
+ /// @notice Returns data for credit accounts that match `caFilter` in a given `creditManager`
132
+ /// @dev The `false` value of `finished` return variable indicates either that return limit was reached or
133
+ /// that gas supplied with a call was insufficient to process all the accounts and next iteration is
134
+ /// needed which should start from the `nextOffset` position
135
+ function getCreditAccounts(address creditManager, CreditAccountFilter memory caFilter, Pagination memory pagination)
209
136
  external
210
137
  view
211
- returns (CreditAccountData[] memory data)
138
+ returns (CreditAccountData[] memory data, bool finished, uint256 nextOffset)
212
139
  {
213
- uint256 num = countCreditAccounts(creditManager, caFilter, 0);
214
- data = new CreditAccountData[](num);
140
+ uint256 num = countCreditAccounts(creditManager, caFilter, pagination);
141
+ if (num == 0) return (data, true, 0);
142
+
143
+ // create a second object to avoid modifying `pagination` which can have side-effects in the calling code
144
+ Pagination memory _p = Pagination({
145
+ offset: pagination.offset,
146
+ scanLimit: pagination.scanLimit == 0 ? type(uint256).max : pagination.scanLimit,
147
+ returnLimit: pagination.returnLimit == 0 ? type(uint256).max : pagination.returnLimit
148
+ });
149
+
150
+ data = new CreditAccountData[](Math.min(_p.returnLimit, num));
151
+ uint256 dataOffset;
215
152
 
216
153
  uint256 len = ICreditManagerV3(creditManager).creditAccountsLen();
217
- _getCreditAccounts({
154
+ // since `num != 0`, we can be sure that `len > offset`
155
+ uint256 limit = Math.min(_p.scanLimit, len - _p.offset);
156
+
157
+ uint256 scanned;
158
+ (dataOffset, scanned) = _getCreditAccounts({
218
159
  creditManager: creditManager,
219
160
  filter: caFilter,
220
161
  data: data,
221
162
  dataOffset: 0,
222
- offset: 0,
223
- limit: len,
224
- gasThreshold: 0
163
+ offset: _p.offset,
164
+ limit: limit
225
165
  });
166
+ nextOffset = _p.offset + scanned;
167
+
168
+ if (dataOffset < data.length) {
169
+ // trim the array to its actual size
170
+ assembly {
171
+ mstore(data, dataOffset)
172
+ }
173
+ }
174
+
175
+ return (data, dataOffset == num, nextOffset);
226
176
  }
227
177
 
228
178
  // -------- //
@@ -230,15 +180,15 @@ contract CreditAccountCompressor is IVersion, SanityCheckTrait {
230
180
  // -------- //
231
181
 
232
182
  /// @notice Counts credit accounts that match `caFilter` in credit managers matching `cmFilter`
233
- /// @dev The `offset` parameter specifies the position to start scanning from in the list of all
234
- /// credit accounts opened in credit managers matching `cmFilter`
235
183
  function countCreditAccounts(
236
184
  CreditManagerFilter memory cmFilter,
237
185
  CreditAccountFilter memory caFilter,
238
- uint256 offset
186
+ Pagination memory pagination
239
187
  ) public view returns (uint256 num) {
240
- address[] memory creditManagers = _getCreditManagers(cmFilter);
188
+ uint256 offset = pagination.offset;
189
+ uint256 scanLimit = pagination.scanLimit == 0 ? type(uint256).max : pagination.scanLimit;
241
190
 
191
+ address[] memory creditManagers = _getCreditManagers(cmFilter);
242
192
  for (uint256 i; i < creditManagers.length; ++i) {
243
193
  uint256 len = ICreditManagerV3(creditManagers[i]).creditAccountsLen();
244
194
 
@@ -248,27 +198,29 @@ contract CreditAccountCompressor is IVersion, SanityCheckTrait {
248
198
  continue;
249
199
  }
250
200
 
251
- while (offset < len) {
252
- num += _countCreditAccounts(creditManagers[i], caFilter, offset, STEP_SIZE);
253
- offset += STEP_SIZE;
254
- }
201
+ uint256 limit = Math.min(len - offset, scanLimit);
202
+ num += _countCreditAccounts(creditManagers[i], caFilter, offset, limit);
203
+
204
+ scanLimit -= limit;
205
+ if (scanLimit == 0) break;
255
206
  offset = 0;
256
207
  }
257
208
  }
258
209
 
259
210
  /// @notice Counts credit accounts that match `caFilter` in a given `creditManager`
260
- /// @dev The `offset` parameter specifies the position to start scanning from in the list of all
261
- /// credit accounts opened in `creditManager`
262
- function countCreditAccounts(address creditManager, CreditAccountFilter memory caFilter, uint256 offset)
263
- public
264
- view
265
- returns (uint256 num)
266
- {
211
+ function countCreditAccounts(
212
+ address creditManager,
213
+ CreditAccountFilter memory caFilter,
214
+ Pagination memory pagination
215
+ ) public view returns (uint256) {
216
+ uint256 offset = pagination.offset;
217
+ uint256 scanLimit = pagination.scanLimit == 0 ? type(uint256).max : pagination.scanLimit;
218
+
267
219
  uint256 len = ICreditManagerV3(creditManager).creditAccountsLen();
268
- while (offset < len) {
269
- num += _countCreditAccounts(creditManager, caFilter, offset, STEP_SIZE);
270
- offset += STEP_SIZE;
271
- }
220
+ if (len <= offset) return 0;
221
+ uint256 limit = Math.min(len - offset, scanLimit);
222
+
223
+ return _countCreditAccounts(creditManager, caFilter, offset, limit);
272
224
  }
273
225
 
274
226
  // --------- //
@@ -282,19 +234,18 @@ contract CreditAccountCompressor is IVersion, SanityCheckTrait {
282
234
  CreditAccountData[] memory data,
283
235
  uint256 dataOffset,
284
236
  uint256 offset,
285
- uint256 limit,
286
- uint256 gasThreshold
287
- ) internal view returns (uint256, bool, uint256) {
237
+ uint256 limit
238
+ ) internal view returns (uint256 nextDataOffset, uint256 scanned) {
288
239
  address[] memory creditAccounts = ICreditManagerV3(creditManager).creditAccounts(offset, limit);
289
- uint256 len = creditAccounts.length;
290
- for (uint256 i; i < len; ++i) {
291
- // early return if remaining gas is below given threshold
292
- if (gasleft() < gasThreshold) return (dataOffset, true, i);
240
+ for (uint256 i; i < creditAccounts.length; ++i) {
293
241
  if (_checkFilterMatch(creditAccounts[i], creditManager, filter)) {
294
242
  data[dataOffset++] = _getCreditAccountData(creditAccounts[i], creditManager);
295
243
  }
244
+
245
+ // either finished or reached one of return size or gas limits
246
+ if (dataOffset == data.length || gasleft() < GAS_THRESHOLD) return (dataOffset, i + 1);
296
247
  }
297
- return (dataOffset, false, len);
248
+ return (dataOffset, creditAccounts.length);
298
249
  }
299
250
 
300
251
  /// @dev Counting implementation
@@ -338,26 +289,30 @@ contract CreditAccountCompressor is IVersion, SanityCheckTrait {
338
289
  data.totalValueUSD = cdd_.totalValueUSD;
339
290
  data.twvUSD = cdd_.twvUSD;
340
291
  data.totalValue = cdd.totalValue;
341
- data.healthFactor = cdd.totalDebtUSD == 0 ? type(uint256).max : cdd.twvUSD * 1e18 / cdd.totalDebtUSD;
292
+
293
+ data.healthFactor = cdd.twvUSD * PERCENTAGE_FACTOR >= type(uint16).max * cdd.totalDebtUSD
294
+ ? type(uint16).max
295
+ : uint16(cdd.twvUSD * PERCENTAGE_FACTOR / cdd.totalDebtUSD);
342
296
  data.success = true;
343
297
  } catch {}
344
298
 
345
299
  uint256 numTokens = ICreditManagerV3(creditManager).collateralTokensCount();
346
300
  data.tokens = new TokenInfo[](numTokens);
347
301
  for (uint256 i; i < numTokens; ++i) {
348
- address token = ICreditManagerV3(creditManager).getTokenByMask(1 << i);
302
+ uint256 mask = 1 << i;
303
+ address token = ICreditManagerV3(creditManager).getTokenByMask(mask);
349
304
  data.tokens[i].token = token;
305
+ data.tokens[i].mask = mask;
350
306
 
351
307
  try IERC20(token).balanceOf(creditAccount) returns (uint256 balance) {
352
308
  data.tokens[i].balance = balance;
353
309
  data.tokens[i].success = true;
354
310
  } catch {}
355
311
 
356
- // down goes quotas logic which does not apply to underlying
357
- if (i == 0) continue;
358
-
359
- (data.tokens[i].quota,) =
360
- IPoolQuotaKeeperV3(cdd._poolQuotaKeeper).getQuotaAndOutstandingInterest(creditAccount, token);
312
+ if (IPoolQuotaKeeperV3(cdd._poolQuotaKeeper).isQuotedToken(token)) {
313
+ (data.tokens[i].quota,) =
314
+ IPoolQuotaKeeperV3(cdd._poolQuotaKeeper).getQuotaAndOutstandingInterest(creditAccount, token);
315
+ }
361
316
  }
362
317
  }
363
318
 
@@ -381,7 +336,9 @@ contract CreditAccountCompressor is IVersion, SanityCheckTrait {
381
336
  try ICreditManagerV3(creditManager).calcDebtAndCollateral(creditAccount, CollateralCalcTask.DEBT_COLLATERAL)
382
337
  returns (CollateralDebtData memory cdd) {
383
338
  if (filter.reverting) return false;
384
- uint256 healthFactor = cdd.totalDebtUSD == 0 ? type(uint256).max : cdd.twvUSD * 1e18 / cdd.totalDebtUSD;
339
+ uint16 healthFactor = cdd.twvUSD * PERCENTAGE_FACTOR >= type(uint16).max * cdd.totalDebtUSD
340
+ ? type(uint16).max
341
+ : uint16(cdd.twvUSD * PERCENTAGE_FACTOR / cdd.totalDebtUSD);
385
342
  if (filter.minHealthFactor != 0 && healthFactor < filter.minHealthFactor) return false;
386
343
  if (filter.maxHealthFactor != 0 && healthFactor > filter.maxHealthFactor) return false;
387
344
  } catch {
@@ -398,7 +355,7 @@ contract CreditAccountCompressor is IVersion, SanityCheckTrait {
398
355
  view
399
356
  returns (address[] memory creditManagers)
400
357
  {
401
- address[] memory configurators = IAddressProviderV3(addressProvider).marketConfigurators();
358
+ address[] memory configurators = IAddressProviderV3(ADDRESS_PROVIDER).marketConfigurators();
402
359
 
403
360
  // rough estimate of maximum number of credit managers
404
361
  uint256 max;
@@ -420,15 +377,15 @@ contract CreditAccountCompressor is IVersion, SanityCheckTrait {
420
377
  address[] memory managers = IContractsRegister(cr).getCreditManagers();
421
378
  for (uint256 j; j < managers.length; ++j) {
422
379
  // we're only concerned with v3 contracts
423
- uint256 ver = IVersion(managers[i]).version();
380
+ uint256 ver = IVersion(managers[j]).version();
424
381
  if (ver < 3_00 || ver > 3_99) continue;
425
382
 
426
383
  if (filter.pool != address(0)) {
427
- if (ICreditManagerV3(managers[i]).pool() != filter.pool) continue;
384
+ if (ICreditManagerV3(managers[j]).pool() != filter.pool) continue;
428
385
  }
429
386
 
430
387
  if (filter.underlying != address(0)) {
431
- if (ICreditManagerV3(managers[i]).underlying() != filter.underlying) continue;
388
+ if (ICreditManagerV3(managers[j]).underlying() != filter.underlying) continue;
432
389
  }
433
390
 
434
391
  creditManagers[num++] = managers[j];
@@ -17,7 +17,7 @@ pragma solidity ^0.8.17;
17
17
  /// @param totalValueUSD Account's total value in USD
18
18
  /// @param twvUSD Account's threshold-weighted value in USD
19
19
  /// @param totalValue Account's total value in underlying
20
- /// @param healthFactor Account's health factor, i.e. ratio of `twvUSD` to `totalDebtUSD` with 18 decimals precision
20
+ /// @param healthFactor Account's health factor, i.e. ratio of `twvUSD` to `totalDebtUSD`, in bps
21
21
  /// @param success Whether collateral calculation was successful
22
22
  /// @param tokens Info on all collateral tokens in account's credit manager ordered by their mask, see `TokenInfo`
23
23
  /// @dev Fields from `totalDebtUSD` through `healthFactor` are not filled if `success` is `false`
@@ -36,7 +36,7 @@ struct CreditAccountData {
36
36
  uint256 totalValueUSD;
37
37
  uint256 twvUSD;
38
38
  uint256 totalValue;
39
- uint256 healthFactor;
39
+ uint16 healthFactor;
40
40
  bool success;
41
41
  TokenInfo[] tokens;
42
42
  }
@@ -50,8 +50,8 @@ struct CreditAccountData {
50
50
  struct CreditAccountFilter {
51
51
  address owner;
52
52
  bool includeZeroDebt;
53
- uint256 minHealthFactor;
54
- uint256 maxHealthFactor;
53
+ uint16 minHealthFactor;
54
+ uint16 maxHealthFactor;
55
55
  bool reverting;
56
56
  }
57
57
 
@@ -65,6 +65,18 @@ struct CreditManagerFilter {
65
65
  address underlying;
66
66
  }
67
67
 
68
+ /// @notice Pagination options
69
+ /// @param offset Position to start scanning from in the list of all credit accounts open in given credit managers
70
+ /// @param scanLimit If set, specifies maximum number of accounts to scan, which can be helpful to parallelize querying
71
+ /// @param returnLimit If set, specifies maximum number of accounts to return, which can be helpful to optimize querying
72
+ /// as at some point allocating memory for returned data leaves almost no room for actual data preparation
73
+ /// @dev `offset` and `scanLimit` apply to both querying and counting, while `returnLimit` only applies to querying
74
+ struct Pagination {
75
+ uint256 offset;
76
+ uint256 scanLimit;
77
+ uint256 returnLimit;
78
+ }
79
+
68
80
  /// @notice Price feed answer packed in a struct
69
81
  struct PriceFeedAnswer {
70
82
  int256 price;
@@ -107,11 +119,13 @@ struct PriceFeedTreeNode {
107
119
 
108
120
  /// @notice Info on credit account's holdings of a token
109
121
  /// @param token Token address
122
+ /// @param mask Token mask in the credit manager
110
123
  /// @param balance Account's balance of token
111
124
  /// @param quota Account's quota of token
112
125
  /// @param success Whether balance call was successful
113
126
  struct TokenInfo {
114
127
  address token;
128
+ uint256 mask;
115
129
  uint256 balance;
116
130
  uint256 quota;
117
131
  bool success;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gearbox-protocol/periphery-v3",
3
- "version": "1.7.0-next.5",
3
+ "version": "1.7.0-next.6",
4
4
  "main": "index.js",
5
5
  "repository": "git@github.com:Gearbox-protocol/periphery-v3.git",
6
6
  "author": "Mikael <26343374+0xmikko@users.noreply.github.com>",