# EscrowVoteManagerV1

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

**EscrowVoteManagerV1** is a governance-related contract that manages gauge creation, voting power distribution, reward claiming, and other functionalities within the EYWA ecosystem. It integrates with:

* **Escrow Manager (IEscrowManager):** Tracks locked EYWA positions.
* **Emission Manager (IEmissionManagerV1):** Supplies emission epochs and total reward amounts.
* **Rewards Distributor Factory (IRewardsDistributorFactoryV1):** Deploys incentive reward distributor contracts for new gauges.
* **Gauge Factory (IGaugeFactoryV1):** Deploys gauge contracts, which are used to distribute rewards based on votes.
* **Reward Distributors (IRewardsDistributor):** Handle depositing and withdrawing of votes and distributing claimable incentives.

The contract also includes logic for:

1. **Voting with multiple token IDs**: A user can cast votes across multiple locks (NFTs) for multiple pools.
2. **Transfer Freeze after Deboost**: Adds a cooldown period preventing NFT transfers after a lock has been deboosted in the Escrow Manager.
3. **Gauge Lifecycle Management**: Owners can create, kill, or revive gauges.
4. **Reward Distribution**: Accumulates gauge rewards and distributes them after each epoch.

By implementing `IEscrowVoteManagerV1`, **EscrowVoteManagerV1** provides a standardized API for gauge-based governance interactions in the EYWA ecosystem.

***

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

* **UUPSUpgradeable (OpenZeppelin):**\
  Provides upgrade functionality under the UUPS proxy pattern, restricted to the contract owner.
* **OwnableUpgradeable (OpenZeppelin):**\
  Manages ownership, allowing only the owner to modify critical parameters and authorize upgrades.
* **IEscrowVoteManagerV1:**\
  Defines core methods (e.g., `vote`, `createGauge`, `distributeRewardsForMultipleGauges`) and events for this contract.

**Additional External References:**

* **SafeERC20, IERC20 (OpenZeppelin):** Handles secure ERC20 operations for distributing and approving token transfers.
* **Math (OpenZeppelin):** Provides utility math functions, e.g., `Math.max`.
* **IEscrowManager:** Holds locked token data and checks voting power and freeze logic.
* **IRewardsDistributorFactoryV1, IGaugeFactoryV1:** Deploy new reward distributors and gauge contracts, respectively.
* **IGaugeV1:** Interface for a gauge contract that can receive reward notifications (`notifyRewardAmount`).
* **IEmissionManagerV1:** Informs about epoch starts and ensures updated weekly distributions.

***

### Constants <a href="#constants-2" id="constants-2"></a>

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

```

* **EPOCH\_DURATION**: Duration of each emission/gauge epoch (1 week).
* **PRECISION**: Scaling factor (1e18) for reward calculations and distribution indexes.

***

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

* **`s_transferFreezeAfterDeboost (uint256)`**\
  Duration (in seconds) that locks are non-transferable after a deboost in the Escrow Manager.
* **`s_emissionManager (address)`**\
  Address of the emission manager contract, which calls this manager to notify new rewards.
* **`s_eywa (address)`**\
  Address of the EYWA token used for gauge rewards.
* **`s_escrowManager (IEscrowManager)`**\
  Reference to the contract that manages locked tokens (NFT-based locks).
* **`s_rewardsDistributorFactory (IRewardsDistributorFactoryV1)`**\
  Creates new rewards distributor contracts for gauges.
* **`s_gaugeFactory (IGaugeFactoryV1)`**\
  Creates new gauge contracts.
* **`_s_distributionIndex (uint256)`**\
  A global distribution index used to compute new reward distribution shares after the emission manager notifies reward amounts.
* **`s_pools (address[])`**\
  An array of pool addresses that have gauges.
* **`s_gaugeByPool (mapping(address => address))`**\
  Maps a pool address to its gauge contract address.
* **`s_poolByGauge (mapping(address => address))`**\
  Reverse map: gauge address to the corresponding pool address.
* **`s_incentiveRewardsDistributorByGauge (mapping(address => address))`**\
  Tracks which incentive rewards distributor belongs to which gauge.
* **`s_votesByEpochAndPool (mapping(uint256 => mapping(address => uint256)))`**\
  Records how many votes each pool has received in a given epoch (keyed by epoch start timestamp).
* **`s_claimableRewardsByGauge (mapping(address => uint256))`**\
  Shows how many EYWA tokens each gauge can claim, waiting to be distributed.
* **`s_lastDistributionTimestampByGauge (mapping(address => uint256))`**\
  Timestamp of the most recent reward distribution for each gauge.
* **`s_isGauge (mapping(address => bool))`**\
  Indicates if an address is recognized as a gauge.
* **`s_isWhitelistedToken (mapping(address => bool))`**\
  Tracks which tokens are whitelisted for reward distributions and voting.
* **`s_isActiveGauge (mapping(address => bool))`**\
  If true, the gauge is active and can receive new rewards.
* **`s_usedVotesByTokenId (mapping(uint256 => uint256))`**\
  How many votes a lock (NFT) has allocated.
* **`s_lastVotedTimestampByTokenId (mapping(uint256 => uint256))`**\
  Timestamp of the last time a token ID voted, used to reset or poke votes.
* **`s_votedPoolsByTokenId (mapping(uint256 => address[]))`**\
  List of pools each token ID is currently voting for.
* **`s_totalVotesByEpoch (mapping(uint256 => uint256))`**\
  Cumulative votes across all pools in a given epoch.
* **`s_votesByTokenIdAndPool (mapping(uint256 => mapping(address => uint256)))`**\
  Number of votes allocated by a specific token ID to a particular pool.
* **`_s_supplyDistributionIndex (mapping(address => uint256))`**\
  Per-gauge distribution index to track how much of the global distribution index the gauge has accounted for.

***

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

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

```

* **Description:**\
  Disables contract initializers to prevent re-initialization in a UUPS proxy context.

***

### External Functions (Defined by IEscrowVoteManagerV1) <a href="#external-functions-defined-by-iescrowvotemanagerv1" id="external-functions-defined-by-iescrowvotemanagerv1"></a>

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

```solidity
function initialize(
    address owner_,
    address emissionManager_,
    address eywa_,
    IEscrowManager escrowManager_,
    IRewardsDistributorFactoryV1 rewardsDistributorFactory_,
    IGaugeFactoryV1 gaugeFactory_,
    address[] calldata whitelistedTokens_
) external initializer

```

**Description:**\
Configures ownership, references, and initial state:

* Sets `s_transferFreezeAfterDeboost` to 4 hours initially.
* References the emission manager, escrow manager, gauge/rewards distributor factories, and whitelists initial tokens.

***

#### `updateTransferFreezeAfterDeboost(uint256 transferFreezeAfterDeboost_)` <a href="#updatetransferfreezeafterdeboostuint256-transferfreezeafterdeboost" id="updatetransferfreezeafterdeboostuint256-transferfreezeafterdeboost"></a>

```solidity
function updateTransferFreezeAfterDeboost(uint256 transferFreezeAfterDeboost_) external onlyOwner

```

**Description:**\
Updates the freeze period (in seconds) that applies after a deboost in Escrow Manager. This restricts NFT transfers for `transferFreezeAfterDeboost_` seconds.

**Events:**

* Emits `TransferFreezeAfterDeboostUpdated(oldDuration, newDuration)`.

***

#### `createGauge(address pool_, IDistributionCreator.CampaignParameters calldata campaignParameters_)` <a href="#creategaugeaddress-pool_-idistributioncreatorcampaignparameters-calldata-campaignparameters" id="creategaugeaddress-pool_-idistributioncreatorcampaignparameters-calldata-campaignparameters"></a>

```solidity
function createGauge(
    address pool_,
    IDistributionCreator.CampaignParameters calldata campaignParameters_
) external onlyOwner

```

**Description:**\
Creates a new gauge for a specified pool by:

1. Deploying an incentive rewards distributor via `s_rewardsDistributorFactory`.
2. Deploying a gauge contract via `s_gaugeFactory`.
3. Tracking the new gauge and marking it as active.

**Checks:**

* Ensures the pool does not already have a gauge.

**Events:**

* Emits `GaugeCreated(pool_, gauge, incentiveRewardsDistributor)`.

***

#### `killGauge(address gauge_)` <a href="#killgaugeaddress-gauge" id="killgaugeaddress-gauge"></a>

```solidity
function killGauge(address gauge_) external onlyOwner

```

**Description:**\
Deactivates a gauge so it stops receiving future rewards. Does not remove votes or claimable rewards; simply marks it inactive.

**Checks:**

* `s_isActiveGauge[gauge_]` must be true.

**Events:**

* Emits `GaugeKilled(gauge_)`.

***

#### `reviveGauge(address gauge_)` <a href="#revivegaugeaddress-gauge" id="revivegaugeaddress-gauge"></a>

```solidity
function reviveGauge(address gauge_) external onlyOwner

```

**Description:**\
Reactivates a previously killed gauge, allowing it to receive rewards again.

**Checks:**

* `s_isActiveGauge[gauge_]` must be false.

**Events:**

* Emits `GaugeRevived(gauge_)`.

***

#### `setWhitelistStatusForTokens(address[] calldata tokens_, bool[] calldata statuses_)` <a href="#setwhiteliststatusfortokensaddress-calldata-tokens_-bool-calldata-statuses" id="setwhiteliststatusfortokensaddress-calldata-tokens_-bool-calldata-statuses"></a>

```solidity
function setWhitelistStatusForTokens(
    address[] calldata tokens_,
    bool[] calldata statuses_
) external onlyOwner

```

**Description:**\
Updates whether tokens are whitelisted for reward distribution. Only callable by the contract owner. Checks input array lengths.

**Events:**

* Emits `WhitelistStatusUpdatedForTokens(operator, tokens_, statuses_)`.

***

#### `vote(uint256[] calldata tokenIds_, address[][] calldata pools_, uint256[][] calldata weights_)` <a href="#voteuint256-calldata-tokenids_-address-calldata-pools_-uint256-calldata-weights" id="voteuint256-calldata-tokenids_-address-calldata-pools_-uint256-calldata-weights"></a>

```solidity
function vote(
    uint256[] calldata tokenIds_,
    address[][] calldata pools_,
    uint256[][] calldata weights_
) external

```

**Description:**\
Allows a user to cast votes for multiple token IDs across multiple pools. For each token ID:

1. Authorizes caller via `IEscrowManager.checkAuthorized`.
2. Validates array lengths for pools and weights.
3. Performs `_vote(...)` to reset old votes, allocate new votes, and update vote counts.

***

#### `reset(uint256 tokenId_)` <a href="#resetuint256-tokenid" id="resetuint256-tokenid"></a>

```solidity
function reset(uint256 tokenId_) external

```

**Description:**\
Removes all existing votes for a given token ID, setting them to zero. The caller must be authorized. Updates the token’s last vote timestamp.

***

#### `poke(uint256 tokenId_)` <a href="#pokeuint256-tokenid" id="pokeuint256-tokenid"></a>

```solidity
function poke(uint256 tokenId_) external

```

**Description:**\
Recalculates (re-casts) votes for a token ID based on updated voting power without changing the vote distribution pattern. The function still calls `_vote(...)` internally using the same pools and weights previously allocated.

***

#### `claimIncentives(address[] calldata incentiveRewardsDistributors_, address[][] calldata rewardTokens_)` <a href="#claimincentivesaddress-calldata-incentiverewardsdistributors_-address-calldata-rewardtokens" id="claimincentivesaddress-calldata-incentiverewardsdistributors_-address-calldata-rewardtokens"></a>

```solidity
function claimIncentives(
    address[] calldata incentiveRewardsDistributors_, 
    address[][] calldata rewardTokens_
) external

```

**Description:**\
Claims incentives for the caller from multiple rewards distributors. Each distributor has a list of reward tokens. Forwards the call to `IRewardsDistributor.getReward`.

***

#### `notifyRewardAmount(uint256 rewardAmount_)` <a href="#notifyrewardamountuint256-rewardamount" id="notifyrewardamountuint256-rewardamount"></a>

```solidity
function notifyRewardAmount(uint256 rewardAmount_) external

```

**Description:**\
Only callable by `s_emissionManager`. Increments `_s_distributionIndex` by an amount proportional to `rewardAmount_ / totalVotesInPreviousEpoch`. If no votes or unauthorized call, reverts.

**Events:**

* Emits `RewardNotified(rewardAmount_)`.

***

#### `distributeRewardsForMultipleGauges(address[] calldata gauges_)` <a href="#distributerewardsformultiplegaugesaddress-calldata-gauges" id="distributerewardsformultiplegaugesaddress-calldata-gauges"></a>

```solidity
function distributeRewardsForMultipleGauges(address[] calldata gauges_) external

```

**Description:**\
Triggers an epoch update in the emission manager, then calls `_distributeRewards(...)` for each gauge in `gauges_`.

***

#### `distributeRewardsForGaugesInRange(uint256 start_, uint256 end_)` <a href="#distributerewardsforgaugesinrangeuint256-start_-uint256-end" id="distributerewardsforgaugesinrangeuint256-start_-uint256-end"></a>

```solidity
function distributeRewardsForGaugesInRange(uint256 start_, uint256 end_) external

```

**Description:**\
Also triggers an emission manager epoch update, then iterates through `s_pools[start_ ... end_]` to distribute rewards to each gauge.

***

#### `getPoolsCount()` <a href="#getpoolscount" id="getpoolscount"></a>

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

```

**Description:**\
Returns the number of pools (and thus gauges) tracked in `s_pools`.

***

#### `nextEpochStart()` <a href="#nextepochstart" id="nextepochstart"></a>

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

```

**Description:**\
Returns the next epoch’s start time as `s_currentEpochStart + EPOCH_DURATION` from the emission manager.

***

#### `currentEpochStart()` <a href="#currentepochstart" id="currentepochstart"></a>

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

```

**Description:**\
Returns the current epoch’s start time from the emission manager.

***

### Internal and Private Functions <a href="#internal-and-private-functions-3" id="internal-and-private-functions-3"></a>

#### `_authorizeUpgrade(address)` <a href="#authorizeupgradeaddress-2" id="authorizeupgradeaddress-2"></a>

```solidity
function _authorizeUpgrade(address) internal override onlyOwner

```

**Description:**\
Restricts contract upgrades to the owner.

***

#### `_vote(...)` <a href="#vote" id="vote"></a>

```solidity
function _vote(
    uint256 tokenId_,
    uint256 votingPower_,
    address[] memory pools_,
    uint256[] memory weights_
) private

```

**Description:**\
Handles the actual voting steps for a single token ID:

1. Calls `_reset(...)` to remove existing votes.
2. Proportionally allocates `votingPower_` across pools based on `weights_`.
3. Updates `s_votesByEpochAndPool`, `s_votesByTokenIdAndPool`, and `s_votedPoolsByTokenId`.
4. Deposits votes into the associated gauge’s incentive rewards distributor.
5. Tracks total votes used by the token ID (`s_usedVotesByTokenId`).
6. Informs escrow manager the token has voted.

**Events:**

* `VoteCast` on each pool voted for.

***

#### `_reset(uint256 tokenId_)` <a href="#resetuint256-tokenid" id="resetuint256-tokenid"></a>

```solidity
function _reset(uint256 tokenId_) private

```

**Description:**\
Clears existing votes for `tokenId_` by:

* Withdrawing votes from all previously voted pools (if the token voted in the current epoch).
* Adjusting `s_votesByEpochAndPool` and `s_votesByTokenIdAndPool`.
* Updating the total votes for the epoch.
* Marking the token as not voted in the escrow manager.
* Emitting `VotesAbstained`.

***

#### `_distributeRewards(address gauge_, uint256 currentEpochStart_)` <a href="#distributerewardsaddress-gauge_-uint256-currentepochstart" id="distributerewardsaddress-gauge_-uint256-currentepochstart"></a>

```solidity
function _distributeRewards(address gauge_, uint256 currentEpochStart_) private

```

**Description:**\
Distributes claimable EYWA tokens to a specified gauge. If the gauge is inactive, does nothing. Otherwise:

1. Calls `_updateRewardIndexForGauge(...)`.
2. Notifies the gauge of claimable rewards via `notifyRewardAmount`.

**Events:**

* `RewardsDistributed` after successful distribution.

***

#### `_updateRewardIndexForGauge(address gauge_, uint256 currentEpochStart_)` <a href="#updaterewardindexforgaugeaddress-gauge_-uint256-currentepochstart" id="updaterewardindexforgaugeaddress-gauge_-uint256-currentepochstart"></a>

```solidity
function _updateRewardIndexForGauge(address gauge_, uint256 currentEpochStart_) private

```

**Description:**\
Calculates how many new tokens a gauge can claim using the global distribution index. If there were votes in the previous epoch (`currentEpochStart_ - EPOCH_DURATION`), the gauge's share of `_s_distributionIndex` is updated, and claimable rewards are added to `s_claimableRewardsByGauge[gauge_]`.

***

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

* **`TransferFreezeAfterDeboostUpdated(oldDuration, newDuration)`**\
  Emitted when the freeze period is changed.
* **`GaugeCreated(pool, gauge, incentiveRewardsDistributor)`**\
  Emitted upon successful gauge creation.
* **`GaugeKilled(gauge)`**, **`GaugeRevived(gauge)`**\
  Emitted when a gauge is deactivated or reactivated.
* **`WhitelistStatusUpdatedForTokens(operator, tokens, statuses)`**\
  Shows updated whitelist status for multiple tokens.
* **`RewardNotified(rewardAmount)`**\
  Occurs when the emission manager notifies a new reward sum.
* **`VoteCast(voter, pool, tokenId, votes)`**\
  Occurs for each pool a token ID votes for.
* **`VotesAbstained(voter, pool, tokenId, votes)`**\
  Occurs when votes are reset/removed for a given pool.
* **`RewardsDistributed(distributor, gauge, rewardAmount)`**\
  Shows distribution of accumulated rewards to a gauge.

***

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

* **`GaugeDoesNotExist()`**\
  Thrown if a user tries to vote for a gauge that is not known (no gauge for that pool).
* **`GaugeAlreadyExists()`**\
  Thrown when creating a gauge for a pool that already has one.
* **`GaugeNotActive()`**\
  Thrown if an action (like voting or distributing) is attempted on an inactive gauge.
* **`GaugeAlreadyActive()`**\
  Thrown if reactivating a gauge that is already active.
* **`UnauthorizedAccess()`**\
  Thrown if a non-authorized caller tries to vote or manage votes.
* **`InvalidArrayLengths()`**\
  Thrown if arrays for `tokenIds_`, `pools_`, or `weights_` do not match in length.
* **`ZeroEntry()`**\
  Thrown if some operation results in zero (e.g., zero votes allocated to a pool).
* **`AlreadyVoted()`**\
  Not used in current logic, but defined in the interface.
* **`ProtectedFunctionSelectorUsed()`**\
  Not used here (relates to proposal manager).
* **`InvalidSliceParameters()`**\
  Not used here, pertains to slicing logic in other modules.

***

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

**EscrowVoteManagerV1** orchestrates multi-token, multi-pool voting within the EYWA ecosystem. By coordinating gauges, incentive reward distribution, epoch-based reward notifications from the emission manager, and freeze logic from the escrow manager, it ensures a secure, transparent mechanism for users to direct token emissions to liquidity pools and other incentive-based programs. With features like deboost freeze periods and gauge lifecycle control, the contract provides flexible governance and reward distribution for token holders.
