Oracle Vault Staking Contract Manual


Contents of Contract Manual

1. Overview

2. Key Concepts & Terminology

3. Contract Lifecycle & Epoch Logic 3.1 Epoch Duration & Alignment with Flare 3.2 Chunk‐Based Activation (Pending → Active) 3.3 Reward Distribution & Finalization 3.4 Retention & Housekeeping

4. User Actions 4.1 Staking FOTON 4.2 Reducing / Unstaking 4.3 Computing & Withdrawing Rewards 4.4 Bulk Reward Claims & “Claim‐All” Operations 4.5 Monthly Airdrop Claims (claimMonthlyAirdropsInRange)

5. Admin & Owner‐Only Actions 5.1 Manually Advancing Epoch 5.2 RewardManager Integration & Best‑Effort Synchronization 5.3 Timelocked Emergency Withdrawals 5.4 Delegation Management & Miscellaneous Functions

6. Pausable Feature

7. Data Structures & Storage 7.1 Epoch Tracking 7.2 Stakes 7.3 Rewards 7.4 Timelock 7.5 Miscellaneous

8. Deployment & Initialization

9. Example User Journeys 9.1 Single User Staking Flow 9.2 Multiple Users & Chunk‐Based Activation 9.3 Emergency Timelock Scenario

10. Error Conditions & Reverts

11. Security & Best Practices


Coming Soon! Tests Ongoing!

DO NOT Interact With This Contract Until Given Notice!


1. Overview

Oracle Vault is an epoch‑based staking contract developed by Flareporium that interacts with Flare’s RewardManager to claim WNAT delegation rewards and distributes them to FOTON stakers. It distributes both WNAT and (optionally) FOTON rewards to stakers, and synchronizes local epoch data with the official RewardManager. Key features include:

  • Best-Effort Synchronization: Uses a try/catch mechanism to synchronize epochs without reverting the entire transaction on partial failure. Errors are logged via the LogError event.

  • Chunk‐Based Activation: Pending stakers are activated in batches (up to 250 addresses per call) to mitigate gas issues.

  • Dual Reward Tracking: The contract separately tracks WNAT and FOTON rewards using a fixed-point reward multiplier (1e12) to ensure precise fractional calculations.

  • Timelocked Emergency Withdrawals: Admin functions for emergency withdrawals are subject to a 2‑day timelock, enhancing security.

  • Pausable Operations: User‑facing functions (staking, unstaking, claiming) can be paused in emergencies.


2. Key Concepts & Terminology

  • Epoch: A discrete period tracked by the contract, synchronized with Flare’s RewardManager. A new epoch is triggered when the official epoch increases.

  • Pending Stake: When a user calls stakeFoton, their FOTON tokens are deposited into a pending queue. They become “active” after epoch synchronization.

  • Active Stake: FOTON that is actively earning rewards for the current epoch.

  • RewardManager: The official Flare contract responsible for delegation rewards (WNAT). OracleVault claims rewards from it.

  • Forfeiture: If a user reduces their active stake mid‑epoch via reduceStakeFoton, the reduced amount forfeits its share of that epoch’s rewards.

  • Scaling Factor: A multiplier of 1e12 is used in reward calculations (accumulated reward per share) to maintain precision.

  • Retention: The contract retains epoch data for 26 epochs (defined by EPOCH_RETENTION), after which old data is pruned.

  • Best‑Effort Synchronization: A mechanism that attempts to update epoch data using try/catch blocks.

  • Custom Errors: The contract uses custom error types (e.g., ZeroAmount(), BelowMinStake()) for efficient revert messages.


3. Contract Lifecycle & Epoch Logic

3.1 Epoch Duration & Alignment with Flare

  • OracleVault does not use a fixed timer; instead, it periodically checks rewardManager.getCurrentRewardEpochId().

  • Important: In order for the local epoch to advance, OracleVault must first successfully claim FTSO delegation rewards from the RewardManager. The contract calls the claim(...) function to process these rewards, and only upon successful processing of the claimable rewards does it update its local epoch data.

  • When a new official epoch is detected (i.e., when the official epoch exceeds lastFlareEpoch and rewards have been successfully claimed), the contract:

    • Finalizes the current local epoch (distributing rewards),

    • Updates lastFlareEpoch and currentEpochId,

    • Sets lastFinalizedEpochId to the previous epoch.

3.2 Chunk‐Based Activation (Pending → Active)

  • New stakers are added to the pendingStake mapping and pendingStakers array via stakeFoton.

  • During synchronization, the internal function _activatePendingStakers() processes up to 250 addresses per call:

    • Pending amounts are transferred to active stake,

    • Global totals (totalActiveStake) and per-user epoch snapshots (userEpochStake) are updated,

    • Reward debt values for both WNAT and FOTON are computed for the upcoming epoch.

3.3 Reward Distribution & Finalization

  • General Process:

    • The contract determines new rewards from the RewardManager.

    • These rewards are then distributed among stakers.

    • The process now covers both WNAT and FOTON rewards.

  • WNAT Rewards:

    • Collection:

      • The contract calls the RewardManager’s claim(...) function to collect delegation rewards in WNAT.

    • Distribution:

      • The collected WNAT rewards are allocated across stakers.

      • Allocation uses an accumulated reward per share variable to ensure precise fractional reward calculations.

  • FOTON Rewards:

    • Finalization Update:

      • In addition to claiming WNAT rewards, the updated contract now finalizes FOTON rewards at the end of an epoch.

    • Process Details:

      • After pending stakers are activated and WNAT rewards are processed:

        • The contract calls the new function _addFotonRewardsToEpoch(currentEpochId - 1).

    • Function Responsibilities:

      • Surplus Calculation:

        • Calculates the “surplus” of FOTON by subtracting the total staked FOTON (i.e., totalFotonStaked) from the contract’s overall FOTON balance.

      • Recycled Rewards Addition:

        • Adds any recycled rewards obtained by the new _computeRecycledFotonRewards() function.

      • Updating Reward Metrics:

        • The total (direct surplus + recycled rewards) is used to update the epoch’s accumulated FOTON reward per share (epochAccFotonRewardPerShare).

        • This ensures stakers receive their proportional share of FOTON rewards.

3.4 Retention & Housekeeping

  • The contract retains detailed data for the most recent 26 epochs, as defined by the constant EPOCH_RETENTION. After 26 epochs, older data is pruned to save storage. Users must claim rewards within this retention window to receive their full allocation.

  • FOTON Rewards Recycling: In the updated contract, FOTON rewards that remain unclaimed beyond the 26-epoch retention period are not discarded. Instead, they are recycled and incorporated into the next epoch’s reward calculation. This recycling is handled automatically during epoch finalization, so stakers benefit from any surplus FOTON available—even if some rewards were not claimed in earlier epochs.


4. User Actions

4.1 Staking FOTON

  • Process:

    • Approve the contract to spend your FOTON.

    • Call stakeFoton(uint256 amount) with an amount ≥ minStakeAmount (and nonzero).

    • The tokens are transferred to the contract and recorded in pendingStake and pendingStakers.

    • Total staked tokens (totalFotonStaked) are updated.

  • Reverts:

    • If amount < minStakeAmount → custom error BelowMinStake().

    • If amount == 0 → custom error ZeroAmount().

4.2 Reducing / Unstaking

  • Reducing Stake (reduceStakeFoton):

    • Call reduceStakeFoton(uint256 reduceAmt) to withdraw part of your active stake.

    • The function deducts reduceAmt from activeStake, updates totalActiveStake, and records forfeiture (in forfeitedStakeThisEpoch).

    • The withdrawn tokens are transferred back to your wallet.

  • Full Unstaking (unstakeFoton):

    • Call unstakeFoton() to withdraw your entire active stake. This internally calls reduceStakeFoton with your full active balance.

  • Note: Any stake reduced mid‑epoch forfeits its share of rewards for that epoch.

4.3 Computing & Withdrawing Rewards

  • Reward Computation: Functions such as computeEpochReward(uint256 epochId) and internal methods (e.g., _ensureEpochRewardsComputed) calculate your pending rewards for a specific epoch based on:

    • Your effective stake (active stake minus any forfeited amount),

    • The epoch’s accumulated reward per share (for both WNAT and FOTON), and

    • Your reward debt.

  • Withdrawing Rewards:

    • Use withdrawEpochReward(uint256 epochId) to transfer your computed rewards to your wallet.

    • Rewards for both WNAT and FOTON (if applicable) are processed separately.

4.4 Bulk Reward Claims & “Claim‐All” Operations

  • Bulk Claiming:

    • Call claimAllEpochRewards(uint256 startEpoch, uint256 endEpoch) to claim rewards for a range of epochs.

    • Alternatively, use claimAllForUser() or claimAndFinalizeAll() as convenience functions to claim all unclaimed rewards within the retention window.

  • Note: Reward data is maintained only for the last 26 epochs.

4.5 Monthly Airdrop Claims (claimMonthlyAirdropsInRange)

  • Process:

    • The function claimMonthlyAirdropsInRange(address _rewardOwner, uint256 startMonth, uint256 endMonth, bool _wrap) allows you to claim a series of monthly airdrops in one call.

    • The function loops over the specified month range and attempts to claim for each month. Partial failures do not revert the entire transaction.

  • Outcome: Claimed amounts are added to the contract’s balance (and may be auto-wrapped into WNAT if _wrap is false).


5. Admin & Owner‐Only Actions

5.1 Manually Advancing Epoch

  • Auto‑Advance Function:

    • Call autoAdvanceEpochPublic() (available to anyone) to trigger epoch synchronization.

    • This function uses the best‑effort synchronization (via the bestEffortSync modifier) to update epoch data if a new official epoch is available.

  • Note: The contract does not expose a separate “advanceEpoch” function; epoch updates occur as part of routine calls.

5.2 RewardManager Integration & Best‑Effort Synchronization

The synchronization process between OracleVault and Flare’s RewardManager is executed via the internal function _fullSynchronize(). This process includes:

  • Retrieving the current official epoch using getCurrentRewardEpochId() and determining the next claimable epoch with getNextClaimableRewardEpochId().

  • Iterating over claimable epochs and calling claim(...) to collect WNAT rewards.

  • During the synchronization routine, after processing WNAT rewards, the contract updates the finalized epoch’s FOTON rewards by calling _addFotonRewardsToEpoch(). This change ensures that both WNAT and FOTON rewards are distributed as part of the epoch finalization process.

  • Activating pending stakers in manageable batches (up to 250 addresses per call) and updating local epoch data accordingly.

  • Logging any errors encountered during synchronization via the LogError event so that partial failures do not revert the entire transaction.

5.3 Timelocked Emergency Withdrawals

  • Scheduling:

    • Use scheduleEmergencyWithdraw(address token, address to, uint256 amount, bool isEther) to schedule an emergency withdrawal.

    • A 2‑day timelock (defined by TIMELOCK_DELAY) is applied.

  • Cancelling & Executing:

    • cancelEmergencyWithdraw() cancels a pending withdrawal.

    • executeEmergencyWithdraw() finalizes the withdrawal after the timelock expires.

  • Reverts:

    • Custom errors (e.g., ZeroAddress(), ActiveRequestExists(), NoActiveRequest(), NotYetAllowed(), EtherTransferFailed()) are used to enforce conditions.

5.4 Delegation Management & Miscellaneous Functions

  • Delegation:

    • delegateWnat(address provider, uint256 bips) delegates a portion of the vault’s WNAT voting power to a provider.

    • undelegateAll() removes all active delegations.

    • Note: The previous batch delegation function (batchDelegate) has been removed.

  • Token Redirection:

    • setYeildVaultAddress(address _redirectAddress) sets the address where extraneous ERC20 tokens (other than FOTON or WNAT) should be sent.

    • redirectERC20ToYieldVault(address tokenAddress) moves any such tokens from the contract to the designated yield vault address.


6. Pausable Feature

  • The contract inherits from OpenZeppelin’s Pausable.

  • The owner can call pause() and unpause() to stop or resume user‑facing functions (e.g., staking, unstaking, claiming rewards).

  • Administrative functions (like delegation or scheduling emergency withdrawals) remain available while paused.


7. Data Structures & Storage

7.1 Epoch Tracking

  • Key Variables:

    • currentEpochId: The contract’s local epoch, updated to match Flare’s RewardManager.

    • lastFinalizedEpochId: The most recent epoch for which rewards have been finalized.

    • lastFlareEpoch: The last official Flare epoch processed.

    • contractStartEpoch: The epoch at which the contract was deployed.

  • Aggregation:

    • Epoch data is used to calculate reward distribution and is pruned after 26 epochs (defined by EPOCH_RETENTION).

7.2 Stakes

  • User Stakes:

    • pendingStake[user]: Tokens awaiting activation.

    • activeStake[user]: Tokens actively earning rewards.

    • forfeitedStakeThisEpoch[epochId][user]: Tracks amounts reduced mid‑epoch (and thus forfeited rewards).

  • Global Totals:

    • totalFotonStaked: Overall FOTON staked in the contract.

    • totalActiveStake: Sum of all active stakes.

7.3 Rewards

  • WNAT Rewards:

    • epochAccRewardPerShare[epochId]: Accumulated reward factor for WNAT.

    • userEpochRewards[epochId][user] and userRewardDebt[epochId][user]: Track user-specific WNAT reward allocation.

    • epochTotalDebt[epochId]: Total WNAT allocated for an epoch.

  • FOTON Rewards:

    • Similar mappings exist for FOTON rewards: epochAccFotonRewardPerShare, userEpochFotonRewards, userFotonRewardDebt, and epochFotonTotalDebt.

  • Reward Multiplier:

    • A scaling factor of 1e12 is used to preserve precision in reward calculations.

7.4 Timelock

  • Structure:

    • The TimelockRequest struct stores details for emergency withdrawals (token, recipient, amount, earliest execution time, and whether the asset is Ether).

  • Enforcement:

    • A constant TIMELOCK_DELAY of 2 days ensures a delay before withdrawal execution.

7.5 Miscellaneous

  • Additional Variables:

    • Variables such as oldFotonRewardBalance, totalDelegationRewards, and others track historical and cumulative reward data.

  • Aggregator Functions:

    • Functions like getGlobalDataAcrossEpochs, getRewardManagerUnclaimedEpochsData, and getEverything provide comprehensive views of global and user-specific data.


8. Deployment & Initialization

  • Deployment:

    • The OracleVault is deployed with references to the FOTON, WNAT, and RewardManager contracts.

    • Hard‑coded addresses for these tokens (and for the airdrop distribution contract) are provided.

  • Initialization:

    • If rewardManager.active() returns true, the contract initializes lastFlareEpoch, currentEpochId, and lastFinalizedEpochId based on the current RewardManager epoch; otherwise, it starts at epoch 1.

  • Owner Setup:

    • The owner can subsequently set parameters such as minStakeAmount and configure delegation or token redirection.


9. Example User Journeys

9.1 Single User Staking Flow

  • Approve OracleVault to spend your FOTON.

  • Call stakeFoton(amount) with an amount greater than or equal to minStakeAmount.

  • Your tokens are added to the pending queue. When a new official epoch is detected, they are activated.

  • Your active stake earns rewards (both WNAT and, if applicable, FOTON).

  • You later compute and withdraw your rewards via computeEpochReward and withdrawEpochReward.

9.2 Multiple Users & Chunk‐Based Activation

  • Many users call stakeFoton, populating the pendingStakers array.

  • During synchronization, up to 250 stakers are activated per call via _activatePendingStakers().

  • If some stakers remain pending, further synchronization calls are needed before finalizing the epoch.

  • Once all stakers are activated, the epoch finalizes and rewards are allocated accordingly.

9.3 Emergency Timelock Scenario

  • The owner calls scheduleEmergencyWithdraw to initiate an emergency withdrawal (of Ether or an ERC20 token), triggering a 2‑day timelock.

  • During the delay, stakers can monitor and, if necessary, adjust their positions.

  • If required, the owner may cancel the withdrawal via cancelEmergencyWithdraw().

  • After 2 days, executeEmergencyWithdraw() is called to transfer the funds.


10. Error Conditions & Reverts

  • Custom Errors: The contract employs custom errors (e.g., ZeroMinStake(), ZeroAmount(), BelowMinStake(), NotEnoughActiveStake(), NoFotonArrived()) for gas‑efficient reverts.

  • Staking:

    • Staking with zero amount or below minStakeAmount will revert.

  • Unstaking:

    • Attempts to unstake more than the active stake or unstake when no active stake exists revert.

  • Epoch & Reward Functions:

    • Incorrect epoch ranges or calls for unfinalized epochs will revert (e.g., InvalidEpochId(), InvalidEpochRange()).

  • Emergency Withdrawals:

    • Reverts occur if a withdrawal is scheduled with a zero address, if an active request exists, or if executed before the timelock expires.

  • Airdrop & Delegation:

    • Reverts for invalid month ranges or for trying to redirect FOTON/WNAT tokens are enforced.


11. Security & Best Practices

  • Reentrancy Protection: User‑facing functions that move tokens are protected by OpenZeppelin’s ReentrancyGuard.

  • Pausability: The owner can pause the contract to prevent new stakes or claims during emergencies.

  • Best‑Effort Synchronization: Epoch data is updated using try/catch to ensure that partial failures do not revert entire transactions. Errors are logged via the LogError event.

  • Timelocked Emergency Withdrawals: A 2‑day delay on critical admin withdrawals provides a window for stakers to react.

  • Accurate Reward Accounting: The contract excludes staked FOTON from reward calculations to prevent double counting. The use of a 1e12 scaling factor ensures precise fractional math.

  • Data Retention: With epoch data retained for 26 epochs, users should claim rewards promptly to avoid loss due to pruning.


Conclusion

OracleVault aligns with Flare’s official RewardManager to facilitate a robust, epoch‑based staking experience for FOTON holders. It handles:

  • Auto‑Epoch Transitions: Synchronizes with the official epoch using best‑effort, try/catch–based logic.

  • Chunk‑Based Staker Activation: Processes pending stakers in manageable batches to mitigate gas issues.

  • Dual Reward Distribution: Separately tracks and distributes WNAT and FOTON rewards with precision.

  • Robust Error Handling: Uses custom errors and logs errors via the LogError event.

  • Pausable Operations & Timelocked Emergencies: Provides safety controls that allow the owner to pause operations and schedule emergency withdrawals with a 2‑day delay.

  • Comprehensive Data Aggregation: Aggregator functions offer detailed insights into global and user‑specific vault data.

Stakers should:

  • Always stake above the minStakeAmount.

  • Monitor epoch transitions to know when pending stakes are activated.

  • Claim rewards regularly (within the 26‑epoch retention period) to avoid forfeiture.

  • Be aware that mid‑epoch stake reductions forfeit rewards for that portion.


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.

Happy staking!



Last updated