@gearbox-protocol/periphery-v3 1.7.0-next.59 → 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.
@@ -0,0 +1,197 @@
1
+ // SPDX-License-Identifier: UNLICENSED
2
+ // Gearbox Protocol. Generalized leverage for DeFi protocols
3
+ // (c) Gearbox Foundation, 2023.
4
+ pragma solidity ^0.8.17;
5
+
6
+ import {Address} from "@openzeppelin/contracts/utils/Address.sol";
7
+ import {Test} from "forge-std/Test.sol";
8
+ import {DataCompressorV3} from "../../data/DataCompressorV3.sol";
9
+ import {MarketCompressor} from "../../compressors/MarketCompressor.sol";
10
+ import {CreditAccountCompressor} from "../../compressors/CreditAccountCompressor.sol";
11
+ import {MarketData} from "../../types/MarketData.sol";
12
+ import {CreditAccountData as CreditAccountDataOld, TokenBalance} from "../../data/Types.sol";
13
+ import {CreditAccountData, TokenInfo} from "../../types/CreditAccountState.sol";
14
+ import {MarketFilter, CreditAccountFilter} from "../../types/Filters.sol";
15
+ import {PriceUpdate} from "@gearbox-protocol/core-v3/contracts/interfaces/IPriceOracleV3.sol";
16
+ import {RedstonePriceFeed} from "@gearbox-protocol/oracles-v3/contracts/oracles/updatable/RedstonePriceFeed.sol";
17
+ import {BaseParams} from "../../types/BaseState.sol";
18
+ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
19
+ import "forge-std/console.sol";
20
+
21
+ contract CreditAccountsEquivalenceTest is Test {
22
+ using Address for address;
23
+
24
+ DataCompressorV3 public dc3;
25
+ MarketCompressor public mc;
26
+ CreditAccountCompressor public cac;
27
+
28
+ function _updateAllPriceFeeds() internal {
29
+ MarketFilter memory filter =
30
+ MarketFilter({configurators: new address[](0), pools: new address[](0), underlying: address(0)});
31
+
32
+ BaseParams[] memory updatablePriceFeeds = mc.getUpdatablePriceFeeds(filter);
33
+ for (uint256 i = 0; i < updatablePriceFeeds.length; i++) {
34
+ _refreshUpdatablePriceFeed(updatablePriceFeeds[i].addr, updatablePriceFeeds[i].contractType);
35
+ }
36
+ }
37
+
38
+ function test_CAE_01_CreditAccounts_equivalence() public {
39
+ address marketCompressor = vm.envOr("MARKET_COMPRESSOR", address(0));
40
+ address dataCompressor = vm.envOr("DATA_COMPRESSOR", address(0));
41
+ address creditAccountCompressor = vm.envOr("CREDIT_ACCOUNT_COMPRESSOR", address(0));
42
+ address creditManager = vm.envOr("CREDIT_MANAGER", address(0));
43
+
44
+ if (
45
+ marketCompressor == address(0) || dataCompressor == address(0) || creditAccountCompressor == address(0)
46
+ || creditManager == address(0) || !marketCompressor.isContract() || !dataCompressor.isContract()
47
+ || !creditAccountCompressor.isContract() || !creditManager.isContract()
48
+ ) {
49
+ console.log(
50
+ "MarketCompressor, DataCompressor, CreditAccountCompressor, or CreditManager not set, or not a contract. Skipping credit accounts equivalence test."
51
+ );
52
+ return;
53
+ }
54
+
55
+ mc = MarketCompressor(marketCompressor);
56
+ dc3 = DataCompressorV3(dataCompressor);
57
+ cac = CreditAccountCompressor(creditAccountCompressor);
58
+
59
+ // Update all price feeds before running the test
60
+ _updateAllPriceFeeds();
61
+
62
+ // Get credit accounts from both implementations
63
+ (CreditAccountData[] memory newCAs,) = cac.getCreditAccounts(
64
+ creditManager,
65
+ CreditAccountFilter({
66
+ owner: address(0),
67
+ minHealthFactor: 0,
68
+ maxHealthFactor: 0,
69
+ includeZeroDebt: true,
70
+ reverting: false
71
+ }),
72
+ 0
73
+ );
74
+
75
+ CreditAccountDataOld[] memory oldCAs = dc3.getCreditAccountsByCreditManager(creditManager, new PriceUpdate[](0));
76
+
77
+ // Compare total number of credit accounts
78
+ assertEq(newCAs.length, oldCAs.length, "Credit accounts length mismatch");
79
+
80
+ // Compare credit accounts in order
81
+ for (uint256 k; k < newCAs.length; k++) {
82
+ assertEq(newCAs[k].creditAccount, oldCAs[k].addr, "Credit account address mismatch");
83
+ assertEq(newCAs[k].creditManager, oldCAs[k].creditManager, "Credit manager mismatch");
84
+ assertEq(newCAs[k].creditFacade, oldCAs[k].creditFacade, "Credit facade mismatch");
85
+ assertEq(newCAs[k].underlying, oldCAs[k].underlying, "Underlying mismatch");
86
+ assertEq(newCAs[k].owner, oldCAs[k].borrower, "Owner/borrower mismatch");
87
+ assertEq(newCAs[k].debt, oldCAs[k].debt, "Debt mismatch");
88
+ assertEq(newCAs[k].enabledTokensMask, oldCAs[k].enabledTokensMask, "Enabled tokens mask mismatch");
89
+ assertEq(newCAs[k].accruedInterest, oldCAs[k].accruedInterest, "Accrued interest mismatch");
90
+ assertEq(newCAs[k].accruedFees, oldCAs[k].accruedFees, "Accrued fees mismatch");
91
+ assertEq(newCAs[k].totalDebtUSD, oldCAs[k].totalDebtUSD, "Total debt USD mismatch");
92
+ assertEq(newCAs[k].totalValueUSD, oldCAs[k].totalValueUSD, "Total value USD mismatch");
93
+ assertEq(newCAs[k].twvUSD, oldCAs[k].twvUSD, "TWV USD mismatch");
94
+ assertEq(newCAs[k].healthFactor, oldCAs[k].healthFactor, "Health factor mismatch");
95
+ assertEq(newCAs[k].expirationDate, oldCAs[k].expirationDate, "Expiration date mismatch");
96
+
97
+ // Compare only tokens with non-zero balance from new implementation
98
+ for (uint256 m; m < newCAs[k].tokens.length; m++) {
99
+ TokenInfo memory newToken = newCAs[k].tokens[m];
100
+
101
+ // Find matching token in old implementation
102
+ bool found = false;
103
+ for (uint256 n; n < oldCAs[k].balances.length; n++) {
104
+ if (oldCAs[k].balances[n].token == newToken.token) {
105
+ TokenBalance memory oldToken = oldCAs[k].balances[n];
106
+ assertEq(newToken.balance, oldToken.balance, "Token balance mismatch");
107
+ assertEq(newToken.quota, oldToken.quota, "Token quota mismatch");
108
+ found = true;
109
+ break;
110
+ }
111
+ }
112
+ require(found, string.concat("Token not found in old implementation: ", vm.toString(newToken.token)));
113
+ }
114
+
115
+ // Verify that any non-zero balance tokens in old implementation are present in new implementation
116
+ for (uint256 n; n < oldCAs[k].balances.length; n++) {
117
+ TokenBalance memory oldToken = oldCAs[k].balances[n];
118
+ if (oldToken.balance > 1) {
119
+ bool found = false;
120
+ for (uint256 m; m < newCAs[k].tokens.length; m++) {
121
+ if (newCAs[k].tokens[m].token == oldToken.token) {
122
+ found = true;
123
+ break;
124
+ }
125
+ }
126
+ require(
127
+ found,
128
+ string.concat(
129
+ "Token with non-zero balance not found in new implementation: ", vm.toString(oldToken.token)
130
+ )
131
+ );
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ function _refreshUpdatablePriceFeed(address priceFeed, bytes32 contractType) internal {
138
+ if (contractType == "PRICE_FEED::REDSTONE") {
139
+ uint256 initialTS = block.timestamp;
140
+
141
+ bytes32 dataFeedId = RedstonePriceFeed(priceFeed).dataFeedId();
142
+ uint8 signersThreshold = RedstonePriceFeed(priceFeed).getUniqueSignersThreshold();
143
+
144
+ bytes memory payload =
145
+ _getRedstonePayload(bytes32ToString((dataFeedId)), Strings.toString(signersThreshold));
146
+
147
+ if (payload.length == 0) return;
148
+
149
+ (uint256 expectedPayloadTimestamp,) = abi.decode(payload, (uint256, bytes));
150
+
151
+ if (expectedPayloadTimestamp > block.timestamp) {
152
+ vm.warp(expectedPayloadTimestamp);
153
+ }
154
+
155
+ try RedstonePriceFeed(priceFeed).updatePrice(payload) {} catch {}
156
+
157
+ vm.warp(initialTS);
158
+ } else {
159
+ revert("Unknown updatable price feed type");
160
+ }
161
+ }
162
+
163
+ function _getRedstonePayload(string memory dataFeedId, string memory signersThreshold)
164
+ internal
165
+ returns (bytes memory)
166
+ {
167
+ string[2] memory dataServiceIds = ["redstone-primary-prod", "redstone-arbitrum-prod"];
168
+
169
+ for (uint256 i = 0; i < dataServiceIds.length; ++i) {
170
+ string[] memory args = new string[](6);
171
+ args[0] = "npx";
172
+ args[1] = "ts-node";
173
+ args[2] = "./scripts/redstone.ts";
174
+ args[3] = dataServiceIds[i];
175
+ args[4] = dataFeedId;
176
+ args[5] = signersThreshold;
177
+
178
+ try vm.ffi(args) returns (bytes memory response) {
179
+ return response;
180
+ } catch {}
181
+ }
182
+
183
+ return "";
184
+ }
185
+
186
+ function bytes32ToString(bytes32 _bytes32) public pure returns (string memory) {
187
+ uint8 i = 0;
188
+ while (i < 32 && _bytes32[i] != 0) {
189
+ i++;
190
+ }
191
+ bytes memory bytesArray = new bytes(i);
192
+ for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
193
+ bytesArray[i] = _bytes32[i];
194
+ }
195
+ return string(bytesArray);
196
+ }
197
+ }
@@ -0,0 +1,198 @@
1
+ // SPDX-License-Identifier: UNLICENSED
2
+ // Gearbox Protocol. Generalized leverage for DeFi protocols
3
+ // (c) Gearbox Foundation, 2023.
4
+ pragma solidity ^0.8.17;
5
+
6
+ import {Address} from "@openzeppelin/contracts/utils/Address.sol";
7
+ import {Test} from "forge-std/Test.sol";
8
+ import {DataCompressorV3} from "../../data/DataCompressorV3.sol";
9
+ import {MarketCompressor} from "../../compressors/MarketCompressor.sol";
10
+ import {MarketData} from "../../types/MarketData.sol";
11
+ import {CreditManagerData} from "../../data/Types.sol";
12
+ import {MarketFilter} from "../../types/Filters.sol";
13
+ import "forge-std/console.sol";
14
+
15
+ contract CreditSuiteEquivalenceTest is Test {
16
+ using Address for address;
17
+
18
+ DataCompressorV3 public dc3;
19
+ MarketCompressor public mc;
20
+
21
+ function _findCreditManager(address cmAddr, CreditManagerData[] memory creditManagersOld)
22
+ internal
23
+ pure
24
+ returns (uint256)
25
+ {
26
+ for (uint256 i; i < creditManagersOld.length; i++) {
27
+ if (creditManagersOld[i].addr == cmAddr) {
28
+ return i;
29
+ }
30
+ }
31
+ revert(string.concat("No matching credit manager found for address: ", vm.toString(cmAddr)));
32
+ }
33
+
34
+ function test_CSE_01_CreditSuite_equivalence() public {
35
+ address marketCompressor = vm.envOr("MARKET_COMPRESSOR", address(0));
36
+ address dataCompressor = vm.envOr("DATA_COMPRESSOR", address(0));
37
+
38
+ if (
39
+ marketCompressor == address(0) || dataCompressor == address(0) || !marketCompressor.isContract()
40
+ || !dataCompressor.isContract()
41
+ ) {
42
+ console.log(
43
+ "MarketCompressor or DataCompressor not set, or not a contract. Skipping credit suite equivalence test."
44
+ );
45
+ return;
46
+ }
47
+
48
+ mc = MarketCompressor(marketCompressor);
49
+ dc3 = DataCompressorV3(dataCompressor);
50
+
51
+ MarketFilter memory filter =
52
+ MarketFilter({configurators: new address[](0), pools: new address[](0), underlying: address(0)});
53
+
54
+ MarketData[] memory markets = mc.getMarkets(filter);
55
+ CreditManagerData[] memory creditManagersOld = dc3.getCreditManagersV3List();
56
+
57
+ uint256 totalCreditManagers;
58
+ for (uint256 i; i < markets.length; i++) {
59
+ totalCreditManagers += markets[i].creditManagers.length;
60
+ }
61
+
62
+ assertEq(totalCreditManagers, creditManagersOld.length, "Credit managers total count mismatch");
63
+
64
+ // For each credit manager in markets, find its match in creditManagersOld
65
+ for (uint256 i; i < markets.length; i++) {
66
+ for (uint256 j; j < markets[i].creditManagers.length; j++) {
67
+ address cmAddr = markets[i].creditManagers[j].creditManager.baseParams.addr;
68
+ uint256 cmIndex = _findCreditManager(cmAddr, creditManagersOld);
69
+
70
+ // Compare credit manager data
71
+ assertEq(
72
+ markets[i].creditManagers[j].creditManager.name,
73
+ creditManagersOld[cmIndex].name,
74
+ "Credit manager name mismatch"
75
+ );
76
+ assertEq(
77
+ markets[i].creditManagers[j].creditManager.underlying,
78
+ creditManagersOld[cmIndex].underlying,
79
+ "Credit manager underlying mismatch"
80
+ );
81
+ assertEq(
82
+ markets[i].creditManagers[j].creditManager.pool,
83
+ creditManagersOld[cmIndex].pool,
84
+ "Credit manager pool mismatch"
85
+ );
86
+
87
+ // Compare credit facade data
88
+ assertEq(
89
+ markets[i].creditManagers[j].creditFacade.baseParams.addr,
90
+ creditManagersOld[cmIndex].creditFacade,
91
+ "Credit facade address mismatch"
92
+ );
93
+ // assertEq(
94
+ // markets[i].creditManagers[j].creditFacade.baseParams.version,
95
+ // creditManagersOld[cmIndex].creditFacadeVersion,
96
+ // "Credit facade version mismatch"
97
+ // );
98
+ assertEq(
99
+ markets[i].creditManagers[j].creditFacade.degenNFT,
100
+ creditManagersOld[cmIndex].degenNFT,
101
+ "Credit facade degenNFT mismatch"
102
+ );
103
+ assertEq(
104
+ markets[i].creditManagers[j].creditFacade.forbiddenTokenMask,
105
+ creditManagersOld[cmIndex].forbiddenTokenMask,
106
+ "Credit facade forbidden token mask mismatch"
107
+ );
108
+ assertEq(
109
+ markets[i].creditManagers[j].creditFacade.isPaused,
110
+ creditManagersOld[cmIndex].isPaused,
111
+ "Credit facade isPaused mismatch"
112
+ );
113
+ assertEq(
114
+ markets[i].creditManagers[j].creditFacade.minDebt,
115
+ creditManagersOld[cmIndex].minDebt,
116
+ "Credit facade minDebt mismatch"
117
+ );
118
+ assertEq(
119
+ markets[i].creditManagers[j].creditFacade.maxDebt,
120
+ creditManagersOld[cmIndex].maxDebt,
121
+ "Credit facade maxDebt mismatch"
122
+ );
123
+
124
+ // Compare credit manager configuration
125
+ assertEq(
126
+ markets[i].creditManagers[j].creditManager.maxEnabledTokens,
127
+ creditManagersOld[cmIndex].maxEnabledTokensLength,
128
+ "Credit manager max enabled tokens mismatch"
129
+ );
130
+ assertEq(
131
+ markets[i].creditManagers[j].creditManager.feeInterest,
132
+ creditManagersOld[cmIndex].feeInterest,
133
+ "Credit manager fee interest mismatch"
134
+ );
135
+ assertEq(
136
+ markets[i].creditManagers[j].creditManager.feeLiquidation,
137
+ creditManagersOld[cmIndex].feeLiquidation,
138
+ "Credit manager fee liquidation mismatch"
139
+ );
140
+ assertEq(
141
+ markets[i].creditManagers[j].creditManager.liquidationDiscount,
142
+ creditManagersOld[cmIndex].liquidationDiscount,
143
+ "Credit manager liquidation discount mismatch"
144
+ );
145
+ assertEq(
146
+ markets[i].creditManagers[j].creditManager.feeLiquidationExpired,
147
+ creditManagersOld[cmIndex].feeLiquidationExpired,
148
+ "Credit manager fee liquidation expired mismatch"
149
+ );
150
+ assertEq(
151
+ markets[i].creditManagers[j].creditManager.liquidationDiscountExpired,
152
+ creditManagersOld[cmIndex].liquidationDiscountExpired,
153
+ "Credit manager liquidation discount expired mismatch"
154
+ );
155
+
156
+ // Compare collateral tokens
157
+ assertEq(
158
+ markets[i].creditManagers[j].creditManager.collateralTokens.length,
159
+ creditManagersOld[cmIndex].collateralTokens.length,
160
+ "Credit manager collateral tokens length mismatch"
161
+ );
162
+
163
+ for (uint256 k; k < markets[i].creditManagers[j].creditManager.collateralTokens.length; k++) {
164
+ assertEq(
165
+ markets[i].creditManagers[j].creditManager.collateralTokens[k].token,
166
+ creditManagersOld[cmIndex].collateralTokens[k],
167
+ "Credit manager collateral token address mismatch"
168
+ );
169
+ assertEq(
170
+ markets[i].creditManagers[j].creditManager.collateralTokens[k].liquidationThreshold,
171
+ creditManagersOld[cmIndex].liquidationThresholds[k],
172
+ "Credit manager collateral token liquidation threshold mismatch"
173
+ );
174
+ }
175
+
176
+ // Compare adapters
177
+ assertEq(
178
+ markets[i].creditManagers[j].adapters.length,
179
+ creditManagersOld[cmIndex].adapters.length,
180
+ "Credit manager adapters length mismatch"
181
+ );
182
+
183
+ for (uint256 k; k < markets[i].creditManagers[j].adapters.length; k++) {
184
+ assertEq(
185
+ markets[i].creditManagers[j].adapters[k].baseParams.addr,
186
+ creditManagersOld[cmIndex].adapters[k].adapter,
187
+ "Credit manager adapter address mismatch"
188
+ );
189
+ assertEq(
190
+ markets[i].creditManagers[j].adapters[k].targetContract,
191
+ creditManagersOld[cmIndex].adapters[k].targetContract,
192
+ "Credit manager adapter target contract mismatch"
193
+ );
194
+ }
195
+ }
196
+ }
197
+ }
198
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gearbox-protocol/periphery-v3",
3
- "version": "1.7.0-next.59",
3
+ "version": "1.7.0-next.60",
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>",