RebaseRewardsDistributorV1
Overview
RebaseRewardsDistributorV1 is an upgradeable contract that distributes rebase rewards to veEYWA token holders (locks) in the EYWA ecosystem. It relies on an Emission Manager (s_emissionManager
) to trigger epoch updates (checkpoints) and fetches locked token data from an Escrow Manager (s_escrowManager
). The contract tracks weekly reward distributions and lets veEYWA token holders claim these rebase rewards either directly (if their lock is expired) or by depositing rewards back into the lock (if itβs still active).
Key features include:
Epoch-Based Distribution: Rewards are recorded in discrete weekly intervals (epochs) and credited to locks according to each lockβs proportional voting power during the relevant weeks.
Checkpointing Logic: Every time new rebase rewards are added, the contract updates an internal βcheckpoint,β distributing the newly added tokens proportionally over the elapsed time since the last checkpoint.
On-Chain Rebase: Locks continuously earn rebase rewards over time; once claimed, if the lock is still active (not past
unlockTime
), the rewards are automatically re-deposited. Otherwise, they are transferred to the lock owner.Owner-Only Upgrades: Using a UUPS proxy pattern, only the contract owner can authorize upgrades to the distributorβs logic.
By implementing IRebaseRewardsDistributorV1
, RebaseRewardsDistributorV1 ensures a standardized interface for initialization, checkpoint updates, and reward claims within the EYWA ecosystem.
Inherited Contracts and Interfaces
UUPSUpgradeable (OpenZeppelin): Provides functionality for upgradeable contracts following the UUPS (Universal Upgradeable Proxy Standard) pattern.
OwnableUpgradeable (OpenZeppelin): Gives ownership control, allowing only the owner to authorize upgrades and perform restricted actions.
IRebaseRewardsDistributorV1 (Custom Interface): Declares the core functions (e.g.,
initialize
,checkpoint
,claim
) and events (Checkpoint
,RewardsClaimed
) that define how rebase rewards are managed and distributed.
Additional External References:
SafeERC20, IERC20 (OpenZeppelin): For safe ERC20 transfers of the EYWA token.
IEscrowManager: Provides locked token data (voting power snapshots,
unlockTime
, ownership).IEmissionManagerV1: Signals new epochs by calling
checkpoint()
and ensures emission periods are up-to-date.
Constants
EPOCH_DURATION: Specifies that each distribution epoch is exactly one week (604,800 seconds).
State Variables
s_rewardsStartTime (uint256)
The timestamp (rounded down to the nearest week) at which rewards distribution officially begins.
s_lastCheckpointTime (uint256)
Timestamp of the most recent checkpoint, marking when the contract last distributed tokens among the weekly intervals.
s_previousTokenBalance (uint256)
The EYWA token balance in the contract before the most recent checkpoint. Used to calculate how many new tokens were added since the last checkpoint.
s_eywa (IERC20)
The EYWA token contract used for rebase rewards.
s_escrowManager (IEscrowManager)
The escrow manager contract that manages veEYWA locks and provides historical voting power data.
s_emissionManager (IEmissionManagerV1)
The emission manager contract that calls
checkpoint()
each time new rebase rewards are determined.
s_weeklyTokensDistributed (uint256[1000000000000000])
A large array mapping from a weekβs starting timestamp (multiple of
EPOCH_DURATION
) to the total tokens distributed in that particular week.
s_lastClaimedTimeByTokenId (mapping(uint256 => uint256))
For each veEYWA token ID, stores the last epochβs timestamp at which it fully claimed rebase rewards.
Constructor
Description: Disables initializers for this upgradeable contract, ensuring
initialize
is called only once in a UUPS proxy context.
External Functions (IRebaseRewardsDistributorV1)
1. initialize(...)
initialize(...)
Description:
Sets up initial state for the rebase rewards distributor:
Assigns contract ownership to
owner_
.Approves the
escrowManager_
to pull unlimited EYWA tokens from this contract.Records the start time of rewards (
s_rewardsStartTime
= current week boundary) and sets initials_lastCheckpointTime
similarly.Links the references to
s_eywa
,s_escrowManager
, ands_emissionManager
.
Parameters:
owner_
: The address designated as the contract owner.eywa_
: The EYWA token contract used for distributing rebase rewards.escrowManager_
: The contract that manages locked positions (NFT-based locks).emissionManager_
: The contract that triggers checkpoint updates.
2. checkpoint()
checkpoint()
Description:
Called only by
s_emissionManager
to record a new checkpoint, distributing newly added tokens across the weeks since the last checkpoint.Reverts if
msg.sender != s_emissionManager
withUnauthorizedCaller()
.
Logic:
Checks authorization.
Calls
_checkpoint()
to distribute any new tokens proportionally among the weeks that have elapsed sinces_lastCheckpointTime
.
3. claim(uint256 tokenId_)
claim(uint256 tokenId_)
Description:
Lets a user claim the rebase rewards for a single veEYWA lock identified by
tokenId_
.Requires that
s_emissionManager.s_currentEpochStart()
be updated to or beyond the current week; otherwise reverts withEmissionPeriodNotUpdated()
.
Logic:
Checks if the emission managerβs
s_currentEpochStart()
is updated to the current epoch.Calls
_claim(tokenId_, lastCheckpointWeekBoundary)
.If the lock is expired (
block.timestamp >= unlockTime
), transfers the earned reward to the last lock owner. Otherwise, it deposits the reward back into the lock viaescrowManager_.depositFor(...)
.Deducts the claimed reward from
s_previousTokenBalance
.
4. claim(uint256[] calldata tokenIds_)
claim(uint256[] calldata tokenIds_)
Description:
Claims rewards for multiple lock IDs in a single transaction.
Similar checks to
claim(uint256)
, verifying the emission period is updated.Iterates over each
tokenId_
intokenIds_
and calls the internal_claim(...)
, summing up the total claimed amount.
5. earned(uint256 tokenId_)
earned(uint256 tokenId_)
Description:
Returns how many tokens the lock with
tokenId_
has accumulated but not yet claimed, using the latest known checkpoint.Internally calls
_earned(...)
withs_lastCheckpointTime / EPOCH_DURATION * EPOCH_DURATION
as the last checkpoint boundary.
Return:
uint256
: The unclaimed rebase reward balance for that particular lock ID.
Internal and Private Functions
_checkpoint()
_checkpoint()
Description:
The core logic that distributes newly added EYWA tokens among each weekly interval from the last checkpoint time until
block.timestamp
.If a portion of a week is included, it distributes a proportional share.
Logic:
Calculates
m_totalReward = currentBalanceOfContract - s_previousTokenBalance
.Spreads this
m_totalReward
proportionally across all full or partial weeks froms_lastCheckpointTime
toblock.timestamp
.For each week boundary, updates
s_weeklyTokensDistributed[weekTimestamp] +=
the portion of tokens corresponding to that interval.Updates
s_previousTokenBalance
to the current contract balance.Sets
s_lastCheckpointTime = block.timestamp
.Emits
Checkpoint(block.timestamp, m_totalReward)
.
_claim(...)
_claim(...)
Description:
Internal function that calculates how many rebase rewards
tokenId_
has earned since its last claim, updates the last claimed time, and emitsRewardsClaimed
.Returns the amount of newly claimed rewards.
Logic:
Calls
_earned(tokenId_, lastCheckpointTime_)
to compute(m_reward, m_initialWeekCursor, m_updatedWeekCursor)
.Sets
s_lastClaimedTimeByTokenId[tokenId_] = m_updatedWeekCursor
.If
m_reward == 0
, returns immediately. Otherwise emitsRewardsClaimed(tokenId_, m_initialWeekCursor, m_updatedWeekCursor, m_reward)
.Returns
m_reward
to the caller for final deposit/transfer logic.
_earned(...)
_earned(...)
Description:
Calculates how many tokens the lock
tokenId_
earned fromm_initialWeekCursor
tolastCheckpointTime_
, iterating in weekly increments.Uses historical voting power from the
escrowManager.getPastVotesByTokenId(...)
and total supply fromescrowManager.getPastTotalSupply(...)
to determine each weekβs share ofs_weeklyTokensDistributed
.
Logic:
Retrieves
m_initialWeekCursor = s_lastClaimedTimeByTokenId[tokenId_]
. If zero, attempts to set it from the lockβs first recorded epoch data ins_votingPowerPointByTokenIdAndEpoch
.If
m_initialWeekCursor >= lastCheckpointTime_
, returns zero reward (nothing new to claim).Iterates at most 52 times (1 year lookback in weekly steps):
For each weekly boundary from
m_updatedWeekCursor
tolastCheckpointTime_
, calculates:m_balance = escrowManager.getPastVotesByTokenId(tokenId_, nextWeekTimestamp)
m_supply = escrowManager.getPastTotalSupply(nextWeekTimestamp)
(set to 1 if 0).Distributes
m_reward += (m_balance * s_weeklyTokensDistributed[weekTimestamp]) / m_supply
.
Increments
m_updatedWeekCursor
byEPOCH_DURATION
.
Returns
(m_reward, m_initialWeekCursor, m_updatedWeekCursor)
.
Events
Checkpoint(uint256 indexed timestamp, uint256 indexed totalReward)
Emitted whenever
_checkpoint()
distributes a new batch of tokens across weekly intervals.timestamp
: The timestamp of the checkpoint (usuallyblock.timestamp
).totalReward
: The number of newly added tokens distributed.
RewardsClaimed(uint256 indexed tokenId, uint256 indexed initialWeekCursor, uint256 indexed updatedWeekCursor, uint256 reward)
Emitted whenever a token ID claims rebase rewards.
tokenId
: The veEYWA NFT lock ID that claimed.initialWeekCursor
: The earliest week boundary for which new rewards are being claimed.updatedWeekCursor
: The new last claimed week boundary.reward
: The total tokens claimed in this operation.
Errors
UnauthorizedCaller()
Thrown if
checkpoint()
is called by an entity other thans_emissionManager
.
EmissionPeriodNotUpdated()
Thrown if a user attempts to claim but the emission managerβs
s_currentEpochStart()
has not been advanced to the current week boundary.
No additional custom errors are introduced beyond those declared in the IRebaseRewardsDistributorV1
interface.
Summary
RebaseRewardsDistributorV1 provides a specialized, upgradeable system for distributing weekly rebase rewards to locks in the EYWA ecosystem. It does so by:
Recording new tokens from the emission manager in discrete weekly intervals via
_checkpoint()
.Allowing token holders to claim their proportional share of rebase rewards at any time, either receiving them directly if their lock is expired or re-depositing them if their lock remains active.
Integrating with the escrow manager to read historical voting power for each epoch and proportionally allocate weekly distributed tokens.
By bridging data from the emission manager and the escrow manager, RebaseRewardsDistributorV1 ensures consistent rebase reward accounting that reflects each lockβs relative stake and time-based accumulation of benefits. It complements the rest of the EYWA ecosystem by providing a secure, time-based rebase distribution mechanism with minimal overhead and clear upgrade paths.
Last updated