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:

  1. 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.

  2. 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.

  3. 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.

  4. 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

uint256 public constant EPOCH_DURATION = 1 weeks;
  • EPOCH_DURATION: Specifies that each distribution epoch is exactly one week (604,800 seconds).


State Variables

  1. s_rewardsStartTime (uint256)

    • The timestamp (rounded down to the nearest week) at which rewards distribution officially begins.

  2. s_lastCheckpointTime (uint256)

    • Timestamp of the most recent checkpoint, marking when the contract last distributed tokens among the weekly intervals.

  3. 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.

  4. s_eywa (IERC20)

    • The EYWA token contract used for rebase rewards.

  5. s_escrowManager (IEscrowManager)

    • The escrow manager contract that manages veEYWA locks and provides historical voting power data.

  6. s_emissionManager (IEmissionManagerV1)

    • The emission manager contract that calls checkpoint() each time new rebase rewards are determined.

  7. 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.

  8. 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 initialize is called only once in a UUPS proxy context.


External Functions (IRebaseRewardsDistributorV1)

1. 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_lastCheckpointTime similarly.

      • 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()

function checkpoint() external
  • 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 with UnauthorizedCaller().

  • Logic:

    1. Checks authorization.

    2. Calls _checkpoint() to distribute any new tokens proportionally among the weeks that have elapsed since s_lastCheckpointTime.


3. 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:

    1. Checks if the emission manager’s s_currentEpochStart() is updated to the current epoch.

    2. Calls _claim(tokenId_, lastCheckpointWeekBoundary).

    3. 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(...).

    4. Deducts the claimed reward from s_previousTokenBalance.


4. 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_)

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_DURATION as the last checkpoint boundary.

  • Return:

    • uint256: The unclaimed rebase reward balance for that particular lock ID.


Internal and Private Functions

_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:

    1. Calculates m_totalReward = currentBalanceOfContract - s_previousTokenBalance.

    2. Spreads this m_totalReward proportionally across all full or partial weeks from s_lastCheckpointTime to block.timestamp.

    3. For each week boundary, updates s_weeklyTokensDistributed[weekTimestamp] += the portion of tokens corresponding to that interval.

    4. Updates s_previousTokenBalance to the current contract balance.

    5. Sets s_lastCheckpointTime = block.timestamp.

    6. Emits Checkpoint(block.timestamp, m_totalReward).


_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:

    1. Calls _earned(tokenId_, lastCheckpointTime_) to compute (m_reward, m_initialWeekCursor, m_updatedWeekCursor).

    2. Sets s_lastClaimedTimeByTokenId[tokenId_] = m_updatedWeekCursor.

    3. If m_reward == 0, returns immediately. Otherwise emits RewardsClaimed(tokenId_, m_initialWeekCursor, m_updatedWeekCursor, m_reward).

    4. Returns m_reward to the caller for final deposit/transfer logic.


_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_initialWeekCursor to 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:

    1. Retrieves m_initialWeekCursor = s_lastClaimedTimeByTokenId[tokenId_]. If zero, attempts to set it from the lock’s first recorded epoch data in s_votingPowerPointByTokenIdAndEpoch.

    2. If m_initialWeekCursor >= lastCheckpointTime_, returns zero reward (nothing new to claim).

    3. Iterates at most 52 times (1 year lookback in weekly steps):

      • For each weekly boundary from m_updatedWeekCursor to 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_updatedWeekCursor by EPOCH_DURATION.

    4. Returns (m_reward, m_initialWeekCursor, m_updatedWeekCursor).


Events

  1. 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.

  2. 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 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