EscrowManager
Overview
EscrowManager is a smart contract that manages locked EYWA tokens, each represented by a unique ERC-721 NFT (sometimes referred to as a "veEYWA" token). Each lock NFT stores information about its locked token amount, unlock time, vesting wallets, and booster NFTs. The contract supports operations like depositing, boosting, extending, deboosting, and withdrawing locked tokens, while tracking voting power via time-based checkpoints and epochs. It integrates with an external vote manager contract for governance operations and a collection contract for booster NFTs.
Key Features:
Lock Creation & Management: Users can create locks by transferring EYWA tokens, vesting wallets, or tokens from an NFT collection. They can add more tokens, apply boosters, or extend the lock duration.
Voting Power: Each lock corresponds to voting power that evolves over time. The contract records checkpoints of ownership and calculates voting weights using a dynamic rate-of-change approach.
Booster Mechanism: Boosters from the collection can increase a lockβs "coverage," thus effectively increasing its lock amount for voting power calculations.
Cooldown & Freeze Logic: After certain actions such as deboosting, token transfers may be temporarily frozen to prevent immediate re-deposit or locking manipulations.
Delegation Checks: The contract can integrate with a delegation manager to move voting power between addresses.
By implementing the IEscrowManager
interface, the EscrowManager contract provides a consistent API for user and external contract interactions in the EYWA ecosystem.
Inherited Contracts and Interfaces
ERC721Enumerable, ERC721 (OpenZeppelin): Provides standard ERC-721 NFT functionality with enumeration capabilities, enabling easy listing of all minted NFTs.
ERC721Holder (OpenZeppelin): Allows the contract to safely hold ERC-721 tokens (necessary when receiving booster NFTs).
ReentrancyGuard (OpenZeppelin): Mitigates re-entrant calls on state-changing functions.
SafeERC20, IERC20 (OpenZeppelin): Provides secure wrappers for ERC20 operations, ensuring safe token transfers.
External Interfaces:
IEscrowManager: The interface implemented by this contract, defining locking and booster logic.
IEscrowVoteManagerV1: The vote manager that tracks epochs, triggers freeze logic, and notifies about gauge rewards.
ICollection, IWalletFactory, IVestingWallet: Components in the ecosystem for NFT boosters, creating/managing vesting wallets, and bridging custom data.
IMetadataProviderV1: Supplies metadata URIs for NFTs.
VotingPowerHelper (Library): Used to compute historical voting power and supply based on stored checkpoints and rate-of-change data.
Constants
ATTACHMENTS_LIMIT
= 100 Maximum number of attachments (vesting wallets or booster NFTs) allowed per lock.EPOCH_DURATION
= 1 weeks Governs epoch-based calculations for voting power and lock logic.MAXIMUM_LOCK_DURATION
= 156 weeks Maximum allowed lock duration in seconds (approximately 3 years).MAXIMUM_NUMBER_OF_DELEGATES
= 1024 Maximum delegates that can be tracked for a single accountβs checkpoints.SIGNED_MAXIMUM_LOCK_DURATION
= 156 weeks Signed integer equivalent for arithmetic purposes.Rarity Multipliers and Capacities: Predefined numerators and capacity values for Common, Uncommon, Rare, Legendary, and Infinity NFT rarities.
PRECISION
= 100,000 Scaling factor for multiplier-based calculations (e.g., coverage, booster effects).
Immutable Addresses:
ESCROW_VOTE_MANAGER (address)
: The escrow vote manager contract address.DELEGATION_MANAGER (address)
: The delegation manager contract address.EYWA (IERC20)
: The EYWA token used in locking.COLLECTION (ICollection)
: The collection contract supporting booster NFTs.WALLET_FACTORY (IWalletFactory)
: Factory contract for validating vesting wallets.METADATA_PROVIDER (IMetadataProviderV1)
: Supplies off-chain metadata for NFTs.
State Variables
s_nextTokenId (uint256)
The next NFT token ID to be minted.s_epoch (uint256)
Tracks how many epochs have occurred, updated whenever lock states change significantly.s_totalLocked (uint256)
Total EYWA tokens locked across all positions.
Lock and Booster Details:
_s_lockInfoByTokenId (mapping(uint256 => LockInfo))
: Internal mapping of lock data.s_pureLockAmountByTokenId, s_vestingWalletsLockAmountByTokenId
: Track base token amounts and vesting wallet contributions.s_currentCoverageByTokenId
: Booster coverage for each lock.s_boostersByTokenId, s_boostersByTokenIdAndRarity
: Maintain sets of booster NFT IDs by token ID and rarity.
Checkpointing and Voting:
s_checkpointsNumberByAccount (mapping(address => uint32))
: Number of checkpoints for each account.s_checkpoints (mapping(address => mapping(uint32 => Checkpoint)))
: Account checkpoints listing owned lock NFTs at different times.s_ownershipChangeBlockNumberByTokenId (mapping(uint256 => uint256))
: Block number of the last ownership change for each NFT (prevents double-voting in the same block).s_rateOfChangeByUnlockTime (mapping(uint256 => int128))
: Tracks changes in voting power at specific unlock times.s_hasVotedByTokenId (mapping(uint256 => bool))
: Whether a token has voted in the current epoch.s_hasVotedByEpochAndTokenId (mapping(uint256 => mapping(uint256 => bool)))
: Tracks vote status for each token in each epoch.s_votingPowerPointByEpoch (mapping(uint256 => VotingPowerPoint))
: Stores aggregated voting power changes per epoch.s_votingPowerPointByTokenIdAndEpoch (mapping(uint256 => VotingPowerPoint[1000000000]))
: Voting power data for each token ID across epochs.
Deboost and Freeze Logic:
s_lastDeboostTimestampByTokenId (mapping(uint256 => uint256))
: Records the last time a token was deboosted, used to freeze subsequent transfers.
Constructor
Description: Deploys the EscrowManager, sets immutable addresses for the escrow vote manager, delegation manager, EYWA token, collection, wallet factory, and metadata provider, and initializes the NFT name and symbol.
External Functions (Defined by IEscrowManager)
createLock(uint256 lockAmount_, uint256 lockDuration_, address recipient_)
createLock(uint256 lockAmount_, uint256 lockDuration_, address recipient_)
Description:
Creates a lock by transferring lockAmount_
of EYWA from the caller, locking it for lockDuration_
(rounded down to epoch boundaries). Mints an NFT for recipient_
.
createLock(uint256 lockDuration_, address recipient_, address[] calldata vestingWallets_)
createLock(uint256 lockDuration_, address recipient_, address[] calldata vestingWallets_)
Description: Creates a lock by transferring tokens from multiple vesting wallets owned by the caller, then mints an NFT lock. Each vesting wallet's beneficiary must match the caller.
createLock(uint256 lockDuration_, address recipient_, uint256[] calldata collectionTokenIds_)
createLock(uint256 lockDuration_, address recipient_, uint256[] calldata collectionTokenIds_)
Description:
Creates a lock using tokens associated with the given collection token IDs. Collects vesting wallets pinned to those NFTs, locks their balances, and automatically applies boosters via _modifyBoost
.
deposit(uint256 tokenId_, uint256 lockAmount_)
deposit(uint256 tokenId_, uint256 lockAmount_)
Description:
Adds extra EYWA to an existing lock. Requires caller authorization (_checkAuthorized
).
depositFor(uint256 tokenId_, uint256 lockAmount_)
depositFor(uint256 tokenId_, uint256 lockAmount_)
Description: Adds EYWA to an existing lock on behalf of the owner, without caller checks.
boost(uint256 tokenId_, uint256[] calldata collectionTokenIds_)
boost(uint256 tokenId_, uint256[] calldata collectionTokenIds_)
Description: Associates additional booster NFTs with a lock, increasing its coverage and potentially its voting power. Booster NFTs may also bring vesting wallets if pinned.
deboost(uint256 tokenId_, uint256[] calldata collectionTokenIds_)
deboost(uint256 tokenId_, uint256[] calldata collectionTokenIds_)
Description:
Removes booster NFTs from a lock. Verifies that the token hasn't voted in the current epoch. Records the block timestamp in s_lastDeboostTimestampByTokenId
to freeze transfers for a defined period.
withdraw(uint256 tokenId_)
withdraw(uint256 tokenId_)
Description: Withdraws the tokens from an expired lock, returning booster NFTs, transferring vesting wallets, and burning the lock NFT. The caller must be authorized.
Events:
Emits
LockWithdrawn(recipient, tokenId, value)
upon completion.
extend(uint256 tokenId_, uint256 lockDuration_)
extend(uint256 tokenId_, uint256 lockDuration_)
Description:
Extends the lock duration to a new unlockTime
. Must be strictly greater than the current one. Updates voting power.
setVoteStatus(uint256 tokenId_, bool status_)
setVoteStatus(uint256 tokenId_, bool status_)
Description:
Only callable by ESCROW_VOTE_MANAGER
. Marks a token as having voted or not.
registerTokenVote(uint256 tokenId_, bool status_)
registerTokenVote(uint256 tokenId_, bool status_)
Description:
Only callable by ESCROW_VOTE_MANAGER
. Records that a token has voted (or not) in the current epoch.
moveVotes(uint256 tokenId_, address from_, address to_)
moveVotes(uint256 tokenId_, address from_, address to_)
Description:
Called by DELEGATION_MANAGER
to transfer voting power if delegated. Ensures the token exists.
getLockInfoByTokenId(uint256 tokenId_)
getLockInfoByTokenId(uint256 tokenId_)
Description: Returns the lockβs current amount, unlock timestamp, and associated vesting wallets.
getRemainingFreezeTimeByTokenId(uint256 tokenId_)
getRemainingFreezeTimeByTokenId(uint256 tokenId_)
Description: Reports how many seconds remain until a token can be transferred again after deboost. Returns 0 if the freeze period has elapsed.
getCheckpoint(address account_, uint32 index_)
getCheckpoint(address account_, uint32 index_)
Description:
Retrieves the checkpoint at index_
for account_
, listing token ownership at that historical checkpoint.
getPastVotes(address account_, uint256 timestamp_)
getPastVotes(address account_, uint256 timestamp_)
Description:
Calculates an accountβs total voting power at a previous timestamp_
, summing the power of all tokens they held at that time.
getTotalSupply()
getTotalSupply()
Description: Returns the total voting power across all locks at the current time.
getPastTotalSupply(uint256 timestamp_)
getPastTotalSupply(uint256 timestamp_)
Description:
Returns the total voting power at a specific timestamp_
. Uses the VotingPowerHelper
for historical calculations.
getPastVotesByTokenId(uint256 tokenId_, uint256 timestamp_)
getPastVotesByTokenId(uint256 tokenId_, uint256 timestamp_)
Description:
Computes a single tokenβs voting power at a past timestamp_
, factoring in rate-of-change data.
getBoostersByTokenId(uint256 tokenId_)
getBoostersByTokenId(uint256 tokenId_)
Description:
Lists all booster NFT IDs currently enhancing the lock identified by tokenId_
.
checkAuthorized(address owner_, address spender_, uint256 tokenId_)
checkAuthorized(address owner_, address spender_, uint256 tokenId_)
Description:
Checks if spender_
is permitted (owner, approved address, or operator) to manage the NFT for tokenId_
.
exists(uint256 tokenId_)
exists(uint256 tokenId_)
Description:
Returns true if an NFT for tokenId_
has been minted (i.e., the lock exists).
Internal and Private Functions
_createLock(...)
_createLock(...)
Signature:
Description: Core logic for creating a new lock. Mints an NFT, stores lock info, and transfers tokens or vesting wallets if needed.
_deposit(...)
_deposit(...)
Signature:
Description: Adds more EYWA to a lock, updating voting power calculations.
_modifyBoost(...)
_modifyBoost(...)
Signature:
Description: Adds or removes booster NFTs to/from a lock. Adjusts coverage and recalculates voting power accordingly.
_updateLock(...)
_updateLock(...)
Signature:
Description: Recomputes and stores new voting power points whenever a lockβs amount or unlock time changes. Adjusts historical checkpoints and the global epoch.
_moveVotes(...)
_moveVotes(...)
Signature:
Description:
Reassigns voting power from from_
to to_
if the NFTβs ownership changes. Creates or updates checkpoints for both addresses.
_clearStorage(...)
_clearStorage(...)
Signature:
Description: Clears all references and data for a withdrawn or burned lock, freeing storage.
_findCheckpoint(...)
_findCheckpoint(...)
Signature:
Description: Helper to find or create the index for a checkpoint at the current timestamp for a given account.
_calculateModifiedLockAmount(...)
_calculateModifiedLockAmount(...)
Signature:
Description: Calculates the final lock amount after factoring in booster NFT coverage and rarity-based multipliers. If coverage exceeds lock amount, the algorithm partial-applies boosters in descending rarity.
Events
LockWithdrawn(address indexed recipient, uint256 indexed tokenId, uint256 indexed value)
LockWithdrawn(address indexed recipient, uint256 indexed tokenId, uint256 indexed value)
Emitted When: A user withdraws tokens from an expired lock. The NFT is burned, and booster NFTs/vesting wallets are returned.
Errors
InvalidBeneficiary()
InvalidVestingWallet()
InvalidLockDuration()
InvalidLockAmount()
InvalidCollectionTokenId(uint256 tokenId)
InvalidArrayLength()
InvalidRarity()
NonExistentToken()
ExtendedUnlockTimeMustBeGreater()
AttachmentsLimitExceeded()
LockStillActive()
LockCurrentlyVoting()
MaximumNumberOfDelegatesExceeded()
BoostModificationAfterVoting()
UnauthorizedCaller()
TransferFrozenDueToDeboost()
These errors ensure that contract rules regarding lock durations, amounts, boosters, freeze periods, and ownership checks are strictly enforced.
Summary
EscrowManager provides a robust system for locking EYWA tokens in exchange for NFT-based positions (veEYWA). Each lock can combine direct token transfers, vesting wallets, and booster NFTs, forming dynamic voting power that evolves over time. Freeze periods after deboosts and careful checkpointing logic protect against immediate manipulations of lock amounts and ownership. Through a clear interface defined in IEscrowManager
, external ecosystem components (e.g., delegation managers, vote managers, NFT collections) can seamlessly integrate with this locking mechanism to support governance, incentive distribution, and user engagement within the EYWA platform.
Last updated