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;
}