EscrowVoteManagerV1
Overview
EscrowVoteManagerV1 is a governance-related contract that manages gauge creation, voting power distribution, reward claiming, and other functionalities within the CrossCurve 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 CrossCurve 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(...)
initialize(...)function initialize(
address owner_,
address emissionManager_,
address eywa_,
IEscrowManager escrowManager_,
IRewardsDistributorFactoryV1 rewardsDistributorFactory_,
IGaugeFactoryV1 gaugeFactory_,
address[] calldata whitelistedTokens_
) external initializerDescription: Configures ownership, references, and initial state:
Sets
s_transferFreezeAfterDeboostto 4 hours initially.References the emission manager, escrow manager, gauge/rewards distributor factories, and whitelists initial tokens.
updateTransferFreezeAfterDeboost(uint256 transferFreezeAfterDeboost_)
updateTransferFreezeAfterDeboost(uint256 transferFreezeAfterDeboost_)function updateTransferFreezeAfterDeboost(uint256 transferFreezeAfterDeboost_) external onlyOwnerDescription:
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_)function createGauge(
address pool_,
IDistributionCreator.CampaignParameters calldata campaignParameters_
) external onlyOwnerDescription: 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_)function killGauge(address gauge_) external onlyOwnerDescription: 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_)function reviveGauge(address gauge_) external onlyOwnerDescription: 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_)function setWhitelistStatusForTokens(
address[] calldata tokens_,
bool[] calldata statuses_
) external onlyOwnerDescription: 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_)function vote(
uint256[] calldata tokenIds_,
address[][] calldata pools_,
uint256[][] calldata weights_
) externalDescription: 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_)function reset(uint256 tokenId_) externalDescription: 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_)function poke(uint256 tokenId_) externalDescription:
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_)function claimIncentives(
address[] calldata incentiveRewardsDistributors_,
address[][] calldata rewardTokens_
) externalDescription:
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_)function notifyRewardAmount(uint256 rewardAmount_) externalDescription:
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_)function distributeRewardsForMultipleGauges(address[] calldata gauges_) externalDescription:
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_)function distributeRewardsForGaugesInRange(uint256 start_, uint256 end_) externalDescription:
Also triggers an emission manager epoch update, then iterates through s_pools[start_ ... end_] to distribute rewards to each gauge.
getPoolsCount()
getPoolsCount()function getPoolsCount() external view returns (uint256)Description:
Returns the number of pools (and thus gauges) tracked in s_pools.
nextEpochStart()
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()
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)
_authorizeUpgrade(address)function _authorizeUpgrade(address) internal override onlyOwnerDescription: Restricts contract upgrades to the owner.
_vote(...)
_vote(...)function _vote(
uint256 tokenId_,
uint256 votingPower_,
address[] memory pools_,
uint256[] memory weights_
) privateDescription: 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:
VoteCaston each pool voted for.
_reset(uint256 tokenId_)
_reset(uint256 tokenId_)function _reset(uint256 tokenId_) privateDescription:
Clears existing votes for tokenId_ by:
Withdrawing votes from all previously voted pools (if the token voted in the current epoch).
Adjusting
s_votesByEpochAndPoolands_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_)function _distributeRewards(address gauge_, uint256 currentEpochStart_) privateDescription: 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:
RewardsDistributedafter successful distribution.
_updateRewardIndexForGauge(address gauge_, uint256 currentEpochStart_)
_updateRewardIndexForGauge(address gauge_, uint256 currentEpochStart_)function _updateRewardIndexForGauge(address gauge_, uint256 currentEpochStart_) privateDescription:
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 CrossCurve 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

