Description:
Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/RewardAllStakersActionGenerator.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import { IHopperActionGenerator } from "./interfaces/IHopperActionGenerator.sol";
import { IRewardsCoordinator, IRewardsCoordinatorTypes } from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";
// We are going to use the standard OZ interfaces and implementations
import "@openzeppelin/contracts/interfaces/IERC20.sol";
/**
* RewardAllStakersActionGenerator
*
* An implementation of the action generator interface that will
* use the RewardsCoordinator::createRewardsForAllEarners() interface.
*
* This implementation gives the deployer the ability to pre-select the
* RewardSubmission array that is passed into that API, with an end timestamp
* that coincides with the end of the last completed rewards
* epoch boundary.
*/
contract RewardAllStakersActionGenerator is IHopperActionGenerator {
// one week in seconds. used in the RewardsCoordinator and as a duration in this contract
uint32 public immutable CALCULATION_INTERVAL_SECONDS;
// the single RewardsCoordinator contract for EigenLayer
address public immutable rewardsCoordinator;
// the bEIGEN token contract
IERC20 public immutable bEIGEN;
// the EIGEN token contract
IERC20 public immutable EIGEN;
// configuration set at construction, used in RewardsSubmissions
IRewardsCoordinatorTypes.StrategyAndMultiplier[][2] public strategiesAndMultipliers;
uint256[2] public amounts;
// timestamps used for special logic for the first submission
// defines the fixed start time of the first submission
uint32 public firstSubmissionStartTimestamp;
// defines the time period after which the special first submission logic will cease
uint256 public firstSubmissionTriggerCutoff;
constructor(
address _rewardsCoordinator,
uint32 _firstSubmissionStartTimestamp,
uint256 _firstSubmissionTriggerCutoff,
uint256[2] memory _amounts,
IRewardsCoordinatorTypes.StrategyAndMultiplier[][2] memory _strategiesAndMultipliers,
IERC20 _bEIGEN,
IERC20 _EIGEN
)
{
require(address(_rewardsCoordinator) != address(0),
"RewardAllStakersActionGenerator: rewardsCoordinator cannot be zero address");
require(address(_bEIGEN) != address(0),
"RewardAllStakersActionGenerator: bEIGEN cannot be zero address");
require(address(_EIGEN) != address(0),
"RewardAllStakersActionGenerator: EIGEN cannot be zero address");
CALCULATION_INTERVAL_SECONDS = 1 weeks;
// RewardsSubmissions must start at a multiple of CALCULATION_INTERVAL_SECONDS
require(_firstSubmissionStartTimestamp % CALCULATION_INTERVAL_SECONDS == 0,
"RewardAllStakersActionGenerator: RewardsSubmissions must start at a multiple of CALCULATION_INTERVAL_SECONDS");
rewardsCoordinator = _rewardsCoordinator;
firstSubmissionStartTimestamp = _firstSubmissionStartTimestamp;
firstSubmissionTriggerCutoff = _firstSubmissionTriggerCutoff;
amounts = _amounts;
for (uint256 i = 0; i < _strategiesAndMultipliers.length; ++i) {
require(_strategiesAndMultipliers[i].length != 0,
"RewardAllStakersActionGenerator: empty strategies array not allowed");
address currAddress = address(0);
for (uint256 j = 0; j < _strategiesAndMultipliers[i].length; ++j) {
require(
currAddress < address(_strategiesAndMultipliers[i][j].strategy),
"RewardAllStakersActionGenerator: strategies must be in ascending order for submission"
);
currAddress = address(_strategiesAndMultipliers[i][j].strategy);
strategiesAndMultipliers[i].push(
IRewardsCoordinatorTypes.StrategyAndMultiplier({
strategy: _strategiesAndMultipliers[i][j].strategy,
multiplier: _strategiesAndMultipliers[i][j].multiplier
})
);
}
}
bEIGEN = _bEIGEN;
EIGEN = _EIGEN;
}
function generateHopperActions(address hopper, address /*hopperToken*/) external view returns (HopperAction[] memory) {
HopperAction[] memory actions = new HopperAction[](5);
uint256 totalAmount;
uint32 startTimestamp;
uint32 duration;
uint256[2] memory amountsToUse;
require(uint32(block.timestamp) >= firstSubmissionStartTimestamp,
"RewardAllStakersActionGenerator: block.timestamp < firstSubmissionStartTimestamp");
// special logic for first submission
if (block.timestamp < firstSubmissionTriggerCutoff) {
uint32 multiple = (uint32(block.timestamp) - firstSubmissionStartTimestamp) / CALCULATION_INTERVAL_SECONDS + 1;
duration = CALCULATION_INTERVAL_SECONDS * multiple;
startTimestamp = firstSubmissionStartTimestamp;
amountsToUse[0] = amounts[0] * multiple;
amountsToUse[1] = amounts[1] * multiple;
// normal logic for all others
} else {
duration = CALCULATION_INTERVAL_SECONDS;
// find the correct startTimestamp.
// RewardsSubmissions must start at a multiple of CALCULATION_INTERVAL_SECONDS
uint32 calculationIntervalNumber = uint32(block.timestamp) / CALCULATION_INTERVAL_SECONDS;
// round to the latest completed calculation interval to find the start
startTimestamp = (calculationIntervalNumber * CALCULATION_INTERVAL_SECONDS);
amountsToUse[0] = amounts[0];
amountsToUse[1] = amounts[1];
}
// HopperAction memory rewardsSubmissions;
// rewardsSubmissions.target = rewardsCoordinator;
IRewardsCoordinatorTypes.RewardsSubmission[] memory rewardsSubmissions = new IRewardsCoordinatorTypes.RewardsSubmission[](2);
for (uint256 i = 0; i < 2; ++i) {
rewardsSubmissions[i] = IRewardsCoordinatorTypes.RewardsSubmission({
strategiesAndMultipliers: strategiesAndMultipliers[i],
token: EIGEN,
amount: amountsToUse[i],
startTimestamp: startTimestamp,
duration: duration
});
totalAmount += amountsToUse[i];
}
// 0) mint new tokens
actions[0] = HopperAction({
target: address(bEIGEN),
callData: abi.encodeWithSignature("mint(address,uint256)", hopper, totalAmount)
});
// 1) approve the bEIGEN token for transfer so it can be wrapped
actions[1] = HopperAction({
target: address(bEIGEN),
callData: abi.encodeWithSelector(IERC20.approve.selector, address(EIGEN), totalAmount)
});
// 2) wrap the bEIGEN token to receive EIGEN
actions[2] = HopperAction({
target: address(EIGEN),
callData: abi.encodeWithSignature("wrap(uint256)", totalAmount)
});
// 3) Set the proper aggregate allowance on the coordinator for the hopper
actions[3] = HopperAction({
target: address(EIGEN),
callData: abi.encodeWithSelector(IERC20.approve.selector, rewardsCoordinator, totalAmount)
});
// 4) Call the reward coordinator's ForAll API, serializing the submission array as calldata.
actions[4] = HopperAction({
target: address(rewardsCoordinator),
callData: abi.encodeWithSelector(IRewardsCoordinator.createRewardsForAllEarners.selector, rewardsSubmissions)
});
// return array of hopper actions
return actions;
}
}
"
},
"src/interfaces/IHopperActionGenerator.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
/**
* IHopperActionGenerator
*
* A permissionless interface component that encapsulates the runtime
* logic for generating hopper actions. A hopper owner must configure a valid
* IHopperActionGenerator when loading a hopper, and any internal logic to the
* production of the actions are within this contract.
*
*/
interface IHopperActionGenerator {
/**
* HopperAction
*
* Represents an action that the hopper can do, *acting as itself* in an un-delegated way.
* An action is specified by the target contract address, along with its call data.
* The call data is the ABI encoded 4-byte function selector followed by the serialized
* parameters of it's methods.
*
* The hopper's design does not support delegated calls, message values (ETH transfers)
* nor the ability to understand, store, or otherwise use any return values.
*
* A hopper can be programmed *once* with a set of actions that are to be executed
* for *each* initiation of the hopper's behavior.
*
* WARNING: If for any reason any of a hopper's actions revert during execution the
* hopper could be "attacked," "bricked," or otherwise rendered inoperable
* until the expiration period, depending on the trust model with the target
* contracts.
*/
struct HopperAction {
address target;
bytes callData;
}
/**
* generateHopperActions()
*
* Hoppers can call this function to generate a list of hopper actions, given its logic.
* This method takes a hopper address instead of assuming the calling function is always the hopper itself,
* which also enables proper "simulation" as well.
*
* This interface purposefully does not take the full hopper configuration because it should be
* considered stateless or otherwise immutable logic for trustless operation.
*
* @param hopper the address of the ITokenHopper you want to generate actions for.
* @param hopperToken the contract address of the token that is loaded into the hopper.
*
* @return a list of hopper actions that are presumably to be executed by the hopper in the same transaction.
*/
function generateHopperActions(address hopper, address hopperToken) external view returns(HopperAction[] memory);
}
"
},
"lib/eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../libraries/OperatorSetLib.sol";
import "./IPauserRegistry.sol";
import "./IStrategy.sol";
interface IRewardsCoordinatorErrors {
/// @dev Thrown when msg.sender is not allowed to call a function
error UnauthorizedCaller();
/// @dev Thrown when a earner not an AVS or Operator
error InvalidEarner();
/// Invalid Inputs
/// @dev Thrown when an input address is zero
error InvalidAddressZero();
/// @dev Thrown when an invalid root is provided.
error InvalidRoot();
/// @dev Thrown when an invalid root index is provided.
error InvalidRootIndex();
/// @dev Thrown when input arrays length is zero.
error InputArrayLengthZero();
/// @dev Thrown when two array parameters have mismatching lengths.
error InputArrayLengthMismatch();
/// @dev Thrown when provided root is not for new calculated period.
error NewRootMustBeForNewCalculatedPeriod();
/// @dev Thrown when rewards end timestamp has not elapsed.
error RewardsEndTimestampNotElapsed();
/// @dev Thrown when an invalid operator set is provided.
error InvalidOperatorSet();
/// Rewards Submissions
/// @dev Thrown when input `amount` is zero.
error AmountIsZero();
/// @dev Thrown when input `amount` exceeds maximum.
error AmountExceedsMax();
/// @dev Thrown when input `split` exceeds `ONE_HUNDRED_IN_BIPS`
error SplitExceedsMax();
/// @dev Thrown when an operator attempts to set a split before the previous one becomes active
error PreviousSplitPending();
/// @dev Thrown when input `duration` exceeds maximum.
error DurationExceedsMax();
/// @dev Thrown when input `duration` is not evenly divisble by CALCULATION_INTERVAL_SECONDS.
error InvalidDurationRemainder();
/// @dev Thrown when GENESIS_REWARDS_TIMESTAMP is not evenly divisble by CALCULATION_INTERVAL_SECONDS.
error InvalidGenesisRewardsTimestampRemainder();
/// @dev Thrown when CALCULATION_INTERVAL_SECONDS is not evenly divisble by SNAPSHOT_CADENCE.
error InvalidCalculationIntervalSecondsRemainder();
/// @dev Thrown when `startTimestamp` is not evenly divisble by CALCULATION_INTERVAL_SECONDS.
error InvalidStartTimestampRemainder();
/// @dev Thrown when `startTimestamp` is too far in the future.
error StartTimestampTooFarInFuture();
/// @dev Thrown when `startTimestamp` is too far in the past.
error StartTimestampTooFarInPast();
/// @dev Thrown when an attempt to use a non-whitelisted strategy is made.
error StrategyNotWhitelisted();
/// @dev Thrown when `strategies` is not sorted in ascending order.
error StrategiesNotInAscendingOrder();
/// @dev Thrown when `operators` are not sorted in ascending order
error OperatorsNotInAscendingOrder();
/// @dev Thrown when an operator-directed rewards submission is not retroactive
error SubmissionNotRetroactive();
/// Claims
/// @dev Thrown when an invalid earner claim proof is provided.
error InvalidClaimProof();
/// @dev Thrown when an invalid token leaf index is provided.
error InvalidTokenLeafIndex();
/// @dev Thrown when an invalid earner leaf index is provided.
error InvalidEarnerLeafIndex();
/// @dev Thrown when cummulative earnings are not greater than cummulative claimed.
error EarningsNotGreaterThanClaimed();
/// Reward Root Checks
/// @dev Thrown if a root has already been disabled.
error RootDisabled();
/// @dev Thrown if a root has not been activated yet.
error RootNotActivated();
/// @dev Thrown if a root has already been activated.
error RootAlreadyActivated();
}
interface IRewardsCoordinatorTypes {
/**
* @notice A linear combination of strategies and multipliers for AVSs to weigh
* EigenLayer strategies.
* @param strategy The EigenLayer strategy to be used for the rewards submission
* @param multiplier The weight of the strategy in the rewards submission
*/
struct StrategyAndMultiplier {
IStrategy strategy;
uint96 multiplier;
}
/**
* @notice A reward struct for an operator
* @param operator The operator to be rewarded
* @param amount The reward amount for the operator
*/
struct OperatorReward {
address operator;
uint256 amount;
}
/**
* @notice A split struct for an Operator
* @param oldSplitBips The old split in basis points. This is the split that is active if `block.timestamp < activatedAt`
* @param newSplitBips The new split in basis points. This is the split that is active if `block.timestamp >= activatedAt`
* @param activatedAt The timestamp at which the split will be activated
*/
struct OperatorSplit {
uint16 oldSplitBips;
uint16 newSplitBips;
uint32 activatedAt;
}
/**
* Sliding Window for valid RewardsSubmission startTimestamp
*
* Scenario A: GENESIS_REWARDS_TIMESTAMP IS WITHIN RANGE
* <-----MAX_RETROACTIVE_LENGTH-----> t (block.timestamp) <---MAX_FUTURE_LENGTH--->
* <--------------------valid range for startTimestamp------------------------>
* ^
* GENESIS_REWARDS_TIMESTAMP
*
*
* Scenario B: GENESIS_REWARDS_TIMESTAMP IS OUT OF RANGE
* <-----MAX_RETROACTIVE_LENGTH-----> t (block.timestamp) <---MAX_FUTURE_LENGTH--->
* <------------------------valid range for startTimestamp------------------------>
* ^
* GENESIS_REWARDS_TIMESTAMP
* @notice RewardsSubmission struct submitted by AVSs when making rewards for their operators and stakers
* RewardsSubmission can be for a time range within the valid window for startTimestamp and must be within max duration.
* See `createAVSRewardsSubmission()` for more details.
* @param strategiesAndMultipliers The strategies and their relative weights
* cannot have duplicate strategies and need to be sorted in ascending address order
* @param token The rewards token to be distributed
* @param amount The total amount of tokens to be distributed
* @param startTimestamp The timestamp (seconds) at which the submission range is considered for distribution
* could start in the past or in the future but within a valid range. See the diagram above.
* @param duration The duration of the submission range in seconds. Must be <= MAX_REWARDS_DURATION
*/
struct RewardsSubmission {
StrategyAndMultiplier[] strategiesAndMultipliers;
IERC20 token;
uint256 amount;
uint32 startTimestamp;
uint32 duration;
}
/**
* @notice OperatorDirectedRewardsSubmission struct submitted by AVSs when making operator-directed rewards for their operators and stakers.
* @param strategiesAndMultipliers The strategies and their relative weights.
* @param token The rewards token to be distributed.
* @param operatorRewards The rewards for the operators.
* @param startTimestamp The timestamp (seconds) at which the submission range is considered for distribution.
* @param duration The duration of the submission range in seconds.
* @param description Describes what the rewards submission is for.
*/
struct OperatorDirectedRewardsSubmission {
StrategyAndMultiplier[] strategiesAndMultipliers;
IERC20 token;
OperatorReward[] operatorRewards;
uint32 startTimestamp;
uint32 duration;
string description;
}
/**
* @notice A distribution root is a merkle root of the distribution of earnings for a given period.
* The RewardsCoordinator stores all historical distribution roots so that earners can claim their earnings against older roots
* if they wish but the merkle tree contains the cumulative earnings of all earners and tokens for a given period so earners (or their claimers if set)
* only need to claim against the latest root to claim all available earnings.
* @param root The merkle root of the distribution
* @param rewardsCalculationEndTimestamp The timestamp (seconds) until which rewards have been calculated
* @param activatedAt The timestamp (seconds) at which the root can be claimed against
*/
struct DistributionRoot {
bytes32 root;
uint32 rewardsCalculationEndTimestamp;
uint32 activatedAt;
bool disabled;
}
/**
* @notice Internal leaf in the merkle tree for the earner's account leaf
* @param earner The address of the earner
* @param earnerTokenRoot The merkle root of the earner's token subtree
* Each leaf in the earner's token subtree is a TokenTreeMerkleLeaf
*/
struct EarnerTreeMerkleLeaf {
address earner;
bytes32 earnerTokenRoot;
}
/**
* @notice The actual leaves in the distribution merkle tree specifying the token earnings
* for the respective earner's subtree. Each leaf is a claimable amount of a token for an earner.
* @param token The token for which the earnings are being claimed
* @param cumulativeEarnings The cumulative earnings of the earner for the token
*/
struct TokenTreeMerkleLeaf {
IERC20 token;
uint256 cumulativeEarnings;
}
/**
* @notice A claim against a distribution root called by an
* earners claimer (could be the earner themselves). Each token claim will claim the difference
* between the cumulativeEarnings of the earner and the cumulativeClaimed of the claimer.
* Each claim can specify which of the earner's earned tokens they want to claim.
* See `processClaim()` for more details.
* @param rootIndex The index of the root in the list of DistributionRoots
* @param earnerIndex The index of the earner's account root in the merkle tree
* @param earnerTreeProof The proof of the earner's EarnerTreeMerkleLeaf against the merkle root
* @param earnerLeaf The earner's EarnerTreeMerkleLeaf struct, providing the earner address and earnerTokenRoot
* @param tokenIndices The indices of the token leaves in the earner's subtree
* @param tokenTreeProofs The proofs of the token leaves against the earner's earnerTokenRoot
* @param tokenLeaves The token leaves to be claimed
* @dev The merkle tree is structured with the merkle root at the top and EarnerTreeMerkleLeaf as internal leaves
* in the tree. Each earner leaf has its own subtree with TokenTreeMerkleLeaf as leaves in the subtree.
* To prove a claim against a specified rootIndex(which specifies the distributionRoot being used),
* the claim will first verify inclusion of the earner leaf in the tree against _distributionRoots[rootIndex].root.
* Then for each token, it will verify inclusion of the token leaf in the earner's subtree against the earner's earnerTokenRoot.
*/
struct RewardsMerkleClaim {
uint32 rootIndex;
uint32 earnerIndex;
bytes earnerTreeProof;
EarnerTreeMerkleLeaf earnerLeaf;
uint32[] tokenIndices;
bytes[] tokenTreeProofs;
TokenTreeMerkleLeaf[] tokenLeaves;
}
}
interface IRewardsCoordinatorEvents is IRewardsCoordinatorTypes {
/// @notice emitted when an AVS creates a valid RewardsSubmission
event AVSRewardsSubmissionCreated(
address indexed avs,
uint256 indexed submissionNonce,
bytes32 indexed rewardsSubmissionHash,
RewardsSubmission rewardsSubmission
);
/// @notice emitted when a valid RewardsSubmission is created for all stakers by a valid submitter
event RewardsSubmissionForAllCreated(
address indexed submitter,
uint256 indexed submissionNonce,
bytes32 indexed rewardsSubmissionHash,
RewardsSubmission rewardsSubmission
);
/// @notice emitted when a valid RewardsSubmission is created when rewardAllStakersAndOperators is called
event RewardsSubmissionForAllEarnersCreated(
address indexed tokenHopper,
uint256 indexed submissionNonce,
bytes32 indexed rewardsSubmissionHash,
RewardsSubmission rewardsSubmission
);
/**
* @notice Emitted when an AVS creates a valid `OperatorDirectedRewardsSubmission`
* @param caller The address calling `createOperatorDirectedAVSRewardsSubmission`.
* @param avs The avs on behalf of which the operator-directed rewards are being submitted.
* @param operatorDirectedRewardsSubmissionHash Keccak256 hash of (`avs`, `submissionNonce` and `operatorDirectedRewardsSubmission`).
* @param submissionNonce Current nonce of the avs. Used to generate a unique submission hash.
* @param operatorDirectedRewardsSubmission The Operator-Directed Rewards Submission. Contains the token, start timestamp, duration, operator rewards, description and, strategy and multipliers.
*/
event OperatorDirectedAVSRewardsSubmissionCreated(
address indexed caller,
address indexed avs,
bytes32 indexed operatorDirectedRewardsSubmissionHash,
uint256 submissionNonce,
OperatorDirectedRewardsSubmission operatorDirectedRewardsSubmission
);
/**
* @notice Emitted when an AVS creates a valid `OperatorDirectedRewardsSubmission` for an operator set.
* @param caller The address calling `createOperatorDirectedOperatorSetRewardsSubmission`.
* @param operatorDirectedRewardsSubmissionHash Keccak256 hash of (`avs`, `submissionNonce` and `operatorDirectedRewardsSubmission`).
* @param operatorSet The operatorSet on behalf of which the operator-directed rewards are being submitted.
* @param submissionNonce Current nonce of the avs. Used to generate a unique submission hash.
* @param operatorDirectedRewardsSubmission The Operator-Directed Rewards Submission. Contains the token, start timestamp, duration, operator rewards, description and, strategy and multipliers.
*/
event OperatorDirectedOperatorSetRewardsSubmissionCreated(
address indexed caller,
bytes32 indexed operatorDirectedRewardsSubmissionHash,
OperatorSet operatorSet,
uint256 submissionNonce,
OperatorDirectedRewardsSubmission operatorDirectedRewardsSubmission
);
/// @notice rewardsUpdater is responsible for submiting DistributionRoots, only owner can set rewardsUpdater
event RewardsUpdaterSet(address indexed oldRewardsUpdater, address indexed newRewardsUpdater);
event RewardsForAllSubmitterSet(
address indexed rewardsForAllSubmitter, bool indexed oldValue, bool indexed newValue
);
event ActivationDelaySet(uint32 oldActivationDelay, uint32 newActivationDelay);
event DefaultOperatorSplitBipsSet(uint16 oldDefaultOperatorSplitBips, uint16 newDefaultOperatorSplitBips);
/**
* @notice Emitted when the operator split for an AVS is set.
* @param caller The address calling `setOperatorAVSSplit`.
* @param operator The operator on behalf of which the split is being set.
* @param avs The avs for which the split is being set by the operator.
* @param activatedAt The timestamp at which the split will be activated.
* @param oldOperatorAVSSplitBips The old split for the operator for the AVS.
* @param newOperatorAVSSplitBips The new split for the operator for the AVS.
*/
event OperatorAVSSplitBipsSet(
address indexed caller,
address indexed operator,
address indexed avs,
uint32 activatedAt,
uint16 oldOperatorAVSSplitBips,
uint16 newOperatorAVSSplitBips
);
/**
* @notice Emitted when the operator split for Programmatic Incentives is set.
* @param caller The address calling `setOperatorPISplit`.
* @param operator The operator on behalf of which the split is being set.
* @param activatedAt The timestamp at which the split will be activated.
* @param oldOperatorPISplitBips The old split for the operator for Programmatic Incentives.
* @param newOperatorPISplitBips The new split for the operator for Programmatic Incentives.
*/
event OperatorPISplitBipsSet(
address indexed caller,
address indexed operator,
uint32 activatedAt,
uint16 oldOperatorPISplitBips,
uint16 newOperatorPISplitBips
);
/**
* @notice Emitted when the operator split for a given operatorSet is set.
* @param caller The address calling `setOperatorSetSplit`.
* @param operator The operator on behalf of which the split is being set.
* @param operatorSet The operatorSet for which the split is being set.
* @param activatedAt The timestamp at which the split will be activated.
* @param oldOperatorSetSplitBips The old split for the operator for the operatorSet.
* @param newOperatorSetSplitBips The new split for the operator for the operatorSet.
*/
event OperatorSetSplitBipsSet(
address indexed caller,
address indexed operator,
OperatorSet operatorSet,
uint32 activatedAt,
uint16 oldOperatorSetSplitBips,
uint16 newOperatorSetSplitBips
);
event ClaimerForSet(address indexed earner, address indexed oldClaimer, address indexed claimer);
/// @notice rootIndex is the specific array index of the newly created root in the storage array
event DistributionRootSubmitted(
uint32 indexed rootIndex,
bytes32 indexed root,
uint32 indexed rewardsCalculationEndTimestamp,
uint32 activatedAt
);
event DistributionRootDisabled(uint32 indexed rootIndex);
/// @notice root is one of the submitted distribution roots that was claimed against
event RewardsClaimed(
bytes32 root,
address indexed earner,
address indexed claimer,
address indexed recipient,
IERC20 token,
uint256 claimedAmount
);
}
/**
* @title Interface for the `IRewardsCoordinator` contract.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice Allows AVSs to make "Rewards Submissions", which get distributed amongst the AVSs' confirmed
* Operators and the Stakers delegated to those Operators.
* Calculations are performed based on the completed RewardsSubmission, with the results posted in
* a Merkle root against which Stakers & Operators can make claims.
*/
interface IRewardsCoordinator is IRewardsCoordinatorErrors, IRewardsCoordinatorEvents {
/**
* @dev Initializes the addresses of the initial owner, pauser registry, rewardsUpdater and
* configures the initial paused status, activationDelay, and defaultOperatorSplitBips.
*/
function initialize(
address initialOwner,
uint256 initialPausedStatus,
address _rewardsUpdater,
uint32 _activationDelay,
uint16 _defaultSplitBips
) 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
* @dev Expected to be called by the ServiceManager of the AVS on behalf of which the submission is being made
* @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION`
* @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 createAVSRewardsSubmission(
RewardsSubmission[] calldata rewardsSubmissions
) external;
/**
* @notice similar to `createAVSRewardsSubmission` except the rewards are split amongst *all* stakers
* rather than just those delegated to operators who are registered to a single avs and is
* a permissioned call based on isRewardsForAllSubmitter mapping.
* @param rewardsSubmissions The rewards submissions being created
*/
function createRewardsForAllSubmission(
RewardsSubmission[] calldata rewardsSubmissions
) external;
/**
* @notice Creates a new rewards submission for all earners across all AVSs.
* Earners in this case indicating all operators and their delegated stakers. Undelegated stake
* is not rewarded from this RewardsSubmission. This interface is only callable
* by the token hopper contract from the Eigen Foundation
* @param rewardsSubmissions The rewards submissions being created
*/
function createRewardsForAllEarners(
RewardsSubmission[] calldata rewardsSubmissions
) external;
/**
* @notice Creates a new operator-directed rewards submission on behalf of an AVS, to be split amongst the operators and
* set of stakers delegated to operators who are registered to the `avs`.
* @param avs The AVS on behalf of which the reward is being submitted
* @param operatorDirectedRewardsSubmissions The operator-directed rewards submissions being created
* @dev Expected to be called by the ServiceManager of the AVS on behalf of which the submission is being made
* @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION`
* @dev The tokens are sent to the `RewardsCoordinator` contract
* @dev The `RewardsCoordinator` contract needs a token approval of sum of all `operatorRewards` in the `operatorDirectedRewardsSubmissions`, before calling this function.
* @dev Strategies must be in ascending order of addresses to check for duplicates
* @dev Operators must be in ascending order of addresses to check for duplicates.
* @dev This function will revert if the `operatorDirectedRewardsSubmissions` is malformed.
*/
function createOperatorDirectedAVSRewardsSubmission(
address avs,
OperatorDirectedRewardsSubmission[] calldata operatorDirectedRewardsSubmissions
) external;
/**
* @notice Creates a new operator-directed rewards submission for an operator set, to be split amongst the operators and
* set of stakers delegated to operators who are part of the operator set.
* @param operatorSet The operator set for which the rewards are being submitted
* @param operatorDirectedRewardsSubmissions The operator-directed rewards submissions being created
* @dev Expected to be called by the AVS that created the operator set
* @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION`
* @dev The tokens are sent to the `RewardsCoordinator` contract
* @dev The `RewardsCoordinator` contract needs a token approval of sum of all `operatorRewards` in the `operatorDirectedRewardsSubmissions`, before calling this function
* @dev Strategies must be in ascending order of addresses to check for duplicates
* @dev Operators must be in ascending order of addresses to check for duplicates
* @dev This function will revert if the `operatorDirectedRewardsSubmissions` is malformed
*/
function createOperatorDirectedOperatorSetRewardsSubmission(
OperatorSet calldata operatorSet,
OperatorDirectedRewardsSubmission[] calldata operatorDirectedRewardsSubmissions
) external;
/**
* @notice Claim rewards against a given root (read from _distributionRoots[claim.rootIndex]).
* Earnings are cumulative so earners don't have to claim against all distribution roots they have earnings for,
* they can simply claim against the latest root and the contract will calculate the difference between
* their cumulativeEarnings and cumulativeClaimed. This difference is then transferred to recipient address.
* @param claim The RewardsMerkleClaim to be processed.
* Contains the root index, earner, token leaves, and required proofs
* @param recipient The address recipient that receives the ERC20 rewards
* @dev only callable by the valid claimer, that is
* if claimerFor[claim.earner] is address(0) then only the earner can claim, otherwise only
* claimerFor[claim.earner] can claim the rewards.
*/
function processClaim(RewardsMerkleClaim calldata claim, address recipient) external;
/**
* @notice Batch claim rewards against a given root (read from _distributionRoots[claim.rootIndex]).
* Earnings are cumulative so earners don't have to claim against all distribution roots they have earnings for,
* they can simply claim against the latest root and the contract will calculate the difference between
* their cumulativeEarnings and cumulativeClaimed. This difference is then transferred to recipient address.
* @param claims The RewardsMerkleClaims to be processed.
* Contains the root index, earner, token leaves, and required proofs
* @param recipient The address recipient that receives the ERC20 rewards
* @dev only callable by the valid claimer, that is
* if claimerFor[claim.earner] is address(0) then only the earner can claim, otherwise only
* claimerFor[claim.earner] can claim the rewards.
* @dev This function may fail to execute with a large number of claims due to gas limits. Use a smaller array of claims if necessary.
*/
function processClaims(RewardsMerkleClaim[] calldata claims, address recipient) external;
/**
* @notice Creates a new distribution root. activatedAt is set to block.timestamp + activationDelay
* @param root The merkle root of the distribution
* @param rewardsCalculationEndTimestamp The timestamp until which rewards have been calculated
* @dev Only callable by the rewardsUpdater
*/
function submitRoot(bytes32 root, uint32 rewardsCalculationEndTimestamp) external;
/**
* @notice allow the rewardsUpdater to disable/cancel a pending root submission in case of an error
* @param rootIndex The index of the root to be disabled
*/
function disableRoot(
uint32 rootIndex
) external;
/**
* @notice Sets the address of the entity that can call `processClaim` on ehalf of an earner
* @param claimer The address of the entity that can call `processClaim` on behalf of the earner
* @dev Assumes msg.sender is the earner
*/
function setClaimerFor(
address claimer
) external;
/**
* @notice Sets the address of the entity that can call `processClaim` on behalf of an earner
* @param earner The address to set the claimer for
* @param claimer The address of the entity that can call `processClaim` on behalf of the earner
* @dev Only callable by operators or AVSs. We define an AVS that has created at least one
* operatorSet in the `AllocationManager`
*/
function setClaimerFor(address earner, address claimer) external;
/**
* @notice Sets the delay in timestamp before a posted root can be claimed against
* @dev Only callable by the contract owner
* @param _activationDelay The new value for activationDelay
*/
function setActivationDelay(
uint32 _activationDelay
) external;
/**
* @notice Sets the default split for all operators across all avss.
* @param split The default split for all operators across all avss in bips.
* @dev Only callable by the contract owner.
*/
function setDefaultOperatorSplit(
uint16 split
) external;
/**
* @notice Sets the split for a specific operator for a specific avs
* @param operator The operator who is setting the split
* @param avs The avs for which the split is being set by the operator
* @param split The split for the operator for the specific avs in bips.
* @dev Only callable by the operator
* @dev Split has to be between 0 and 10000 bips (inclusive)
* @dev The split will be activated after the activation delay
*/
function setOperatorAVSSplit(address operator, address avs, uint16 split) external;
/**
* @notice Sets the split for a specific operator for Programmatic Incentives.
* @param operator The operator on behalf of which the split is being set.
* @param split The split for the operator for Programmatic Incentives in bips.
* @dev Only callable by the operator
* @dev Split has to be between 0 and 10000 bips (inclusive)
* @dev The split will be activated after the activation delay
*/
function setOperatorPISplit(address operator, uint16 split) external;
/**
* @notice Sets the split for a specific operator for a specific operatorSet.
* @param operator The operator who is setting the split.
* @param operatorSet The operatorSet for which the split is being set by the operator.
* @param split The split for the operator for the specific operatorSet in bips.
* @dev Only callable by the operator
* @dev Split has to be between 0 and 10000 bips (inclusive)
* @dev The split will be activated after the activation delay
*/
function setOperatorSetSplit(address operator, OperatorSet calldata operatorSet, uint16 split) external;
/**
* @notice Sets the permissioned `rewardsUpdater` address which can post new roots
* @dev Only callable by the contract owner
* @param _rewardsUpdater The address of the new rewardsUpdater
*/
function setRewardsUpdater(
address _rewardsUpdater
) external;
/**
* @notice Sets the permissioned `rewardsForAllSubmitter` address which can submit createRewardsForAllSubmission
* @dev Only callable by the contract owner
* @param _submitter The address of the rewardsForAllSubmitter
* @param _newValue The new value for isRewardsForAllSubmitter
*/
function setRewardsForAllSubmitter(address _submitter, bool _newValue) external;
/**
*
* VIEW FUNCTIONS
*
*/
/// @notice Delay in timestamp (seconds) before a posted root can be claimed against
function activationDelay() external view returns (uint32);
/// @notice The timestamp until which RewardsSubmissions have been calculated
function currRewardsCalculationEndTimestamp() external view returns (uint32);
/// @notice Mapping: earner => the address of the entity who can call `processClaim` on behalf of the earner
function claimerFor(
address earner
) external view returns (address);
/// @notice Mapping: claimer => token => total amount claimed
function cumulativeClaimed(address claimer, IERC20 token) external view returns (uint256);
/// @notice the defautl split for all operators across all avss
function defaultOperatorSplitBips() external view returns (uint16);
/// @notice the split for a specific `operator` for a specific `avs`
function getOperatorAVSSplit(address operator, address avs) external view returns (uint16);
/// @notice the split for a specific `operator` for Programmatic Incentives
function getOperatorPISplit(
address operator
) external view returns (uint16);
/// @notice Returns the split for a specific `operator` for a given `operatorSet`
function getOperatorSetSplit(address operator, OperatorSet calldata operatorSet) external view returns (uint16);
/// @notice return the hash of the earner's leaf
function calculateEarnerLeafHash(
EarnerTreeMerkleLeaf calldata leaf
) external pure returns (bytes32);
/// @notice returns the hash of the earner's token leaf
function calculateTokenLeafHash(
TokenTreeMerkleLeaf calldata leaf
) external pure returns (bytes32);
/// @notice returns 'true' if the claim would currently pass the check in `processClaims`
/// but will revert if not valid
function checkClaim(
RewardsMerkleClaim calldata claim
) external view returns (bool);
/// @notice returns the number of distribution roots posted
function getDistributionRootsLength() external view returns (uint256);
/// @notice returns the distributionRoot at the specified index
function getDistributionRootAtIndex(
uint256 index
) external view returns (DistributionRoot memory);
/// @notice returns the current distributionRoot
function getCurrentDistributionRoot() external view returns (DistributionRoot memory);
/// @notice loop through the distribution roots from reverse and get latest root that is not disabled and activated
/// i.e. a root that can be claimed against
function getCurrentClaimableDistributionRoot() external view returns (DistributionRoot memory);
/// @notice loop through distribution roots from reverse and return index from hash
function getRootIndexFromHash(
bytes32 rootHash
) external view returns (uint32);
/// @notice The address of the entity that can update the contract with new merkle roots
function rewardsUpdater() external view returns (address);
/**
* @notice The interval in seconds at which the calculation for a RewardsSubmission distribution is done.
* @dev Rewards Submission durations must be multiples of this interval.
*/
function CALCULATION_INTERVAL_SECONDS() external view returns (uint32);
/// @notice The maximum amount of time (seconds) that a RewardsSubmission can span over
function MAX_REWARDS_DURATION() external view returns (uint32);
/// @notice max amount of time (seconds) that a submission can start in the past
function MAX_RETROACTIVE_LENGTH() external view returns (uint32);
/// @notice max amount of time (seconds) that a submission can start in the future
function MAX_FUTURE_LENGTH() external view returns (uint32);
/// @notice absolute min timestamp (seconds) that a submission can start at
function GENESIS_REWARDS_TIMESTAMP() external view returns (uint32);
}
"
},
"lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/contracts/interfaces/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)
pragma solidity ^0.8.0;
import "../token/ERC20/IERC20.sol";
"
},
"lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
"
},
"lib/eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
/**
* @notice An operator set identified by the AVS address and an identifier
* @param avs The address of the AVS this operator set belongs to
* @param id The unique identifier for the operator set
*/
struct OperatorSet {
address avs;
uint32 id;
}
library OperatorSetLib {
function key(
OperatorSet memory os
) internal pure returns (bytes32) {
return bytes32(abi.encodePacked(os.avs, uint96(os.id)));
}
function decode(
bytes32 _key
) internal pure returns (OperatorSet memory) {
/// forgefmt: disable-next-item
return OperatorSet({
avs: address(uint160(uint256(_key) >> 96)),
id: uint32(uint256(_key) & type(uint96).max)
});
}
}
"
},
"lib/eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
/**
* @title Interface for the `PauserRegistry` contract.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
*/
interface IPauserRegistry {
error OnlyUnpauser();
error InputAddressZero();
event PauserStatusChanged(address pauser, bool canPause);
event UnpauserChanged(address previousUnpauser, address newUnpauser);
/// @notice Mapping of addresses to whether they hold the pauser role.
function isPauser(
address pauser
) external view returns (bool);
/// @notice Unique address that holds the unpauser role. Capable of changing *both* the pauser and unpauser addresses.
function unpauser() external view returns (address);
}
"
},
"lib/eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../libraries/SlashingLib.sol";
interface IStrategyErrors {
/// @dev Thrown when called by an account that is not strategy manager.
error OnlyStrategyManager();
/// @dev Thrown when new shares value is zero.
error NewSharesZero();
/// @dev Thrown when total shares exceeds max.
error TotalSharesExceedsMax();
/// @dev Thrown when amount shares is greater than total shares.
error WithdrawalAmountExceedsTotalDeposits();
/// @dev Thrown when attempting an action with a token that is not accepted.
error OnlyUnderlyingToken();
/// StrategyBaseWithTVLLimits
/// @dev Thrown when `maxPerDeposit` exceeds max.
error MaxPerDepositExceedsMax();
/// @dev Thrown when balance exceeds max total deposits.
error BalanceExceedsMaxTotalDeposits();
}
interface IStrategyEvents {
/**
* @notice Used to emit an event for the exchange rate between 1 share and underlying token in a strategy contract
* @param rate is the exchange rate in wad 18 decimals
* @dev Tokens that do not have 18 decimals must have offchain services scale the exchange rate by the proper magnitude
*/
event ExchangeRateEmitted(uint256 rate);
/**
* Used to emit the underlying token and its decimals on strategy creation
* @notice token
* @param token is the ERC20 token of the strategy
* @param decimals are the decimals of the ERC20 token in the strategy
*/
event StrategyTokenSet(IERC20 token, uint8 decimals);
}
/**
* @title Minimal interface for an `Strategy` contract.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice Custom `Strategy` implementations may expand extensively on this interface.
*/
interface IStrategy is IStrategyErrors, IStrategyEvents {
/**
* @notice Used to deposit tokens into this Strategy
* @param token is the ERC20 token being deposited
* @param amount is the amount of token being deposited
* @dev This function is only callable by the strategyManager contract. It is invoked inside of the strategyManager's
* `depositIntoStrategy` function, and individual share balances are recorded in the strategyManager as well.
* @return newShares is the number of new shares issued at the current exchange ratio.
*/
function deposit(IERC20 token, uint256 amount) external returns (uint256);
/**
* @notice Used to withdraw tokens from this Strategy, to the `recipient`'s address
* @param recipient is the address to receive the withdrawn funds
* @param token is the ERC20 token being transferred out
* @param amountShares is the amount of shares being withdrawn
* @dev This function is only callable by the strategyManager contract. It is invoked inside of the strategyManager's
* other functions, and individual share balances are recorded in the strategyManager as well.
*/
function withdraw(address recipient, IERC20 token, uint256 amountShares) external;
/**
* @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy.
* @notice In contrast to `sharesToUnderlyingView`, this function **may** make state modifications
* @param amountShares is the amount of shares to calculate its conversion into the underlying token
* @return The amount of underlying tokens corresponding to the input `amountShares`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function sharesToUnderlying(
uint256 amountShares
) external returns (uint256);
/**
* @notice Used to convert an amount of underlying tokens to the equivalent amount of shares in this strategy.
* @notice In contrast to `underlyingToSharesView`, this function **may** make state modifications
* @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into strategy shares
* @return The amount of underlying tokens corresponding to the input `amountShares`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function underlyingToShares(
uint256 amountUnderlying
) external returns (uint256);
/**
* @notice convenience function for fetching the current underlying value of all of the `user`'s shares in
* this strategy. In contrast to `userUnderlyingView`, this function **may** make state modifications
*/
function userUnderlying(
address user
) external returns (uint256);
/**
* @notice convenience function for fetching the current total shares of `user` in this strategy, by
* querying the `strategyManager` contract
*/
function shares(
address user
) external view returns (uint256);
/**
* @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy.
* @notice In contrast to `sharesToUnderlying`, this function guarantees no state modifications
* @param amountShares is the amount of shares to calculate its conversion into the underlying token
* @return The amount of shares corresponding to the input `amountUnderlying`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function sharesToUnderlyingView(
uint256 amountShares
) external view returns (uint256);
/**
* @notice Used to convert an amount of underlying tokens to the equivalent amount of shares in this strategy.
* @notice In contrast to `underlyingToShares`, this function guarantees no state modifications
* @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into strategy shares
* @return The amount of shares corresponding to the input `amountUnderlying`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function underlyingToSharesView(
uint256 amountUnderlying
) external view returns (uint256);
/**
* @notice convenience function for fetching the current underlying value of all of the `user`'s shares in
* this strategy. In contrast to `userUnderlying`, this function guarantees no state modifications
*/
function userUnderlyingView(
address user
) external view returns (uint256);
/// @notice The underlying token for shares in this Strategy
function underlyingToken() external view returns (IERC20);
/// @notice The total number of extant shares in this Strategy
function totalShares() external view returns (uint256);
/// @notice Returns either a brief string explaining the strategy's goal & purpose, or a link to metadata that explains in more detail.
function explanation() external view returns (string memory);
}
"
},
"lib/eigenlayer-contracts/src/contracts/libraries/SlashingLib.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin-upgrades/contracts/utils/math/SafeCastUpgradeable.sol";
/// @dev All scaling factors have `1e18` as an initial/default value. This value is represented
/// by the constant `WAD`, which is used to preserve precision with uint256 math.
///
/// When applying scaling factors, they are typically multiplied/divided by `WAD`, allowing this
/// constant to act as a "1" in mathematical formulae.
uint64 constant WAD = 1e18;
/*
* There are 2 types of shares:
* 1. deposit shares
* - These can be converted to an amount of tokens given a strategy
* - by calling `sharesToUnderlying` on the strategy address (they're already tokens
* in the case of EigenPods)
* - These live in the storage of the EigenPodManager and individual StrategyManager strategies
* 2. withdrawable shares
* - For a staker, this is the amount of shares that they can withdraw
* - For an operator, the shares delegated to them are equal to the sum of their stakers'
* withdrawable shares
*
* Along with a slashing factor, the DepositScalingFactor is used to convert between the two share types.
*/
struct DepositScalingFactor {
uint256 _scalingFactor;
}
using SlashingLib for DepositScalingFactor global;
library SlashingLib {
using Math for uint256;
using SlashingLib for uint256;
using SafeCastUpgradeable for uint256;
// WAD MATH
function mulWad(uint256 x, uint256 y) internal pure returns (uint256) {
return x.mulDiv(y, WAD);
}
function divWad(uint256 x, uint256 y) internal pure returns (uint256) {
return x.mulDiv(WAD, y);
}
/**
* @notice Used explicitly for calculating slashed magnitude, we want to ensure even in the
* situation where an operator is slashed several times and precision has been lost over time,
* an incoming slashing request isn't rounded down to 0 and an operator is able to avoid slashing penalties.
*/
function mulWadRoundUp(uint256 x, uint256 y) internal pure returns (uint256) {
return x.mulDiv(y, WAD, Math.Rounding.Up);
}
/**
* @notice Used as part of calculating wadSlashed in the EPM to ensure that we don't overslash
*/
function divWadRoundUp(uint256 x, uint256 y) internal pure returns (uint256) {
return x.mulDiv(WAD, y, Math.Rounding.Up);
}
// GETTERS
function scalingFactor(
DepositScalingFactor memory dsf
) internal pure returns (uint256) {
return dsf._scalingFactor == 0 ? WAD : dsf._scalingFactor;
}
function scaleForQueueWithdrawal(
DepositScalingFactor memory dsf,
uint256 depositSharesToWithdraw
) internal pure returns (uint256) {
return depositSharesToWithdraw.mulWad(dsf.scalingFactor());
}
function scaleForCompleteWithdrawal(uint256 scaledShares, uint256 slashingFactor) internal pure returns (uint256) {
return scaledShares.mulWad(slashingFactor);
}
/**
* @notice Scales shares according to the difference in an operator's magnitude before and
* after being slashed. This is used to calculate the number of slashable shares in the
* withdrawal queue.
* NOTE: max magnitude is guaranteed to only ever decrease.
*/
function scaleForBurning(
uint256 scaledShares,
uint64 prevMaxMagnitude,
uint64 newMaxMagnitude
) internal pure returns (uint256) {
return scaledShares.mulWad(prevMaxMagnitude - newMaxMagnitude);
}
function update(
DepositScalingFactor storage dsf,
uint256 prevDepositShares,
uint256 addedShares,
uint256 slashingFactor
) internal {
// If this is the staker's first deposit, set the scaling factor to
// the inverse of slashingFactor
if (prevDepositShares == 0) {
dsf._scalingFactor = uint256(WAD).divWad(slashingFactor);
return;
}
/**
* Base Equations:
* (1) newShares = currentShares + addedShares
* (2) newDepositShares = prevDepositShares + addedShares
* (3) newShares = newDepositShares * newDepositScalingFactor * slashingFactor
*
* Plugging (1) into (3):
* (4) newDepositShares * newDepositScalingFactor * slashingFactor = currentShares + addedShares
*
* Solving for newDepositScalingFactor
* (5) newDepositScalingFactor = (currentShares + addedShares) / (newDepositShares * slashingFactor)
*
* Plugging in (2) into (5):
* (7) newDepositScalingFactor = (currentShares + addedShares) / ((prevDepositShares + addedShares) * slashingFactor)
* Note that magnitudes must be divided by WAD for precision. Thus,
*
* (8) newDepositScalingFactor = WAD * (currentShares + addedShares) / ((prevDepositShares + addedShares) * slashingFactor / WAD)
* (9) newDepositScalingFactor = (currentShares + addedShares) * WAD / (prevDepositShares + addedShares) * WAD / slashingFactor
*/
// Step 1: Calculate Numerator
uint256 currentShares = dsf.calcWithdrawable(prevDepositShares, slashingFactor);
// Step 2: Compute currentShares + addedShares
uint256 newShares = currentShares + addedShares;
// Step 3: Calculate newDepositScalingFactor
/// forgefmt: disable-next-item
uint256 newDepositScalingFactor = newShares
.divWad(prevDepositShares + addedShares)
.divWad(slashingFactor);
dsf._scalingFactor = newDepositScalingFactor;
}
// CONVERSION
function calcWithdrawable(
DepositScalingFactor memory dsf,
uint256 depositShares,
uint256 slashingFactor
) internal pure returns (uint256) {
/// forgefmt: disable-next-item
return depositShares
.mulWad(dsf.scalingFactor())
.mulWad(slashingFactor);
}
function calcDepositShares(
DepositScalingFactor memory dsf,
uint256 withdrawableShares,
uint256 slashingFactor
) internal pure returns (uint256) {
/// forgefmt: disable-next-item
return withdrawableShares
.divWad(dsf.scalingFactor())
.divWad(slashingFactor);
}
function calcSlashedAmount(
uint256 operatorShares,
uint256 prevMaxMagnitude,
uint256 newMaxMagnitude
) internal pure returns (uint256) {
// round up mulDiv so we don't overslash
return operatorShares - operatorShares.mulDiv(newMaxMagnitude, prevMaxMagnitude, Math.Rounding.Up);
}
}
"
},
"lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/contracts/utils/math/Math.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
Submitted on: 2025-09-26 17:49:58
Comments
Log in to comment.
No comments yet.