# IncentiveRewardsDistributor

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

**IncentiveRewardsDistributor** is a contract that builds upon an abstract **RewardsDistributor** to manage the distribution of incentive rewards within the EYWA ecosystem. The **RewardsDistributor** base contract provides fundamental logic for tracking deposits, withdrawals, reward notifications, and reward claims over discrete epochs. The **IncentiveRewardsDistributor** adds:

1. **Whitelisting of Tokens:** Before accepting a new token as a reward token, it checks with the escrow vote manager to ensure it is whitelisted.
2. **Escrow Vote Manager Authorization:** Restricts calls to certain functions (like `getReward`) to only the authorized vote manager.
3. **Inheritance and Extended Logic:** Integrates seamlessly with the epoch-based deposit/withdraw system in `RewardsDistributor` to finalize reward amounts and calculations.

By implementing `IIncentiveRewardsDistributor`, this contract conforms to a standard reward distributor interface used throughout the EYWA ecosystem.

***

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

* **RewardsDistributor (Abstract Contract):**
  * Manages core reward distribution functionality, such as:
    * Tracking how many tokens each owner has deposited during a specific epoch.
    * Storing how many rewards are allocated to a token for each epoch.
    * Calculating earned rewards for each owner at specific epochs and enabling claim logic.
  * Declares and implements the `IRewardsDistributor` interface.
* **IIncentiveRewardsDistributor:**
  * Extends `IRewardsDistributor` with additional constraints and methods (`getReward` & `notifyRewardAmount` restricted to certain checks).

**Additional External References:**

* **IEscrowVoteManagerV1:**
  * Provides `s_isWhitelistedToken` to validate new reward tokens.
  * Acts as an authorization gate for methods that require `msg.sender` to be the vote manager.
* **IEscrowManager:**
  * Gives ownership and deposit/withdraw data for NFT-based locks.
  * Indirectly leveraged via `RewardsDistributor` functions for deposit/withdraw logic.
* **ReentrancyGuard (OpenZeppelin):**
  * Inherited in `RewardsDistributor` to prevent re-entrant calls.
* **SafeERC20, IERC20 (OpenZeppelin):**
  * Used for secure ERC20 transfers.

***

### Contract Architecture <a href="#contract-architecture" id="contract-architecture"></a>

```
IncentiveRewardsDistributor (Concrete)
 └── RewardsDistributor (Abstract)
      └── IRewardsDistributor (Interface)

```

#### Inherited from **RewardsDistributor** <a href="#inherited-from-rewardsdistributor" id="inherited-from-rewardsdistributor"></a>

The base **RewardsDistributor** constructor sets immutable references for `ESCROW_VOTE_MANAGER` and `ESCROW_MANAGER`. It also defines the following main categories of functionality:

1. **Epoch-Based Deposit and Withdrawal:**
   * `deposit(uint256 amount_, uint256 tokenId_)`
   * `withdraw(uint256 amount_, uint256 tokenId_)`
2. **Reward Notification and Calculation:**
   * `notifyRewardAmount(address rewardToken_, uint256 rewardAmount_)` (abstract here, implemented in the child contract)
   * `earned(address owner_, address rewardToken_)` and `earnedByEpoch(address owner_, address rewardToken_, uint256 epoch_)`
   * `rewardPerToken(address rewardToken_, uint256 epoch_)`
3. **Claiming Rewards:**
   * `getReward(address owner_, address[] calldata rewardTokens_)` (abstract here, partially implemented in the child)
   * `_getReward(address owner_, address[] calldata rewardTokens_)` (internal)
4. **Data Structures for Epoch Accounting and Balances:**
   * Mappings storing how much each user has deposited per epoch, total supply per epoch, and how much reward is allocated per epoch.

We now detail each of these base functions.

***

### Functions in the **Base Contract**: `RewardsDistributor` <a href="#functions-in-the-base-contract-rewardsdistributor" id="functions-in-the-base-contract-rewardsdistributor"></a>

#### 1. **Constructor** <a href="#id-1-constructor" id="id-1-constructor"></a>

```solidity
constructor(address escrowVoteManager_, address escrowManager_) {
    ESCROW_VOTE_MANAGER = escrowVoteManager_;
    ESCROW_MANAGER = escrowManager_;
}

```

* **Description:**\
  Sets `ESCROW_VOTE_MANAGER` and `ESCROW_MANAGER` as immutable addresses, used for authorization and lock ownership lookups.
* **Parameters:**
  * `escrowVoteManager_`: The address of the escrow vote manager contract.
  * `escrowManager_`: The address of the escrow manager contract.

***

#### 2. **`deposit(uint256 amount_, uint256 tokenId_)`** <a href="#id-2-deposituint256-amount_-uint256-tokenid" id="id-2-deposituint256-amount_-uint256-tokenid"></a>

```solidity
function deposit(uint256 amount_, uint256 tokenId_) external

```

* **Description:**
  * Called by the escrow vote manager to record that `amount_` of tokens have been deposited for the lock represented by `tokenId_` in the current epoch.
  * Increments the total supply and the lock owner’s balance for the current epoch.
* **Checks:**
  * `msg.sender == ESCROW_VOTE_MANAGER`; otherwise `UnauthorizedAccess()`.
* **Logic:**
  1. Queries the current epoch start via `IEscrowVoteManagerV1(ESCROW_VOTE_MANAGER).currentEpochStart()`.
  2. Increments `s_totalSupplyByEpoch[m_currentEpochStart]` by `amount_`.
  3. Retrieves lock owner via `IEscrowManager(ESCROW_MANAGER).ownerOf(tokenId_)`.
  4. Increments `s_balanceByOwnerAndEpoch[m_owner][m_currentEpochStart]` by `amount_`.
  5. Emits `TokensDeposited(m_owner, tokenId_, amount_)`.

***

#### 3. **`withdraw(uint256 amount_, uint256 tokenId_)`** <a href="#id-3-withdrawuint256-amount_-uint256-tokenid" id="id-3-withdrawuint256-amount_-uint256-tokenid"></a>

```solidity
function withdraw(uint256 amount_, uint256 tokenId_) external

```

* **Description:**
  * Called by the escrow vote manager to record that `amount_` of tokens have been removed from the lock represented by `tokenId_` in the current epoch.
  * Decrements the total supply and the lock owner’s balance for the current epoch.
* **Checks:**
  * `msg.sender == ESCROW_VOTE_MANAGER`; otherwise `UnauthorizedAccess()`.
* **Logic:**
  1. Gets the current epoch start, as with `deposit`.
  2. Decrements `s_totalSupplyByEpoch[m_currentEpochStart]` by `amount_`.
  3. Finds lock owner and decreases `s_balanceByOwnerAndEpoch[m_owner][m_currentEpochStart]` by `amount_`.
  4. Emits `TokensWithdrawn(m_owner, tokenId_, amount_)`.

***

#### 4. **`getRewardTokensCount()`** <a href="#id-4-getrewardtokenscount" id="id-4-getrewardtokenscount"></a>

```solidity
function getRewardTokensCount() external view returns (uint256)

```

* **Description:**\
  Returns the length of the `s_rewardTokens` array, i.e., how many reward tokens the distributor currently recognizes.
* **Return:**
  * `uint256`: number of recognized reward tokens.

***

#### 5. **`earned(address owner_, address rewardToken_)`** <a href="#id-5-earnedaddress-owner_-address-rewardtoken" id="id-5-earnedaddress-owner_-address-rewardtoken"></a>

```solidity
function earned(address owner_, address rewardToken_) public view returns (uint256, uint256)

```

* **Description:**
  * Computes how many tokens of `rewardToken_` the `owner_` has earned across epochs since their last claim.
  * Iterates through epochs in weekly increments up to a maximum of 52 (1-year look-back) or until reaching the current epoch.
* **Returns:**
  * `(uint256 m_reward, uint256 m_lastRewardClaim)`: the total reward due and the final epoch boundary used for calculation.
* **Logic:**
  1. Checks if `rewardToken_` is recognized in `s_isRewardToken[rewardToken_]`. If not, returns `(0,0)`.
  2. Locates the last claimed epoch timestamp for `(owner_, rewardToken_)`.
  3. Repeatedly calls `earnedByEpoch(...)` for each epoch from `m_lastRewardClaim` up to but not including `currentEpochStart`.
  4. Summation is the total pending rewards.

***

#### 6. **`earnedByEpoch(address owner_, address rewardToken_, uint256 epoch_)`** <a href="#id-6-earnedbyepochaddress-owner_-address-rewardtoken_-uint256-epoch" id="id-6-earnedbyepochaddress-owner_-address-rewardtoken_-uint256-epoch"></a>

```solidity
function earnedByEpoch(address owner_, address rewardToken_, uint256 epoch_) public view returns (uint256)

```

* **Description:**
  * Returns how many `rewardToken_` tokens were earned by `owner_` specifically in a single epoch.
  * Calculated as `rewardPerToken(rewardToken_, epoch_) * s_balanceByOwnerAndEpoch[owner_][epoch_] / 1e18`.

***

#### 7. **`rewardPerToken(address rewardToken_, uint256 epoch_)`** <a href="#id-7-rewardpertokenaddress-rewardtoken_-uint256-epoch" id="id-7-rewardpertokenaddress-rewardtoken_-uint256-epoch"></a>

```solidity
function rewardPerToken(address rewardToken_, uint256 epoch_) public view returns (uint256)

```

* **Description:**
  * Calculates the distribution ratio for `rewardToken_` in a given `epoch_`.
  * If `s_totalSupplyByEpoch[epoch_]` is zero, it simply returns `s_rewardAmountByTokenAndEpoch[rewardToken_][epoch_]`.
  * Otherwise, divides that reward amount by the epoch’s total supply (scaled by `1e18`).
* **Return:**
  * `uint256`: The per-token reward ratio used to multiply by an owner’s deposit amount to get their share.

***

#### 8. **`getReward(address owner_, address[] calldata rewardTokens_)` (abstract)** <a href="#id-8-getrewardaddress-owner_-address-calldata-rewardtokens_-abstract" id="id-8-getrewardaddress-owner_-address-calldata-rewardtokens_-abstract"></a>

```solidity
function getReward(address owner_, address[] calldata rewardTokens_) external virtual;

```

* **Description:**
  * Abstract in the base **RewardsDistributor**. The child contract (`IncentiveRewardsDistributor`) provides the actual implementation.
  * Child’s implementation calls `_getReward(...)` internally after verifying authorization.

***

#### 9. **`notifyRewardAmount(address rewardToken_, uint256 rewardAmount_)` (abstract)** <a href="#id-9-notifyrewardamountaddress-rewardtoken_-uint256-rewardamount_-abstract" id="id-9-notifyrewardamountaddress-rewardtoken_-uint256-rewardamount_-abstract"></a>

```solidity
function notifyRewardAmount(address rewardToken_, uint256 rewardAmount_) external virtual;

```

* **Description:**
  * Abstract method for adding new reward tokens. Child contract overrides this to add whitelisting checks or additional logic.
  * The base logic is available in `_notifyRewardAmount(...)` (see below).

***

#### 10. **`_getReward(address owner_, address[] calldata rewardTokens_)` (internal)** <a href="#id-10-_getrewardaddress-owner_-address-calldata-rewardtokens_-internal" id="id-10-_getrewardaddress-owner_-address-calldata-rewardtokens_-internal"></a>

```solidity
function _getReward(address owner_, address[] calldata rewardTokens_) internal

```

* **Description:**
  * The internal function that loops over `rewardTokens_`, calculates `earned(...)`, updates `s_lastRewardClaimByOwnerAndToken`, and transfers the reward to `owner_`.
  * Finally, emits `RewardsClaimed(owner_, token, amount)`.

***

#### 11. **`_notifyRewardAmount(address sender_, address rewardToken_, uint256 rewardAmount_)` (internal)** <a href="#id-11-_notifyrewardamountaddress-sender_-address-rewardtoken_-uint256-rewardamount_-internal" id="id-11-_notifyrewardamountaddress-sender_-address-rewardtoken_-uint256-rewardamount_-internal"></a>

```solidity
function _notifyRewardAmount(address sender_, address rewardToken_, uint256 rewardAmount_) internal

```

* **Description:**
  * Transfers `rewardAmount_` of `rewardToken_` from `sender_` to this distributor.
  * Adds the difference in the contract’s balance to `s_rewardAmountByTokenAndEpoch[rewardToken_][currentEpochStart]`.
  * If `s_initialIncentiveTimestamp == 0`, sets it to the current epoch start.
  * Emits `RewardNotified(sender_, rewardToken_, epoch, difference)`.

***

### Functions in **IncentiveRewardsDistributor** <a href="#functions-in-incentiverewardsdistributor" id="functions-in-incentiverewardsdistributor"></a>

#### Constructor <a href="#constructor-6" id="constructor-6"></a>

```solidity
constructor(address escrowVoteManager_, address escrowManager_) 
    RewardsDistributor(escrowVoteManager_, escrowManager_)
{}

```

* **Description:**
  * Calls the **RewardsDistributor** constructor to set `ESCROW_VOTE_MANAGER` and `ESCROW_MANAGER`.
  * No additional logic besides inheritance.

***

#### `getReward(address owner_, address[] calldata rewardTokens_)` <a href="#getrewardaddress-owner_-address-calldata-rewardtokens" id="getrewardaddress-owner_-address-calldata-rewardtokens"></a>

```solidity
function getReward(
    address owner_, 
    address[] calldata rewardTokens_
)
    external
    override (RewardsDistributor, IIncentiveRewardsDistributor)
    nonReentrant

```

* **Description:**
  * Implementation of `getReward` from both `RewardsDistributor` (abstract) and `IIncentiveRewardsDistributor`.
  * Allows the escrow vote manager to claim rewards for `owner_`.
  * Calls `_getReward(...)` internally after checking `msg.sender == ESCROW_VOTE_MANAGER`.

***

#### `notifyRewardAmount(address rewardToken_, uint256 rewardAmount_)` <a href="#notifyrewardamountaddress-rewardtoken_-uint256-rewardamount" id="notifyrewardamountaddress-rewardtoken_-uint256-rewardamount"></a>

```solidity
function notifyRewardAmount(
    address rewardToken_, 
    uint256 rewardAmount_
)
    external
    override (RewardsDistributor, IIncentiveRewardsDistributor)
    nonReentrant

```

* **Description:**
  * Implementation of `notifyRewardAmount` from both `RewardsDistributor` (abstract) and `IIncentiveRewardsDistributor`.
  * First ensures `rewardToken_` is whitelisted via `s_isWhitelistedToken` in `IEscrowVoteManagerV1`.
    * If the token is not recognized (`!s_isRewardToken[rewardToken_]`), checks the vote manager to see if it’s whitelisted; if so, it’s now added to `s_rewardTokens`.
    * If not whitelisted, reverts with `NotWhitelisted()`.
  * Calls `_notifyRewardAmount(msg.sender, rewardToken_, rewardAmount_)` to handle actual fund transfer and epoch reward updates.

***

### Events and Errors <a href="#events-and-errors" id="events-and-errors"></a>

#### Inherited Events <a href="#inherited-events" id="inherited-events"></a>

1. **`TokensDeposited(from, tokenId, amount)`**
2. **`TokensWithdrawn(from, tokenId, amount)`**
3. **`RewardNotified(from, token, epoch, amount)`**
4. **`RewardsClaimed(from, token, amount)`**

#### Inherited Errors <a href="#inherited-errors" id="inherited-errors"></a>

* **`InvalidRewardToken()`**
* **`UnauthorizedAccess()`**
* **`ZeroAmountProvided()`**
* **`NotWhitelisted()`**

No new custom events or errors are introduced in **IncentiveRewardsDistributor** beyond what’s inherited.

***

### Summary <a href="#summary-6" id="summary-6"></a>

**IncentiveRewardsDistributor** leverages the robust, epoch-based reward accounting system from **RewardsDistributor** to facilitate secure incentive distribution in the EYWA ecosystem, adding token whitelisting checks and limiting reward claims (`getReward`) to calls from the escrow vote manager. This design ensures:

* **Authorized Control:** Only recognized and whitelisted tokens can be notified as rewards; only the escrow vote manager can trigger claims.
* **Accurate Reward Calculations:** Inherits deposit/withdraw logic to track each user’s epoch-based balance.
* **Easy Integration:** Conforms to the `IIncentiveRewardsDistributor` interface, providing consistent methods for reward notifications and claims across the system.

Together with the **RewardsDistributor** base contract, **IncentiveRewardsDistributor** offers a flexible, robust means of awarding incentive tokens in a multi-lock, epoch-based environment.
