Stakinator Overview

This document provides a comprehensive guide to the Stakinator facet in the Diamond Vaultinator framework.

Table of Contents

  1. Introduction

  2. Features

  3. Architecture

  4. Usage

  5. Staking Pools

  6. Reward Calculation

  7. Security Considerations

  8. Best Practices

  9. Integration Examples

Introduction

The Stakinator facet extends the Diamond Vaultinator with comprehensive staking functionality, allowing vault owners to stake tokens, earn rewards, and participate in various staking strategies. It provides a flexible system for creating and managing staking pools with customizable parameters.

Features

  • Multiple Staking Pools: Support for multiple token staking pools with different reward rates and lock periods

  • Flexible Locking: Customizable lock periods for staked tokens

  • Reward Distribution: Automated reward calculation and distribution

  • Emergency Withdrawals: Safety mechanism for emergency situations

  • Role-Based Access Control: Secure management of staking pools and parameters

  • Vault Integration: Seamless integration with the Diamond Vaultinator vault system

Architecture

The Stakinator facet follows the Diamond pattern and consists of:

  1. IStakinatorFacet: Interface defining all functions and events

  2. StakinatorFacet: Implementation of the interface

  3. StakinatorStorage: Dedicated storage structure for staking data

Storage Structure

struct StakinatorStorage {
    address rewardToken;
    EnumerableSet.AddressSet activePoolTokens;
    mapping(address => StakingPool) stakingPools;
    bool initialized;
}

struct StakingPool {
    bool active;
    address token;
    uint256 rewardRate; // Basis points per year (e.g., 500 = 5%)
    uint256 totalStaked;
    uint256 lockPeriod; // Lock period in seconds
    uint256 lastUpdateTime;
    mapping(uint256 => UserStake) stakes; // vaultId => UserStake
}

struct UserStake {
    uint256 amount;
    uint256 stakedAt;
    uint256 lockUntil;
    uint256 lastClaimTime;
    uint256 rewardDebt;
}

Access Control

The Stakinator facet uses the following role:

  • STAKING_MANAGER_ROLE: Required for creating and updating staking pools and managing system parameters

Usage

Initialization

Before using the Stakinator facet, it must be initialized:

function initializeStakinatorStorage(address _admin, address _rewardToken) external;

This function:

  • Sets the reward token address

  • Grants the STAKING_MANAGER_ROLE to the admin

  • Marks the storage as initialized

Creating Staking Pools

Staking pools can be created by accounts with the STAKING_MANAGER_ROLE:

function createStakingPool(
    address _token,
    uint256 _rewardRate,
    uint256 _lockPeriod
) external;

Parameters:

  • _token: Address of the token to be staked

  • _rewardRate: Annual reward rate in basis points (e.g., 500 = 5%)

  • _lockPeriod: Duration in seconds that tokens must remain staked

Staking Tokens

Vault owners or approved operators can stake tokens:

function stake(
    uint256 _vaultId,
    address _token,
    uint256 _amount,
    uint256 _lockPeriod
) external;

Parameters:

  • _vaultId: ID of the vault

  • _token: Address of the token to stake

  • _amount: Amount to stake

  • _lockPeriod: Optional custom lock period (0 for default pool period)

Managing Stakes

Users can increase their stakes, withdraw after the lock period, or claim rewards:

// Increase existing stake
function increaseStake(uint256 _vaultId, address _token, uint256 _amount) external;

// Withdraw after lock period
function withdraw(uint256 _vaultId, address _token, uint256 _amount) external;

// Claim rewards without withdrawing
function claimRewards(uint256 _vaultId, address _token) external returns (uint256);

// Emergency withdrawal (may forfeit rewards)
function emergencyWithdraw(uint256 _vaultId, address _token) external;

Viewing Information

The facet provides several view functions to check staking information:

// Check pending rewards
function pendingRewards(uint256 _vaultId, address _token) external view returns (uint256);

// Get staking pool information
function getStakingPoolInfo(address _token) external view returns (...);

// Get user stake information
function getUserStakeInfo(uint256 _vaultId, address _token) external view returns (...);

// Get all active staking pools
function getActiveStakingPools() external view returns (address[] memory);

Staking Pools

Pool Parameters

Each staking pool has the following parameters:

  • Token: The ERC20 token that can be staked

  • Reward Rate: Annual percentage yield in basis points

  • Lock Period: Minimum duration tokens must remain staked

  • Total Staked: Total amount of tokens staked in the pool

Updating Pools

Pool parameters can be updated by accounts with the STAKING_MANAGER_ROLE:

function updateStakingPool(
    address _token,
    uint256 _rewardRate,
    uint256 _lockPeriod
) external;

When updating a pool, all pending rewards are calculated and stored before applying the new parameters.

Reward Calculation

Rewards are calculated based on:

  1. Staked Amount: The amount of tokens staked

  2. Reward Rate: Annual percentage yield in basis points

  3. Time Elapsed: Duration since the last reward calculation

The formula used is:

rewardPerSecond = (rewardRate * stakedAmount) / (10000 * 31536000)
reward = rewardPerSecond * timeElapsed

Where:

  • 10000 is the basis points denominator (100%)

  • 31536000 is the number of seconds in a year (365 days)

Security Considerations

Lock Period Enforcement

The lock period is strictly enforced. Users cannot withdraw staked tokens before the lock period expires unless they use the emergency withdrawal function, which may forfeit rewards.

Emergency Withdrawals

The emergency withdrawal function provides a safety mechanism for users to withdraw their staked tokens in case of emergencies, even if the lock period has not expired. However, this may result in forfeiting accumulated rewards.

Access Control

All administrative functions are protected by role-based access control. Only accounts with the appropriate roles can create or update staking pools or change system parameters.

Best Practices

Setting Appropriate Reward Rates

When setting reward rates, consider:

  • The value of the staked token

  • The value of the reward token

  • Market conditions and competitive rates

  • Sustainability of the reward system

Lock Periods

Choose lock periods that balance:

  • User flexibility

  • Protocol stability

  • Reward economics

Common lock periods range from 7 days to 365 days.

Reward Token Management

Ensure that:

  • The reward token contract is secure and well-audited

  • Sufficient reward tokens are available for distribution

  • The reward distribution is sustainable over time

Integration Examples

Basic Staking Example

// Initialize the Stakinator
stakinatorFacet.initializeStakinatorStorage(admin, rewardTokenAddress);

// Create a staking pool for DAI with 5% APY and 30-day lock
stakinatorFacet.createStakingPool(daiAddress, 500, 30 days);

// Stake 1000 DAI from vault #123
dai.approve(diamondAddress, 1000e18);
stakinatorFacet.stake(123, daiAddress, 1000e18, 0);

// After 30 days, claim rewards
uint256 rewards = stakinatorFacet.claimRewards(123, daiAddress);

// Withdraw staked tokens
stakinatorFacet.withdraw(123, daiAddress, 1000e18);

Staking with Custom Lock Period

// Stake with a custom 60-day lock period
dai.approve(diamondAddress, 1000e18);
stakinatorFacet.stake(123, daiAddress, 1000e18, 60 days);

Emergency Withdrawal

// In case of emergency, withdraw without waiting for lock period
stakinatorFacet.emergencyWithdraw(123, daiAddress);