RebaseRewardsDistributorV1
Overview
RebaseRewardsDistributorV1 is an upgradeable contract that distributes rebase rewards to veEYWA token holders (locks) in the CrossCurve 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 CrossCurve 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
uint256 public constant EPOCH_DURATION = 1 weeks;- 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
constructor() {
    _disableInitializers();
}- Description: Disables initializers for this upgradeable contract, ensuring - initializeis called only once in a UUPS proxy context.
External Functions (IRebaseRewardsDistributorV1)
1. initialize(...)
initialize(...)function initialize(
    address owner_,
    IERC20 eywa_,
    IEscrowManager escrowManager_,
    IEmissionManagerV1 emissionManager_
) 
    external
    initializer- 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 initial- s_lastCheckpointTimesimilarly.
- Links the references to - s_eywa,- s_escrowManager, and- s_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()function checkpoint() external- Description: - Called only by - s_emissionManagerto record a new checkpoint, distributing newly added tokens across the weeks since the last checkpoint.
- Reverts if - msg.sender != s_emissionManagerwith- UnauthorizedCaller().
 
- Logic: - Checks authorization. 
- Calls - _checkpoint()to distribute any new tokens proportionally among the weeks that have elapsed since- s_lastCheckpointTime.
 
3. claim(uint256 tokenId_)
claim(uint256 tokenId_)function claim(uint256 tokenId_) external- 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 with- EmissionPeriodNotUpdated().
 
- 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 via- escrowManager_.depositFor(...).
- Deducts the claimed reward from - s_previousTokenBalance.
 
4. claim(uint256[] calldata tokenIds_)
claim(uint256[] calldata tokenIds_)function claim(uint256[] calldata tokenIds_) external- 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_in- tokenIds_and calls the internal- _claim(...), summing up the total claimed amount.
 
5. earned(uint256 tokenId_)
earned(uint256 tokenId_)function earned(uint256 tokenId_) external view returns (uint256)- Description: - Returns how many tokens the lock with - tokenId_has accumulated but not yet claimed, using the latest known checkpoint.
- Internally calls - _earned(...)with- s_lastCheckpointTime / EPOCH_DURATION * EPOCH_DURATIONas the last checkpoint boundary.
 
- Return: - uint256: The unclaimed rebase reward balance for that particular lock ID.
 
Internal and Private Functions
_checkpoint()
_checkpoint()function _checkpoint() private- 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_totalRewardproportionally across all full or partial weeks from- s_lastCheckpointTimeto- block.timestamp.
- For each week boundary, updates - s_weeklyTokensDistributed[weekTimestamp] +=the portion of tokens corresponding to that interval.
- Updates - s_previousTokenBalanceto the current contract balance.
- Sets - s_lastCheckpointTime = block.timestamp.
- Emits - Checkpoint(block.timestamp, m_totalReward).
 
_claim(...)
_claim(...)function _claim(uint256 tokenId_, uint256 lastCheckpointTime_) private returns (uint256)- Description: - Internal function that calculates how many rebase rewards - tokenId_has earned since its last claim, updates the last claimed time, and emits- RewardsClaimed.
- 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 emits- RewardsClaimed(tokenId_, m_initialWeekCursor, m_updatedWeekCursor, m_reward).
- Returns - m_rewardto the caller for final deposit/transfer logic.
 
_earned(...)
_earned(...)function _earned(
    uint256 tokenId_,
    uint256 lastCheckpointTime_
)
    private
    view
    returns (
        uint256 m_reward,
        uint256 m_initialWeekCursor,
        uint256 m_updatedWeekCursor
    )- Description: - Calculates how many tokens the lock - tokenId_earned from- m_initialWeekCursorto- lastCheckpointTime_, iterating in weekly increments.
- Uses historical voting power from the - escrowManager.getPastVotesByTokenId(...)and total supply from- escrowManager.getPastTotalSupply(...)to determine each week’s share of- s_weeklyTokensDistributed.
 
- Logic: - Retrieves - m_initialWeekCursor = s_lastClaimedTimeByTokenId[tokenId_]. If zero, attempts to set it from the lock’s first recorded epoch data in- s_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_updatedWeekCursorto- lastCheckpointTime_, 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_updatedWeekCursorby- EPOCH_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 (usually- block.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 than- s_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 CrossCurve 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 CrossCurve ecosystem by providing a secure, time-based rebase distribution mechanism with minimal overhead and clear upgrade paths.
Last updated

