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