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:
Voting with multiple token IDs: A user can cast votes across multiple locks (NFTs) for multiple pools.
Transfer Freeze after Deboost: Adds a cooldown period preventing NFT transfers after a lock has been deboosted in the Escrow Manager.
Gauge Lifecycle Management: Owners can create, kill, or revive gauges.
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
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
Description: Disables contract initializers to prevent re-initialization in a UUPS proxy context.
External Functions (Defined by IEscrowVoteManagerV1)
initialize(...)
initialize(...)
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_)
updateTransferFreezeAfterDeboost(uint256 transferFreezeAfterDeboost_)
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_)
createGauge(address pool_, IDistributionCreator.CampaignParameters calldata campaignParameters_)
Description: Creates a new gauge for a specified pool by:
Deploying an incentive rewards distributor via
s_rewardsDistributorFactory
.Deploying a gauge contract via
s_gaugeFactory
.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_)
killGauge(address gauge_)
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_)
reviveGauge(address gauge_)
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_)
setWhitelistStatusForTokens(address[] calldata tokens_, bool[] calldata statuses_)
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_)
vote(uint256[] calldata tokenIds_, address[][] calldata pools_, uint256[][] calldata weights_)
Description: Allows a user to cast votes for multiple token IDs across multiple pools. For each token ID:
Authorizes caller via
IEscrowManager.checkAuthorized
.Validates array lengths for pools and weights.
Performs
_vote(...)
to reset old votes, allocate new votes, and update vote counts.
reset(uint256 tokenId_)
reset(uint256 tokenId_)
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_)
poke(uint256 tokenId_)
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_)
claimIncentives(address[] calldata incentiveRewardsDistributors_, address[][] calldata rewardTokens_)
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_)
notifyRewardAmount(uint256 rewardAmount_)
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_)
distributeRewardsForMultipleGauges(address[] calldata gauges_)
Description:
Triggers an epoch update in the emission manager, then calls _distributeRewards(...)
for each gauge in gauges_
.
distributeRewardsForGaugesInRange(uint256 start_, uint256 end_)
distributeRewardsForGaugesInRange(uint256 start_, uint256 end_)
Description:
Also triggers an emission manager epoch update, then iterates through s_pools[start_ ... end_]
to distribute rewards to each gauge.
getPoolsCount()
getPoolsCount()
Description:
Returns the number of pools (and thus gauges) tracked in s_pools
.
nextEpochStart()
nextEpochStart()
Description:
Returns the next epochβs start time as s_currentEpochStart + EPOCH_DURATION
from the emission manager.
currentEpochStart()
currentEpochStart()
Description: Returns the current epochβs start time from the emission manager.
Internal and Private Functions
_authorizeUpgrade(address)
_authorizeUpgrade(address)
Description: Restricts contract upgrades to the owner.
_vote(...)
_vote(...)
Description: Handles the actual voting steps for a single token ID:
Calls
_reset(...)
to remove existing votes.Proportionally allocates
votingPower_
across pools based onweights_
.Updates
s_votesByEpochAndPool
,s_votesByTokenIdAndPool
, ands_votedPoolsByTokenId
.Deposits votes into the associated gaugeβs incentive rewards distributor.
Tracks total votes used by the token ID (
s_usedVotesByTokenId
).Informs escrow manager the token has voted.
Events:
VoteCast
on each pool voted for.
_reset(uint256 tokenId_)
_reset(uint256 tokenId_)
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
ands_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_)
_distributeRewards(address gauge_, uint256 currentEpochStart_)
Description: Distributes claimable EYWA tokens to a specified gauge. If the gauge is inactive, does nothing. Otherwise:
Calls
_updateRewardIndexForGauge(...)
.Notifies the gauge of claimable rewards via
notifyRewardAmount
.
Events:
RewardsDistributed
after successful distribution.
_updateRewardIndexForGauge(address gauge_, uint256 currentEpochStart_)
_updateRewardIndexForGauge(address gauge_, uint256 currentEpochStart_)
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 fortokenIds_
,pools_
, orweights_
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