Skip to content
This repository was archived by the owner on Oct 21, 2025. It is now read-only.

Commit fd76fc1

Browse files
authored
Merge pull request #77 from aave/feat/76-emission-manager-contract
feat: Add emission manager contract
2 parents 212104c + 70e1c0f commit fd76fc1

File tree

4 files changed

+474
-0
lines changed

4 files changed

+474
-0
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// SPDX-License-Identifier: agpl-3.0
2+
pragma solidity 0.8.10;
3+
4+
import {Ownable} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/Ownable.sol';
5+
import {IEACAggregatorProxy} from '../misc/interfaces/IEACAggregatorProxy.sol';
6+
import {IEmissionManager} from './interfaces/IEmissionManager.sol';
7+
import {ITransferStrategyBase} from './interfaces/ITransferStrategyBase.sol';
8+
import {IRewardsController} from './interfaces/IRewardsController.sol';
9+
import {RewardsDataTypes} from './libraries/RewardsDataTypes.sol';
10+
11+
/**
12+
* @title EmissionManager
13+
* @author Aave
14+
* @notice It manages the list of admins of reward emissions and provides functions to control reward emissions.
15+
*/
16+
contract EmissionManager is Ownable, IEmissionManager {
17+
// reward => emissionAdmin
18+
mapping(address => address) internal _emissionAdmins;
19+
20+
IRewardsController internal _rewardsController;
21+
22+
/**
23+
* @dev Only emission admin of the given reward can call functions marked by this modifier.
24+
**/
25+
modifier onlyEmissionAdmin(address reward) {
26+
require(msg.sender == _emissionAdmins[reward], 'ONLY_EMISSION_ADMIN');
27+
_;
28+
}
29+
30+
/**
31+
* Constructor.
32+
* @param controller The address of the RewardsController contract
33+
*/
34+
constructor(address controller) {
35+
_rewardsController = IRewardsController(controller);
36+
}
37+
38+
/// @inheritdoc IEmissionManager
39+
function configureAssets(RewardsDataTypes.RewardsConfigInput[] memory config) external override {
40+
for (uint256 i = 0; i < config.length; i++) {
41+
require(_emissionAdmins[config[i].reward] == msg.sender, 'ONLY_EMISSION_ADMIN');
42+
}
43+
_rewardsController.configureAssets(config);
44+
}
45+
46+
/// @inheritdoc IEmissionManager
47+
function setTransferStrategy(address reward, ITransferStrategyBase transferStrategy)
48+
external
49+
override
50+
onlyEmissionAdmin(reward)
51+
{
52+
_rewardsController.setTransferStrategy(reward, transferStrategy);
53+
}
54+
55+
/// @inheritdoc IEmissionManager
56+
function setRewardOracle(address reward, IEACAggregatorProxy rewardOracle)
57+
external
58+
override
59+
onlyEmissionAdmin(reward)
60+
{
61+
_rewardsController.setRewardOracle(reward, rewardOracle);
62+
}
63+
64+
/// @inheritdoc IEmissionManager
65+
function setDistributionEnd(
66+
address asset,
67+
address reward,
68+
uint32 newDistributionEnd
69+
) external override onlyEmissionAdmin(reward) {
70+
_rewardsController.setDistributionEnd(asset, reward, newDistributionEnd);
71+
}
72+
73+
/// @inheritdoc IEmissionManager
74+
function setEmissionPerSecond(
75+
address asset,
76+
address[] calldata rewards,
77+
uint88[] calldata newEmissionsPerSecond
78+
) external override {
79+
for (uint256 i = 0; i < rewards.length; i++) {
80+
require(_emissionAdmins[rewards[i]] == msg.sender, 'ONLY_EMISSION_ADMIN');
81+
}
82+
_rewardsController.setEmissionPerSecond(asset, rewards, newEmissionsPerSecond);
83+
}
84+
85+
/// @inheritdoc IEmissionManager
86+
function setClaimer(address user, address claimer) external override onlyOwner {
87+
_rewardsController.setClaimer(user, claimer);
88+
}
89+
90+
/// @inheritdoc IEmissionManager
91+
function setEmissionManager(address emissionManager) external override onlyOwner {
92+
_rewardsController.setEmissionManager(emissionManager);
93+
}
94+
95+
/// @inheritdoc IEmissionManager
96+
function setEmissionAdmin(address reward, address admin) external override onlyOwner {
97+
address oldAdmin = _emissionAdmins[reward];
98+
_emissionAdmins[reward] = admin;
99+
emit EmissionAdminUpdated(reward, oldAdmin, admin);
100+
}
101+
102+
/// @inheritdoc IEmissionManager
103+
function setRewardsController(address controller) external override onlyOwner {
104+
_rewardsController = IRewardsController(controller);
105+
}
106+
107+
/// @inheritdoc IEmissionManager
108+
function getRewardsController() external view override returns (IRewardsController) {
109+
return _rewardsController;
110+
}
111+
112+
/// @inheritdoc IEmissionManager
113+
function getEmissionAdmin(address reward) external view override returns (address) {
114+
return _emissionAdmins[reward];
115+
}
116+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// SPDX-License-Identifier: agpl-3.0
2+
pragma solidity 0.8.10;
3+
4+
import {IEACAggregatorProxy} from '../../misc/interfaces/IEACAggregatorProxy.sol';
5+
import {RewardsDataTypes} from '../libraries/RewardsDataTypes.sol';
6+
import {ITransferStrategyBase} from './ITransferStrategyBase.sol';
7+
import {IRewardsController} from './IRewardsController.sol';
8+
9+
/**
10+
* @title IEmissionManager
11+
* @author Aave
12+
* @notice Defines the basic interface for the Emission Manager
13+
*/
14+
interface IEmissionManager {
15+
/**
16+
* @dev Emitted when the admin of a reward emission is updated.
17+
* @param reward The address of the rewarding token
18+
* @param oldAdmin The address of the old emission admin
19+
* @param newAdmin The address of the new emission admin
20+
*/
21+
event EmissionAdminUpdated(
22+
address indexed reward,
23+
address indexed oldAdmin,
24+
address indexed newAdmin
25+
);
26+
27+
/**
28+
* @dev Configure assets to incentivize with an emission of rewards per second until the end of distribution.
29+
* @dev Only callable by the emission admin of the given rewards
30+
* @param config The assets configuration input, the list of structs contains the following fields:
31+
* uint104 emissionPerSecond: The emission per second following rewards unit decimals.
32+
* uint256 totalSupply: The total supply of the asset to incentivize
33+
* uint40 distributionEnd: The end of the distribution of the incentives for an asset
34+
* address asset: The asset address to incentivize
35+
* address reward: The reward token address
36+
* ITransferStrategy transferStrategy: The TransferStrategy address with the install hook and claim logic.
37+
* IEACAggregatorProxy rewardOracle: The Price Oracle of a reward to visualize the incentives at the UI Frontend.
38+
* Must follow Chainlink Aggregator IEACAggregatorProxy interface to be compatible.
39+
*/
40+
function configureAssets(RewardsDataTypes.RewardsConfigInput[] memory config) external;
41+
42+
/**
43+
* @dev Sets a TransferStrategy logic contract that determines the logic of the rewards transfer
44+
* @dev Only callable by the emission admin of the given reward
45+
* @param reward The address of the reward token
46+
* @param transferStrategy The address of the TransferStrategy logic contract
47+
*/
48+
function setTransferStrategy(address reward, ITransferStrategyBase transferStrategy) external;
49+
50+
/**
51+
* @dev Sets an Aave Oracle contract to enforce rewards with a source of value.
52+
* @dev Only callable by the emission admin of the given reward
53+
* @notice At the moment of reward configuration, the Incentives Controller performs
54+
* a check to see if the reward asset oracle is compatible with IEACAggregator proxy.
55+
* This check is enforced for integrators to be able to show incentives at
56+
* the current Aave UI without the need to setup an external price registry
57+
* @param reward The address of the reward to set the price aggregator
58+
* @param rewardOracle The address of price aggregator that follows IEACAggregatorProxy interface
59+
*/
60+
function setRewardOracle(address reward, IEACAggregatorProxy rewardOracle) external;
61+
62+
/**
63+
* @dev Sets the end date for the distribution
64+
* @dev Only callable by the emission admin of the given reward
65+
* @param asset The asset to incentivize
66+
* @param reward The reward token that incentives the asset
67+
* @param newDistributionEnd The end date of the incentivization, in unix time format
68+
**/
69+
function setDistributionEnd(
70+
address asset,
71+
address reward,
72+
uint32 newDistributionEnd
73+
) external;
74+
75+
/**
76+
* @dev Sets the emission per second of a set of reward distributions
77+
* @param asset The asset is being incentivized
78+
* @param rewards List of reward addresses are being distributed
79+
* @param newEmissionsPerSecond List of new reward emissions per second
80+
*/
81+
function setEmissionPerSecond(
82+
address asset,
83+
address[] calldata rewards,
84+
uint88[] calldata newEmissionsPerSecond
85+
) external;
86+
87+
/**
88+
* @dev Whitelists an address to claim the rewards on behalf of another address
89+
* @dev Only callable by the owner of the EmissionManager
90+
* @param user The address of the user
91+
* @param claimer The address of the claimer
92+
*/
93+
function setClaimer(address user, address claimer) external;
94+
95+
/**
96+
* @dev Updates the address of the emission manager
97+
* @dev Only callable by the owner of the EmissionManager
98+
* @param emissionManager The address of the new EmissionManager
99+
*/
100+
function setEmissionManager(address emissionManager) external;
101+
102+
/**
103+
* @dev Updates the admin of the reward emission
104+
* @dev Only callable by the owner of the EmissionManager
105+
* @param reward The address of the reward token
106+
* @param admin The address of the new admin of the emission
107+
*/
108+
function setEmissionAdmin(address reward, address admin) external;
109+
110+
/**
111+
* @dev Updates the address of the rewards controller
112+
* @dev Only callable by the owner of the EmissionManager
113+
* @param controller the address of the RewardsController contract
114+
*/
115+
function setRewardsController(address controller) external;
116+
117+
/**
118+
* @dev Returns the rewards controller address
119+
* @return The address of the RewardsController contract
120+
*/
121+
function getRewardsController() external view returns (IRewardsController);
122+
123+
/**
124+
* @dev Returns the admin of the given reward emission
125+
* @param reward The address of the reward token
126+
* @return The address of the emission admin
127+
*/
128+
function getEmissionAdmin(address reward) external view returns (address);
129+
}

test/helpers/make-suite.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import {
5353
} from '@aave/deploy-v3';
5454
import { deployATokenMock } from '../rewards/helpers/deploy';
5555
import { parseEther } from 'ethers/lib/utils';
56+
import { EmissionManager, EmissionManager__factory } from '../../types';
5657

5758
chai.use(bignumberChai());
5859
chai.use(solidity);
@@ -84,6 +85,7 @@ export interface TestEnv {
8485
addressesProvider: PoolAddressesProvider;
8586
registry: PoolAddressesProviderRegistry;
8687
wethGateway: WETHGateway;
88+
emissionManager: EmissionManager;
8789
rewardsController: RewardsController;
8890
rewardsVault: SignerWithAddress;
8991
stakedAave: StakedAaveV3;
@@ -129,6 +131,7 @@ const testEnv: TestEnv = {
129131
addressesProvider: {} as PoolAddressesProvider,
130132
registry: {} as PoolAddressesProviderRegistry,
131133
wethGateway: {} as WETHGateway,
134+
emissionManager: {} as EmissionManager,
132135
rewardsController: {} as RewardsController,
133136
rewardsVault: {} as SignerWithAddress,
134137
stakedAave: {} as StakedAaveV3,
@@ -237,6 +240,9 @@ export async function initializeMakeSuite() {
237240
const rewardTokens = await getSubTokensByPrefix(TESTNET_REWARD_TOKEN_PREFIX);
238241
const rewardsController = ((await getIncentivesV2()) as any) as RewardsController;
239242
testEnv.rewardsController = rewardsController;
243+
testEnv.emissionManager = await new EmissionManager__factory(deployer.signer).deploy(
244+
rewardsController.address
245+
);
240246
testEnv.rewardsVault = rewardsVault;
241247
testEnv.stakedAave = await getStakeAave();
242248
testEnv.aaveToken = testEnv.aave;

0 commit comments

Comments
 (0)