@@ -5,7 +5,9 @@ import 'forge-std/Test.sol';
55import {VmSafe} from 'forge-std/Base.sol ' ;
66import {IAaveV3ConfigEngine} from '../../../src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol ' ;
77import {AaveV3MockListing} from './mocks/AaveV3MockListing.sol ' ;
8+ import {AaveV3MockListingWithEModeCreation} from './mocks/AaveV3MockListingWithEModeCreation.sol ' ;
89import {AaveV3MockListingCustom} from './mocks/AaveV3MockListingCustom.sol ' ;
10+ import {AaveV3MockListingCustomWithEModeCreation} from './mocks/AaveV3MockListingCustomWithEModeCreation.sol ' ;
911import {AaveV3MockCapUpdate} from './mocks/AaveV3MockCapUpdate.sol ' ;
1012import {AaveV3MockCollateralUpdate} from './mocks/AaveV3MockCollateralUpdate.sol ' ;
1113import {AaveV3MockCollateralUpdateNoChange} from './mocks/AaveV3MockCollateralUpdateNoChange.sol ' ;
@@ -126,6 +128,111 @@ contract AaveV3ConfigEngineTest is TestnetProcedures, ProtocolV3TestBase {
126128 );
127129 }
128130
131+ function testListingWithEModeCategoryCreation () public {
132+ address asset = address (new TestnetERC20 ('1INCH ' , '1INCH ' , 18 , address (this )));
133+
134+ address feed = address (new MockAggregator (int256 (25e8 )));
135+ AaveV3MockListingWithEModeCreation payload = new AaveV3MockListingWithEModeCreation (
136+ asset,
137+ feed,
138+ configEngine
139+ );
140+
141+ vm.prank (roleList.marketOwner);
142+ contracts.aclManager.addPoolAdmin (address (payload));
143+
144+ ReserveConfig[] memory allConfigsBefore = createConfigurationSnapshot (
145+ 'preTestEngineListingWithEModeCreation ' ,
146+ IPool (address (contracts.poolProxy))
147+ );
148+
149+ payload.execute ();
150+
151+ ReserveConfig[] memory allConfigsAfter = createConfigurationSnapshot (
152+ 'postTestEngineListingWithEModeCreation ' ,
153+ IPool (address (contracts.poolProxy))
154+ );
155+
156+ diffReports ('preTestEngineListingWithEModeCreation ' , 'postTestEngineListingWithEModeCreation ' );
157+
158+ ReserveConfig memory expectedAssetConfig = ReserveConfig ({
159+ symbol: '1INCH ' ,
160+ underlying: asset,
161+ aToken: address (0 ), // Mock, as they don't get validated, because of the "dynamic" deployment on proposal execution
162+ variableDebtToken: address (0 ), // Mock, as they don't get validated, because of the "dynamic" deployment on proposal execution
163+ decimals: 18 ,
164+ ltv: 82_50 ,
165+ liquidationThreshold: 86_00 ,
166+ liquidationBonus: 105_00 ,
167+ liquidationProtocolFee: 10_00 ,
168+ reserveFactor: 10_00 ,
169+ usageAsCollateralEnabled: true ,
170+ borrowingEnabled: true ,
171+ interestRateStrategy: AaveV3ConfigEngine (configEngine).DEFAULT_INTEREST_RATE_STRATEGY (),
172+ isPaused: false ,
173+ isActive: true ,
174+ isFrozen: false ,
175+ isSiloed: false ,
176+ isBorrowableInIsolation: false ,
177+ isFlashloanable: false ,
178+ supplyCap: 85_000 ,
179+ borrowCap: 60_000 ,
180+ debtCeiling: 0 ,
181+ virtualBalance: 0 ,
182+ aTokenUnderlyingBalance: 0
183+ });
184+
185+ _validateReserveConfig (expectedAssetConfig, allConfigsAfter);
186+
187+ _noReservesConfigsChangesApartNewListings (allConfigsBefore, allConfigsAfter);
188+
189+ _validateReserveTokensImpls (
190+ _findReserveConfigBySymbol (allConfigsAfter, '1INCH ' ),
191+ ReserveTokens ({
192+ aToken: address (contracts.aToken),
193+ variableDebtToken: address (contracts.variableDebtToken)
194+ })
195+ );
196+
197+ _validateAssetSourceOnOracle (
198+ IPoolAddressesProvider (address (contracts.poolAddressesProvider)),
199+ asset,
200+ feed
201+ );
202+
203+ _validateInterestRateStrategy (
204+ asset,
205+ contracts.protocolDataProvider.getInterestRateStrategyAddress (asset),
206+ AaveV3ConfigEngine (configEngine).DEFAULT_INTEREST_RATE_STRATEGY (),
207+ IDefaultInterestRateStrategyV2.InterestRateDataRay ({
208+ optimalUsageRatio: _bpsToRay (payload.newListings ()[0 ].rateStrategyParams.optimalUsageRatio),
209+ baseVariableBorrowRate: _bpsToRay (
210+ payload.newListings ()[0 ].rateStrategyParams.baseVariableBorrowRate
211+ ),
212+ variableRateSlope1: _bpsToRay (
213+ payload.newListings ()[0 ].rateStrategyParams.variableRateSlope1
214+ ),
215+ variableRateSlope2: _bpsToRay (
216+ payload.newListings ()[0 ].rateStrategyParams.variableRateSlope2
217+ )
218+ })
219+ );
220+
221+ DataTypes.EModeCategory memory eModeCategoryData;
222+ eModeCategoryData.ltv = 97_40 ;
223+ eModeCategoryData.liquidationThreshold = 97_60 ;
224+ eModeCategoryData.liquidationBonus = 101_50 ; // 100_00 + 1_50
225+ eModeCategoryData.label = 'Listed Asset EMode ' ;
226+ eModeCategoryData.collateralBitmap = 8 ; // 1000
227+ eModeCategoryData.borrowableBitmap = 8 ; // 1000
228+
229+ _validateEmodeCategory (
230+ IPoolAddressesProvider (address (contracts.poolAddressesProvider)),
231+ 1 ,
232+ eModeCategoryData
233+ );
234+ }
235+
129236 function testListingsCustom () public {
130237 address asset = address (new TestnetERC20 ('PSP ' , 'PSP ' , 18 , address (this )));
131238
@@ -225,6 +332,122 @@ contract AaveV3ConfigEngineTest is TestnetProcedures, ProtocolV3TestBase {
225332 );
226333 }
227334
335+ function testListingsCustomWithEModeCategoryCreation () public {
336+ address asset = address (new TestnetERC20 ('PSP ' , 'PSP ' , 18 , address (this )));
337+
338+ address feed = address (new MockAggregator (int256 (15e8 )));
339+ address aTokenImpl = address (
340+ new ATokenInstance (contracts.poolProxy, report.rewardsControllerProxy, report.treasury)
341+ );
342+ address vTokenImpl = address (
343+ new VariableDebtTokenInstance (contracts.poolProxy, report.rewardsControllerProxy)
344+ );
345+
346+ AaveV3MockListingCustomWithEModeCreation payload = new AaveV3MockListingCustomWithEModeCreation (
347+ asset,
348+ feed,
349+ configEngine,
350+ aTokenImpl,
351+ vTokenImpl
352+ );
353+
354+ vm.prank (roleList.marketOwner);
355+ contracts.aclManager.addPoolAdmin (address (payload));
356+
357+ ReserveConfig[] memory allConfigsBefore = createConfigurationSnapshot (
358+ 'preTestEngineListingCustomWithEModeCreation ' ,
359+ IPool (address (contracts.poolProxy))
360+ );
361+
362+ payload.execute ();
363+
364+ ReserveConfig[] memory allConfigsAfter = createConfigurationSnapshot (
365+ 'postTestEngineListingCustomWithEModeCreation ' ,
366+ IPool (address (contracts.poolProxy))
367+ );
368+
369+ diffReports (
370+ 'preTestEngineListingCustomWithEModeCreation ' ,
371+ 'postTestEngineListingCustomWithEModeCreation '
372+ );
373+
374+ ReserveConfig memory expectedAssetConfig = ReserveConfig ({
375+ symbol: 'PSP ' ,
376+ underlying: asset,
377+ aToken: address (0 ), // Mock, as they don't get validated, because of the "dynamic" deployment on proposal execution
378+ variableDebtToken: address (0 ), // Mock, as they don't get validated, because of the "dynamic" deployment on proposal execution
379+ decimals: 18 ,
380+ ltv: 82_50 ,
381+ liquidationThreshold: 86_00 ,
382+ liquidationBonus: 105_00 ,
383+ liquidationProtocolFee: 10_00 ,
384+ reserveFactor: 10_00 ,
385+ usageAsCollateralEnabled: true ,
386+ borrowingEnabled: true ,
387+ interestRateStrategy: AaveV3ConfigEngine (configEngine).DEFAULT_INTEREST_RATE_STRATEGY (),
388+ isPaused: false ,
389+ isActive: true ,
390+ isFrozen: false ,
391+ isSiloed: false ,
392+ isBorrowableInIsolation: false ,
393+ isFlashloanable: false ,
394+ supplyCap: 85_000 ,
395+ borrowCap: 60_000 ,
396+ debtCeiling: 0 ,
397+ virtualBalance: 0 ,
398+ aTokenUnderlyingBalance: 0
399+ });
400+
401+ _validateReserveConfig (expectedAssetConfig, allConfigsAfter);
402+
403+ _noReservesConfigsChangesApartNewListings (allConfigsBefore, allConfigsAfter);
404+
405+ _validateReserveTokensImpls (
406+ _findReserveConfigBySymbol (allConfigsAfter, 'PSP ' ),
407+ ReserveTokens ({aToken: aTokenImpl, variableDebtToken: vTokenImpl})
408+ );
409+
410+ _validateAssetSourceOnOracle (
411+ IPoolAddressesProvider (address (contracts.poolAddressesProvider)),
412+ asset,
413+ feed
414+ );
415+
416+ _validateInterestRateStrategy (
417+ asset,
418+ contracts.protocolDataProvider.getInterestRateStrategyAddress (asset),
419+ AaveV3ConfigEngine (configEngine).DEFAULT_INTEREST_RATE_STRATEGY (),
420+ IDefaultInterestRateStrategyV2.InterestRateDataRay ({
421+ optimalUsageRatio: _bpsToRay (
422+ payload.newListingsCustom ()[0 ].base.rateStrategyParams.optimalUsageRatio
423+ ),
424+ baseVariableBorrowRate: _bpsToRay (
425+ payload.newListingsCustom ()[0 ].base.rateStrategyParams.baseVariableBorrowRate
426+ ),
427+ variableRateSlope1: _bpsToRay (
428+ payload.newListingsCustom ()[0 ].base.rateStrategyParams.variableRateSlope1
429+ ),
430+ variableRateSlope2: _bpsToRay (
431+ payload.newListingsCustom ()[0 ].base.rateStrategyParams.variableRateSlope2
432+ )
433+ })
434+ );
435+
436+ DataTypes.EModeCategory memory eModeCategoryData;
437+ eModeCategoryData.ltv = 97_40 ;
438+ eModeCategoryData.liquidationThreshold = 97_60 ;
439+ eModeCategoryData.liquidationBonus = 101_50 ; // 100_00 + 1_50
440+ eModeCategoryData.label = 'Listed Asset EMode ' ;
441+ eModeCategoryData.collateralBitmap = 8 ; // 1000
442+ eModeCategoryData.borrowableBitmap = 8 ; // 1000
443+
444+ _validateEmodeCategory (
445+ IPoolAddressesProvider (address (contracts.poolAddressesProvider)),
446+ 1 ,
447+ eModeCategoryData
448+ );
449+ }
450+
228451 function testCapsUpdate () public {
229452 // this asset has been listed before
230453 address asset = tokenList.usdx;
0 commit comments