Skip to content

Commit

Permalink
feat: add new emode category via generator (bgd-labs#2)
Browse files Browse the repository at this point in the history
* fix: typo

* chore: fix text

* feat: add new e-mode category via the generator

* fix: KEEP_CURRENT_STRING

* fix: emode label

* fix: generator booleanSelect ENABLED DISABLED

* fix: collateral updates code artifacts

* refactor: eModesUpdates

* fix: lint

* fix: generator shadows an existing priceFeedsUpdates var

* fix: v3 config engine rates import generator

* fix: v2 v3 engine rates import generator

* forge install: aave-address-book

v2.8.0

* chore: update address book

* fix: use correct contract an asset listings

- targets bgd-labs#3

* fix: some minor patches

* fix: remapping

* chore: some cleanup

* fix: do not revert when no e-mode category exists on the pool

* fix: makefile

* fix: eModesSelect

* fix: typo

* fix: asset listing approve before supply and collector funds

* fix: use forceApprove on generator for assetListing

* chore: update aave helpers

* fix: test

* fix: typo

---------

Co-authored-by: sakulstra <lukasstrassel@googlemail.com>
  • Loading branch information
2 people authored and Rozengarden committed Nov 2, 2023
1 parent f1608dd commit c431266
Show file tree
Hide file tree
Showing 15 changed files with 185 additions and 85 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ update:; forge update
# Build & test
build :; forge build --sizes
test :; forge test -vvv
test-contract :; forge test --match-contract ${filter} -vvv

# Deploy
deploy-ledger :; forge script ${contract} --rpc-url ${chain} $(if ${dry},--sender 0x25F2226B597E8F9514B3F68F00f494cF4f286491 -vvvv,--broadcast --ledger --mnemonics foo --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv)
Expand Down
33 changes: 21 additions & 12 deletions generator/features/__snapshots__/assetListing.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ pragma solidity ^0.8.0;
import {AaveV3Ethereum, AaveV3EthereumEModes} from 'aave-address-book/AaveV3Ethereum.sol';
import {AaveV3PayloadEthereum} from 'aave-helpers/v3-config-engine/AaveV3PayloadEthereum.sol';
import {IAaveV3ConfigEngine} from 'aave-helpers/v3-config-engine/IAaveV3ConfigEngine.sol';
import {IV3RateStrategyFactory} from 'aave-helpers/v3-config-engine/IV3RateStrategyFactory.sol';
import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol';
import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol';
/**
* @title test
Expand All @@ -97,10 +100,13 @@ import {IAaveV3ConfigEngine} from 'aave-helpers/v3-config-engine/IAaveV3ConfigEn
* - Discussion: test
*/
contract AaveV3Ethereum_Test_20231023 is AaveV3PayloadEthereum {
address public constant PSP = address(0xcAfE001067cDEF266AfB7Eb5A286dCFD277f3dE5);
using SafeERC20 for IERC20;
address public constant PSP = 0xcAfE001067cDEF266AfB7Eb5A286dCFD277f3dE5;
function _postExecute() internal override {
AaveV3Ethereum.POOL.supply(PSP, 10 ** 18, AaveV3Ethereum.COLLECTOR, 0);
IERC20(PSP).forceApprove(address(AaveV3Ethereum.POOL), 10 ** 18);
AaveV3Ethereum.POOL.supply(PSP, 10 ** 18, address(AaveV3Ethereum.COLLECTOR), 0);
}
function newListings() public pure override returns (IAaveV3ConfigEngine.Listing[] memory) {
Expand All @@ -124,7 +130,7 @@ contract AaveV3Ethereum_Test_20231023 is AaveV3PayloadEthereum {
borrowCap: 5_000,
debtCeiling: 100_000,
liqProtocolFee: 20_00,
rateStrategyParams: Rates.RateStrategyParams({
rateStrategyParams: IV3RateStrategyFactory.RateStrategyParams({
optimalUsageRatio: _bpsToRay(80_00),
baseVariableBorrowRate: _bpsToRay(0_00),
variableRateSlope1: _bpsToRay(10_00),
Expand All @@ -146,6 +152,7 @@ pragma solidity ^0.8.0;
import {GovV3Helpers} from 'aave-helpers/GovV3Helpers.sol';
import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol';
import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol';
import 'forge-std/Test.sol';
import {ProtocolV3TestBase, ReserveConfig} from 'aave-helpers/ProtocolV3TestBase.sol';
Expand All @@ -159,7 +166,7 @@ contract AaveV3Ethereum_Test_20231023_Test is ProtocolV3TestBase {
AaveV3Ethereum_Test_20231023 internal proposal;
function setUp() public {
vm.createSelectFork(vm.rpcUrl('mainnet'), 18420741);
vm.createSelectFork(vm.rpcUrl('mainnet'), 18461057);
proposal = new AaveV3Ethereum_Test_20231023();
}
Expand All @@ -172,10 +179,10 @@ contract AaveV3Ethereum_Test_20231023_Test is ProtocolV3TestBase {
function test_collectorHasPSPFunds() public {
GovV3Helpers.executePayload(vm, address(proposal));
assertGte(
IERC20(0xcAfE001067cDEF266AfB7Eb5A286dCFD277f3dE5).balanceOf(AaveV3Ethereum.COLLECTOR),
10 ** 18
);
(address aTokenAddress, , ) = AaveV3Ethereum
.AAVE_PROTOCOL_DATA_PROVIDER
.getReserveTokensAddresses(proposal.PSP());
assertGe(IERC20(aTokenAddress).balanceOf(address(AaveV3Ethereum.COLLECTOR)), 10 ** 18);
}
}
",
Expand Down Expand Up @@ -237,10 +244,11 @@ exports[`feature: assetListing > should return reasonable code 1`] = `
{
"code": {
"constants": [
"address public constant PSP = address(0xcAfE001067cDEF266AfB7Eb5A286dCFD277f3dE5);",
"address public constant PSP = 0xcAfE001067cDEF266AfB7Eb5A286dCFD277f3dE5;",
],
"execute": [
"AaveV3Ethereum.POOL.supply(PSP, 10 ** 18, AaveV3Ethereum.COLLECTOR, 0);",
"IERC20(PSP).forceApprove(address(AaveV3Ethereum.POOL), 10 ** 18);
AaveV3Ethereum.POOL.supply(PSP, 10 ** 18, address(AaveV3Ethereum.COLLECTOR), 0);",
],
"fn": [
"function newListings() public pure override returns (IAaveV3ConfigEngine.Listing[] memory) {
Expand All @@ -264,7 +272,7 @@ exports[`feature: assetListing > should return reasonable code 1`] = `
borrowCap: 5_000,
debtCeiling: 100_000,
liqProtocolFee: 20_00,
rateStrategyParams: Rates.RateStrategyParams({
rateStrategyParams: IV3RateStrategyFactory.RateStrategyParams({
optimalUsageRatio: _bpsToRay(80_00),
baseVariableBorrowRate: _bpsToRay(0_00),
variableRateSlope1: _bpsToRay(10_00),
Expand All @@ -285,7 +293,8 @@ exports[`feature: assetListing > should return reasonable code 1`] = `
"fn": [
"function test_collectorHasPSPFunds() public {
GovV3Helpers.executePayload(vm,address(proposal));
assertGte(IERC20(0xcAfE001067cDEF266AfB7Eb5A286dCFD277f3dE5).balanceOf(AaveV3Ethereum.COLLECTOR), 10 ** 18);
(address aTokenAddress, , ) = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses(proposal.PSP());
assertGe(IERC20(aTokenAddress).balanceOf(address(AaveV3Ethereum.COLLECTOR)), 10 ** 18);
}",
],
},
Expand Down
26 changes: 14 additions & 12 deletions generator/features/assetListing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {TEST_EXECUTE_PROPOSAL} from '../utils/constants';

async function fetchListing(pool: PoolIdentifier): Promise<Listing> {
const asset = await addressInput({
message: 'enter the asset you want to list',
message: 'Enter the address of the asset you want to list',
disableKeepCurrent: true,
});

Expand Down Expand Up @@ -100,12 +100,11 @@ export const assetListing: FeatureModule<Listing[]> = {
build(opt, pool, cfg) {
const response: CodeArtifact = {
code: {
constants: cfg.map(
(cfg) => `address public constant ${cfg.assetSymbol} = address(${cfg.asset});`
),
constants: cfg.map((cfg) => `address public constant ${cfg.assetSymbol} = ${cfg.asset};`),
execute: cfg.map(
(cfg) =>
`${pool}.POOL.supply(${cfg.assetSymbol}, 10 ** ${cfg.decimals}, ${pool}.COLLECTOR, 0);`
`IERC20(${cfg.assetSymbol}).forceApprove(address(${pool}.POOL), 10 ** ${cfg.decimals});
${pool}.POOL.supply(${cfg.assetSymbol}, 10 ** ${cfg.decimals}, address(${pool}.COLLECTOR), 0);`
),
fn: [
`function newListings() public pure override returns (IAaveV3ConfigEngine.Listing[] memory) {
Expand Down Expand Up @@ -133,7 +132,7 @@ export const assetListing: FeatureModule<Listing[]> = {
borrowCap: ${cfg.borrowCap},
debtCeiling: ${cfg.debtCeiling},
liqProtocolFee: ${cfg.liqProtocolFee},
rateStrategyParams: Rates.RateStrategyParams({
rateStrategyParams: IV3RateStrategyFactory.RateStrategyParams({
optimalUsageRatio: ${cfg.rateStrategyParams.optimalUtilizationRate},
baseVariableBorrowRate: ${cfg.rateStrategyParams.baseVariableBorrowRate},
variableRateSlope1: ${cfg.rateStrategyParams.variableRateSlope1},
Expand All @@ -156,7 +155,8 @@ export const assetListing: FeatureModule<Listing[]> = {
fn: cfg.map(
(cfg) => `function test_collectorHas${cfg.assetSymbol}Funds() public {
${TEST_EXECUTE_PROPOSAL}
assertGte(IERC20(${cfg.asset}).balanceOf(${pool}.COLLECTOR), 10 ** ${cfg.decimals});
(address aTokenAddress, , ) = ${pool}.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses(proposal.${cfg.assetSymbol}());
assertGe(IERC20(aTokenAddress).balanceOf(address(${pool}.COLLECTOR)), 10 ** ${cfg.decimals});
}`
),
},
Expand All @@ -167,7 +167,7 @@ export const assetListing: FeatureModule<Listing[]> = {

export const assetListingCustom: FeatureModule<ListingWithCustomImpl[]> = {
value: FEATURE.ASSET_LISTING_CUSTOM,
description: 'newListingsCustom (listing a new asset, with custom imeplementations)',
description: 'newListingsCustom (listing a new asset, with custom implementations)',
async cli(opt, pool) {
const response: ListingWithCustomImpl[] = [];
let more: boolean = true;
Expand All @@ -181,11 +181,12 @@ export const assetListingCustom: FeatureModule<ListingWithCustomImpl[]> = {
const response: CodeArtifact = {
code: {
constants: cfg.map(
(cfg) => `address public constant ${cfg.base.assetSymbol} = address(${cfg.base.asset});`
(cfg) => `address public constant ${cfg.base.assetSymbol} = ${cfg.base.asset};`
),
execute: cfg.map(
(cfg) =>
`${pool}.POOL.supply(${cfg.base.assetSymbol}, 10 ** ${cfg.base.decimals}, ${pool}.COLLECTOR, 0);`
`IERC20(${cfg.base.assetSymbol}).forceApprove(address(${pool}.POOL), 10 ** ${cfg.base.decimals});
${pool}.POOL.supply(${cfg.base.assetSymbol}, 10 ** ${cfg.base.decimals}, ${pool}.COLLECTOR, 0);`
),
fn: [
`function newListingsCustom() public pure override returns (IAaveV3ConfigEngine.ListingWithCustomImpl[] memory) {
Expand Down Expand Up @@ -214,7 +215,7 @@ export const assetListingCustom: FeatureModule<ListingWithCustomImpl[]> = {
borrowCap: ${cfg.base.borrowCap},
debtCeiling: ${cfg.base.debtCeiling},
liqProtocolFee: ${cfg.base.liqProtocolFee},
rateStrategyParams: Rates.RateStrategyParams({
rateStrategyParams: IV3RateStrategyFactory.RateStrategyParams({
optimalUsageRatio: ${cfg.base.rateStrategyParams.optimalUtilizationRate},
baseVariableBorrowRate: ${cfg.base.rateStrategyParams.baseVariableBorrowRate},
variableRateSlope1: ${cfg.base.rateStrategyParams.variableRateSlope1},
Expand Down Expand Up @@ -243,7 +244,8 @@ export const assetListingCustom: FeatureModule<ListingWithCustomImpl[]> = {
fn: cfg.map(
(cfg) => `function test_collectorHas${cfg.base.assetSymbol}Funds() public {
${TEST_EXECUTE_PROPOSAL}
assertGte(IERC20(${cfg.base.asset}).balanceOf(${pool}.COLLECTOR), 10 ** ${cfg.base.decimals});
(address aTokenAddress, , ) = ${pool}.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses(proposal.${cfg.base.assetSymbol}());
assertGte(IERC20(aTokenAddress).balanceOf(${pool}.COLLECTOR), 10 ** ${cfg.base.decimals});
}`
),
},
Expand Down
7 changes: 3 additions & 4 deletions generator/features/collateralsUpdates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ export const collateralsUpdates: FeatureModule<CollateralUpdates> = {
code: {
fn: [
`function collateralsUpdates() public pure override returns (IAaveV3ConfigEngine.CollateralUpdate[] memory) {
IAaveV3ConfigEngine.CollateralUpdate[] memory collateralUpdate = new IAaveV3ConfigEngine.CollateralUpdate[](${
cfg.length
});
IAaveV3ConfigEngine.CollateralUpdate[] memory collateralUpdate = new IAaveV3ConfigEngine.CollateralUpdate[](${
cfg.length
});
${cfg
.map(
Expand All @@ -68,7 +68,6 @@ export const collateralsUpdates: FeatureModule<CollateralUpdates> = {
liqBonus: ${cfg.liqBonus},
debtCeiling: ${cfg.debtCeiling},
liqProtocolFee: ${cfg.liqProtocolFee}
}
});`
)
.join('\n')}
Expand Down
86 changes: 63 additions & 23 deletions generator/features/eModesUpdates.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,71 @@
import {CodeArtifact, FEATURE, FeatureModule, PoolIdentifier} from '../types';
import {addressInput, eModesSelect, percentInput, stringInput} from '../prompts';
import {EModeCategoryUpdate} from './types';
import {confirm} from '@inquirer/prompts';

async function fetchEmodeCategoryUpdate<T extends boolean>(
disableKeepCurrent?: T,
eModeCategory?: string
): Promise<EModeCategoryUpdate> {
return {
eModeCategory:
eModeCategory ?? (await stringInput({message: 'eModeCategory', disableKeepCurrent})),
ltv: await percentInput({
message: 'ltv',
disableKeepCurrent,
}),
liqThreshold: await percentInput({
message: 'liqThreshold',
disableKeepCurrent,
}),
liqBonus: await percentInput({
message: 'liqBonus',
disableKeepCurrent,
}),
priceSource: await addressInput({
message: 'Price Source',
disableKeepCurrent,
}),
label: await stringInput({
message: 'label',
disableKeepCurrent,
}),
};
}

async function subCli(pool: PoolIdentifier) {
console.log(`Fetching information for EModes on ${pool}`);
const eModeCategories = await eModesSelect({
message: 'Select the eModes you want to amend',
pool,
});
const answers: EmodeUpdates = [];
for (const eModeCategory of eModeCategories) {
console.log(`collecting info for ${eModeCategory}`);
answers.push({
eModeCategory,
ltv: await percentInput({
message: 'ltv',
}),
liqThreshold: await percentInput({
message: 'liqThreshold',
}),
liqBonus: await percentInput({
message: 'liqBonus',
}),
priceSource: await addressInput({
message: 'Price Source',
}),
label: await stringInput({message: 'label'}),

const shouldAddNewCategory = await confirm({
message: 'Do you wish to add a new emode category?',
default: false,
});
if (shouldAddNewCategory) {
let more: boolean = true;
while (more) {
answers.push(await fetchEmodeCategoryUpdate(true));
more = await confirm({message: 'Do you want to add another emode category?', default: false});
}
}

const shouldAmendCategory = await confirm({
message: 'Do you wish to amend existing emode category?',
default: false,
});
if (shouldAmendCategory) {
const eModeCategories = await eModesSelect({
message: 'Select the eModes you want to amend',
pool,
});

if (eModeCategories) {
for (const eModeCategory of eModeCategories) {
console.log(`collecting info for ${eModeCategory}`);
answers.push(await fetchEmodeCategoryUpdate(false, eModeCategory));
}
}
}

return answers;
}

Expand Down Expand Up @@ -57,7 +95,9 @@ export const eModeUpdates: FeatureModule<EmodeUpdates> = {
liqThreshold: ${cfg.liqThreshold},
liqBonus: ${cfg.liqBonus},
priceSource: ${cfg.priceSource},
label: ${cfg.label}
label: ${
cfg.label == 'EngineFlags.KEEP_CURRENT_STRING' ? cfg.label : `"${cfg.label}"`
}
});`
)
.join('\n')}
Expand Down
6 changes: 3 additions & 3 deletions generator/features/priceFeedsUpdates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,20 @@ export const priceFeedsUpdates: FeatureModule<PriceFeedUpdate[]> = {
code: {
fn: [
`function priceFeedsUpdates() public pure override returns (IAaveV3ConfigEngine.PriceFeedUpdate[] memory) {
IAaveV3ConfigEngine.PriceFeedUpdate[] memory priceFeedsUpdates = new IAaveV3ConfigEngine.PriceFeedUpdate[](${
IAaveV3ConfigEngine.PriceFeedUpdate[] memory priceFeedUpdates = new IAaveV3ConfigEngine.PriceFeedUpdate[](${
cfg.length
});
${cfg
.map(
(cfg, ix) => `priceFeedsUpdates[${ix}] = IAaveV3ConfigEngine.PriceFeedUpdate({
(cfg, ix) => `priceFeedUpdates[${ix}] = IAaveV3ConfigEngine.PriceFeedUpdate({
asset: ${cfg.asset},
priceFeed: ${cfg.priceFeed}
});`
)
.join('\n')}
return priceFeedsUpdates;
return priceFeedUpdates;
}`,
],
},
Expand Down
6 changes: 3 additions & 3 deletions generator/features/rateUpdates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export async function fetchRateStrategyParamsV3(disableKeepCurrent?: boolean) {
disableKeepCurrent,
}),
optimalStableToTotalDebtRatio: await percentInput({
message: 'stableRateExcessOffset',
message: 'optimalStableToTotalDebtRatio',
toRay: true,
disableKeepCurrent,
}),
Expand Down Expand Up @@ -94,7 +94,7 @@ export const rateUpdatesV2: FeatureModule<RateStrategyUpdate[]> = {
.map(
(cfg, ix) => `rateStrategies[${ix}] = IAaveV2ConfigEngine.RateStrategyUpdate({
asset: ${cfg.asset},
params: Rates.RateStrategyParams({
params: IV2RateStrategyFactory.RateStrategyParams({
optimalUtilizationRate: ${cfg.params.optimalUtilizationRate},
baseVariableBorrowRate: ${cfg.params.baseVariableBorrowRate},
variableRateSlope1: ${cfg.params.variableRateSlope1},
Expand Down Expand Up @@ -149,7 +149,7 @@ export const rateUpdatesV3: FeatureModule<RateStrategyUpdate[]> = {
.map(
(cfg, ix) => `rateStrategies[${ix}] = IAaveV3ConfigEngine.RateStrategyUpdate({
asset: ${cfg.asset},
params: Rates.RateStrategyParams({
params: IV3RateStrategyFactory.RateStrategyParams({
optimalUsageRatio: ${cfg.params.optimalUtilizationRate},
baseVariableBorrowRate: ${cfg.params.baseVariableBorrowRate},
variableRateSlope1: ${cfg.params.variableRateSlope1},
Expand Down
2 changes: 2 additions & 0 deletions generator/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export async function generateFiles(options: Options, poolConfigs: PoolConfigs):
default: false,
});
}

async function createPayloadAndTest(options: Options, pool: PoolIdentifier) {
const contractName = generateContractName(options, pool);
const testCode = await testTemplate(options, poolConfigs[pool]!);
Expand All @@ -62,6 +63,7 @@ export async function generateFiles(options: Options, poolConfigs: PoolConfigs):
contractName: contractName,
};
}

console.log('generating script');
const script = prettier.format(generateScript(options), {
...prettierSolCfg,
Expand Down
Loading

0 comments on commit c431266

Please sign in to comment.