@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
|
|
28
|
-
///
|
|
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
|
|
36
|
+
address public immutable ADDRESS_PROVIDER;
|
|
35
37
|
|
|
36
|
-
/// @dev
|
|
37
|
-
|
|
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
|
|
44
|
-
constructor(address
|
|
45
|
-
if (
|
|
46
|
-
try IAddressProviderV3(
|
|
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
|
-
|
|
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 `
|
|
65
|
-
///
|
|
66
|
-
///
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
86
|
-
uint256
|
|
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
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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:
|
|
149
|
-
limit:
|
|
150
|
-
gasThreshold: 0
|
|
108
|
+
offset: _p.offset,
|
|
109
|
+
limit: limit
|
|
151
110
|
});
|
|
152
|
-
|
|
153
|
-
}
|
|
111
|
+
nextOffset += scanned;
|
|
154
112
|
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
127
|
+
|
|
128
|
+
return (data, dataOffset == num, nextOffset);
|
|
205
129
|
}
|
|
206
130
|
|
|
207
|
-
/// @
|
|
208
|
-
|
|
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,
|
|
214
|
-
|
|
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
|
-
|
|
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:
|
|
223
|
-
limit:
|
|
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
|
-
|
|
186
|
+
Pagination memory pagination
|
|
239
187
|
) public view returns (uint256 num) {
|
|
240
|
-
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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
|
-
|
|
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(
|
|
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[
|
|
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[
|
|
384
|
+
if (ICreditManagerV3(managers[j]).pool() != filter.pool) continue;
|
|
428
385
|
}
|
|
429
386
|
|
|
430
387
|
if (filter.underlying != address(0)) {
|
|
431
|
-
if (ICreditManagerV3(managers[
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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