EscrowVoteManagerV1

Overview

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

  • 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

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

  • 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

constructor() {
    _disableInitializers();
}
  • Description: Disables contract initializers to prevent re-initialization in a UUPS proxy context.


External Functions (Defined by IEscrowVoteManagerV1)

initialize(...)

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

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

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

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

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

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

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

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

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

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

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

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

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

function getPoolsCount() external view returns (uint256)

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


nextEpochStart()

function nextEpochStart() external view returns (uint256)

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


currentEpochStart()

function currentEpochStart() external view returns (uint256)

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


Internal and Private Functions

_authorizeUpgrade(address)

function _authorizeUpgrade(address) internal override onlyOwner

Description: Restricts contract upgrades to the owner.


_vote(...)

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

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

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

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

  • 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

  • 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

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.

Last updated