diff --git a/generator/features/__snapshots__/assetListing.spec.ts.snap b/generator/features/__snapshots__/assetListing.spec.ts.snap index 51da2df28..c11571469 100644 --- a/generator/features/__snapshots__/assetListing.spec.ts.snap +++ b/generator/features/__snapshots__/assetListing.spec.ts.snap @@ -571,12 +571,14 @@ exports[`feature: assetListing > should return reasonable code 1`] = ` }, "code": { "constants": [ - "address public constant PSP = 0xcAfE001067cDEF266AfB7Eb5A286dCFD277f3dE5;", - "uint256 public constant PSP_SEED_AMOUNT = 1e18;", + "address public constant PSP = 0xcAfE001067cDEF266AfB7Eb5A286dCFD277f3dE5; +uint256 public constant PSP_SEED_AMOUNT = 1e18; +", ], "execute": [ "IERC20(PSP).forceApprove(address(AaveV3Ethereum.POOL), PSP_SEED_AMOUNT); - AaveV3Ethereum.POOL.supply(PSP, PSP_SEED_AMOUNT, address(AaveV3Ethereum.COLLECTOR), 0);", +AaveV3Ethereum.POOL.supply(PSP, PSP_SEED_AMOUNT, address(AaveV3Ethereum.COLLECTOR), 0); +", ], "fn": [ "function newListings() public pure override returns (IAaveV3ConfigEngine.Listing[] memory) { @@ -618,7 +620,8 @@ exports[`feature: assetListing > should return reasonable code 1`] = ` GovV3Helpers.executePayload(vm,address(proposal)); (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 b0fd71da7..9a9872aa9 100644 --- a/generator/features/assetListing.ts +++ b/generator/features/assetListing.ts @@ -5,8 +5,8 @@ import {fetchRateStrategyParamsV3} from './rateUpdates'; import {fetchCollateralUpdate} from './collateralsUpdates'; import {fetchCapsUpdate} from './capsUpdates'; import {Listing, ListingWithCustomImpl, TokenImplementations} from './types'; -import {CHAIN_TO_CHAIN_ID, getPoolChain} from '../common'; -import {getContract} from 'viem'; +import {CHAIN_TO_CHAIN_ID, getPoolChain, getExplorerLink} from '../common'; +import {getContract, isAddress} from 'viem'; import {confirm} from '@inquirer/prompts'; import {TEST_EXECUTE_PROPOSAL} from '../utils/constants'; import {addressPrompt, translateJsAddressToSol} from '../prompts/addressPrompt'; @@ -79,6 +79,7 @@ async function fetchListing(pool: PoolIdentifier): Promise { pool, }), asset, + admin: await addressPrompt({message: 'Emission admin address (optional)', required: false}), }; } @@ -134,17 +135,24 @@ export const assetListing: FeatureModule = { build({pool, cfg}) { const response: CodeArtifact = { code: { - constants: cfg - .map((cfg) => [ - `address public constant ${cfg.assetSymbol} = ${translateJsAddressToSol(cfg.asset)};`, - `uint256 public constant ${cfg.assetSymbol}_SEED_AMOUNT = 1e${cfg.decimals};`, - ]) - .flat(), - execute: cfg.map( - (cfg) => - `IERC20(${cfg.assetSymbol}).forceApprove(address(${pool}.POOL), ${cfg.assetSymbol}_SEED_AMOUNT); - ${pool}.POOL.supply(${cfg.assetSymbol}, ${cfg.assetSymbol}_SEED_AMOUNT, address(${pool}.COLLECTOR), 0);`, - ), + constants: cfg.map((cfg) => { + let listingConstant = `address public constant ${cfg.assetSymbol} = ${translateJsAddressToSol(cfg.asset)};\n`; + listingConstant += `uint256 public constant ${cfg.assetSymbol}_SEED_AMOUNT = 1e${cfg.decimals};\n`; + if (isAddress(cfg.admin)) { + listingConstant += `address public constant ${cfg.assetSymbol}_LM_ADMIN = ${translateJsAddressToSol(cfg.admin)};\n`; + } + return listingConstant; + }), + execute: cfg.map((cfg) => { + let listingExe = `IERC20(${cfg.assetSymbol}).forceApprove(address(${pool}.POOL), ${cfg.assetSymbol}_SEED_AMOUNT);\n`; + listingExe += `${pool}.POOL.supply(${cfg.assetSymbol}, ${cfg.assetSymbol}_SEED_AMOUNT, address(${pool}.COLLECTOR), 0);\n`; + if (isAddress(cfg.admin)) { + listingExe += `\n(address a${cfg.assetSymbol}, , ) = ${pool}.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses(${cfg.assetSymbol});\n`; + listingExe += `IEmissionManager(${pool}.EMISSION_MANAGER).setEmissionAdmin(${cfg.assetSymbol}, ${cfg.assetSymbol}_ADMIN);\n`; + listingExe += `IEmissionManager(${pool}.EMISSION_MANAGER).setEmissionAdmin(a${cfg.assetSymbol}, ${cfg.assetSymbol}_ADMIN);\n`; + } + return listingExe; + }), fn: [ `function newListings() public pure override returns (IAaveV3ConfigEngine.Listing[] memory) { IAaveV3ConfigEngine.Listing[] memory listings = new IAaveV3ConfigEngine.Listing[](${ @@ -164,13 +172,22 @@ export const assetListing: FeatureModule = { ], }, test: { - fn: cfg.map( - (cfg) => `function test_collectorHas${cfg.assetSymbol}Funds() public { + fn: cfg.map((cfg) => { + let listingTest = `function test_collectorHas${cfg.assetSymbol}Funds() public { ${TEST_EXECUTE_PROPOSAL} (address aTokenAddress, , ) = ${pool}.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses(proposal.${cfg.assetSymbol}()); assertGe(IERC20(aTokenAddress).balanceOf(address(${pool}.COLLECTOR)), 10 ** ${cfg.decimals}); - }`, - ), + }\n`; + if (isAddress(cfg.admin)) { + listingTest += `\nfunction test_${cfg.assetSymbol}Admin() public { + ${TEST_EXECUTE_PROPOSAL} + (address a${cfg.assetSymbol}, , ) = ${pool}.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses(proposal.${cfg.assetSymbol}()); + assertEq(IEmissionManager(${pool}.EMISSION_MANAGER).getEmissionAdmin(proposal.${cfg.assetSymbol}()), proposal.${cfg.assetSymbol}_ADMIN()); + assertEq(IEmissionManager(${pool}.EMISSION_MANAGER).getEmissionAdmin(a${cfg.assetSymbol}), proposal.${cfg.assetSymbol}_ADMIN()); + }\n`; + } + return listingTest; + }), }, aip: { specification: cfg.map((cfg) => { @@ -215,6 +232,9 @@ export const assetListing: FeatureModule = { listingTemplate += `| Siloed Borrowing | ${cfg.withSiloedBorrowing} |\n`; listingTemplate += `| Borrowable in Isolation | ${cfg.borrowableInIsolation} |\n`; listingTemplate += `| Oracle | ${cfg.priceFeed} |\n`; + if (isAddress(cfg.admin)) { + listingTemplate += `\nAdditionaly [${cfg.admin}](${getExplorerLink(CHAIN_TO_CHAIN_ID[getPoolChain(pool)], cfg.admin)}) has been set as the emission admin for ${cfg.assetSymbol} and the corresponding aToken.\n`; + } return listingTemplate; }), }, diff --git a/generator/features/types.ts b/generator/features/types.ts index 08205636c..c09cb8f61 100644 --- a/generator/features/types.ts +++ b/generator/features/types.ts @@ -88,6 +88,7 @@ export interface Listing rateStrategyParams: RateStrategyParams; eModeCategory: string; decimals: number; + admin?: Hex | ''; } export interface ListingWithCustomImpl { diff --git a/generator/utils/importsResolver.ts b/generator/utils/importsResolver.ts index 1c5c4d47d..b5a77cbc6 100644 --- a/generator/utils/importsResolver.ts +++ b/generator/utils/importsResolver.ts @@ -104,5 +104,6 @@ export function prefixWithImports(code: string) { if (findMatch(code, 'IEmissionManager')) { imports += `import {IEmissionManager} from 'aave-v3-periphery/contracts/rewards/interfaces/IEmissionManager.sol';\n`; } + return imports + code; }