@gearbox-protocol/periphery-v3 1.7.0-next.6 → 1.7.0-next.60
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.
- package/contracts/compressors/AdapterCompressor.sol +86 -0
- package/contracts/compressors/CreditAccountCompressor.sol +218 -208
- package/contracts/compressors/CreditSuiteCompressor.sol +99 -0
- package/contracts/compressors/GaugeCompressor.sol +200 -0
- package/contracts/compressors/MarketCompressor.sol +223 -0
- package/contracts/compressors/PeripheryCompressor.sol +115 -0
- package/contracts/compressors/PoolCompressor.sol +207 -0
- package/contracts/compressors/PriceFeedCompressor.sol +104 -58
- package/contracts/compressors/RewardsCompressor.sol +279 -0
- package/contracts/compressors/TokenCompressor.sol +51 -0
- package/contracts/data/DataCompressorV3.sol +9 -8
- package/contracts/data/Types.sol +1 -1
- package/contracts/data/ZapperRegister.sol +7 -8
- package/contracts/emergency/MultiPause.sol +19 -11
- package/contracts/interfaces/IAdapterCompressor.sol +12 -0
- package/contracts/interfaces/ICreditAccountCompressor.sol +61 -0
- package/contracts/interfaces/ICreditSuiteCompressor.sol +18 -0
- package/contracts/interfaces/IGaugeCompressor.sol +14 -0
- package/contracts/interfaces/IMarketCompressor.sol +22 -0
- package/contracts/interfaces/IPeripheryCompressor.sol +16 -0
- package/contracts/interfaces/IPoolCompressor.sol +23 -0
- package/contracts/interfaces/IPriceFeedCompressor.sol +28 -0
- package/contracts/interfaces/IRewardsCompressor.sol +80 -0
- package/contracts/interfaces/{IStateSerializer.sol → IStateSerializerLegacy.sol} +1 -1
- package/contracts/interfaces/ITokenCompressor.sol +13 -0
- package/contracts/libraries/BaseLib.sol +42 -0
- package/contracts/libraries/Contains.sol +15 -0
- package/contracts/libraries/Literals.sol +15 -0
- package/contracts/serializers/oracles/BoundedPriceFeedSerializer.sol +2 -2
- package/contracts/serializers/oracles/LPPriceFeedSerializer.sol +4 -4
- package/contracts/serializers/oracles/PendleTWAPPTPriceFeedSerializer.sol +24 -0
- package/contracts/serializers/oracles/PythPriceFeedSerializer.sol +2 -2
- package/contracts/serializers/oracles/RedstonePriceFeedSerializer.sol +2 -2
- package/contracts/serializers/pool/GaugeSerializer.sol +32 -0
- package/contracts/serializers/pool/LinearInterestRateModelSerializer.sol +26 -0
- package/contracts/test/ForkTest.sol +33 -11
- package/contracts/test/MultiPause.t.sol +14 -20
- package/contracts/test/equivalence/CreditAccountsEquivalenceTest.t.sol +197 -0
- package/contracts/test/equivalence/CreditSuiteEquivalenceTest.t.sol +198 -0
- package/contracts/test/equivalence/GaugeEquivalenceTest.t.sol +96 -0
- package/contracts/test/equivalence/PoolsEquivalenceTest.t.sol +149 -0
- package/contracts/test/interfaces/IACLExt.sol +1 -1
- package/contracts/test/interfaces/IContractsRegisterExt.sol +1 -1
- package/contracts/types/BaseState.sol +15 -0
- package/contracts/types/CreditAccountState.sol +58 -0
- package/contracts/types/CreditFacadeState.sol +20 -0
- package/contracts/types/CreditManagerState.sol +28 -0
- package/contracts/types/CreditSuiteData.sol +20 -0
- package/contracts/types/Filters.sol +30 -0
- package/contracts/types/MarketData.sol +32 -0
- package/contracts/types/PeripheryState.sol +19 -0
- package/contracts/types/PoolQuotaKeeperState.sol +24 -0
- package/contracts/types/PoolState.sol +49 -0
- package/contracts/types/PriceOracleState.sol +58 -0
- package/contracts/types/RateKeeperState.sol +39 -0
- package/contracts/types/TokenData.sol +11 -0
- package/package.json +12 -12
- package/contracts/compressors/Types.sol +0 -132
- package/contracts/data/LinearInterestModelHelper.sol +0 -19
- package/contracts/interfaces/IStateSerializerTrait.sol +0 -10
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
// Gearbox Protocol. Generalized leverage for DeFi protocols
|
|
3
|
+
// (c) Gearbox Holdings, 2024
|
|
4
|
+
pragma solidity ^0.8.17;
|
|
5
|
+
|
|
6
|
+
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
|
|
7
|
+
import {IStateSerializer} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IStateSerializer.sol";
|
|
8
|
+
import {AdapterType} from "@gearbox-protocol/sdk-gov/contracts/AdapterType.sol";
|
|
9
|
+
import {AdapterState} from "../types/CreditSuiteData.sol";
|
|
10
|
+
import {IAdapterCompressor} from "../interfaces/IAdapterCompressor.sol";
|
|
11
|
+
import {ICreditConfiguratorV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditConfiguratorV3.sol";
|
|
12
|
+
import {ICreditManagerV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditManagerV3.sol";
|
|
13
|
+
import {AP_ADAPTER_COMPRESSOR} from "../libraries/Literals.sol";
|
|
14
|
+
|
|
15
|
+
interface ILegacyAdapter {
|
|
16
|
+
function _gearboxAdapterVersion() external view returns (uint16);
|
|
17
|
+
function _gearboxAdapterType() external view returns (uint8);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
contract AdapterCompressor is IAdapterCompressor {
|
|
21
|
+
uint256 public constant override version = 3_10;
|
|
22
|
+
bytes32 public constant override contractType = AP_ADAPTER_COMPRESSOR;
|
|
23
|
+
|
|
24
|
+
mapping(uint8 => bytes32) public contractTypes;
|
|
25
|
+
|
|
26
|
+
constructor() {
|
|
27
|
+
contractTypes[uint8(AdapterType.BALANCER_VAULT)] = "ADAPTER::BALANCER_VAULT";
|
|
28
|
+
contractTypes[uint8(AdapterType.BALANCER_V3_ROUTER)] = "ADAPTER::BALANCER_V3_ROUTER";
|
|
29
|
+
contractTypes[uint8(AdapterType.CAMELOT_V3_ROUTER)] = "ADAPTER::CAMELOT_V3_ROUTER";
|
|
30
|
+
contractTypes[uint8(AdapterType.CONVEX_V1_BASE_REWARD_POOL)] = "ADAPTER::CVX_V1_BASE_REWARD_POOL";
|
|
31
|
+
contractTypes[uint8(AdapterType.CONVEX_V1_BOOSTER)] = "ADAPTER::CVX_V1_BOOSTER";
|
|
32
|
+
contractTypes[uint8(AdapterType.CURVE_V1_2ASSETS)] = "ADAPTER::CURVE_V1_2ASSETS";
|
|
33
|
+
contractTypes[uint8(AdapterType.CURVE_V1_3ASSETS)] = "ADAPTER::CURVE_V1_3ASSETS";
|
|
34
|
+
contractTypes[uint8(AdapterType.CURVE_V1_4ASSETS)] = "ADAPTER::CURVE_V1_4ASSETS";
|
|
35
|
+
contractTypes[uint8(AdapterType.CURVE_STABLE_NG)] = "ADAPTER::CURVE_STABLE_NG";
|
|
36
|
+
contractTypes[uint8(AdapterType.CURVE_V1_STECRV_POOL)] = "ADAPTER::CURVE_V1_STECRV_POOL";
|
|
37
|
+
contractTypes[uint8(AdapterType.CURVE_V1_WRAPPER)] = "ADAPTER::CURVE_V1_WRAPPER";
|
|
38
|
+
contractTypes[uint8(AdapterType.DAI_USDS_EXCHANGE)] = "ADAPTER::DAI_USDS_EXCHANGE";
|
|
39
|
+
contractTypes[uint8(AdapterType.EQUALIZER_ROUTER)] = "ADAPTER::EQUALIZER_ROUTER";
|
|
40
|
+
contractTypes[uint8(AdapterType.ERC4626_VAULT)] = "ADAPTER::ERC4626_VAULT";
|
|
41
|
+
contractTypes[uint8(AdapterType.LIDO_V1)] = "ADAPTER::LIDO_V1";
|
|
42
|
+
contractTypes[uint8(AdapterType.LIDO_WSTETH_V1)] = "ADAPTER::LIDO_WSTETH_V1";
|
|
43
|
+
contractTypes[uint8(AdapterType.MELLOW_ERC4626_VAULT)] = "ADAPTER::MELLOW_ERC4626_VAULT";
|
|
44
|
+
contractTypes[uint8(AdapterType.MELLOW_LRT_VAULT)] = "ADAPTER::MELLOW_LRT_VAULT";
|
|
45
|
+
contractTypes[uint8(AdapterType.PENDLE_ROUTER)] = "ADAPTER::PENDLE_ROUTER";
|
|
46
|
+
contractTypes[uint8(AdapterType.STAKING_REWARDS)] = "ADAPTER::STAKING_REWARDS";
|
|
47
|
+
contractTypes[uint8(AdapterType.UNISWAP_V2_ROUTER)] = "ADAPTER::UNISWAP_V2_ROUTER";
|
|
48
|
+
contractTypes[uint8(AdapterType.UNISWAP_V3_ROUTER)] = "ADAPTER::UNISWAP_V3_ROUTER";
|
|
49
|
+
contractTypes[uint8(AdapterType.VELODROME_V2_ROUTER)] = "ADAPTER::VELODROME_V2_ROUTER";
|
|
50
|
+
contractTypes[uint8(AdapterType.YEARN_V2)] = "ADAPTER::YEARN_V2";
|
|
51
|
+
contractTypes[uint8(AdapterType.ZIRCUIT_POOL)] = "ADAPTER::ZIRCUIT_POOL";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getAdapters(address creditManager) external view returns (AdapterState[] memory adapters) {
|
|
55
|
+
ICreditConfiguratorV3 creditConfigurator =
|
|
56
|
+
ICreditConfiguratorV3(ICreditManagerV3(creditManager).creditConfigurator());
|
|
57
|
+
|
|
58
|
+
address[] memory allowedAdapters = creditConfigurator.allowedAdapters();
|
|
59
|
+
uint256 len = allowedAdapters.length;
|
|
60
|
+
|
|
61
|
+
adapters = new AdapterState[](len);
|
|
62
|
+
unchecked {
|
|
63
|
+
for (uint256 i = 0; i < len; ++i) {
|
|
64
|
+
address adapter = allowedAdapters[i];
|
|
65
|
+
adapters[i].baseParams.addr = adapter;
|
|
66
|
+
try IVersion(adapter).contractType() returns (bytes32 contractType_) {
|
|
67
|
+
adapters[i].baseParams.contractType = contractType_;
|
|
68
|
+
} catch {
|
|
69
|
+
adapters[i].baseParams.contractType = contractTypes[ILegacyAdapter(adapter)._gearboxAdapterType()];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try IVersion(adapter).version() returns (uint256 v) {
|
|
73
|
+
adapters[i].baseParams.version = v;
|
|
74
|
+
} catch {
|
|
75
|
+
adapters[i].baseParams.version = ILegacyAdapter(adapter)._gearboxAdapterVersion();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try IStateSerializer(adapter).serialize() returns (bytes memory serializedParams) {
|
|
79
|
+
adapters[i].baseParams.serializedParams = serializedParams;
|
|
80
|
+
} catch {}
|
|
81
|
+
|
|
82
|
+
adapters[i].targetContract = ICreditManagerV3(creditManager).adapterToContract(adapter);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -3,55 +3,67 @@
|
|
|
3
3
|
// (c) Gearbox Foundation, 2024.
|
|
4
4
|
pragma solidity ^0.8.17;
|
|
5
5
|
|
|
6
|
+
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
|
6
7
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
7
8
|
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
|
|
8
9
|
|
|
9
|
-
import {IContractsRegister} from "@gearbox-protocol/core-v3/contracts/interfaces/IContractsRegister.sol";
|
|
10
10
|
import {ICreditAccountV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditAccountV3.sol";
|
|
11
|
+
import {ICreditFacadeV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditFacadeV3.sol";
|
|
11
12
|
import {
|
|
12
13
|
CollateralCalcTask,
|
|
13
14
|
CollateralDebtData,
|
|
14
15
|
ICreditManagerV3
|
|
15
16
|
} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditManagerV3.sol";
|
|
16
17
|
import {IPoolQuotaKeeperV3} from "@gearbox-protocol/core-v3/contracts/interfaces/IPoolQuotaKeeperV3.sol";
|
|
17
|
-
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
|
|
18
18
|
import {PERCENTAGE_FACTOR} from "@gearbox-protocol/core-v3/contracts/libraries/Constants.sol";
|
|
19
19
|
import {SanityCheckTrait} from "@gearbox-protocol/core-v3/contracts/traits/SanityCheckTrait.sol";
|
|
20
|
+
import {ICreditAccountCompressor} from "../interfaces/ICreditAccountCompressor.sol";
|
|
20
21
|
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
22
|
+
import {IContractsRegister} from "@gearbox-protocol/governance/contracts/interfaces/IContractsRegister.sol";
|
|
23
|
+
import {IAddressProvider} from "@gearbox-protocol/governance/contracts/interfaces/IAddressProvider.sol";
|
|
24
|
+
import {IMarketConfigurator} from "@gearbox-protocol/governance/contracts/interfaces/IMarketConfigurator.sol";
|
|
25
|
+
import {IMarketConfiguratorFactory} from
|
|
26
|
+
"@gearbox-protocol/governance/contracts/interfaces/IMarketConfiguratorFactory.sol";
|
|
27
|
+
import {
|
|
28
|
+
AP_MARKET_CONFIGURATOR_FACTORY,
|
|
29
|
+
NO_VERSION_CONTROL
|
|
30
|
+
} from "@gearbox-protocol/governance/contracts/libraries/ContractLiterals.sol";
|
|
23
31
|
|
|
24
|
-
import {CreditAccountData,
|
|
32
|
+
import {CreditAccountData, TokenInfo} from "../types/CreditAccountState.sol";
|
|
33
|
+
import {CreditAccountFilter, MarketFilter} from "../types/Filters.sol";
|
|
25
34
|
|
|
35
|
+
import {Contains} from "../libraries/Contains.sol";
|
|
36
|
+
import {AP_CREDIT_ACCOUNT_COMPRESSOR} from "../libraries/Literals.sol";
|
|
26
37
|
/// @title Credit account compressor
|
|
27
38
|
/// @notice Allows to fetch data on all credit accounts matching certain criteria in an efficient manner
|
|
28
39
|
/// @dev The contract is not gas optimized and is thus not recommended for on-chain use
|
|
29
|
-
/// @dev Querying functions try to process as many accounts as possible and stop when they get close to gas limit
|
|
30
|
-
|
|
31
|
-
contract CreditAccountCompressor is
|
|
40
|
+
/// @dev Querying functions try to process as many accounts as possible and stop when they get close to gas limit
|
|
41
|
+
|
|
42
|
+
contract CreditAccountCompressor is ICreditAccountCompressor, SanityCheckTrait {
|
|
43
|
+
using Contains for address[];
|
|
44
|
+
|
|
32
45
|
/// @notice Contract version
|
|
33
46
|
uint256 public constant override version = 3_10;
|
|
47
|
+
bytes32 public constant override contractType = AP_CREDIT_ACCOUNT_COMPRESSOR;
|
|
34
48
|
|
|
35
49
|
/// @notice Address provider contract address
|
|
36
|
-
address public immutable
|
|
50
|
+
address public immutable addressProvider;
|
|
37
51
|
|
|
38
|
-
|
|
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;
|
|
52
|
+
address public immutable marketConfiguratorFactory;
|
|
42
53
|
|
|
43
|
-
/// @notice Thrown when address provider is not a contract
|
|
54
|
+
/// @notice Thrown when address provider is not a contract
|
|
44
55
|
error InvalidAddressProviderException();
|
|
45
56
|
|
|
46
57
|
/// @notice Constructor
|
|
47
|
-
/// @param
|
|
48
|
-
constructor(address
|
|
49
|
-
if (
|
|
50
|
-
try IAddressProviderV3(addressProvider).marketConfigurators() {}
|
|
51
|
-
catch {
|
|
58
|
+
/// @param addressProvider_ Address provider contract address
|
|
59
|
+
constructor(address addressProvider_) nonZeroAddress(addressProvider_) {
|
|
60
|
+
if (addressProvider_.code.length == 0) {
|
|
52
61
|
revert InvalidAddressProviderException();
|
|
53
62
|
}
|
|
54
|
-
|
|
63
|
+
|
|
64
|
+
addressProvider = addressProvider_;
|
|
65
|
+
marketConfiguratorFactory =
|
|
66
|
+
IAddressProvider(addressProvider_).getAddressOrRevert(AP_MARKET_CONFIGURATOR_FACTORY, NO_VERSION_CONTROL);
|
|
55
67
|
}
|
|
56
68
|
|
|
57
69
|
// -------- //
|
|
@@ -64,163 +76,77 @@ contract CreditAccountCompressor is IVersion, SanityCheckTrait {
|
|
|
64
76
|
return _getCreditAccountData(creditAccount, creditManager);
|
|
65
77
|
}
|
|
66
78
|
|
|
67
|
-
/// @notice Returns data for credit accounts that match `caFilter` in credit managers matching `
|
|
68
|
-
/// @dev The
|
|
69
|
-
///
|
|
70
|
-
|
|
79
|
+
/// @notice Returns data for credit accounts that match `caFilter` in credit managers matching `marketFilter`
|
|
80
|
+
/// @dev The non-zero value of `nextOffset` return variable indicates that gas supplied with a call was
|
|
81
|
+
/// insufficient to process all the accounts and next iteration starting from this value is needed
|
|
82
|
+
function getCreditAccounts(MarketFilter memory marketFilter, CreditAccountFilter memory caFilter, uint256 offset)
|
|
83
|
+
external
|
|
84
|
+
view
|
|
85
|
+
returns (CreditAccountData[] memory data, uint256 nextOffset)
|
|
86
|
+
{
|
|
87
|
+
address[] memory creditManagers = _getCreditManagers(marketFilter);
|
|
88
|
+
return _getCreditAccounts(creditManagers, caFilter, offset, type(uint256).max);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/// @dev Same as above but with `limit` parameter that specifies the number of accounts to process
|
|
71
92
|
function getCreditAccounts(
|
|
72
|
-
|
|
93
|
+
MarketFilter memory marketFilter,
|
|
73
94
|
CreditAccountFilter memory caFilter,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
});
|
|
85
|
-
|
|
86
|
-
data = new CreditAccountData[](Math.min(_p.returnLimit, num));
|
|
87
|
-
uint256 dataOffset;
|
|
88
|
-
|
|
89
|
-
nextOffset = _p.offset;
|
|
90
|
-
address[] memory creditManagers = _getCreditManagers(cmFilter);
|
|
91
|
-
for (uint256 i; i < creditManagers.length; ++i) {
|
|
92
|
-
uint256 len = ICreditManagerV3(creditManagers[i]).creditAccountsLen();
|
|
93
|
-
|
|
94
|
-
// first, we need to get to the `offset` position
|
|
95
|
-
if (len <= _p.offset) {
|
|
96
|
-
_p.offset -= len;
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
uint256 limit = Math.min(len - _p.offset, _p.scanLimit);
|
|
101
|
-
|
|
102
|
-
uint256 scanned;
|
|
103
|
-
(dataOffset, scanned) = _getCreditAccounts({
|
|
104
|
-
creditManager: creditManagers[i],
|
|
105
|
-
filter: caFilter,
|
|
106
|
-
data: data,
|
|
107
|
-
dataOffset: dataOffset,
|
|
108
|
-
offset: _p.offset,
|
|
109
|
-
limit: limit
|
|
110
|
-
});
|
|
111
|
-
nextOffset += scanned;
|
|
112
|
-
|
|
113
|
-
// either finished or reached one of return size or gas limits
|
|
114
|
-
if (dataOffset == data.length || scanned < limit) break;
|
|
115
|
-
|
|
116
|
-
_p.scanLimit -= limit;
|
|
117
|
-
if (_p.scanLimit == 0) break;
|
|
118
|
-
_p.offset = 0;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (dataOffset < data.length) {
|
|
122
|
-
// trim the array to its actual size
|
|
123
|
-
assembly {
|
|
124
|
-
mstore(data, dataOffset)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return (data, dataOffset == num, nextOffset);
|
|
95
|
+
uint256 offset,
|
|
96
|
+
uint256 limit
|
|
97
|
+
) public view returns (CreditAccountData[] memory data, uint256 nextOffset) {
|
|
98
|
+
address[] memory creditManagers = _getCreditManagers(marketFilter);
|
|
99
|
+
return _getCreditAccounts(creditManagers, caFilter, offset, limit);
|
|
129
100
|
}
|
|
130
101
|
|
|
131
102
|
/// @notice Returns data for credit accounts that match `caFilter` in a given `creditManager`
|
|
132
|
-
/// @dev The
|
|
133
|
-
///
|
|
134
|
-
|
|
135
|
-
function getCreditAccounts(address creditManager, CreditAccountFilter memory caFilter, Pagination memory pagination)
|
|
103
|
+
/// @dev The non-zero value of `nextOffset` return variable indicates that gas supplied with a call was
|
|
104
|
+
/// insufficient to process all the accounts and next iteration starting from this value is needed
|
|
105
|
+
function getCreditAccounts(address creditManager, CreditAccountFilter memory caFilter, uint256 offset)
|
|
136
106
|
external
|
|
137
107
|
view
|
|
138
|
-
returns (CreditAccountData[] memory data,
|
|
108
|
+
returns (CreditAccountData[] memory data, uint256 nextOffset)
|
|
139
109
|
{
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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;
|
|
152
|
-
|
|
153
|
-
uint256 len = ICreditManagerV3(creditManager).creditAccountsLen();
|
|
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({
|
|
159
|
-
creditManager: creditManager,
|
|
160
|
-
filter: caFilter,
|
|
161
|
-
data: data,
|
|
162
|
-
dataOffset: 0,
|
|
163
|
-
offset: _p.offset,
|
|
164
|
-
limit: limit
|
|
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
|
-
}
|
|
110
|
+
address[] memory creditManagers = new address[](1);
|
|
111
|
+
creditManagers[0] = creditManager;
|
|
112
|
+
return _getCreditAccounts(creditManagers, caFilter, offset, type(uint256).max);
|
|
113
|
+
}
|
|
174
114
|
|
|
175
|
-
|
|
115
|
+
/// @dev Same as above but with `limit` parameter that specifies the number of accounts to process
|
|
116
|
+
function getCreditAccounts(
|
|
117
|
+
address creditManager,
|
|
118
|
+
CreditAccountFilter memory caFilter,
|
|
119
|
+
uint256 offset,
|
|
120
|
+
uint256 limit
|
|
121
|
+
) external view returns (CreditAccountData[] memory data, uint256 nextOffset) {
|
|
122
|
+
address[] memory creditManagers = new address[](1);
|
|
123
|
+
creditManagers[0] = creditManager;
|
|
124
|
+
return _getCreditAccounts(creditManagers, caFilter, offset, limit);
|
|
176
125
|
}
|
|
177
126
|
|
|
178
127
|
// -------- //
|
|
179
128
|
// COUNTING //
|
|
180
129
|
// -------- //
|
|
181
130
|
|
|
182
|
-
/// @notice Counts credit accounts that match `caFilter` in credit managers matching `
|
|
183
|
-
function countCreditAccounts(
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
address[] memory creditManagers = _getCreditManagers(cmFilter);
|
|
192
|
-
for (uint256 i; i < creditManagers.length; ++i) {
|
|
193
|
-
uint256 len = ICreditManagerV3(creditManagers[i]).creditAccountsLen();
|
|
194
|
-
|
|
195
|
-
// first, we need to get to the `offset` position
|
|
196
|
-
if (len <= offset) {
|
|
197
|
-
offset -= len;
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
|
|
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;
|
|
206
|
-
offset = 0;
|
|
207
|
-
}
|
|
131
|
+
/// @notice Counts credit accounts that match `caFilter` in credit managers matching `marketFilter`
|
|
132
|
+
function countCreditAccounts(MarketFilter memory marketFilter, CreditAccountFilter memory caFilter)
|
|
133
|
+
external
|
|
134
|
+
view
|
|
135
|
+
returns (uint256)
|
|
136
|
+
{
|
|
137
|
+
address[] memory creditManagers = _getCreditManagers(marketFilter);
|
|
138
|
+
return _countCreditAccounts(creditManagers, caFilter, 0, type(uint256).max);
|
|
208
139
|
}
|
|
209
140
|
|
|
210
141
|
/// @notice Counts credit accounts that match `caFilter` in a given `creditManager`
|
|
211
|
-
function countCreditAccounts(
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
uint256 len = ICreditManagerV3(creditManager).creditAccountsLen();
|
|
220
|
-
if (len <= offset) return 0;
|
|
221
|
-
uint256 limit = Math.min(len - offset, scanLimit);
|
|
222
|
-
|
|
223
|
-
return _countCreditAccounts(creditManager, caFilter, offset, limit);
|
|
142
|
+
function countCreditAccounts(address creditManager, CreditAccountFilter memory caFilter)
|
|
143
|
+
external
|
|
144
|
+
view
|
|
145
|
+
returns (uint256)
|
|
146
|
+
{
|
|
147
|
+
address[] memory creditManagers = new address[](1);
|
|
148
|
+
creditManagers[0] = creditManager;
|
|
149
|
+
return _countCreditAccounts(creditManagers, caFilter, 0, type(uint256).max);
|
|
224
150
|
}
|
|
225
151
|
|
|
226
152
|
// --------- //
|
|
@@ -229,37 +155,108 @@ contract CreditAccountCompressor is IVersion, SanityCheckTrait {
|
|
|
229
155
|
|
|
230
156
|
/// @dev Querying implementation
|
|
231
157
|
function _getCreditAccounts(
|
|
232
|
-
address
|
|
158
|
+
address[] memory creditManagers,
|
|
233
159
|
CreditAccountFilter memory filter,
|
|
234
|
-
CreditAccountData[] memory data,
|
|
235
|
-
uint256 dataOffset,
|
|
236
160
|
uint256 offset,
|
|
237
161
|
uint256 limit
|
|
238
|
-
) internal view returns (
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
162
|
+
) internal view returns (CreditAccountData[] memory data, uint256 nextOffset) {
|
|
163
|
+
uint256 num = _countCreditAccounts(creditManagers, filter, offset, limit);
|
|
164
|
+
if (num == 0) return (data, 0);
|
|
165
|
+
|
|
166
|
+
// allocating the `CreditAccountData` array might consume most of the gas leaving no room for computations,
|
|
167
|
+
// so we instead allocate and gradually fill the array of pointers to structs which takes much less space
|
|
168
|
+
bytes32[] memory dataPointers = new bytes32[](num);
|
|
169
|
+
uint256 dataOffset;
|
|
170
|
+
|
|
171
|
+
// to adjust to RPC provider's call gas limit, the function stops when gas left gets below gas reserve, which
|
|
172
|
+
// starts at this number that should be enough to cover a single account processing, and increases with each
|
|
173
|
+
// new data struct to accommodate the cost of memory expansion that happens upon ABI-encoding returned data
|
|
174
|
+
uint256 gasReserve = 2e6;
|
|
175
|
+
|
|
176
|
+
nextOffset = offset;
|
|
177
|
+
for (uint256 i; i < creditManagers.length; ++i) {
|
|
178
|
+
address creditManager = creditManagers[i];
|
|
179
|
+
uint256 len = ICreditManagerV3(creditManager).creditAccountsLen();
|
|
180
|
+
|
|
181
|
+
// first, we need to get to the `offset` position
|
|
182
|
+
if (len <= offset) {
|
|
183
|
+
offset -= len;
|
|
184
|
+
continue;
|
|
243
185
|
}
|
|
244
186
|
|
|
245
|
-
|
|
246
|
-
|
|
187
|
+
uint256 count = Math.min(len - offset, limit);
|
|
188
|
+
address[] memory creditAccounts = ICreditManagerV3(creditManager).creditAccounts(offset, count);
|
|
189
|
+
|
|
190
|
+
// circumvent the "Stack too deep." error
|
|
191
|
+
CreditAccountFilter memory filter_ = filter;
|
|
192
|
+
|
|
193
|
+
for (uint256 j; j < creditAccounts.length; ++j) {
|
|
194
|
+
address creditAccount = creditAccounts[j];
|
|
195
|
+
if (_checkFilterMatch(creditAccount, creditManager, filter_)) {
|
|
196
|
+
uint256 gasBefore = gasleft();
|
|
197
|
+
|
|
198
|
+
CreditAccountData memory d = _getCreditAccountData(creditAccount, creditManager);
|
|
199
|
+
++dataOffset;
|
|
200
|
+
assembly {
|
|
201
|
+
// save the pointer to created struct
|
|
202
|
+
mstore(add(dataPointers, mul(0x20, dataOffset)), d)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// rough approximation of gas that will be needed to accommodate additional memory expansion cost
|
|
206
|
+
gasReserve += (gasBefore - gasleft()) / 2;
|
|
207
|
+
}
|
|
208
|
+
--count;
|
|
209
|
+
|
|
210
|
+
if (dataOffset == num || gasleft() < gasReserve) break;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
nextOffset += creditAccounts.length - count;
|
|
214
|
+
if (dataOffset == num || count != 0) break;
|
|
215
|
+
|
|
216
|
+
limit -= creditAccounts.length;
|
|
217
|
+
if (limit == 0) break;
|
|
218
|
+
offset = 0;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
assembly {
|
|
222
|
+
// cast array of pointers to structs to array of structs
|
|
223
|
+
data := dataPointers
|
|
224
|
+
// trim array to its actual size
|
|
225
|
+
mstore(data, dataOffset)
|
|
247
226
|
}
|
|
248
|
-
|
|
227
|
+
|
|
228
|
+
// set `nextOffset` to zero to indicate that scanning is finished
|
|
229
|
+
if (dataOffset == num) nextOffset = 0;
|
|
249
230
|
}
|
|
250
231
|
|
|
251
232
|
/// @dev Counting implementation
|
|
252
233
|
function _countCreditAccounts(
|
|
253
|
-
address
|
|
234
|
+
address[] memory creditManagers,
|
|
254
235
|
CreditAccountFilter memory filter,
|
|
255
236
|
uint256 offset,
|
|
256
237
|
uint256 limit
|
|
257
238
|
) internal view returns (uint256 num) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
239
|
+
for (uint256 i; i < creditManagers.length; ++i) {
|
|
240
|
+
address creditManager = creditManagers[i];
|
|
241
|
+
uint256 len = ICreditManagerV3(creditManager).creditAccountsLen();
|
|
242
|
+
|
|
243
|
+
// first, we need to get to the `offset` position
|
|
244
|
+
if (len <= offset) {
|
|
245
|
+
offset -= len;
|
|
246
|
+
continue;
|
|
262
247
|
}
|
|
248
|
+
|
|
249
|
+
address[] memory creditAccounts =
|
|
250
|
+
ICreditManagerV3(creditManager).creditAccounts(offset, Math.min(len - offset, limit));
|
|
251
|
+
for (uint256 j; j < creditAccounts.length; ++j) {
|
|
252
|
+
if (_checkFilterMatch(creditAccounts[j], creditManager, filter)) {
|
|
253
|
+
++num;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
limit -= creditAccounts.length;
|
|
258
|
+
if (limit == 0) break;
|
|
259
|
+
offset = 0;
|
|
263
260
|
}
|
|
264
261
|
}
|
|
265
262
|
|
|
@@ -274,6 +271,7 @@ contract CreditAccountCompressor is IVersion, SanityCheckTrait {
|
|
|
274
271
|
data.creditFacade = ICreditManagerV3(creditManager).creditFacade();
|
|
275
272
|
data.underlying = ICreditManagerV3(creditManager).underlying();
|
|
276
273
|
data.owner = ICreditManagerV3(creditManager).getBorrowerOrRevert(creditAccount);
|
|
274
|
+
data.expirationDate = ICreditFacadeV3(data.creditFacade).expirationDate();
|
|
277
275
|
|
|
278
276
|
CollateralDebtData memory cdd =
|
|
279
277
|
ICreditManagerV3(creditManager).calcDebtAndCollateral(creditAccount, CollateralCalcTask.DEBT_ONLY);
|
|
@@ -285,21 +283,41 @@ contract CreditAccountCompressor is IVersion, SanityCheckTrait {
|
|
|
285
283
|
// collateral is computed separately since it might revert on `balanceOf` and `latestRoundData` calls
|
|
286
284
|
try ICreditManagerV3(creditManager).calcDebtAndCollateral(creditAccount, CollateralCalcTask.DEBT_COLLATERAL)
|
|
287
285
|
returns (CollateralDebtData memory cdd_) {
|
|
288
|
-
data.totalDebtUSD =
|
|
286
|
+
data.totalDebtUSD = cdd_.totalDebtUSD;
|
|
289
287
|
data.totalValueUSD = cdd_.totalValueUSD;
|
|
290
288
|
data.twvUSD = cdd_.twvUSD;
|
|
291
|
-
data.totalValue =
|
|
289
|
+
data.totalValue = cdd_.totalValue;
|
|
292
290
|
|
|
293
|
-
data.healthFactor =
|
|
291
|
+
data.healthFactor = cdd_.twvUSD * PERCENTAGE_FACTOR >= type(uint16).max * cdd_.totalDebtUSD
|
|
294
292
|
? type(uint16).max
|
|
295
|
-
: uint16(
|
|
293
|
+
: uint16((cdd_.twvUSD * PERCENTAGE_FACTOR) / cdd_.totalDebtUSD);
|
|
296
294
|
data.success = true;
|
|
297
295
|
} catch {}
|
|
298
296
|
|
|
299
|
-
uint256
|
|
297
|
+
uint256 maxTokens = ICreditManagerV3(creditManager).collateralTokensCount();
|
|
298
|
+
|
|
299
|
+
// the function is called for every account, so allocating an array of size `maxTokens` and trimming it
|
|
300
|
+
// might cause issues with memory expansion and we must count the precise number of tokens in advance
|
|
301
|
+
uint256 numTokens;
|
|
302
|
+
uint256 returnedTokensMask;
|
|
303
|
+
for (uint256 k; k < maxTokens; ++k) {
|
|
304
|
+
uint256 mask = 1 << k;
|
|
305
|
+
if (cdd.enabledTokensMask & mask == 0) {
|
|
306
|
+
address token = ICreditManagerV3(creditManager).getTokenByMask(mask);
|
|
307
|
+
try IERC20(token).balanceOf(creditAccount) returns (uint256 balance) {
|
|
308
|
+
if (balance <= 1) continue;
|
|
309
|
+
} catch {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
++numTokens;
|
|
314
|
+
returnedTokensMask |= mask;
|
|
315
|
+
}
|
|
316
|
+
|
|
300
317
|
data.tokens = new TokenInfo[](numTokens);
|
|
301
|
-
|
|
302
|
-
|
|
318
|
+
uint256 i;
|
|
319
|
+
while (returnedTokensMask != 0) {
|
|
320
|
+
uint256 mask = returnedTokensMask & uint256(-int256(returnedTokensMask));
|
|
303
321
|
address token = ICreditManagerV3(creditManager).getTokenByMask(mask);
|
|
304
322
|
data.tokens[i].token = token;
|
|
305
323
|
data.tokens[i].mask = mask;
|
|
@@ -313,6 +331,9 @@ contract CreditAccountCompressor is IVersion, SanityCheckTrait {
|
|
|
313
331
|
(data.tokens[i].quota,) =
|
|
314
332
|
IPoolQuotaKeeperV3(cdd._poolQuotaKeeper).getQuotaAndOutstandingInterest(creditAccount, token);
|
|
315
333
|
}
|
|
334
|
+
|
|
335
|
+
returnedTokensMask ^= mask;
|
|
336
|
+
++i;
|
|
316
337
|
}
|
|
317
338
|
}
|
|
318
339
|
|
|
@@ -338,7 +359,7 @@ contract CreditAccountCompressor is IVersion, SanityCheckTrait {
|
|
|
338
359
|
if (filter.reverting) return false;
|
|
339
360
|
uint16 healthFactor = cdd.twvUSD * PERCENTAGE_FACTOR >= type(uint16).max * cdd.totalDebtUSD
|
|
340
361
|
? type(uint16).max
|
|
341
|
-
: uint16(cdd.twvUSD * PERCENTAGE_FACTOR / cdd.totalDebtUSD);
|
|
362
|
+
: uint16((cdd.twvUSD * PERCENTAGE_FACTOR) / cdd.totalDebtUSD);
|
|
342
363
|
if (filter.minHealthFactor != 0 && healthFactor < filter.minHealthFactor) return false;
|
|
343
364
|
if (filter.maxHealthFactor != 0 && healthFactor > filter.maxHealthFactor) return false;
|
|
344
365
|
} catch {
|
|
@@ -350,45 +371,34 @@ contract CreditAccountCompressor is IVersion, SanityCheckTrait {
|
|
|
350
371
|
}
|
|
351
372
|
|
|
352
373
|
/// @dev Credit managers discovery
|
|
353
|
-
function _getCreditManagers(
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
{
|
|
358
|
-
address[] memory configurators = IAddressProviderV3(ADDRESS_PROVIDER).marketConfigurators();
|
|
374
|
+
function _getCreditManagers(MarketFilter memory filter) internal view returns (address[] memory creditManagers) {
|
|
375
|
+
address[] memory configurators = filter.configurators.length != 0
|
|
376
|
+
? filter.configurators
|
|
377
|
+
: IMarketConfiguratorFactory(marketConfiguratorFactory).getMarketConfigurators();
|
|
359
378
|
|
|
360
379
|
// rough estimate of maximum number of credit managers
|
|
361
380
|
uint256 max;
|
|
362
381
|
for (uint256 i; i < configurators.length; ++i) {
|
|
363
|
-
address cr =
|
|
382
|
+
address cr = IMarketConfigurator(configurators[i]).contractsRegister();
|
|
364
383
|
max += IContractsRegister(cr).getCreditManagers().length;
|
|
365
384
|
}
|
|
366
385
|
|
|
367
|
-
// allocate the array with maximum potentially needed size (total number of credit managers
|
|
368
|
-
//
|
|
386
|
+
// allocate the array with maximum potentially needed size (total number of credit managers can be assumed
|
|
387
|
+
// to be relatively small and the function is only called once, so memory expansion cost is not an issue)
|
|
369
388
|
creditManagers = new address[](max);
|
|
370
389
|
uint256 num;
|
|
371
390
|
for (uint256 i; i < configurators.length; ++i) {
|
|
372
|
-
|
|
373
|
-
if (IMarketConfiguratorV3(configurators[i]).owner() != filter.curator) continue;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
address cr = IMarketConfiguratorV3(configurators[i]).contractsRegister();
|
|
391
|
+
address cr = IMarketConfigurator(configurators[i]).contractsRegister();
|
|
377
392
|
address[] memory managers = IContractsRegister(cr).getCreditManagers();
|
|
378
393
|
for (uint256 j; j < managers.length; ++j) {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
if (
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (ICreditManagerV3(managers[j]).pool() != filter.pool) continue;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if (filter.underlying != address(0)) {
|
|
388
|
-
if (ICreditManagerV3(managers[j]).underlying() != filter.underlying) continue;
|
|
394
|
+
address manager = managers[j];
|
|
395
|
+
// FIXME: there's room for optimization that allows to avoid scanning over all configurators
|
|
396
|
+
if (filter.pools.length != 0 && !filter.pools.contains(ICreditManagerV3(manager).pool())) continue;
|
|
397
|
+
if (filter.underlying != address(0) && ICreditManagerV3(manager).underlying() != filter.underlying) {
|
|
398
|
+
continue;
|
|
389
399
|
}
|
|
390
400
|
|
|
391
|
-
creditManagers[num++] =
|
|
401
|
+
creditManagers[num++] = manager;
|
|
392
402
|
}
|
|
393
403
|
}
|
|
394
404
|
|