Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion script/configs/devnet/deploy_from_scratch.anvil.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
"calculation_interval_seconds": 604800,
"global_operator_commission_bips": 1000,
"OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP": 1720656000,
"OPERATOR_SET_MAX_RETROACTIVE_LENGTH": 2592000
"OPERATOR_SET_MAX_RETROACTIVE_LENGTH": 2592000,
"emissions_controller_address": "0x0000000000000000000000000000000000000000"
},
"ethPOSDepositAddress": "0x00000000219ab540356cBB839Cbe05303d7705Fa",
"semver": "v1.0.3"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
"calculation_interval_seconds": 604800,
"global_operator_commission_bips": 1000,
"OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP": 1720656000,
"OPERATOR_SET_MAX_RETROACTIVE_LENGTH": 2592000
"OPERATOR_SET_MAX_RETROACTIVE_LENGTH": 2592000,
"emissions_controller_address": "0x0000000000000000000000000000000000000000"
},
"ethPOSDepositAddress": "0x00000000219ab540356cBB839Cbe05303d7705Fa",
"semver": "v0.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"calculation_interval_seconds": 604800,
"global_operator_commission_bips": 1000,
"OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP": 1720656000,
"OPERATOR_SET_MAX_RETROACTIVE_LENGTH": 2592000
"OPERATOR_SET_MAX_RETROACTIVE_LENGTH": 2592000,
"emissions_controller_address": "0x0000000000000000000000000000000000000000"
},
"allocationManager": {
"init_paused_status": 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@
"calculation_interval_seconds": 604800,
"global_operator_commission_bips": 1000,
"OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP": 1720656000,
"OPERATOR_SET_MAX_RETROACTIVE_LENGTH": 2592000
"OPERATOR_SET_MAX_RETROACTIVE_LENGTH": 2592000,
"emissions_controller_address": "0x0000000000000000000000000000000000000000"
},
"addresses": {
"token": {
Expand Down
6 changes: 6 additions & 0 deletions script/deploy/devnet/deploy_from_scratch.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ contract DeployFromScratch is Script, Test {
uint32 REWARDS_COORDINATOR_OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP;
uint32 REWARDS_COORDINATOR_OPERATOR_SET_MAX_RETROACTIVE_LENGTH;

address REWARDS_COORDINATOR_EMISSIONS_CONTROLLER;

// AllocationManager
uint256 ALLOCATION_MANAGER_INIT_PAUSED_STATUS;

Expand Down Expand Up @@ -172,6 +174,9 @@ contract DeployFromScratch is Script, Test {
require(executorMultisig != address(0), "executorMultisig address not configured correctly!");
require(operationsMultisig != address(0), "operationsMultisig address not configured correctly!");

REWARDS_COORDINATOR_EMISSIONS_CONTROLLER =
stdJson.readAddress(config_data, ".rewardsCoordinator.emissions_controller_address");

// START RECORDING TRANSACTIONS FOR DEPLOYMENT
vm.startBroadcast();

Expand Down Expand Up @@ -249,6 +254,7 @@ contract DeployFromScratch is Script, Test {
delegation,
strategyManager,
IAllocationManager(address(allocationManager)),
IEmissionsController(address(REWARDS_COORDINATOR_EMISSIONS_CONTROLLER)),
eigenLayerPauserReg,
permissionController,
REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS,
Expand Down
6 changes: 6 additions & 0 deletions script/deploy/local/deploy_from_scratch.slashing.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ contract DeployFromScratch is Script, Test {
uint32 REWARDS_COORDINATOR_OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP;
uint32 REWARDS_COORDINATOR_OPERATOR_SET_MAX_RETROACTIVE_LENGTH;

address REWARDS_COORDINATOR_EMISSIONS_CONTROLLER;

// AllocationManager
uint256 ALLOCATION_MANAGER_INIT_PAUSED_STATUS;

Expand Down Expand Up @@ -160,6 +162,9 @@ contract DeployFromScratch is Script, Test {
REWARDS_COORDINATOR_OPERATOR_SET_MAX_RETROACTIVE_LENGTH =
uint32(stdJson.readUint(config_data, ".rewardsCoordinator.OPERATOR_SET_MAX_RETROACTIVE_LENGTH"));

REWARDS_COORDINATOR_EMISSIONS_CONTROLLER =
stdJson.readAddress(config_data, ".rewardsCoordinator.emissions_controller_address");

STRATEGY_MANAGER_INIT_WITHDRAWAL_DELAY_BLOCKS =
uint32(stdJson.readUint(config_data, ".strategyManager.init_withdrawal_delay_blocks"));

Expand Down Expand Up @@ -258,6 +263,7 @@ contract DeployFromScratch is Script, Test {
delegation,
strategyManager,
IAllocationManager(address(allocationManager)),
IEmissionsController(address(REWARDS_COORDINATOR_EMISSIONS_CONTROLLER)),
eigenLayerPauserReg,
permissionController,
REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS,
Expand Down
1 change: 1 addition & 0 deletions script/releases/CoreContractsDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ abstract contract CoreContractsDeployer is EOADeployer {
delegationManager: Env.proxy.delegationManager(),
strategyManager: Env.proxy.strategyManager(),
allocationManager: Env.proxy.allocationManager(),
emissionsController: Env.proxy.emissionsController(),
pauserRegistry: Env.impl.pauserRegistry(),
permissionController: Env.proxy.permissionController(),
CALCULATION_INTERVAL_SECONDS: Env.CALCULATION_INTERVAL_SECONDS(),
Expand Down
5 changes: 4 additions & 1 deletion src/contracts/core/EmissionsController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,10 @@ contract EmissionsController is
);
} else if (distribution.distributionType == DistributionType.EigenDA) {
success = _tryCallRewardsCoordinator(
abi.encodeCall(IRewardsCoordinator.createAVSRewardsSubmission, (rewardsSubmissions))
abi.encodeCall(
IRewardsCoordinator.createEigenDARewardsSubmission,
(distribution.operatorSet.avs, rewardsSubmissions)
)
);
}
} else {
Expand Down
62 changes: 41 additions & 21 deletions src/contracts/core/RewardsCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ contract RewardsCoordinator is
params.delegationManager,
params.strategyManager,
params.allocationManager,
params.emissionsController,
params.CALCULATION_INTERVAL_SECONDS,
params.MAX_REWARDS_DURATION,
params.MAX_RETROACTIVE_LENGTH,
Expand Down Expand Up @@ -87,30 +88,19 @@ contract RewardsCoordinator is
/// -----------------------------------------------------------------------

/// @inheritdoc IRewardsCoordinator
function createAVSRewardsSubmission(
function createEigenDARewardsSubmission(
address avs,
RewardsSubmission[] calldata rewardsSubmissions
) external onlyWhenNotPaused(PAUSED_AVS_REWARDS_SUBMISSION) nonReentrant {
for (uint256 i = 0; i < rewardsSubmissions.length; i++) {
RewardsSubmission memory rewardsSubmission = rewardsSubmissions[i];

// First validate the submission.
_validateRewardsSubmission(rewardsSubmission);

// Then transfer the full amount to the contract.
rewardsSubmission.token.safeTransferFrom(msg.sender, address(this), rewardsSubmission.amount);

// Then take the protocol fee (if the submitter is opted in for protocol fees).
rewardsSubmission.amount = _takeProtocolFee(msg.sender, rewardsSubmission.token, rewardsSubmission.amount);

// Last update storage.
uint256 nonce = submissionNonce[msg.sender];
bytes32 rewardsSubmissionHash = keccak256(abi.encode(msg.sender, nonce, rewardsSubmission));

isAVSRewardsSubmissionHash[msg.sender][rewardsSubmissionHash] = true;
submissionNonce[msg.sender] = nonce + 1;
require(msg.sender == address(emissionsController), UnauthorizedCaller());
_createAVSRewardsSubmission(avs, rewardsSubmissions);
}

emit AVSRewardsSubmissionCreated(msg.sender, nonce, rewardsSubmissionHash, rewardsSubmission);
}
/// @inheritdoc IRewardsCoordinator
function createAVSRewardsSubmission(
RewardsSubmission[] calldata rewardsSubmissions
) external onlyWhenNotPaused(PAUSED_AVS_REWARDS_SUBMISSION) nonReentrant {
_createAVSRewardsSubmission(msg.sender, rewardsSubmissions);
}

/// @inheritdoc IRewardsCoordinator
Expand Down Expand Up @@ -469,6 +459,36 @@ contract RewardsCoordinator is
/// Internal Helper Functions
/// -----------------------------------------------------------------------

/// @notice Internal helper to create AVS rewards submissions.
/// @param avs The address of the AVS submitting the rewards.
/// @param rewardsSubmissions The RewardsSubmissions to be created.
function _createAVSRewardsSubmission(
address avs,
RewardsSubmission[] calldata rewardsSubmissions
) internal {
for (uint256 i = 0; i < rewardsSubmissions.length; i++) {
RewardsSubmission memory rewardsSubmission = rewardsSubmissions[i];

// First validate the submission.
_validateRewardsSubmission(rewardsSubmission);

// Then transfer the full amount to the contract.
rewardsSubmission.token.safeTransferFrom(avs, address(this), rewardsSubmission.amount);

// Then take the protocol fee (if the submitter is opted in for protocol fees).
rewardsSubmission.amount = _takeProtocolFee(avs, rewardsSubmission.token, rewardsSubmission.amount);

// Last update storage.
uint256 nonce = submissionNonce[avs];
bytes32 rewardsSubmissionHash = keccak256(abi.encode(avs, nonce, rewardsSubmission));

isAVSRewardsSubmissionHash[avs][rewardsSubmissionHash] = true;
submissionNonce[avs] = nonce + 1;

emit AVSRewardsSubmissionCreated(avs, nonce, rewardsSubmissionHash, rewardsSubmission);
}
}

/// @notice Internal helper to process reward claims.
/// @param claim The RewardsMerkleClaims to be processed.
/// @param recipient The address recipient that receives the ERC20 rewards
Expand Down
5 changes: 5 additions & 0 deletions src/contracts/core/storage/RewardsCoordinatorStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ abstract contract RewardsCoordinatorStorage is IRewardsCoordinator {
/// @notice The AllocationManager contract for EigenLayer
IAllocationManager public immutable allocationManager;

/// @notice The RewardsCoordinator contract for EigenLayer
IEmissionsController public immutable emissionsController;

/// @notice The interval in seconds at which the calculation for rewards distribution is done.
/// @dev RewardsSubmission durations must be multiples of this interval. This is going to be configured to 1 day
uint32 public immutable CALCULATION_INTERVAL_SECONDS;
Expand Down Expand Up @@ -150,6 +153,7 @@ abstract contract RewardsCoordinatorStorage is IRewardsCoordinator {
IDelegationManager _delegationManager,
IStrategyManager _strategyManager,
IAllocationManager _allocationManager,
IEmissionsController _emissionsController,
uint32 _CALCULATION_INTERVAL_SECONDS,
uint32 _MAX_REWARDS_DURATION,
uint32 _MAX_RETROACTIVE_LENGTH,
Expand All @@ -163,6 +167,7 @@ abstract contract RewardsCoordinatorStorage is IRewardsCoordinator {
delegationManager = _delegationManager;
strategyManager = _strategyManager;
allocationManager = _allocationManager;
emissionsController = _emissionsController;
CALCULATION_INTERVAL_SECONDS = _CALCULATION_INTERVAL_SECONDS;
MAX_REWARDS_DURATION = _MAX_REWARDS_DURATION;
MAX_RETROACTIVE_LENGTH = _MAX_RETROACTIVE_LENGTH;
Expand Down
2 changes: 2 additions & 0 deletions src/contracts/interfaces/IEmissionsController.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

import "./IAllocationManager.sol";
import "./IRewardsCoordinator.sol";
import "./IEigen.sol";
import "./IBackingEigen.sol";
import "./IPausable.sol";
import {OperatorSet} from "../libraries/OperatorSetLib.sol";

/// @title IEmissionsControllerErrors
/// @notice Errors for the IEmissionsController contract.
Expand Down
17 changes: 17 additions & 0 deletions src/contracts/interfaces/IRewardsCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import "./IStrategyManager.sol";
import "./IPauserRegistry.sol";
import "./IPermissionController.sol";
import "./IStrategy.sol";
import "./IEmissionsController.sol";

interface IRewardsCoordinatorErrors {
/// @dev Thrown when msg.sender is not allowed to call a function
Expand Down Expand Up @@ -244,6 +245,7 @@ interface IRewardsCoordinatorTypes {
IDelegationManager delegationManager;
IStrategyManager strategyManager;
IAllocationManager allocationManager;
IEmissionsController emissionsController;
IPauserRegistry pauserRegistry;
IPermissionController permissionController;
uint32 CALCULATION_INTERVAL_SECONDS;
Expand Down Expand Up @@ -446,6 +448,21 @@ interface IRewardsCoordinator is IRewardsCoordinatorErrors, IRewardsCoordinatorE
address _feeRecipient
) external;

/// @notice Creates a new rewards submission on behalf of the Eigen DA AVS, to be split amongst the
/// set of stakers delegated to operators who are registered to the `avs`
/// @param rewardsSubmissions The rewards submissions being created
/// @dev Expected to be called by the EmissionsController behalf of the Eigen DA AVS of which the submission is being made
/// @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION`
/// @dev The duration of the `rewardsSubmission` cannot be 0 and must be a multiple of `CALCULATION_INTERVAL_SECONDS`
/// @dev The tokens are sent to the `RewardsCoordinator` contract
/// @dev Strategies must be in ascending order of addresses to check for duplicates
/// @dev This function will revert if the `rewardsSubmission` is malformed,
/// e.g. if the `strategies` and `weights` arrays are of non-equal lengths
function createEigenDARewardsSubmission(
address avs,
RewardsSubmission[] calldata rewardsSubmissions
) external;

/// @notice Creates a new rewards submission on behalf of an AVS, to be split amongst the
/// set of stakers delegated to operators who are registered to the `avs`
/// @param rewardsSubmissions The rewards submissions being created
Expand Down
1 change: 1 addition & 0 deletions src/test/integration/IntegrationDeployer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ abstract contract IntegrationDeployer is ExistingDeploymentParser {
delegationManager: delegationManager,
strategyManager: strategyManager,
allocationManager: allocationManager,
emissionsController: emissionsController,
pauserRegistry: eigenLayerPauserReg,
permissionController: permissionController,
CALCULATION_INTERVAL_SECONDS: REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS,
Expand Down
44 changes: 44 additions & 0 deletions src/test/unit/RewardsCoordinatorUnit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ contract RewardsCoordinatorUnitTests is EigenLayerUnitTestSetup, IRewardsCoordin
delegationManager: IDelegationManager(address(delegationManagerMock)),
strategyManager: IStrategyManager(address(strategyManagerMock)),
allocationManager: IAllocationManager(address(allocationManagerMock)),
emissionsController: IEmissionsController(address(vm.addr(0x123123ff))),
pauserRegistry: pauserRegistry,
permissionController: IPermissionController(address(permissionController)),
CALCULATION_INTERVAL_SECONDS: CALCULATION_INTERVAL_SECONDS,
Expand Down Expand Up @@ -1548,6 +1549,49 @@ contract RewardsCoordinatorUnitTests_createAVSRewardsSubmission is RewardsCoordi
}
}

contract RewardsCoordinatorUnitTests_createEigenDARewardsSubmission is RewardsCoordinatorUnitTests {
// Revert when not called by emissionsController
function testFuzz_Revert_WhenNotEmissionsController(address unauthorizedCaller) public filterFuzzedAddressInputs(unauthorizedCaller) {
address emissionsController = address(vm.addr(0x123123ff));
cheats.assume(unauthorizedCaller != emissionsController);

cheats.prank(unauthorizedCaller);
cheats.expectRevert(UnauthorizedCaller.selector);
RewardsSubmission[] memory rewardsSubmissions;
rewardsCoordinator.createEigenDARewardsSubmission(address(defaultAVS), rewardsSubmissions);
}

// Test that emissionsController can successfully call
function test_EmissionsControllerCanCall() public {
address emissionsController = address(vm.addr(0x123123ff));
address avs = defaultAVS;

// Setup token and approval - AVS needs to have the tokens since it's the one submitting rewards
IERC20 rewardToken = new ERC20PresetFixedSupply("dog wif hat", "MOCK1", mockTokenInitialSupply, avs);
uint amount = 1e18;

RewardsSubmission[] memory rewardsSubmissions = new RewardsSubmission[](1);
rewardsSubmissions[0] = RewardsSubmission({
strategiesAndMultipliers: defaultStrategyAndMultipliers,
token: rewardToken,
amount: amount,
startTimestamp: uint32(block.timestamp),
duration: CALCULATION_INTERVAL_SECONDS
});

// AVS approves the token transfer
cheats.prank(avs);
rewardToken.approve(address(rewardsCoordinator), amount);

// Call from emissionsController should succeed
cheats.prank(emissionsController);
rewardsCoordinator.createEigenDARewardsSubmission(avs, rewardsSubmissions);

// Verify the rewards submission was created
assertEq(rewardToken.balanceOf(address(rewardsCoordinator)), amount);
}
}

contract RewardsCoordinatorUnitTests_createRewardsForAllSubmission is RewardsCoordinatorUnitTests {
// Revert when paused
function test_Revert_WhenPaused() public {
Expand Down
Loading