Skip to content

Commit 6c1b680

Browse files
committed
Merge branch 'main' into v3.4.0
2 parents 093e8c7 + 496c6ea commit 6c1b680

File tree

9 files changed

+252
-17
lines changed

9 files changed

+252
-17
lines changed

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
blank_issues_enabled: false
1+
blank_issues_enabled: true
22
contact_links:
33
- name: Bug report
44
url: https://immunefi.com/bounty/aave/

src/contracts/extensions/v3-config-engine/AaveV3ConfigEngine.sol

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,17 @@ contract AaveV3ConfigEngine is IAaveV3ConfigEngine {
175175
);
176176
}
177177

178+
/// @inheritdoc IAaveV3ConfigEngine
179+
function createEModeCategories(EModeCategoryCreation[] calldata creations) external {
180+
EMODE_ENGINE.functionDelegateCall(
181+
abi.encodeWithSelector(
182+
EModeEngine.executeEModeCategoriesCreate.selector,
183+
_getEngineConstants(),
184+
creations
185+
)
186+
);
187+
}
188+
178189
/// @inheritdoc IAaveV3ConfigEngine
179190
function updateEModeCategories(EModeCategoryUpdate[] calldata updates) external {
180191
EMODE_ENGINE.functionDelegateCall(

src/contracts/extensions/v3-config-engine/AaveV3Payload.sol

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ abstract contract AaveV3Payload {
4242
function execute() external {
4343
_preExecute();
4444

45+
IEngine.EModeCategoryCreation[] memory newEmodes = eModeCategoryCreations();
4546
IEngine.EModeCategoryUpdate[] memory eModeCategories = eModeCategoriesUpdates();
4647
IEngine.Listing[] memory listings = newListings();
4748
IEngine.ListingWithCustomImpl[] memory listingsCustom = newListingsCustom();
@@ -52,6 +53,12 @@ abstract contract AaveV3Payload {
5253
IEngine.AssetEModeUpdate[] memory assetsEModes = assetsEModeUpdates();
5354
IEngine.CapsUpdate[] memory caps = capsUpdates();
5455

56+
if (newEmodes.length != 0) {
57+
address(CONFIG_ENGINE).functionDelegateCall(
58+
abi.encodeWithSelector(CONFIG_ENGINE.createEModeCategories.selector, newEmodes)
59+
);
60+
}
61+
5562
if (eModeCategories.length != 0) {
5663
address(CONFIG_ENGINE).functionDelegateCall(
5764
abi.encodeWithSelector(CONFIG_ENGINE.updateEModeCategories.selector, eModeCategories)
@@ -143,6 +150,14 @@ abstract contract AaveV3Payload {
143150
/// @dev to be defined in the child with a list of priceFeeds to update
144151
function priceFeedsUpdates() public view virtual returns (IEngine.PriceFeedUpdate[] memory) {}
145152

153+
/// @dev to be defined in the child with a list of eMode categories to create
154+
function eModeCategoryCreations()
155+
public
156+
view
157+
virtual
158+
returns (IEngine.EModeCategoryCreation[] memory)
159+
{}
160+
146161
/// @dev to be defined in the child with a list of eMode categories to update
147162
function eModeCategoriesUpdates()
148163
public

src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,26 @@ interface IAaveV3ConfigEngine {
219219
string label;
220220
}
221221

222+
/**
223+
* @dev Example (mock):
224+
* EModeCategoryUpdate({
225+
* ltv: 60_00,
226+
* liqThreshold: 70_00,
227+
* liqBonus: 3_00,
228+
* label: 'WETH USDC',
229+
* borrowables:[USDC],
230+
* collaterals:[ETH]
231+
* })
232+
*/
233+
struct EModeCategoryCreation {
234+
uint256 ltv;
235+
uint256 liqThreshold;
236+
uint256 liqBonus;
237+
string label;
238+
address[] borrowables;
239+
address[] collaterals;
240+
}
241+
222242
/**
223243
* @dev Example (mock):
224244
* RateStrategyUpdate({
@@ -295,6 +315,13 @@ interface IAaveV3ConfigEngine {
295315
*/
296316
function updateBorrowSide(BorrowUpdate[] memory updates) external;
297317

318+
/**
319+
* @notice Performs creation of new e-mode categories, in the Aave pool configured in this engine instance
320+
* @param creations `EModeCategoryCreation[]` list of declarative creations containing the new parameters
321+
* More information on the documentation of the struct.
322+
*/
323+
function createEModeCategories(EModeCategoryCreation[] memory creations) external;
324+
298325
/**
299326
* @notice Performs an update of the e-mode categories, in the Aave pool configured in this engine instance
300327
* @param updates `EModeCategoryUpdate[]` list of declarative updates containing the new parameters

src/contracts/extensions/v3-config-engine/libraries/EModeEngine.sol

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,52 @@ library EModeEngine {
1111
using PercentageMath for uint256;
1212
using SafeCast for uint256;
1313

14+
error NoAvailableEmodeCategory();
15+
1416
function executeAssetsEModeUpdate(
1517
IEngine.EngineConstants calldata engineConstants,
1618
IEngine.AssetEModeUpdate[] memory updates
1719
) external {
1820
require(updates.length != 0, 'AT_LEAST_ONE_UPDATE_REQUIRED');
1921

20-
_configAssetsEMode(engineConstants.poolConfigurator, updates);
22+
_configAssetsEMode(engineConstants.poolConfigurator, engineConstants.pool, updates);
23+
}
24+
25+
function executeEModeCategoriesCreate(
26+
IEngine.EngineConstants calldata engineConstants,
27+
IEngine.EModeCategoryCreation[] memory creations
28+
) external {
29+
for (uint256 i; i < creations.length; i++) {
30+
require(
31+
keccak256(abi.encode(creations[i].label)) !=
32+
keccak256(abi.encode(EngineFlags.KEEP_CURRENT_STRING)),
33+
'INVALID_LABEL'
34+
);
35+
uint8 categoryId = _findFirstUnusedEmodeCategory(engineConstants.pool);
36+
engineConstants.poolConfigurator.setEModeCategory(
37+
categoryId,
38+
creations[i].ltv.toUint16(),
39+
creations[i].liqThreshold.toUint16(),
40+
// For reference, this is to simplify the interaction with the Aave protocol,
41+
// as there the definition is as e.g. 105% (5% bonus for liquidators)
42+
(100_00 + creations[i].liqBonus).toUint16(),
43+
creations[i].label
44+
);
45+
for (uint256 j; j < creations[i].collaterals.length; j++) {
46+
engineConstants.poolConfigurator.setAssetCollateralInEMode(
47+
creations[i].collaterals[j],
48+
categoryId,
49+
true
50+
);
51+
}
52+
for (uint256 k; k < creations[i].borrowables.length; k++) {
53+
engineConstants.poolConfigurator.setAssetBorrowableInEMode(
54+
creations[i].borrowables[k],
55+
categoryId,
56+
true
57+
);
58+
}
59+
}
2160
}
2261

2362
function executeEModeCategoriesUpdate(
@@ -31,9 +70,14 @@ library EModeEngine {
3170

3271
function _configAssetsEMode(
3372
IPoolConfigurator poolConfigurator,
73+
IPool pool,
3474
IEngine.AssetEModeUpdate[] memory updates
3575
) internal {
3676
for (uint256 i = 0; i < updates.length; i++) {
77+
DataTypes.CollateralConfig memory cfg = pool.getEModeCategoryCollateralConfig(
78+
updates[i].eModeCategory
79+
);
80+
require(cfg.liquidationThreshold != 0, 'INVALID_UPDATE');
3781
if (updates[i].collateral != EngineFlags.KEEP_CURRENT) {
3882
poolConfigurator.setAssetCollateralInEMode(
3983
updates[i].asset,
@@ -69,11 +113,13 @@ library EModeEngine {
69113
keccak256(abi.encode(updates[i].label)) !=
70114
keccak256(abi.encode(EngineFlags.KEEP_CURRENT_STRING));
71115

72-
if (notAllKeepCurrent && atLeastOneKeepCurrent) {
73-
DataTypes.CollateralConfig memory cfg = pool.getEModeCategoryCollateralConfig(
74-
updates[i].eModeCategory
75-
);
116+
DataTypes.CollateralConfig memory cfg = pool.getEModeCategoryCollateralConfig(
117+
updates[i].eModeCategory
118+
);
119+
// should only be able to update existing eModes, not create new ones
120+
require(cfg.liquidationThreshold != 0, 'INVALID_UPDATE');
76121

122+
if (notAllKeepCurrent && atLeastOneKeepCurrent) {
77123
if (updates[i].ltv == EngineFlags.KEEP_CURRENT) {
78124
updates[i].ltv = cfg.ltv;
79125
}
@@ -114,4 +160,15 @@ library EModeEngine {
114160
}
115161
}
116162
}
163+
164+
/**
165+
* @dev eModes must have a non-zero lt so we select the first that has a zero lt.
166+
*/
167+
function _findFirstUnusedEmodeCategory(IPool pool) private view returns (uint8) {
168+
// eMode id 0 is skipped intentially as it is the reserved default
169+
for (uint8 i = 1; i < 256; i++) {
170+
if (pool.getEModeCategoryCollateralConfig(i).liquidationThreshold == 0) return i;
171+
}
172+
revert NoAvailableEmodeCategory();
173+
}
117174
}

tests/extensions/v3-config-engine/AaveV3ConfigEngineTest.t.sol

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {AaveV3MockPriceFeedUpdate} from './mocks/AaveV3MockPriceFeedUpdate.sol';
1717
import {AaveV3MockEModeCategoryUpdate, AaveV3MockEModeCategoryUpdateEdgeBonus} from './mocks/AaveV3MockEModeCategoryUpdate.sol';
1818
import {AaveV3MockEModeCategoryUpdateNoChange} from './mocks/AaveV3MockEModeCategoryUpdateNoChange.sol';
1919
import {AaveV3MockAssetEModeUpdate} from './mocks/AaveV3MockAssetEModeUpdate.sol';
20+
import {AaveV3MockEModeCategoryCreation} from './mocks/AaveV3MockEModeCategoryCreation.sol';
2021

2122
import {IPoolConfigurator} from '../../../src/contracts/interfaces/IPoolConfigurator.sol';
2223
import {ATokenInstance} from '../../../src/contracts/instances/ATokenInstance.sol';
@@ -494,14 +495,59 @@ contract AaveV3ConfigEngineTest is TestnetProcedures, ProtocolV3TestBase {
494495
);
495496
}
496497

498+
function testEModeCategoryCreation() public {
499+
AaveV3MockEModeCategoryCreation payload = new AaveV3MockEModeCategoryCreation(
500+
tokenList.weth,
501+
tokenList.usdx,
502+
tokenList.wbtc,
503+
tokenList.weth,
504+
configEngine
505+
);
506+
507+
vm.prank(roleList.marketOwner);
508+
contracts.aclManager.addPoolAdmin(address(payload));
509+
510+
payload.execute();
511+
512+
DataTypes.EModeCategory memory prevEmodeCategoryData;
513+
prevEmodeCategoryData.ltv = 50_00;
514+
prevEmodeCategoryData.liquidationThreshold = 60_00;
515+
prevEmodeCategoryData.liquidationBonus = 101_00; // 100_00 + 1_00
516+
prevEmodeCategoryData.label = 'No assets';
517+
518+
uint256 bitmap = contracts.poolProxy.getEModeCategoryBorrowableBitmap(1);
519+
assertEq(bitmap, 0);
520+
bitmap = contracts.poolProxy.getEModeCategoryCollateralBitmap(1);
521+
assertEq(bitmap, 0);
522+
_validateEmodeCategory(
523+
IPoolAddressesProvider(address(contracts.poolAddressesProvider)),
524+
1,
525+
prevEmodeCategoryData
526+
);
527+
528+
prevEmodeCategoryData.ltv = 97_40;
529+
prevEmodeCategoryData.liquidationThreshold = 97_60;
530+
prevEmodeCategoryData.liquidationBonus = 101_50; // 100_00 + 1_50
531+
prevEmodeCategoryData.label = 'Test';
532+
prevEmodeCategoryData.collateralBitmap = 10; // 1010
533+
prevEmodeCategoryData.borrowableBitmap = 12; // 1100
534+
_validateEmodeCategory(
535+
IPoolAddressesProvider(address(contracts.poolAddressesProvider)),
536+
2,
537+
prevEmodeCategoryData
538+
);
539+
}
540+
497541
function testEModeCategoryUpdates() public {
542+
EModeCategoryInput memory ct = _genCategoryOne();
543+
vm.prank(poolAdmin);
544+
contracts.poolConfiguratorProxy.setEModeCategory(ct.id, ct.ltv, ct.lt, ct.lb, ct.label);
545+
498546
AaveV3MockEModeCategoryUpdate payload = new AaveV3MockEModeCategoryUpdate(configEngine);
499547

500548
vm.prank(roleList.marketOwner);
501549
contracts.aclManager.addPoolAdmin(address(payload));
502550

503-
contracts.poolProxy.getEModeCategoryData(1);
504-
505551
createConfigurationSnapshot(
506552
'preTestEngineEModeCategoryUpdate',
507553
IPool(address(contracts.poolProxy))
@@ -530,6 +576,10 @@ contract AaveV3ConfigEngineTest is TestnetProcedures, ProtocolV3TestBase {
530576
}
531577

532578
function testEModeCategoryUpdatesWrongBonus() public {
579+
EModeCategoryInput memory ct = _genCategoryOne();
580+
vm.prank(poolAdmin);
581+
contracts.poolConfiguratorProxy.setEModeCategory(ct.id, ct.ltv, ct.lt, ct.lb, ct.label);
582+
533583
AaveV3MockEModeCategoryUpdateEdgeBonus payload = new AaveV3MockEModeCategoryUpdateEdgeBonus(
534584
configEngine
535585
);
@@ -543,6 +593,10 @@ contract AaveV3ConfigEngineTest is TestnetProcedures, ProtocolV3TestBase {
543593

544594
// TODO manage this after testFail* deprecation.
545595
function testEModeCategoryUpdatesNoChangeShouldNotEmit() public {
596+
EModeCategoryInput memory ct = _genCategoryOne();
597+
vm.prank(poolAdmin);
598+
contracts.poolConfiguratorProxy.setEModeCategory(ct.id, ct.ltv, ct.lt, ct.lb, ct.label);
599+
546600
AaveV3MockEModeCategoryUpdateNoChange payload = new AaveV3MockEModeCategoryUpdateNoChange(
547601
configEngine
548602
);
@@ -558,6 +612,9 @@ contract AaveV3ConfigEngineTest is TestnetProcedures, ProtocolV3TestBase {
558612

559613
// Same as testEModeCategoryUpdatesNoChangeShouldNotEmit, but this time should work, as we are not expecting any event emitted
560614
function testEModeCategoryUpdatesNoChange() public {
615+
EModeCategoryInput memory ct = _genCategoryOne();
616+
vm.prank(poolAdmin);
617+
contracts.poolConfiguratorProxy.setEModeCategory(ct.id, ct.ltv, ct.lt, ct.lb, ct.label);
561618
AaveV3MockEModeCategoryUpdateNoChange payload = new AaveV3MockEModeCategoryUpdateNoChange(
562619
configEngine
563620
);
@@ -597,12 +654,12 @@ contract AaveV3ConfigEngineTest is TestnetProcedures, ProtocolV3TestBase {
597654
}
598655

599656
function testAssetEModeUpdates() public {
657+
vm.prank(poolAdmin);
658+
contracts.poolConfiguratorProxy.setEModeCategory(1, 97_40, 97_60, 101_50, 'ETH Correlated');
659+
600660
address asset = tokenList.usdx;
601661
address asset2 = tokenList.wbtc;
602662

603-
AaveV3MockEModeCategoryUpdate payloadToAddEMode = new AaveV3MockEModeCategoryUpdate(
604-
configEngine
605-
);
606663
AaveV3MockAssetEModeUpdate payload = new AaveV3MockAssetEModeUpdate(
607664
asset,
608665
asset2,
@@ -611,11 +668,8 @@ contract AaveV3ConfigEngineTest is TestnetProcedures, ProtocolV3TestBase {
611668

612669
vm.startPrank(roleList.marketOwner);
613670
contracts.aclManager.addPoolAdmin(address(payload));
614-
contracts.aclManager.addPoolAdmin(address(payloadToAddEMode));
615671
vm.stopPrank();
616672

617-
payloadToAddEMode.execute();
618-
619673
createConfigurationSnapshot(
620674
'preTestEngineAssetEModeUpdate',
621675
IPool(address(contracts.poolProxy))

0 commit comments

Comments
 (0)