Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions src/contracts/extensions/v3-config-engine/AaveV3Payload.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,30 @@ abstract contract AaveV3Payload {
function execute() external {
_preExecute();

IEngine.EModeCategoryCreation[] memory newEmodes = eModeCategoryCreations();
IEngine.EModeCategoryUpdate[] memory eModeCategories = eModeCategoriesUpdates();
IEngine.Listing[] memory listings = newListings();
IEngine.ListingWithCustomImpl[] memory listingsCustom = newListingsCustom();
IEngine.EModeCategoryUpdate[] memory eModeCategories = eModeCategoriesUpdates();
IEngine.AssetEModeUpdate[] memory assetsEModes = assetsEModeUpdates();
IEngine.EModeCategoryCreation[] memory newEmodes = eModeCategoryCreations();
IEngine.CollateralUpdate[] memory collaterals = collateralsUpdates();
IEngine.BorrowUpdate[] memory borrows = borrowsUpdates();
IEngine.RateStrategyUpdate[] memory rates = rateStrategiesUpdates();
IEngine.PriceFeedUpdate[] memory priceFeeds = priceFeedsUpdates();
IEngine.AssetEModeUpdate[] memory assetsEModes = assetsEModeUpdates();
IEngine.CapsUpdate[] memory caps = capsUpdates();

if (newEmodes.length != 0) {
if (listings.length != 0) {
address(CONFIG_ENGINE).functionDelegateCall(
abi.encodeWithSelector(CONFIG_ENGINE.createEModeCategories.selector, newEmodes)
abi.encodeWithSelector(CONFIG_ENGINE.listAssets.selector, getPoolContext(), listings)
);
}

if (listingsCustom.length != 0) {
address(CONFIG_ENGINE).functionDelegateCall(
abi.encodeWithSelector(
CONFIG_ENGINE.listAssetsCustom.selector,
getPoolContext(),
listingsCustom
)
);
}

Expand All @@ -65,19 +75,15 @@ abstract contract AaveV3Payload {
);
}

if (listings.length != 0) {
if (assetsEModes.length != 0) {
address(CONFIG_ENGINE).functionDelegateCall(
abi.encodeWithSelector(CONFIG_ENGINE.listAssets.selector, getPoolContext(), listings)
abi.encodeWithSelector(CONFIG_ENGINE.updateAssetsEMode.selector, assetsEModes)
);
}

if (listingsCustom.length != 0) {
if (newEmodes.length != 0) {
address(CONFIG_ENGINE).functionDelegateCall(
abi.encodeWithSelector(
CONFIG_ENGINE.listAssetsCustom.selector,
getPoolContext(),
listingsCustom
)
abi.encodeWithSelector(CONFIG_ENGINE.createEModeCategories.selector, newEmodes)
);
}

Expand Down Expand Up @@ -105,12 +111,6 @@ abstract contract AaveV3Payload {
);
}

if (assetsEModes.length != 0) {
address(CONFIG_ENGINE).functionDelegateCall(
abi.encodeWithSelector(CONFIG_ENGINE.updateAssetsEMode.selector, assetsEModes)
);
}

if (caps.length != 0) {
address(CONFIG_ENGINE).functionDelegateCall(
abi.encodeWithSelector(CONFIG_ENGINE.updateCaps.selector, caps)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ interface IAaveV3ConfigEngine {
* borrowCap: 60_000, // 60k AAVE
* debtCeiling: 100_000, // 100k USD
* liqProtocolFee: 10_00, // 10%
* eModeCategory: 0, // No category
* }
*/
struct Listing {
Expand Down
23 changes: 13 additions & 10 deletions src/contracts/extensions/v3-config-engine/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,26 @@ Change eMode category configuration? Same as previous, just define the update wi

Change eMode category of a particular asset? Same as previous, just define the update within a `assetsEModeUpdates()` function, and the base payload will take care of the rest.

Create a new eMode category? Same as previous, just define the update within a `eModeCategoryCreations()` function, and the base payload will take care of the rest.

### Internal aspects to consider

- Frequently, at the same time that you want to do an update of parameters or listing, you also want to do something extra before or after.
The `Base Aave v3 Payload` defines `_preExecute()` and `_postExecute()` hook functions, that you can redefine on your payload and will the execute before and after all configs changes/listings you define.

- The payload also allow you to group changes of parameters and listings, just by defining at the same time the aforementioned `newListings()`, `capsUpdate()` and/or `collateralsUpdates()` and so on. For reference, the execution ordering is the following:
1. `_preExecute()`
2. `eModeCategoriesUpdates()`
3. `newListings()`
4. `newListingsCustom()`
5. `borrowsUpdates()`
6. `collateralsUpdates()`
7. `rateStrategiesUpdates()`
8. `priceFeedsUpdates()`
9. `assetsEModeUpdates()`
10. `capsUpdates()`
11. `_postExecute()`
2. `newListings()`
3. `newListingsCustom()`
4. `eModeCategoriesUpdates()`
5. `assetsEModeUpdates()`
6. `eModeCategoryCreations()`
7. `borrowsUpdates()`
8. `collateralsUpdates()`
9. `rateStrategiesUpdates()`
10. `priceFeedsUpdates()`
11. `capsUpdates()`
12. `_postExecute()`

## Links to examples

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {PriceFeedEngine} from './PriceFeedEngine.sol';
import {CapsEngine} from './CapsEngine.sol';
import {BorrowEngine} from './BorrowEngine.sol';
import {CollateralEngine} from './CollateralEngine.sol';
import {EModeEngine} from './EModeEngine.sol';
import {ConfiguratorInputTypes} from '../../../protocol/libraries/types/ConfiguratorInputTypes.sol';
import {SafeCast} from 'openzeppelin-contracts/contracts/utils/math/SafeCast.sol';

Expand Down
223 changes: 223 additions & 0 deletions tests/extensions/v3-config-engine/AaveV3ConfigEngineTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import 'forge-std/Test.sol';
import {VmSafe} from 'forge-std/Base.sol';
import {IAaveV3ConfigEngine} from '../../../src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
import {AaveV3MockListing} from './mocks/AaveV3MockListing.sol';
import {AaveV3MockListingWithEModeCreation} from './mocks/AaveV3MockListingWithEModeCreation.sol';
import {AaveV3MockListingCustom} from './mocks/AaveV3MockListingCustom.sol';
import {AaveV3MockListingCustomWithEModeCreation} from './mocks/AaveV3MockListingCustomWithEModeCreation.sol';
import {AaveV3MockCapUpdate} from './mocks/AaveV3MockCapUpdate.sol';
import {AaveV3MockCollateralUpdate} from './mocks/AaveV3MockCollateralUpdate.sol';
import {AaveV3MockCollateralUpdateNoChange} from './mocks/AaveV3MockCollateralUpdateNoChange.sol';
Expand Down Expand Up @@ -126,6 +128,111 @@ contract AaveV3ConfigEngineTest is TestnetProcedures, ProtocolV3TestBase {
);
}

function testListingWithEModeCategoryCreation() public {
address asset = address(new TestnetERC20('1INCH', '1INCH', 18, address(this)));

address feed = address(new MockAggregator(int256(25e8)));
AaveV3MockListingWithEModeCreation payload = new AaveV3MockListingWithEModeCreation(
asset,
feed,
configEngine
);

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

ReserveConfig[] memory allConfigsBefore = createConfigurationSnapshot(
'preTestEngineListingWithEModeCreation',
IPool(address(contracts.poolProxy))
);

payload.execute();

ReserveConfig[] memory allConfigsAfter = createConfigurationSnapshot(
'postTestEngineListingWithEModeCreation',
IPool(address(contracts.poolProxy))
);

diffReports('preTestEngineListingWithEModeCreation', 'postTestEngineListingWithEModeCreation');

ReserveConfig memory expectedAssetConfig = ReserveConfig({
symbol: '1INCH',
underlying: asset,
aToken: address(0), // Mock, as they don't get validated, because of the "dynamic" deployment on proposal execution
variableDebtToken: address(0), // Mock, as they don't get validated, because of the "dynamic" deployment on proposal execution
decimals: 18,
ltv: 82_50,
liquidationThreshold: 86_00,
liquidationBonus: 105_00,
liquidationProtocolFee: 10_00,
reserveFactor: 10_00,
usageAsCollateralEnabled: true,
borrowingEnabled: true,
interestRateStrategy: AaveV3ConfigEngine(configEngine).DEFAULT_INTEREST_RATE_STRATEGY(),
isPaused: false,
isActive: true,
isFrozen: false,
isSiloed: false,
isBorrowableInIsolation: false,
isFlashloanable: false,
supplyCap: 85_000,
borrowCap: 60_000,
debtCeiling: 0,
virtualBalance: 0,
aTokenUnderlyingBalance: 0
});

_validateReserveConfig(expectedAssetConfig, allConfigsAfter);

_noReservesConfigsChangesApartNewListings(allConfigsBefore, allConfigsAfter);

_validateReserveTokensImpls(
_findReserveConfigBySymbol(allConfigsAfter, '1INCH'),
ReserveTokens({
aToken: address(contracts.aToken),
variableDebtToken: address(contracts.variableDebtToken)
})
);

_validateAssetSourceOnOracle(
IPoolAddressesProvider(address(contracts.poolAddressesProvider)),
asset,
feed
);

_validateInterestRateStrategy(
asset,
contracts.protocolDataProvider.getInterestRateStrategyAddress(asset),
AaveV3ConfigEngine(configEngine).DEFAULT_INTEREST_RATE_STRATEGY(),
IDefaultInterestRateStrategyV2.InterestRateDataRay({
optimalUsageRatio: _bpsToRay(payload.newListings()[0].rateStrategyParams.optimalUsageRatio),
baseVariableBorrowRate: _bpsToRay(
payload.newListings()[0].rateStrategyParams.baseVariableBorrowRate
),
variableRateSlope1: _bpsToRay(
payload.newListings()[0].rateStrategyParams.variableRateSlope1
),
variableRateSlope2: _bpsToRay(
payload.newListings()[0].rateStrategyParams.variableRateSlope2
)
})
);

DataTypes.EModeCategory memory eModeCategoryData;
eModeCategoryData.ltv = 97_40;
eModeCategoryData.liquidationThreshold = 97_60;
eModeCategoryData.liquidationBonus = 101_50; // 100_00 + 1_50
eModeCategoryData.label = 'Listed Asset EMode';
eModeCategoryData.collateralBitmap = 8; // 1000
eModeCategoryData.borrowableBitmap = 8; // 1000

_validateEmodeCategory(
IPoolAddressesProvider(address(contracts.poolAddressesProvider)),
1,
eModeCategoryData
);
}

function testListingsCustom() public {
address asset = address(new TestnetERC20('PSP', 'PSP', 18, address(this)));

Expand Down Expand Up @@ -225,6 +332,122 @@ contract AaveV3ConfigEngineTest is TestnetProcedures, ProtocolV3TestBase {
);
}

function testListingsCustomWithEModeCategoryCreation() public {
address asset = address(new TestnetERC20('PSP', 'PSP', 18, address(this)));

address feed = address(new MockAggregator(int256(15e8)));
address aTokenImpl = address(
new ATokenInstance(contracts.poolProxy, report.rewardsControllerProxy, report.treasury)
);
address vTokenImpl = address(
new VariableDebtTokenInstance(contracts.poolProxy, report.rewardsControllerProxy)
);

AaveV3MockListingCustomWithEModeCreation payload = new AaveV3MockListingCustomWithEModeCreation(
asset,
feed,
configEngine,
aTokenImpl,
vTokenImpl
);

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

ReserveConfig[] memory allConfigsBefore = createConfigurationSnapshot(
'preTestEngineListingCustomWithEModeCreation',
IPool(address(contracts.poolProxy))
);

payload.execute();

ReserveConfig[] memory allConfigsAfter = createConfigurationSnapshot(
'postTestEngineListingCustomWithEModeCreation',
IPool(address(contracts.poolProxy))
);

diffReports(
'preTestEngineListingCustomWithEModeCreation',
'postTestEngineListingCustomWithEModeCreation'
);

ReserveConfig memory expectedAssetConfig = ReserveConfig({
symbol: 'PSP',
underlying: asset,
aToken: address(0), // Mock, as they don't get validated, because of the "dynamic" deployment on proposal execution
variableDebtToken: address(0), // Mock, as they don't get validated, because of the "dynamic" deployment on proposal execution
decimals: 18,
ltv: 82_50,
liquidationThreshold: 86_00,
liquidationBonus: 105_00,
liquidationProtocolFee: 10_00,
reserveFactor: 10_00,
usageAsCollateralEnabled: true,
borrowingEnabled: true,
interestRateStrategy: AaveV3ConfigEngine(configEngine).DEFAULT_INTEREST_RATE_STRATEGY(),
isPaused: false,
isActive: true,
isFrozen: false,
isSiloed: false,
isBorrowableInIsolation: false,
isFlashloanable: false,
supplyCap: 85_000,
borrowCap: 60_000,
debtCeiling: 0,
virtualBalance: 0,
aTokenUnderlyingBalance: 0
});

_validateReserveConfig(expectedAssetConfig, allConfigsAfter);

_noReservesConfigsChangesApartNewListings(allConfigsBefore, allConfigsAfter);

_validateReserveTokensImpls(
_findReserveConfigBySymbol(allConfigsAfter, 'PSP'),
ReserveTokens({aToken: aTokenImpl, variableDebtToken: vTokenImpl})
);

_validateAssetSourceOnOracle(
IPoolAddressesProvider(address(contracts.poolAddressesProvider)),
asset,
feed
);

_validateInterestRateStrategy(
asset,
contracts.protocolDataProvider.getInterestRateStrategyAddress(asset),
AaveV3ConfigEngine(configEngine).DEFAULT_INTEREST_RATE_STRATEGY(),
IDefaultInterestRateStrategyV2.InterestRateDataRay({
optimalUsageRatio: _bpsToRay(
payload.newListingsCustom()[0].base.rateStrategyParams.optimalUsageRatio
),
baseVariableBorrowRate: _bpsToRay(
payload.newListingsCustom()[0].base.rateStrategyParams.baseVariableBorrowRate
),
variableRateSlope1: _bpsToRay(
payload.newListingsCustom()[0].base.rateStrategyParams.variableRateSlope1
),
variableRateSlope2: _bpsToRay(
payload.newListingsCustom()[0].base.rateStrategyParams.variableRateSlope2
)
})
);

DataTypes.EModeCategory memory eModeCategoryData;
eModeCategoryData.ltv = 97_40;
eModeCategoryData.liquidationThreshold = 97_60;
eModeCategoryData.liquidationBonus = 101_50; // 100_00 + 1_50
eModeCategoryData.label = 'Listed Asset EMode';
eModeCategoryData.collateralBitmap = 8; // 1000
eModeCategoryData.borrowableBitmap = 8; // 1000

_validateEmodeCategory(
IPoolAddressesProvider(address(contracts.poolAddressesProvider)),
1,
eModeCategoryData
);
}

function testCapsUpdate() public {
// this asset has been listed before
address asset = tokenList.usdx;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ contract AaveV3MockEModeCategoryCreation is AaveV3Payload {
returns (IEngine.EModeCategoryCreation[] memory)
{
IEngine.EModeCategoryCreation[] memory eModeUpdates = new IEngine.EModeCategoryCreation[](2);

address[] memory empty = new address[](0);

eModeUpdates[0] = IEngine.EModeCategoryCreation({
Expand All @@ -47,12 +46,14 @@ contract AaveV3MockEModeCategoryCreation is AaveV3Payload {
borrowables: empty,
collaterals: empty
});

address[] memory collaterals = new address[](2);
address[] memory borrowables = new address[](2);
collaterals[0] = COLLATERAL_ONE;
collaterals[1] = COLLATERAL_TWO;
borrowables[0] = BORROWABLE_ONE;
borrowables[1] = BORROWABLE_TWO;

eModeUpdates[1] = IEngine.EModeCategoryCreation({
ltv: 97_40,
liqThreshold: 97_60,
Expand Down
Loading