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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.eywa.fi/developer-documentation/guide-for-developers/technical-documentation-for-eywa-dao-smart-contracts/escrowvotemanagerv1.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
