diff --git a/Makefile b/Makefile
index 7c4ad6630..8b662e763 100644
--- a/Makefile
+++ b/Makefile
@@ -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)
diff --git a/generator/features/__snapshots__/assetListing.spec.ts.snap b/generator/features/__snapshots__/assetListing.spec.ts.snap
index 428e19738..336e7ee2e 100644
--- a/generator/features/__snapshots__/assetListing.spec.ts.snap
+++ b/generator/features/__snapshots__/assetListing.spec.ts.snap
@@ -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
@@ -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) {
@@ -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),
@@ -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';
@@ -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();
}
@@ -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);
}
}
",
@@ -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) {
@@ -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),
@@ -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);
}",
],
},
diff --git a/generator/features/assetListing.ts b/generator/features/assetListing.ts
index 00bae7c5a..b88cf08a0 100644
--- a/generator/features/assetListing.ts
+++ b/generator/features/assetListing.ts
@@ -12,7 +12,7 @@ import {TEST_EXECUTE_PROPOSAL} from '../utils/constants';
async function fetchListing(pool: PoolIdentifier): Promise
{
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,
});
@@ -100,12 +100,11 @@ export const assetListing: FeatureModule = {
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) {
@@ -133,7 +132,7 @@ export const assetListing: FeatureModule = {
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},
@@ -156,7 +155,8 @@ export const assetListing: FeatureModule = {
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});
}`
),
},
@@ -167,7 +167,7 @@ export const assetListing: FeatureModule = {
export const assetListingCustom: FeatureModule = {
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;
@@ -181,11 +181,12 @@ export const assetListingCustom: FeatureModule = {
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) {
@@ -214,7 +215,7 @@ export const assetListingCustom: FeatureModule = {
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},
@@ -243,7 +244,8 @@ export const assetListingCustom: FeatureModule = {
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});
}`
),
},
diff --git a/generator/features/collateralsUpdates.ts b/generator/features/collateralsUpdates.ts
index b34f6c4ce..6989d1343 100644
--- a/generator/features/collateralsUpdates.ts
+++ b/generator/features/collateralsUpdates.ts
@@ -55,9 +55,9 @@ export const collateralsUpdates: FeatureModule = {
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(
@@ -68,7 +68,6 @@ export const collateralsUpdates: FeatureModule = {
liqBonus: ${cfg.liqBonus},
debtCeiling: ${cfg.debtCeiling},
liqProtocolFee: ${cfg.liqProtocolFee}
- }
});`
)
.join('\n')}
diff --git a/generator/features/eModesUpdates.ts b/generator/features/eModesUpdates.ts
index 8a6ff2455..af37cc582 100644
--- a/generator/features/eModesUpdates.ts
+++ b/generator/features/eModesUpdates.ts
@@ -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(
+ disableKeepCurrent?: T,
+ eModeCategory?: string
+): Promise {
+ 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;
}
@@ -57,7 +95,9 @@ export const eModeUpdates: FeatureModule = {
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')}
diff --git a/generator/features/priceFeedsUpdates.ts b/generator/features/priceFeedsUpdates.ts
index 42e1b5c2b..397c88d5d 100644
--- a/generator/features/priceFeedsUpdates.ts
+++ b/generator/features/priceFeedsUpdates.ts
@@ -31,20 +31,20 @@ export const priceFeedsUpdates: FeatureModule = {
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;
}`,
],
},
diff --git a/generator/features/rateUpdates.ts b/generator/features/rateUpdates.ts
index 68e1b4152..a46717a3f 100644
--- a/generator/features/rateUpdates.ts
+++ b/generator/features/rateUpdates.ts
@@ -54,7 +54,7 @@ export async function fetchRateStrategyParamsV3(disableKeepCurrent?: boolean) {
disableKeepCurrent,
}),
optimalStableToTotalDebtRatio: await percentInput({
- message: 'stableRateExcessOffset',
+ message: 'optimalStableToTotalDebtRatio',
toRay: true,
disableKeepCurrent,
}),
@@ -94,7 +94,7 @@ export const rateUpdatesV2: FeatureModule = {
.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},
@@ -149,7 +149,7 @@ export const rateUpdatesV3: FeatureModule = {
.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},
diff --git a/generator/generator.ts b/generator/generator.ts
index 0b818ec40..e888c1d27 100644
--- a/generator/generator.ts
+++ b/generator/generator.ts
@@ -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]!);
@@ -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,
diff --git a/generator/prompts.ts b/generator/prompts.ts
index ee15db28b..c4c5de71a 100644
--- a/generator/prompts.ts
+++ b/generator/prompts.ts
@@ -15,7 +15,7 @@ function isNumberOrKeepCurrent(value: string) {
function isAddressOrKeepCurrent(value: string) {
if (value == ENGINE_FLAGS.KEEP_CURRENT_ADDRESS || isAddress(value)) return true;
- return 'Must be a calid address';
+ return 'Must be a valid address';
}
// TRANSFORMS
@@ -51,6 +51,24 @@ function translateJsAddressToSol(value: string) {
return getAddress(value);
}
+function translateJsBoolToSol(value: string) {
+ switch (value) {
+ case ENGINE_FLAGS.ENABLED:
+ return `EngineFlags.ENABLED`;
+ case ENGINE_FLAGS.DISABLED:
+ return `EngineFlags.DISABLED`;
+ case ENGINE_FLAGS.KEEP_CURRENT:
+ return `EngineFlags.KEEP_CURRENT`;
+ default:
+ return value;
+ }
+}
+
+function translateJsStringToSol(value: string) {
+ if (value === ENGINE_FLAGS.KEEP_CURRENT_STRING) return `EngineFlags.KEEP_CURRENT_STRING`;
+ return value;
+}
+
function translateEModeToEModeLib(value: string, pool: PoolIdentifier) {
if (value === ENGINE_FLAGS.KEEP_CURRENT) return `EngineFlags.KEEP_CURRENT`;
return `${pool}EModes.${value}`;
@@ -89,7 +107,7 @@ export async function booleanSelect({
message,
choices: choices,
});
- return value as T extends true
+ return translateJsBoolToSol(value) as T extends true
? Exclude
: BooleanSelectValues;
}
@@ -169,27 +187,36 @@ export async function eModeSelect({
pool,
}: EModeSelectPrompt) {
const eModes = getEModes(pool as any);
- const eMode = await select({
- message,
- choices: [
- ...(disableKeepCurrent ? [] : [{value: ENGINE_FLAGS.KEEP_CURRENT}]),
- ...Object.keys(eModes).map((eMode) => ({value: eMode})),
- ],
- });
- return translateEModeToEModeLib(eMode, pool);
+ if (Object.keys(eModes).length != 0) {
+ const eMode = await select({
+ message,
+ choices: [
+ ...(disableKeepCurrent ? [] : [{value: ENGINE_FLAGS.KEEP_CURRENT}]),
+ ...Object.keys(eModes).map((eMode) => ({value: eMode})),
+ ],
+ });
+ return translateEModeToEModeLib(eMode, pool);
+ } else {
+ console.log('No e-mode category active on the current pool');
+ return '0';
+ }
}
export async function eModesSelect({message, pool}: EModeSelectPrompt) {
const eModes = getEModes(pool as any);
- const values = await checkbox({
- message,
- choices: [
- ...Object.keys(eModes)
- .filter((e) => e != 'NONE')
- .map((eMode) => ({value: eMode})),
- ],
- });
- return values.map((mode) => translateEModeToEModeLib(mode, pool));
+ if (Object.keys(eModes).length != 0) {
+ const values = await checkbox({
+ message,
+ choices: [
+ ...Object.keys(eModes)
+ .filter((e) => e != 'NONE')
+ .map((eMode) => ({value: eMode})),
+ ],
+ });
+ return values.map((mode) => translateEModeToEModeLib(mode, pool));
+ } else {
+ console.log('No e-mode category active on the current pool');
+ }
}
export async function stringInput({
@@ -197,9 +224,10 @@ export async function stringInput({
defaultValue,
disableKeepCurrent,
}: GenericPrompt) {
- return input({
+ const value = await input({
message,
default: defaultValue,
...(disableKeepCurrent ? {} : {default: ENGINE_FLAGS.KEEP_CURRENT_STRING}),
});
+ return translateJsStringToSol(value);
}
diff --git a/generator/templates/proposal.template.ts b/generator/templates/proposal.template.ts
index 97ad23f80..237bebac1 100644
--- a/generator/templates/proposal.template.ts
+++ b/generator/templates/proposal.template.ts
@@ -29,6 +29,9 @@ export const proposalTemplate = (options: Options, poolConfig: PoolConfig) => {
const usesConfigEngine = poolConfig.features.some(
(f) => ![FEATURE.OTHERS, FEATURE.FLASH_BORROWER].includes(f)
);
+ const isAssetListing = poolConfig.features.some((f) =>
+ [FEATURE.ASSET_LISTING, FEATURE.ASSET_LISTING_CUSTOM].includes(f)
+ );
if (innerExecute) {
if (usesConfigEngine) {
optionalExecute = `function _postExecute() internal override {
@@ -50,10 +53,12 @@ export const proposalTemplate = (options: Options, poolConfig: PoolConfig) => {
contract ${contractName} is ${
usesConfigEngine ? `Aave${version}Payload${chain}` : 'IProposalGenericExecutor'
} {
+ ${isAssetListing ? 'using SafeERC20 for IERC20;' : ''}
+
${constants}
-
+
${optionalExecute}
-
+
${functions}
}`;
diff --git a/generator/templates/test.template.ts b/generator/templates/test.template.ts
index 72823d634..7c277733c 100644
--- a/generator/templates/test.template.ts
+++ b/generator/templates/test.template.ts
@@ -28,6 +28,7 @@ export const testTemplate = async (options: Options, poolConfig: PoolConfig) =>
.flat()
.filter((f) => f !== undefined)
.join('\n');
+
let template = `
import 'forge-std/Test.sol';
import {${testBase}, ReserveConfig} from 'aave-helpers/${testBase}.sol';
@@ -46,7 +47,7 @@ contract ${contractName}_Test is ${testBase} {
}
/**
- * @dev executes the generic test suite including e2e and config snapshots
+ * @dev executes the generic test suite including e2e and config snapshots
*/
function test_defaultProposalExecution() public {
defaultTest('${contractName}', ${poolConfig.pool}.POOL, address(proposal));
diff --git a/generator/utils/importsResolver.spec.ts b/generator/utils/importsResolver.spec.ts
index e70ff2190..400ba34e8 100644
--- a/generator/utils/importsResolver.spec.ts
+++ b/generator/utils/importsResolver.spec.ts
@@ -3,6 +3,12 @@ import {expect, describe, it} from 'vitest';
import {prefixWithImports} from './importsResolver';
describe('prefixWithImports', () => {
+ it('should resolve IProposalGenericExecutor', () => {
+ expect(prefixWithImports(`is IProposalGenericExecutor {`)).toContain(
+ `import {IProposalGenericExecutor} from 'aave-helpers/interfaces/IProposalGenericExecutor.sol';`
+ );
+ });
+
it('should resolve Engine imports', () => {
expect(prefixWithImports(`GovV3Helpers.createPayload`)).toContain(
`import {GovV3Helpers} from 'aave-helpers/GovV3Helpers.sol';`
diff --git a/generator/utils/importsResolver.ts b/generator/utils/importsResolver.ts
index 39d97aef2..0d71d8fd6 100644
--- a/generator/utils/importsResolver.ts
+++ b/generator/utils/importsResolver.ts
@@ -10,7 +10,7 @@ const GovernanceImports = [
] as const;
/**
- * @dev matches the code fro known address book imports and generates an import statment satisfying the used libraries
+ * @dev matches the code from known address book imports and generates an import statement satisfying the used libraries
* @param code
* @returns
*/
@@ -44,11 +44,11 @@ function generateEngineImport(code: string) {
}
function findMatches(code: string, needles: string[] | readonly string[]) {
- return needles.filter((needle) => RegExp(needle + '\\.', 'g').test(code));
+ return needles.filter((needle) => RegExp(needle, 'g').test(code));
}
function findMatch(code: string, needle: string) {
- return RegExp(needle + '\\.', 'g').test(code);
+ return RegExp(needle, 'g').test(code);
}
/**
@@ -93,6 +93,13 @@ export function prefixWithImports(code: string) {
if (findMatch(code, 'IV2RateStrategyFactory')) {
imports += `import {IV2RateStrategyFactory} from 'aave-helpers/v2-config-engine/IV2RateStrategyFactory.sol';\n`;
}
+ // common imports
+ if (findMatch(code, 'IERC20')) {
+ imports += `import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol';\n`;
+ }
+ if (findMatch(code, 'forceApprove')) {
+ imports += `import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol';\n`;
+ }
return imports + code;
}
diff --git a/lib/aave-helpers b/lib/aave-helpers
index 707ad78ff..3478bd372 160000
--- a/lib/aave-helpers
+++ b/lib/aave-helpers
@@ -1 +1 @@
-Subproject commit 707ad78ffd9f58fedd85a029f32ac52ef74af15a
+Subproject commit 3478bd37275db888630b9fbce31525b3f973654b
diff --git a/remappings.txt b/remappings.txt
index 620a76fc2..b3b219556 100644
--- a/remappings.txt
+++ b/remappings.txt
@@ -6,4 +6,4 @@ aave-v3-core/=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-core/
aave-v3-periphery/=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-periphery/
ds-test/=lib/aave-helpers/lib/forge-std/lib/ds-test/src/
forge-std/=lib/aave-helpers/lib/forge-std/src/
-solidity-utils/=lib/aave-helpers/lib/solidity-utils/src/
\ No newline at end of file
+solidity-utils/=lib/aave-helpers/lib/solidity-utils/src/