Skip to content

feat: incentive council + duration vaults release#1740

Merged
0xClandestine merged 31 commits intomainfrom
release-dev/incentive-council
Mar 5, 2026
Merged

feat: incentive council + duration vaults release#1740
0xClandestine merged 31 commits intomainfrom
release-dev/incentive-council

Conversation

@0xClandestine
Copy link
Copy Markdown
Member

v1.12.0 Incentive Council (ELIP-012)

Release Manager

@0xClandestine

Overview

Core Features

  • EmissionsController: Mints EIGEN at a fixed inflation rate per epoch and distributes via gauge weights (bips 0-10,000)

  • Permissionless Trigger: Anyone can call pressButton() to process epoch emissions—no trusted keeper required

  • 5 Distribution Types:

    • RewardsForAllEarners — Protocol-wide rewards for all delegated stake
    • OperatorSetTotalStake — Rewards proportional to total stake in operator set
    • OperatorSetUniqueStake — Rewards proportional to unique stake allocations
    • EigenDA — Special pathway for EigenDA (pre-OperatorSets AVS)
    • Manual — Off-chain computed distributions sent directly to Incentives Committee
  • Silent Failure Handling: Distribution failures don't block other distributions (except reentrancy/OOG attacks)

  • Protocol Fee Mechanism: Opt-in 20% fee on reward submissions in RewardsCoordinator

    • Disabled by default (backward compatible)
    • Submitters opt-in via setOptInForProtocolFee()
    • Fee recipient configurable by owner

Governance & Roles

  • Protocol Council: Sets Incentives Committee address via setIncentiveCouncil()
  • Incentives Committee:
    • Configure distributions: addDistribution(), updateDistribution()
    • Receive swept tokens via sweep()
  • AVSs: Must grant EmissionsController permission for OperatorSet distributions

Key Design Points

  • Epoch-Based: EIGEN minted once per epoch at EMISSIONS_INFLATION_RATE
  • Future-Only Updates: Distributions can only be added/updated for future epochs
  • Immutable Config: Inflation rate, start time, and epoch length set at deployment
  • Pausable: Both pressButton() and sweep() respect PAUSED_TOKEN_FLOWS flag
  • Missed Epochs Skipped: No accumulation—if pressButton() isn't called during an epoch, those emissions are permanently lost

Changelog

  • feat(incentives): add interface #1678
  • feat(incentives): add implementation #1681
  • feat(incentives): add protocol fee #1691
  • feat(incentives): add deploy scripts #1699
  • fix: internal review changes #1703
  • feat: add EigenDA rewards submission type #1705

Scope

New Contracts:

  • EmissionsController.sol — Main implementation
  • EmissionsControllerStorage.sol — Storage layout
  • IEmissionsController.sol — Interface

Modified Contracts:

  • RewardsCoordinator.sol — Added protocol fee mechanism and createEigenDARewardsSubmission()
  • RewardsCoordinatorStorage.sol — Added PROTOCOL_FEE_BIPS, isOptedInForProtocolFee, feeRecipient, emissionsController

0xrajath and others added 30 commits January 14, 2026 11:09
**Motivation:**

We need to support Rewards v2.2 :
- Rewards that are linear to allocated unique stake (Needs to be both
retroactive and future-looking).
- Rewards that are linear to total stake (Needs to be both retroactive
and future-looking).

TDD:
https://www.notion.so/eigen-labs/PRD-Rewards-v2-2-Operator-Set-Rewards-with-Unique-Total-Stake-28513c11c3e080cbb856e41ddc6362f5

**Modifications:**

Updates in `RewardsCoordinator`:
- New `createUniqueStakeRewardsSubmission` function with
`UniqueStakeRewardsSubmissionCreated` event emission
- New `PAUSED_UNIQUE_STAKE_REWARDS_SUBMISSION` constant
- New `isUniqueStakeRewardsSubmissionHash` mapping
- New `createTotalStakeRewardsSubmission` function with
`TotalStakeRewardsSubmissionCreated` event emission
- New `PAUSED_TOTAL_STAKE_REWARDS_SUBMISSION` constant
- New `isTotalStakeRewardsSubmissionHash` mapping
- Updated storage gap to 33 slots (from 35 slots)

Updated Bindings

**Result:**

Support for Rewards v2.2
**Motivation:**

We need approval on initial design of the upcoming incentives council
feature release. Historically, we've used
[EigenHopper](https://github.com/Layr-Labs/EigenHopper/tree/master) for
token emissions. Now we're looking to make some improvements.

**Modifications:**

- Drafted a consolidated interface that combines the two `EigenHopper`
contracts into a single new contract.

**Result:**

Single consolidated interface with hopper functionality and proposed
ELIP-012 functionality.
**Motivation:**

We need to implement the ELIP-012 proposed implementation.

**Modifications:**

- Adds new `EmissionsController`.

**Result:**

New contract with owner who sets incentive council, an address
privileged with configuring EIGEN emissions.
**Motivation:**

We want to add a configurable protocol fee mechanism to the
RewardsCoordinator contract to enable revenue generation. This allows
reward submitters to opt into paying fees while maintaining backward
compatibility with existing integrations.

**Modifications:**

- Added protocolFee (fixed constant amount) and feeRecipient state
variables
- Implemented setOptInProtocolFee() for submitters to opt into fee
payments
- Implemented setFeeRecipient() (owner-only) to set the fee destination
address

**Result:**

Submitters can voluntarily enable protocol fees on their reward
distributions. When opted in, the fixed fee amount is deducted and sent
to the designated recipient. Existing integrations continue working
unchanged since fees are disabled by default.
**Motivation:**

This upgrade improves programmatic incentives for the EigenLayer
protocol through a new EmissionsController that gives the Incentive
Council flexible control over EIGEN token emissions and distribution. It
also adds fee functionality to the RewardsCoordinator to support
protocol fees.

**Modifications:**

- Deploy EmissionsController implementation and proxy, plus new
RewardsCoordinator implementation with fee support.
- Upgrade and initialize EmissionsController proxy.
- Upgrade and reinitialize RewardsCoordinator with new feeRecipient
parameter.
- Register EmissionsController in ProtocolRegistry.
- Transfer minting rights from old hopper to EmissionsController.
- Complete the upgrade after timelock delay and validate all changes.

**Result:**

EmissionsController becomes the sole bEIGEN minter, and
RewardsCoordinator has opt-in fees.

---------

Co-authored-by: Rajath Alex <rajathalex@gmail.com>
**Motivation:**

*Explain here the context, and why you're making that change. What is
the problem you're trying to solve.*

**Modifications:**

*Describe the modifications you've done.*

**Result:**

*After your change, what will change.*
**Motivation:**

The `RewardsCoordinator` needs to support a special pathway for EigenDA
rewards submissions that can be initiated by the EmissionsController.
Currently, all AVS rewards submissions go through the same
`createAVSRewardsSubmission` function which doesn't distinguish between
different reward submission sources or allow for privileged callers.
This creates a need for a dedicated mechanism that allows the
EmissionsController to submit EigenDA rewards on behalf of the EigenDA
AVS.

**Modifications:**

- Added `emissionsController` as an immutable storage variable in
`RewardsCoordinatorStorage`
- Created new `createEigenDARewardsSubmission` function that accepts an
AVS address parameter and can only be called by the EmissionsController
- Refactored existing `createAVSRewardsSubmission` to use a new internal
helper function `_createAVSRewardsSubmission` to avoid code duplication
- Updated `EmissionsController` to call `createEigenDARewardsSubmission`
instead of `createAVSRewardsSubmission` for EigenDA distribution type

**Result:**

The `RewardsCoordinator` now has a dedicated pathway for EigenDA rewards
submissions from the EmissionsController.
**Motivation:**

We need E2E tests to assert the new `EmissionsController` and
`RewardsCoordinator` changes work together as expected.

**Modifications:**

- Improved existing EC integration state checks (which improves every
existing test).

**Result:**

Full E2E coverage of our new changes.
**Motivation:**

Address findings from the Certora security audit of the Incentive
Council implementation (January 2026).

**Modifications:**

- **H-01 Fix**: Modified `_createAVSRewardsSubmission` to transfer
tokens from `msg.sender` instead of `avs` parameter, ensuring
EmissionsController correctly sources funds for EigenDA rewards
distributions
- **M-02 Fix**: Simplified `_takeProtocolFee` to consistently check
`isOptedInForProtocolFee[msg.sender]` across all submission types,
removing logic inconsistency in fee opt-in checks
- **L-01 Fix**: Standardized `MAX_REWARDS_AMOUNT` validation to check
pre-fee amounts in `_validateOperatorDirectedRewardsSubmission`,
ensuring consistent 1e38-1 cap enforcement
- **I-02 & I-04 Fix**: Updated natspec documentation in
`RewardsCoordinatorStorage` and `IRewardsCoordinator` to reference
correct function names (createRewardsForAllEarners,
createOperatorDirectedOperatorSetRewardsSubmission,
createUniqueStakeRewardsSubmission) and current architecture
(EmissionsController vs deprecated token hopper)
- Enhanced integration tests with improved state checks for
comprehensive E2E coverage

**Result:**

Improved protocol correctness and security.
**Motivation:**

Financial AVS's need stake constraints (duration, quantity) in order to
use EigenLayer.

**Modifications:**

A new type of strategy, `DurationVaultStrategy`, that enforces delegate
stake caps and duration constraints, and registers as an operator. Also
includes modifications to the `StrategyManager` and default strategies,
including hooks before shares are added/removed, to accommodate
alternative stake models.

**Result:**

Financial AVS's will have the tools to build on Eigenlayer more easily.
<!-- 
    🚨 ATTENTION! 🚨 
    
This PR template is REQUIRED. PRs not following this format will be
closed without review.
    
    Requirements:
- PR title must follow commit conventions:
https://www.conventionalcommits.org/en/v1.0.0/
- Label your PR with the correct type (e.g., 🐛 Bug, ✨ Enhancement, 🧪
Test, etc.)
    - Provide clear and specific details in each section
-->

**Motivation:**

use call instead of try/catch

**Modifications:**

*Describe the modifications you've done.*

**Result:**

*After your change, what will change.*
**Motivation:**

*DurationVaultStrategy*  documentation

**Modifications:**

- added durationVaultStrategy documantation

**Result:**
**Motivation:**

*Explain here the context, and why you're making that change. What is
the problem you're trying to solve.*

**Modifications:**

*Describe the modifications you've done.*

**Result:**

*After your change, what will change.*
<!-- 
    🚨 ATTENTION! 🚨 
    
This PR template is REQUIRED. PRs not following this format will be
closed without review.
    
    Requirements:
- PR title must follow commit conventions:
https://www.conventionalcommits.org/en/v1.0.0/
- Label your PR with the correct type (e.g., 🐛 Bug, ✨ Enhancement, 🧪
Test, etc.)
    - Provide clear and specific details in each section
-->

**Motivation:**

*Explain here the context, and why you're making that change. What is
the problem you're trying to solve.*

**Modifications:**

*Describe the modifications you've done.*

**Result:**

*After your change, what will change.*
**Motivation:**

Address findings from the Certora security audit of the Duration Vaults implementation.

**Modifications:**

### Medium Severity

- **M-01 Fix**: Added `setRewardsClaimer(address claimer)` to designate a claimer for operator-attributed rewards, ensuring if any rewards accrued during split activation delay  or via a new rewards type  can be claimed
- **M-02 Fix**: Implemented retry mechanism in `markMatured()` and `advanceToWithdrawals()` - both now check if vault is in WITHDRAWALS state and re-attempt operator cleanup (`_deallocateAll()` and `_deregisterFromOperatorSet()`) if needed
- **M-03 Fix**: Added `updateDelegationApprover(address newDelegationApprover)` calling `DelegationManager.modifyOperatorDetails()` for delegation approver updates
- **M-04 Fix**: Same retry mechanism as M-02 allows subsequent calls to complete cleanup if initial call was gas-griefed

### Informational Severity

- **I-02 Fix**: Added `DeallocateAttempted(bool success)` and `DeregisterAttempted(bool success)` events for off-chain monitoring
- **I-03 Fix**: changed to `DepositExceedsMaxPerDeposit()` error for clearer messaging in `beforeAddShares()`
- **I-04 Fix**: Added strategy support check in `lock()` via `AllocationManager.getStrategiesInOperatorSet()`, reverts with `StrategyNotSupportedByOperatorSet()` if unsupported
- **I-05 Fix**: Added `@param arbitrator` NatSpec to VaultConfig struct
- **I-06 Fix**: Changed `operatorSetRegistered()` to query `AllocationManager.isMemberOfOperatorSet()` directly


**Result:**

Improved protocol correctness and security for Duration Vaults.
<!-- 
    🚨 ATTENTION! 🚨 
    
This PR template is REQUIRED. PRs not following this format will be
closed without review.
    
    Requirements:
- PR title must follow commit conventions:
https://www.conventionalcommits.org/en/v1.0.0/
- Label your PR with the correct type (e.g., 🐛 Bug, ✨ Enhancement, 🧪
Test, etc.)
    - Provide clear and specific details in each section
-->

**Motivation:**

*Explain here the context, and why you're making that change. What is
the problem you're trying to solve.*

**Modifications:**

*Describe the modifications you've done.*

**Result:**

*After your change, what will change.*
**Motivation:**

Some environments are failing due to missing Zeus env parameters, and we
also want to add default distributions in order to maintain the current
emissions schedule as the TokenHopper initially.

**Modifications:**

- Added missing zeus params.
- Added missing check to skip environments that core isn't deployed.

**Result:**

CI is passing, and default distributions are added.
**Motivation:**

Out of date bindings.

**Modifications:**

Updated bindings.

**Result:**

Updated bindings.
…o release-dev/v1.12-combined

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	pkg/bindings/RewardsCoordinator/binding.go
#	script/releases/TestUtils.sol
#	script/releases/v1.11.0-duration-vault/3-completeUpgrade.s.sol
#	script/releases/v1.9.0-slashing-ux-destination/1-deployProtocolRegistryProxy.s.sol
#	src/test/integration/IntegrationDeployer.t.sol
#	src/test/mocks/RewardsCoordinatorMock.sol
…nto single v1.12 release

Delete v1.11.0-duration-vault scripts and fold their deployments/upgrades
into the v1.12.0-incentive-council scripts. The combined release goes from
1.9.0 → 1.12.0, deploying Duration Vault strategies alongside the
Incentive Council EmissionsController in a single upgrade.

Co-authored-by: Cursor <cursoragent@cursor.com>
**Motivation:**

*Explain here the context, and why you're making that change. What is
the problem you're trying to solve.*

**Modifications:**

*Describe the modifications you've done.*

**Result:**

*After your change, what will change.*

---------

Co-authored-by: Cursor Bot <michael.muehl@eigenlabs.org>
@0xClandestine 0xClandestine changed the title feat: incentive council + duration vaults release. feat: incentive council + duration vaults release Mar 5, 2026
@0xClandestine 0xClandestine requested a review from eigenmikem March 5, 2026 20:25
@0xClandestine 0xClandestine merged commit 53d5fc9 into main Mar 5, 2026
21 checks passed
@0xClandestine 0xClandestine deleted the release-dev/incentive-council branch March 5, 2026 20:42
Copy link
Copy Markdown

@certora-run certora-run bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verification Results

  • Group ID: 1ee0b386-41b2-4593-95e6-bbfbde99c20e
  • Commit: 486d005
Job Result VERIFIED VIOLATED Link
strategies/StrategyBase.conf 2 0 Link
pods/EigenPodManagerRules.conf 9 0 Link
permissions/Pausable.conf 2 0 Link
multichain/OperatorTableUpdater.conf 11 0 Link
multichain/KeyRegistrar.conf 13 0 Link
multichain/ECDSACertificateVerifier.conf 7 0 Link
multichain/CrossChainRegistry.conf 21 0 Link
multichain/BN254CertificateVerifier.conf 6 0 Link
core/StrategyManager.conf 5 1 Link
core/DelegationManagerValidState.conf 11 0 Link
core/DelegationManager.conf 9 0 Link
core/AllocationManagerValidState.conf (Rule(s): sumOfPendingDiffCurrentMagnitudeRespectsWAD) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): SetInRegisteredIFFStatusIsTrue) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): registeredSetsInvariant) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): operatorSetsInvariant) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): noZeroKeyInDealocationQ) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): noPositivePendingDiffInDeallocationQ) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): negativePendingDiffAtMostCurrentMagnitude) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): maxMagnitudeMonotonicallyDecreasing) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): maxMagnitudeLeqWAD) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): maxMagnitudeHistoryPastLengthNullified) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): maxMagnitudeHistoryKeysMonotonicInc) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): maxMagnitudeHistoryKeysLessThanCurrentBlock) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): maxMagnitudeGEencumberedMagnitude) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): maxMagnitudeGECurrentMagnitude) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): EndGreaterThenBegin) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): encumberedMagnitudeLeqWAD) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): encumberedMagnitudeEqSumOfCurrentMagnitudesAndPositivePending) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): effectBlockZeroHasNoPendingDiff) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): deallocationQueueEffectBlocLessThanCurrBlockNumberPlushDelayPlusOne) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): deallocationQueueEffectBlockAscendingOrder) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): deallocationQueueDataUniqueness) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): deallocationQueueDataOutOfBoundsAreNullified) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): currentMagnitudeLeqWAD) 2 0 Link
core/AllocationManagerValidState.conf (Rule(s): allocatedSetsInvariant) 2 0 Link
core/AllocationManagerSanity.conf 2 0 Link
core/AllocationManagerOverslashing.conf 4 0 Link
core/AllocationManager.conf 4 0 Link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants