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/