# RebaseRewardsDistributorV1

### Overview <a href="#overview" id="overview"></a>

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

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

***

### Inherited Contracts and Interfaces <a href="#inherited-contracts-and-interfaces" id="inherited-contracts-and-interfaces"></a>

* **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 <a href="#constants" id="constants"></a>

```solidity
uint256 public constant EPOCH_DURATION = 1 weeks;
```

* **EPOCH\_DURATION**: Specifies that each distribution epoch is exactly one week (604,800 seconds).

***

### State Variables <a href="#state-variables" id="state-variables"></a>

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 <a href="#constructor" id="constructor"></a>

```solidity
constructor() {
    _disableInitializers();
}
```

* **Description:**\
  Disables initializers for this upgradeable contract, ensuring `initialize` is called only once in a UUPS proxy context.

***

### External Functions (IRebaseRewardsDistributorV1) <a href="#external-functions-irebaserewardsdistributorv1" id="external-functions-irebaserewardsdistributorv1"></a>

#### 1. **`initialize(...)`** <a href="#id-1-initialize" id="id-1-initialize"></a>

```solidity
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()`** <a href="#id-2-checkpoint" id="id-2-checkpoint"></a>

```solidity
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_)`** <a href="#id-3-claim-uint256-tokenid" id="id-3-claim-uint256-tokenid"></a>

```solidity
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_)`** <a href="#id-4-claim-uint256-calldata-tokenids" id="id-4-claim-uint256-calldata-tokenids"></a>

```solidity
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_)`** <a href="#id-5-earned-uint256-tokenid" id="id-5-earned-uint256-tokenid"></a>

```solidity
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 <a href="#internal-and-private-functions" id="internal-and-private-functions"></a>

#### **`_checkpoint()`** <a href="#checkpoint" id="checkpoint"></a>

```solidity
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(...)`** <a href="#claim" id="claim"></a>

```solidity
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(...)`** <a href="#earned" id="earned"></a>

```solidity
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 <a href="#events" id="events"></a>

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 <a href="#errors" id="errors"></a>

* **`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 <a href="#summary" id="summary"></a>

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