Yield Vault Staking Contract Manual
Contents of Contract Manual
3. Contract Lifecycle & Epoch Logic 3.1. Epoch Duration & Alignment with Flare 3.2. Chunk-Based Activation (Pending → Active) 3.3. Multi-Token Fee Distribution & Finalization 3.4. FOTON as the Staked Token and Optional Reward Token 3.5. Reward Retention & Data Pruning
4. User Actions 4.1. Staking FOTON 4.2. Withdrawing Pending Stake 4.3. Unstaking Active Stake 4.4. Claiming Rewards 4.5. Additional Utility Functions
5. Admin & Owner-Only Actions 5.1. Adding & Removing Reward Tokens 5.2. Setting the Minimum Stake Amount 5.3. Timelocked Emergency Withdrawals 5.4. Pausing and Unpausing the Vault
7. Data Structures & Storage 7.1. Epoch Tracking 7.2. Stakes 7.3. Rewards 7.4. Timelock Mechanism 7.5. Additional Aggregator Structures
8. Deployment & Initialization 8.1. Constructor Logic 8.2. External Address Setup
9. Example User Journeys 9.1. A Single User's Process for Token Allocation and Fee Distribution 9.2. Multiple Users & Chunk-Based Activation 9.3. Unstaking Mid-Epoch 9.4. Claiming All Rewards 9.5. Emergency Withdrawal Process
10. Error Conditions & Reverts
1. Overview
Yield Vault is a mechanism within the Flareporium ecosystem that allows FOTON holders to deposit tokens. Participants may receive a share of platform-generated fees in the form of ERC-20 tokens, which are distributed according to platform activity. The vault aligns its internal epoch system with an external epoch provider (via the IFlareEpoch
interface) and finalizes fee distributions every epoch. Key features include:
Pending-to-Active Stake Activation: New deposits enter a pending state and become active on the next epoch boundary.
Multi-Token Fee Distributions: The vault supports the redistribution of platform-generated fees across multiple ERC-20 tokens.
Epoch Synchronization & Finalization: The vault monitors external epochs, finalizes epochs when new epochs are detected, and properly allocates newly arrived rewards.
Emergency Controls & Pausability: Owner-only timelocked emergency withdrawal functions and pausable operations provide security and control.
Auto-Wrapping: Incoming native FLR is automatically wrapped into WNAT.
2. Key Concepts & Terminology
Epoch: A discrete period synchronized with the external epoch provider. Each epoch finalizes fee distributions based on the active stake.
Pending Stake: Newly deposited FOTON enters a pending state. Pending stakes do not earn rewards until activated in the following epoch.
Active Stake: Once pending stakes are activated, they become active and participate in the fee distribution until unstaked.
Reward Tokens: Tokens distributed as rewards each epoch. WNAT is added by default, but the owner can add additional tokens.
Forfeiture: When a user unstakes active FOTON mid-epoch, that portion is forfeited for the current epoch’s rewards.
Epoch Finalization: The process that locks in reward parameters (such as the accumulated reward per share) for an epoch.
Data Pruning: To conserve storage, epoch data older than a set depth (26 epochs) is pruned. Any leftover rewards from pruned epochs are reallocated if applicable.
Timelock: A security delay (2 days) enforced on emergency withdrawals to allow community oversight.
3. Contract Lifecycle & Epoch Logic
3.1. Epoch Duration & Alignment with Flare
The vault does not manage time-based epochs internally. Instead, it continuously polls an external epoch provider (via the
IFlareEpoch.getCurrentRewardEpochId()
function).On detecting a new external epoch, the vault finalizes the previous epoch and advances its internal epoch counters:
currentEpochId
is updated to match the external epoch.lastFinalizedEpochId
records the most recent epoch that has been finalized.
3.2. Chunk-Based Activation (Pending → Active)
Newly staked FOTON is initially recorded in the
pendingStake
mapping.On an epoch transition, the internal function
_activatePendingStakers()
processes pending stakers in manageable batches (up to a constantMAX_ACTIVATIONS
, currently 250) to convert pending stakes into active stakes.Once activated, a user’s active stake is recorded and will participate in the epoch’s fee distribution.
3.3. Multi-Token Fee Distribution & Finalization
At the end of each epoch, the vault calculates the newly arrived rewards for each recognized token by comparing the current balance (after exclusions) with a previously recorded value.
For each reward token:
The contract determines the distribution rate of collected platform fees per participant (
epochAccRewardPerShare
) based on the total active stake.It records any leftover tokens in
epochTokenLeftover
for adjustments and reallocation.
Exclusion of Staked FOTON from Reward Calculations:
To prevent double counting, the contract excludes the total staked FOTON from the available balance when FOTON is considered as a reward token.
The function
_getCurrentBalanceExcludingStake(address token)
handles this:If the token is not FOTON, it simply returns the entire balance of that token held by the contract.
If the token is FOTON, it subtracts the total staked FOTON (both active and pending) from the contract’s balance.
Only any "surplus" FOTON (i.e., FOTON not already allocated as a stake) is considered as newly arrived reward, ensuring that staked tokens aren’t inadvertently counted as rewards.
Reward calculations also account for any forfeited stake (from mid-epoch unstaking) using dedicated functions.
3.4. FOTON as the Staked Token and Optional Reward Token
FOTON is the token that users stake. It does not automatically function as a reward token.
However, if desired, the owner may add FOTON to the recognized reward tokens list via the
addRewardToken
function.By default, only WNAT is pre-added as a reward token in the constructor.
Note: When FOTON is optionally added as a reward token, its reward calculation follows the mechanism detailed in Section 3.3, where the contract excludes any staked FOTON from the balance used to calculate rewards.
3.5. Reward Retention & Data Pruning
The vault retains detailed epoch data for a fixed number of epochs. In this version, epochs older than 26 (defined by
EPOCH_PRUNE_DEPTH
) are pruned.During pruning, any unclaimed or leftover rewards are reallocated to the most recent finalizable epoch (typically
flareEpoch - 1
), ensuring that stale rewards are not lost.
4. User Actions
4.1. Staking FOTON
Function:
stake(uint256 amount)
Process:
The user must approve the vault to spend their FOTON.
Upon calling
stake
, the specified amount (which must be nonzero and, if a minimum stake is set, at leastminStakeAmount
) is transferred from the user to the vault.The deposited FOTON is recorded in the user’s
pendingStake
balance and the user is added to thependingStakers
list if not already present.
4.2. Withdrawing Pending Stake
Function:
withdrawPendingStake(uint256 amount)
Process:
If a user decides not to wait for activation, they may withdraw any portion (or the entirety) of their pending stake.
If withdrawing a partial amount results in a remaining balance below
minStakeAmount
, the entire pending stake may be withdrawn to avoid unusable leftovers.
4.3. Unstaking Active Stake
Function:
unstake(uint256 amount)
Process:
Active staked FOTON can be withdrawn by calling
unstake
with a positive amount.The function deducts the unstaked amount from the user’s active stake and the global total.
The unstaked amount is immediately transferred back to the user.
Importantly, any amount unstaked during an epoch forfeits its share of rewards for that epoch; the corresponding forfeiture is recorded against the user’s epoch info.
4.4. Claiming Rewards
Function:
claimAll()
Process:
This function claims all rewards for the caller across all epochs that have been finalized (and not pruned).
The rewards for each recognized token are calculated based on the user’s effective stake (active stake minus any forfeited portion).
Upon a successful claim, the appropriate reward token amounts are transferred to the user.
Claiming also marks the user’s epoch stake as claimed to prevent double claims.
4.5. Additional Utility Functions
The contract includes several read-only functions to aid in data retrieval and aggregation:
Aggregators:
getUserDataAcrossEpochs
,getGlobalDataAcrossEpochs
,getEverything
, andgetEpochDetails
return detailed views of a user’s staking data and epoch-by-epoch reward information.
Balances & Rewards:
getRewardTokens
returns the list of recognized reward tokens.getUserTotalUnclaimed
provides an aggregated view of pending rewards across epochs.
5. Admin & Owner-Only Actions
5.1. Adding & Removing Reward Tokens
Adding Reward Tokens:
Function:
addRewardToken(address token)
The owner can designate additional ERC-20 tokens for fee distribution.
Removing Reward Tokens:
Function:
removeRewardToken(address token)
Tokens may be removed from the recognized list except for WNAT, which is permanently retained.
5.2. Setting the Minimum Stake Amount
Function:
setMinStakeAmount(uint256 _amount)
The owner may update the minimum FOTON deposit required from users. This ensures that very small stakes are avoided.
5.3. Timelocked Emergency Withdrawals
Functions:
scheduleEmergencyWithdraw(address token, address to, uint256 amount, bool isNative)
cancelEmergencyWithdraw()
executeEmergencyWithdraw()
Process:
The owner can schedule an emergency withdrawal by specifying the token, recipient address, amount, and whether the token is native (the parameter is named
isNative
).A timelock delay of 2 days is imposed before the withdrawal can be executed, allowing time for oversight.
The owner may cancel a pending request before execution.
5.4. Pausing and Unpausing the Vault
Functions:
pause()
andunpause()
The owner can pause user-facing operations (staking, unstaking, claiming) during emergencies while retaining the ability to execute administrative functions.
6. Pausable Feature
The contract inherits from OpenZeppelin’s Pausable.
When paused, functions related to staking, unstaking, and reward claiming will revert.
The pause mechanism provides an important safety valve during unforeseen issues or attacks.
7. Data Structures & Storage
7.1. Epoch Tracking
Variables:
currentEpochId
: The current internal epoch, synchronized with the external provider.lastFinalizedEpochId
: The most recently finalized epoch.epochs
: A mapping where each epoch record contains:A boolean
finalized
flag.totalActiveStake
for that epoch.
Pruning:
The constant
EPOCH_PRUNE_DEPTH
(set to 26) defines how many epochs are retained. Epochs older than this depth are pruned and their leftover rewards reallocated if necessary.
7.2. Stakes
Mappings:
pendingStake
: Records FOTON amounts deposited by users but not yet activated.activeStake
: Tracks the user’s active stake which participates in rewards.
Global Variables:
totalActiveStake
andtotalPendingFotonStake
aggregate user stakes.
Activation Queue:
pendingStakers
(an array) and theinPendingList
mapping are used to manage and process pending stakers in batches.
7.3. Rewards
Reward Tokens:
The array
rewardTokens
and the mappingisRewardToken
manage the list of tokens eligible for rewards.
Reward Accounting:
totalRecognizedForToken
tracks the cumulative rewards recognized for each token.epochAccRewardPerShare
stores the accumulated reward per share for each token and epoch.userRewardDebt
tracks the reward debt for each user per token and epoch.epochTokenLeftover
maintains any leftover reward tokens for reallocation.
7.4. Timelock Mechanism
Structure:
TimelockRequest
records the parameters for an emergency withdrawal (token, recipient, amount, whether native, and the earliest execution time).
Delay:
A constant
TIMELOCK_DELAY
of 2 days is enforced.
7.5. Additional Aggregator Structures
The contract defines several structs (such as
TokenRewardEpochData
,UserEpochData
,TokenBalances
,VaultGlobalData
, and others) to facilitate comprehensive data retrieval via its view functions.
8. Deployment & Initialization
8.1. Constructor Logic
The constructor accepts three addresses:
The external epoch provider (conforming to
IFlareEpoch
).The wrapped native token (WNAT).
The FOTON token.
It initializes the epoch counters by querying the external epoch provider.
WNAT is added by default to the recognized reward tokens.
8.2. External Address Setup
Unlike previous versions that hard-coded addresses, the current contract receives all key external addresses (epoch provider, WNAT, and FOTON) via the constructor parameters.
This design improves flexibility and reusability across different network environments.
9. Example User Journeys
9.1. A Single User's Process for Token Allocation and Fee Distribution
Approval & Deposit:
The user approves the vault to deposit their FOTON and calls
stake(amount)
.
Pending State:
The deposited amount is recorded in the user’s pending stake.
Epoch Transition:
On the next epoch boundary (triggered by any function call that synchronizes epochs), the pending stake is activated.
Reward Accrual & Claim:
The user’s active stake may receive a proportional share of collected platform fees during the epoch. The
claimAll()
function allows users to retrieve their calculated share.
9.2. Multiple Users & Chunk-Based Activation
Numerous users deposit FOTON concurrently, populating the
pendingStakers
list.During epoch synchronization, up to 250 pending stakers are processed in a single batch, converting their pending stakes to active stakes.
Remaining pending stakers will be processed in subsequent activations.
9.3. Unstaking Mid-Epoch
A user calls
unstake(amount)
to withdraw part of their active stake.The specified amount is removed from their active stake, and the user immediately receives the tokens.
The withdrawn amount forfeits its share of rewards for the current epoch.
9.4. Claiming All Rewards
A user can call
claimAll()
at any time to retrieve their share of platform fee distributions from finalized epochs (subject to pruning limits).The function iterates over eligible epochs, calculates pending rewards (accounting for any forfeitures), and transfers the tokens.
9.5. Emergency Withdrawal Process
Scheduling:
The owner calls
scheduleEmergencyWithdraw
with the target token, recipient, amount, and whether the token is native.A timelock is set (2 days delay).
Cancellation (Optional):
The owner may cancel the scheduled withdrawal via
cancelEmergencyWithdraw
before execution.
Execution:
After the delay,
executeEmergencyWithdraw
can be called by the owner to complete the transfer.
10. Error Conditions & Reverts
Zero Amounts:
Functions revert if the provided amount is zero (e.g.,
"Zero stake"
or"Zero unstake"
).
Minimum Stake:
Deposits below the
minStakeAmount
(when set) will revert with an error (e.g.,"Below minStake"
).
Insufficient Balance:
Withdrawals of pending or active stake will revert if the user does not have sufficient balance.
Pausable State:
When the vault is paused, calls to stake, unstake, or claim will revert.
Timelock Violations:
Emergency withdrawals cannot be executed before the timelock delay has passed, and attempts to schedule a new withdrawal while one is pending will revert.
11. Security & Best Practices
Reentrancy Protection:
All functions that transfer tokens (e.g. stake, unstake, claim) are protected with OpenZeppelin’s ReentrancyGuard to prevent reentrancy attacks
Pausability:
The owner can pause the contract during emergencies to prevent further deposits, withdrawals, or claims.
Timelock on Critical Functions:
Emergency withdrawals are subject to a 2-day timelock, providing a window for community oversight.
Data Pruning:
By limiting the number of retained epochs (26), the contract reduces long-term storage growth and potential performance issues.
Auto-Wrapping of Native Tokens:
Incoming native FLR is automatically wrapped into WNAT, ensuring consistency in reward token accounting.
Careful Reward Accounting:
The fee distribution mechanism excludes staked FOTON from reward calculations, preventing accidental double-counting.
Administrative Controls:
Owner-only functions (such as pausing, setting minimum stakes, and managing reward tokens) are secured through access control (Ownable).
Conclusion
The Yield Vault contract provides a structured mechanism for FOTON holders to participate in platform activity and receive distributions in multiple ERC-20 tokens. It aligns with external Flare epochs, handles staker onboarding in manageable chunks to mitigate gas spikes, and allows stakers to claim rewards and withdraw any stake as they see fit.
Remember:
Always stake above the
minStakeAmount
.Monitor the vault’s epoch transitions to know when your stake becomes active.
Claim rewards before data pruning (26 epochs) to avoid forfeiture.
In any emergency, watch for timelock announcements of large withdrawals.
We hope this manual provides a clear and comprehensive guide for both end users and technical integrators. If you have any additional questions, please refer to the contract’s source code comments or contact the project maintainers.
Last updated