From 30db9dba04d5182bb672f2755a26928103396b92 Mon Sep 17 00:00:00 2001 From: Ben DiFrancesco Date: Wed, 12 Feb 2025 14:59:30 -0500 Subject: [PATCH] Updates and improvements to natspec and README --- LICENSE | 2 +- README.md | 22 ++++----- src/Staker.sol | 45 ++++++++++++------- ...ligibilityOracleEarningPowerCalculator.sol | 24 +++++----- .../StakerDelegateSurrogateVotes.sol | 4 +- src/extensions/StakerOnBehalf.sol | 17 ++++--- src/extensions/StakerPermitAndStake.sol | 10 +++-- src/interfaces/IDelegates.sol | 8 +++- src/interfaces/IEarningPowerCalculator.sol | 36 ++++++++++++++- src/interfaces/INotifiableRewardReceiver.sol | 2 +- 10 files changed, 112 insertions(+), 58 deletions(-) diff --git a/LICENSE b/LICENSE index 0780b86..31ea356 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2024 Tally +Copyright (c) 2025 Tally info@tally.xyz GNU AFFERO GENERAL PUBLIC LICENSE diff --git a/README.md b/README.md index 0d3ee32..a24f61c 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # Staker -Staker is a flexible, configurable staking contract. Staker makes it easy to distribute onchain staking rewards for any ERC20 token. +Staker is a flexible, configurable staking contract. Staker makes it easy to distribute onchain staking rewards for any ERC20 token, including DAO governance tokens. ## How it works: ### 1. Deploy and configure a Staker -- Staker is deployed with a single staking token +- Staker is deployed with a single staking token. - Staker is deployed with an admin, such as a DAO. -- Staker is configured to distribute one or more reward tokens +- Staker is configured to collect and distribute reward tokens. ### 2. Tokenholders stake - Tokenholders of the staking token can deposit those tokens in Staker. - There is no delay to deposit or withdraw. -- If the staking token is a governance token, depositors can delegate their staked tokens' voting power to themselves or someone else +- If the staking token is a governance token, depositors can delegate their staked tokens' voting power to themselves or someone else. - The depositor sets a claimer who can claim the staking rewards, such as themselves or someone else. ### 3. Staker distributes rewards @@ -28,17 +28,17 @@ When Staker is used for a protocol or DAO, the rewards are generally funded by p Staker can be deployed as an immutable contract with minimal governance. It does have some admin functions: -- Adding a new source of rewards -- Changing the eligibility oracle or the emergency pause guardian -- Overriding eligibility for a particular address +- Adding a new source of rewards. +- Changing the eligibility oracle or the emergency pause guardian. +- Overriding eligibility for a particular address. The staking token can be an `ERC20` token, including `ERC20Votes` governance tokens. Staker splits up all voting power in Staker by creating a surrogate contract for each delegate. -Staker distributes rewards over a fixed period of time. That gives everyone a chance to stake and minimizes discontinuities from flash staking. +Staker distributes rewards over a fixed period of time. This minimizes discontinuities from flash staking, and prevents frontrunning attacks, aimed at gaining a disproportionate share of rewards, ahead of reward distributions. ### Staking system -The staking system accepts user stake, delegates their voting power, and distributes rewards for eligibile stakers. +The staking system accepts user stake, delegates their voting power, and distributes rewards for eligible stakers. ```mermaid @@ -83,7 +83,7 @@ stateDiagram-v2 ### Earning Power Calculator -The earning power calculator determines which stakers are eligible for a reward. This implementation uses an oracle. An oracle is needed because eligibility depends on off-chain behavior. +The earning power calculator determines which depositors are eligible for rewards—and the rate at which those rewards are earned—based on their stake and their governance delegatee. The calculator is a modular component of the staker, which can be customized and updated by owner of the Staker, such as a DAO. One provided implementation uses an oracle. An oracle is needed because eligibility depends on the off-chain behavior of DAO delegates. ```mermaid stateDiagram-v2 @@ -165,4 +165,4 @@ This command will use the names of the contract's unit tests to generate a human The code in this repository is licensed under the [GNU Affero General Public License](LICENSE) unless otherwise indicated. -Copyright (C) 2024 Tally +Copyright (C) 2025 Tally diff --git a/src/Staker.sol b/src/Staker.sol index c97d38d..5785c7b 100644 --- a/src/Staker.sol +++ b/src/Staker.sol @@ -26,9 +26,18 @@ import {SafeCast} from "openzeppelin/utils/math/SafeCast.sol"; /// received, the reward duration restarts, and the rate at which rewards are streamed is updated /// to include the newly received rewards along with any remaining rewards that have finished /// streaming since the last time a reward was received. +/// +/// The rate at which a depositor earns rewards is proportional to their earning power. Earning +/// power is based on the amount the depositor has staked and the activity of their delegatee. +/// The calculation of earning power is handled by a separate module called the earning power +/// calculator. This module is set by the owner, and can be updated by the owner. If the owner of +/// the Staker contract is a DAO, which is the expected common case, this means the DAO has +/// the ability to define and iterate on its own definition of active, aligned participation, +/// and to decide how to reward it. abstract contract Staker is INotifiableRewardReceiver, Multicall { using SafeCast for uint256; + /// @notice A unique identifier assigned to each deposit. type DepositIdentifier is uint256; /// @notice Emitted when stake is deposited by a depositor, either to a new deposit or one that @@ -119,8 +128,8 @@ abstract contract Staker is INotifiableRewardReceiver, Multicall { /// duration. error Staker__InsufficientRewardBalance(); - /// @notice Thrown if the unclaimed rewards are insufficient to cover a bumpers requested tip or - /// in the case of an earning power decrease the tip of a subsequent earning power increase. + /// @notice Thrown if the unclaimed rewards are insufficient to cover a bumper's requested tip, + /// or in the case of an earning power decrease the tip of a subsequent earning power increase. error Staker__InsufficientUnclaimedRewards(); /// @notice Thrown if a caller attempts to specify address zero for certain designated addresses. @@ -139,6 +148,7 @@ abstract contract Staker is INotifiableRewardReceiver, Multicall { error Staker__InvalidSignature(); /// @notice Thrown if an earning power update is unqualified to be bumped. + /// @param score The would-be new earning power which did not qualify. error Staker__Unqualified(uint256 score); /// @notice Metadata associated with a discrete staking deposit. @@ -171,7 +181,7 @@ abstract contract Staker is INotifiableRewardReceiver, Multicall { /// @notice Parameters associated with the fee assessed when rewards are claimed. /// @param feeAmount The absolute amount of the reward token that is taken as a fee when rewards - /// claimed for a given deposit. + /// are claimed for a given deposit. /// @param feeCollector The address to which reward token fees are sent. struct ClaimFeeParameters { uint96 feeAmount; @@ -199,7 +209,8 @@ abstract contract Staker is INotifiableRewardReceiver, Multicall { /// @dev Unique identifier that will be used for the next deposit. DepositIdentifier private nextDepositId; - /// @notice Permissioned actor that can enable/disable `rewardNotifier` addresses. + /// @notice Permissioned actor that can enable/disable `rewardNotifier` addresses, set the max + /// bump tip, set the claim fee parameters, and update the earning power calculator. address public admin; /// @notice Maximum tip a bumper can request. @@ -212,7 +223,7 @@ abstract contract Staker is INotifiableRewardReceiver, Multicall { uint256 public totalEarningPower; /// @notice Contract that determines a deposit's earning power based on their delegatee. - /// @dev An earning power calculator should take into account that a deposit's earning power is an + /// @dev An earning power calculator should take into account that a deposit's earning power is a /// uint96. There may be overflow issues within governance staker if this is not taken into /// account. Also, there should be some mechanism to prevent the deposit from frequently being /// bumpable: if earning power changes frequently, this will eat into a users unclaimed rewards. @@ -250,7 +261,8 @@ abstract contract Staker is INotifiableRewardReceiver, Multicall { /// @param _stakeToken Delegable governance token which users will stake to earn rewards. /// @param _earningPowerCalculator The contract that will serve as the initial calculator of /// earning power for the staker system. - /// @param _admin Address which will have permission to manage rewardNotifiers. + /// @param _admin Address which will have permission to manage reward notifiers, claim fee + /// parameters, the max bump tip, and the reward calculator. constructor( IERC20 _rewardToken, IERC20 _stakeToken, @@ -305,12 +317,12 @@ abstract contract Staker is INotifiableRewardReceiver, Multicall { _setClaimFeeParameters(_params); } - /// @notice A method to get a delegation surrogate contract for a given delegate. - /// @param _delegatee The address the delegation surrogate is delegating voting power. + /// @notice A method to get the delegation surrogate contract for a given delegate. + /// @param _delegatee The address to which the delegation surrogate is delegating voting power. /// @return The delegation surrogate. /// @dev A concrete implementation should return a delegate surrogate address for a given /// delegatee. In practice this may be as simple as returning an address stored in a mapping or - /// computing it's create2 address. + /// computing its create2 address. function surrogates(address _delegatee) public view virtual returns (DelegationSurrogate); /// @notice Timestamp representing the last time at which rewards have been distributed, which is @@ -367,7 +379,7 @@ abstract contract Staker is INotifiableRewardReceiver, Multicall { /// contract to spend at least the would-be staked amount of the token. /// @param _amount Quantity of the staking token to stake. /// @param _delegatee Address to assign the governance voting weight of the staked tokens. - /// @param _claimer Address that will accrue rewards for this stake. + /// @param _claimer Address that will have the right to claim rewards for this stake. /// @return _depositId Unique identifier for this deposit. /// @dev Neither the delegatee nor the claimer may be the zero address. The deposit will be /// owned by the message sender. @@ -427,7 +439,7 @@ abstract contract Staker is INotifiableRewardReceiver, Multicall { } /// @notice Claim reward tokens earned by a given deposit. Message sender must be the claimer - /// address of the deposit. Tokens are sent to the claimer address. + /// address of the deposit or the owner of the deposit. Tokens are sent to the caller. /// @param _depositId Identifier of the deposit from which accrued rewards will be claimed. /// @return Amount of reward tokens claimed, after the fee has been assessed. function claimReward(DepositIdentifier _depositId) external virtual returns (uint256) { @@ -825,6 +837,8 @@ abstract contract Staker is INotifiableRewardReceiver, Multicall { maxBumpTip = _newMaxTip; } + /// @notice Internal helper method which sets the claim fee parameters. + /// @param _params The new fee parameters. function _setClaimFeeParameters(ClaimFeeParameters memory _params) internal virtual { if ( _params.feeAmount > MAX_CLAIM_FEE @@ -848,12 +862,11 @@ abstract contract Staker is INotifiableRewardReceiver, Multicall { } /// @notice Internal helper method which reverts Staker__Unauthorized if the alleged - /// owner is - /// not the true owner of the deposit. + /// owner is not the true owner of the deposit. /// @param deposit Deposit to validate. - /// @param owner Alleged owner of deposit. - function _revertIfNotDepositOwner(Deposit storage deposit, address owner) internal view virtual { - if (owner != deposit.owner) revert Staker__Unauthorized("not owner", owner); + /// @param _owner Alleged owner of deposit. + function _revertIfNotDepositOwner(Deposit storage deposit, address _owner) internal view virtual { + if (_owner != deposit.owner) revert Staker__Unauthorized("not owner", _owner); } /// @notice Internal helper method which reverts with Staker__InvalidAddress if the diff --git a/src/calculators/BinaryEligibilityOracleEarningPowerCalculator.sol b/src/calculators/BinaryEligibilityOracleEarningPowerCalculator.sol index ba1d70e..bed0e62 100644 --- a/src/calculators/BinaryEligibilityOracleEarningPowerCalculator.sol +++ b/src/calculators/BinaryEligibilityOracleEarningPowerCalculator.sol @@ -7,6 +7,14 @@ import {IEarningPowerCalculator} from "src/interfaces/IEarningPowerCalculator.so /// @title BinaryEligibilityOracleEarningPowerCalculator /// @author [ScopeLift](https://scopelift.co) /// @notice This contract calculates the earning power of a staker based on their delegatee's score. +/// The score is provided by a permissioned oracle, that can be updated by the contract owner. The +/// oracle can also be paused by a permissioned pauser role. The contract ensures the oracle +/// remains fresh. The contract allows the owner to set a threshold score over which staker's +/// receive earning power equal to the amount they've staked, and below which they receive no +/// earning power at all. The contract enforces a grace period before which a staker's earning +/// power does not qualify for being bumped. The contract also allows the owner to override a +/// score for a given delegatee. Note that, in practice, it is expected that the owner will be the +/// DAO itself. contract BinaryEligibilityOracleEarningPowerCalculator is Ownable, IEarningPowerCalculator { /// @notice Emitted when a delegatee's score is updated. /// @param delegatee The address of the delegatee whose score was updated. @@ -99,7 +107,6 @@ contract BinaryEligibilityOracleEarningPowerCalculator is Ownable, IEarningPower /// @notice Mapping to store the lock status of delegate scores. mapping(address delegate => bool isLocked) public delegateeScoreLockStatus; - /// @notice Initializes the EarningPowerCalculator contract. /// @param _owner The DAO governor address. /// @param _scoreOracle The address of the trusted oracle address. /// @param _delegateeScoreEligibilityThreshold The threshold for delegatee score eligibility to @@ -122,11 +129,7 @@ contract BinaryEligibilityOracleEarningPowerCalculator is Ownable, IEarningPower lastOracleUpdateTime = block.timestamp; } - /// @notice Calculates the earning power for a given delegatee and staking amount. - /// @param _amountStaked The amount of tokens staked. - /// @param /* _staker */ The address of the staker. - /// @param _delegatee The address of the delegatee. - /// @return The calculated earning power. + /// @inheritdoc IEarningPowerCalculator function getEarningPower(uint256 _amountStaked, address, /* _staker */ address _delegatee) external view @@ -136,13 +139,7 @@ contract BinaryEligibilityOracleEarningPowerCalculator is Ownable, IEarningPower return _isDelegateeEligible(_delegatee) ? _amountStaked : 0; } - /// @notice Calculates the new earning power and determines if it qualifies for an update.` - /// @param _amountStaked The amount of tokens staked. - /// @param /* _staker */ The address of the staker. - /// @param _delegatee The address of the delegatee. - /// @param /* _oldEarningPower */ The previous earning power value. - /// @return The newly calculated earning power. - /// @return Boolean indicating if the new earning power qualifies for an update. + /// @inheritdoc IEarningPowerCalculator function getNewEarningPower( uint256 _amountStaked, address, /* _staker */ @@ -163,6 +160,7 @@ contract BinaryEligibilityOracleEarningPowerCalculator is Ownable, IEarningPower /// @notice Updates the eligibility score of a delegatee. /// @dev This function can only be called by the authorized `scoreOracle` address. /// @dev If the delegatee's score is locked, the update will be reverted. + /// @dev If the oracle is paused, the update will be reverted. /// @param _delegatee The address of the delegatee whose score is being updated. /// @param _newScore The new score to be assigned to the delegatee. function updateDelegateeScore(address _delegatee, uint256 _newScore) public { diff --git a/src/extensions/StakerDelegateSurrogateVotes.sol b/src/extensions/StakerDelegateSurrogateVotes.sol index d1ff357..344557d 100644 --- a/src/extensions/StakerDelegateSurrogateVotes.sol +++ b/src/extensions/StakerDelegateSurrogateVotes.sol @@ -18,9 +18,11 @@ abstract contract StakerDelegateSurrogateVotes is Staker { /// the staked tokens from deposits which assign voting weight to said delegate. mapping(address delegatee => DelegationSurrogate surrogate) private storedSurrogates; - /// @notice Thrown if an inheritor uses a seperate staking token. + /// @notice Thrown if an inheritor misconfigures the staking token on deployment. error StakerDelegateSurrogateVotes__UnauthorizedToken(); + /// @param _votingToken The token that is used for voting, which must be the same as the parent + /// Staker's STAKE_TOKEN. constructor(IERC20Delegates _votingToken) { if (address(STAKE_TOKEN) != address(_votingToken)) { revert StakerDelegateSurrogateVotes__UnauthorizedToken(); diff --git a/src/extensions/StakerOnBehalf.sol b/src/extensions/StakerOnBehalf.sol index 22bb067..f5fda79 100644 --- a/src/extensions/StakerOnBehalf.sol +++ b/src/extensions/StakerOnBehalf.sol @@ -62,7 +62,7 @@ abstract contract StakerOnBehalf is Staker, EIP712, Nonces { /// would-be staked amount of the token. /// @param _amount Quantity of the staking token to stake. /// @param _delegatee Address to assign the governance voting weight of the staked tokens. - /// @param _claimer Address that will accrue rewards for this stake. + /// @param _claimer Address that will have the right to claim rewards for this stake. /// @param _depositor Address of the user on whose behalf this stake is being made. /// @param _deadline The timestamp after which the signature should expire. /// @param _signature Signature of the user authorizing this stake. @@ -99,7 +99,9 @@ abstract contract StakerOnBehalf is Staker, EIP712, Nonces { /// @notice Add more staking tokens to an existing deposit on behalf of a user, using a signature /// to validate the user's intent. A staker should call this method when they have an existing - /// deposit, and wish to stake more while retaining the same delegatee and claimer. + /// deposit, and wish to stake more while retaining the same delegatee and claimer. The caller + /// must pre-approve the staking contract to spend at least the would-be staked amount of the + /// token. /// @param _depositId Unique identifier of the deposit to which stake will be added. /// @param _amount Quantity of stake to be added. /// @param _depositor Address of the user on whose behalf this stake is being made. @@ -134,7 +136,7 @@ abstract contract StakerOnBehalf is Staker, EIP712, Nonces { /// assigned on behalf of a user, using a signature to validate the user's intent. /// @param _depositId Unique identifier of the deposit which will have its delegatee altered. /// @param _newDelegatee Address of the new governance delegate. - /// @param _depositor Address of the user on whose behalf this stake is being made. + /// @param _depositor Address of the user on whose behalf this action is being taken. /// @param _deadline The timestamp after which the signature should expire. /// @param _signature Signature of the user authorizing this stake. /// @dev The new delegatee may not be the zero address. @@ -173,7 +175,7 @@ abstract contract StakerOnBehalf is Staker, EIP712, Nonces { /// user's intent. /// @param _depositId Unique identifier of the deposit which will have its claimer altered. /// @param _newClaimer Address of the new claimer. - /// @param _depositor Address of the user on whose behalf this stake is being made. + /// @param _depositor Address of the user on whose behalf this action is being taken. /// @param _deadline The timestamp after which the signature should expire. /// @param _signature Signature of the user authorizing this stake. /// @dev The new claimer may not be the zero address. @@ -211,7 +213,7 @@ abstract contract StakerOnBehalf is Staker, EIP712, Nonces { /// signature to validate the user's intent. /// @param _depositId Unique identifier of the deposit from which stake will be withdrawn. /// @param _amount Quantity of staked token to withdraw. - /// @param _depositor Address of the user on whose behalf this stake is being made. + /// @param _depositor Address of the user on whose behalf this action is being taken. /// @param _deadline The timestamp after which the signature should expire. /// @param _signature Signature of the user authorizing this stake. /// @dev Stake is withdrawn to the deposit owner's account. @@ -245,7 +247,7 @@ abstract contract StakerOnBehalf is Staker, EIP712, Nonces { /// the claimer. /// @param _depositId The identifier for the deposit for which to claim rewards. /// @param _deadline The timestamp after which the signature should expire. - /// @param _signature Signature of the claimer authorizing this reward claim. + /// @param _signature Signature of the claimer or owner authorizing this reward claim. /// @return Amount of reward tokens claimed, after the fee has been assessed. function claimRewardOnBehalf( DepositIdentifier _depositId, @@ -273,7 +275,8 @@ abstract contract StakerOnBehalf is Staker, EIP712, Nonces { return _claimReward(_depositId, deposit, deposit.owner); } - /// @notice Internal helper method which reverts if the provided deadline has passed. + /// @notice Internal helper method which reverts with StakerOnBehalf__ExpiredDeadline if the + /// provided deadline has passed. /// @param _deadline The timestamp that represents when the operation should no longer be valid. function _revertIfPastDeadline(uint256 _deadline) internal view virtual { if (block.timestamp > _deadline) revert StakerOnBehalf__ExpiredDeadline(); diff --git a/src/extensions/StakerPermitAndStake.sol b/src/extensions/StakerPermitAndStake.sol index 1f8df08..e26606f 100644 --- a/src/extensions/StakerPermitAndStake.sol +++ b/src/extensions/StakerPermitAndStake.sol @@ -9,12 +9,14 @@ import {IERC20Permit} from "openzeppelin/token/ERC20/extensions/IERC20Permit.sol /// @notice This contract extension adds permit functionality to the Staker base contract, /// allowing token approvals to happen via signatures rather than requiring a separate transaction. /// The permit functionality is used in conjunction with staking operations, improving UX by -/// enabling users to approve and stake tokens in a single transaction. -/// Note that this extension requires the stake token to support EIP-2612 permit functionality. +/// enabling users to approve and stake tokens in a single transaction. Note that this extension +/// requires the stake token to support EIP-2612 permit functionality. abstract contract StakerPermitAndStake is Staker { - /// @notice Thrown if an inheritor uses a seperate staking token. + /// @notice Thrown if an inheritor misconfigures the staking token on deployment. error StakerPermitAndStake__UnauthorizedToken(); + /// @param _permitToken The token that is used for staking, which must support EIP-2612. It also + /// must be the same as the parent Staker's STAKE_TOKEN. constructor(IERC20Permit _permitToken) { if (address(STAKE_TOKEN) != address(_permitToken)) { revert StakerPermitAndStake__UnauthorizedToken(); @@ -26,7 +28,7 @@ abstract contract StakerPermitAndStake is Staker { /// of the token. /// @param _amount Quantity of the staking token to stake. /// @param _delegatee Address to assign the governance voting weight of the staked tokens. - /// @param _claimer Address that will accrue rewards for this stake. + /// @param _claimer Address that will have the right to claim rewards for this stake. /// @param _deadline The timestamp after which the permit signature should expire. /// @param _v ECDSA signature component: Parity of the `y` coordinate of point `R` /// @param _r ECDSA signature component: x-coordinate of `R` diff --git a/src/interfaces/IDelegates.sol b/src/interfaces/IDelegates.sol index c4a31bb..063ba09 100644 --- a/src/interfaces/IDelegates.sol +++ b/src/interfaces/IDelegates.sol @@ -4,6 +4,10 @@ pragma solidity ^0.8.23; /// @notice An interface that contains the necessary `IVotes` functions for the governance staking /// system. interface IDelegates { - function delegate(address delegatee) external; - function delegates(address account) external view returns (address); + /// @notice Method which assigns voting weight from the sender to delegatee. + function delegate(address _delegatee) external; + + /// @notice Method which returns the delegatee to which the account's voting weight is currently + /// delegated. + function delegates(address _account) external view returns (address); } diff --git a/src/interfaces/IEarningPowerCalculator.sol b/src/interfaces/IEarningPowerCalculator.sol index 3d2131a..7345267 100644 --- a/src/interfaces/IEarningPowerCalculator.sol +++ b/src/interfaces/IEarningPowerCalculator.sol @@ -1,14 +1,46 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.23; -/// @notice Interface for calculating earning power of a staker based on their delegate score in -/// Staker. +/// @title IEarningPowerCalculator +/// @author [ScopeLift](https://scopelift.co) +/// @notice Interface to which Earning Power Calculators must conform in order to provide earning +/// power updates to an instance of Staker. Well behaving earning power calculators should: +/// +/// 1. Be deterministic, i.e. produce the same output for the same input at a given time. +/// 2. Return values that are in the same order of magnitude as reasonable stake token amounts. +/// Avoid returning values that are dramatically detached from the staked amount. +/// 3. Avoid too much "churn" on earning power values, in particular, avoid returning "true" for +/// the `getNewEarningPower` method's `_isQualifiedForBump` too frequently, as such an earning +/// calculator would result in repeated bumps on a user's deposit, requiring excessive +/// monitoring on their behalf to avoid eating into their rewards. interface IEarningPowerCalculator { + /// @notice Returns the current earning power for a given staker, delegatee and staking amount. + /// @param _amountStaked The amount of tokens staked. + /// @param _staker The address of the staker. + /// @param _delegatee The address of their chosen delegatee. + /// @return _earningPower The calculated earning power. function getEarningPower(uint256 _amountStaked, address _staker, address _delegatee) external view returns (uint256 _earningPower); + /// @notice Returns the current earning power for a given staker, delegatee, staking amount, and + /// old earning power, along with a flag denoting whether the change in earning power warrants + /// "bumping." Bumping means paying a third party a bit of the rewards to update the deposit's + /// earning power on the depositor's behalf. + /// @param _amountStaked The amount of tokens staked. + /// @param _staker The address of the staker. + /// @param _delegatee The address of their chosen delegatee. + /// @param _oldEarningPower The earning power currently assigned to the deposit for which new + /// earning power is being calculated. + /// @return _newEarningPower The calculated earning power. + /// @return _isQualifiedForBump A flag indicating whether or not this new earning power qualifies + /// the deposit for having its earning power bumped. + /// @dev Earning Power calculators should only "qualify" a bump when the difference warrants a + /// forced update by a third party. This could be, for example, to reduce a deposit's earning + /// power because their delegatee has become inactive. Even in these cases, a calculator should + /// avoid qualifying for a bump too frequently. A calculator implementer may, for example, want + /// to implement a grace period or a threshold difference before qualifying a deposit for a bump. function getNewEarningPower( uint256 _amountStaked, address _staker, diff --git a/src/interfaces/INotifiableRewardReceiver.sol b/src/interfaces/INotifiableRewardReceiver.sol index ee79f54..c7882fd 100644 --- a/src/interfaces/INotifiableRewardReceiver.sol +++ b/src/interfaces/INotifiableRewardReceiver.sol @@ -7,7 +7,7 @@ import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol"; /// @author [ScopeLift](https://scopelift.co) /// @notice The communication interface between contracts that distribute rewards and the /// Staker contract. In particular, said contracts only need to know the staker -/// implements the specified method in order to forward payouts to the staker contract. The +/// implements the specified methods in order to forward payouts to the staker contract. The /// Staker contract receives the rewards and abstracts the distribution mechanics. interface INotifiableRewardReceiver { /// @notice ERC20 token in which rewards are denominated and distributed.