Stakinator Technical Details

The Stakinator Facet is a core component of the Vaultinator Protocol that provides staking functionality. This document outlines the technical implementation details, including the contract architecture, function specifications, storage layout, and security considerations.

Contract Architecture

The Stakinator Facet follows the Diamond Standard (EIP-2535) pattern and is implemented as a facet that can be added to the Vaultinator Diamond. It manages staking pools and user stakes through a standardized interface.

Class Diagram

┌─────────────────┐      ┌───────────────────┐      ┌───────────────────┐
│  VaultinatorFacet│◄─────┤  StakinatorFacet  │─────►│  YieldinatorFacet │
└─────────────────┘      └───────────────────┘      └───────────────────┘
         ▲                         │                          ▲
         │                         │                          │
         │                         ▼                          │
┌─────────────────┐      ┌───────────────────┐      ┌───────────────────┐
│  DiamondStorage  │◄─────┤StakinatorStorage  │─────►│CollatinatorFacet  │
└─────────────────┘      └───────────────────┘      └───────────────────┘


                          ┌───────────────────┐
                          │    Pool Registry   │
                          └───────────────────┘


                          ┌───────────────────┐
                          │   Staking Adapters │
                          └───────────────────┘


                          ┌───────────────────┐
                          │ Staking Protocols  │
                          └───────────────────┘

Component Interaction

  1. Stakinator Facet: The main contract that implements the staking functionality

  2. Pool Registry: A registry of staking pools that the Stakinator manages

  3. Staking Adapters: Standardized adapters for interacting with different staking protocols

  4. Staking Protocols: External protocols that provide staking services

Function Specifications

Pool Management

function createPool(
    address stakingToken,
    address rewardToken,
    uint256 apr,
    uint256 minStake,
    uint256 maxStake,
    uint256 lockDuration
) external returns (uint256)

Creates a new staking pool.

  • Parameters:

    • stakingToken: The address of the token to stake

    • rewardToken: The address of the token used for rewards

    • apr: The annual percentage rate in basis points (1/100 of a percent)

    • minStake: The minimum amount that can be staked

    • maxStake: The maximum amount that can be staked

    • lockDuration: The minimum lock duration in seconds

  • Returns: The ID of the created pool

  • Access Control: Only administrators can call this function

  • Events Emitted: PoolCreated(uint256 indexed poolId, address stakingToken, address rewardToken, uint256 apr, uint256 lockDuration)

function updatePool(
    uint256 poolId,
    uint256 apr,
    uint256 minStake,
    uint256 maxStake,
    uint256 lockDuration
) external returns (bool)

Updates an existing staking pool.

  • Parameters:

    • poolId: The ID of the pool to update

    • apr: The new annual percentage rate in basis points

    • minStake: The new minimum stake amount

    • maxStake: The new maximum stake amount

    • lockDuration: The new minimum lock duration in seconds

  • Returns: bool indicating success

  • Access Control: Only administrators can call this function

  • Events Emitted: PoolUpdated(uint256 indexed poolId, uint256 apr, uint256 minStake, uint256 maxStake, uint256 lockDuration)

function deactivatePool(uint256 poolId) external returns (bool)

Deactivates a staking pool, preventing new stakes but allowing existing stakes to continue.

  • Parameters:

    • poolId: The ID of the pool to deactivate

  • Returns: bool indicating success

  • Access Control: Only administrators can call this function

  • Events Emitted: PoolDeactivated(uint256 indexed poolId)

function getPool(uint256 poolId) external view returns (Pool memory)

Gets information about a staking pool.

  • Parameters:

    • poolId: The ID of the pool

  • Returns: A Pool struct containing information about the pool

  • Access Control: Anyone can call this function

function getPools() external view returns (uint256[] memory)

Gets a list of all staking pool IDs.

  • Returns: An array of pool IDs

  • Access Control: Anyone can call this function

Staking Operations

function stake(uint256 poolId, uint256 amount, uint256 lockDuration) external returns (uint256)

Stakes tokens in a pool.

  • Parameters:

    • poolId: The ID of the pool to stake in

    • amount: The amount of tokens to stake

    • lockDuration: The lock duration in seconds

  • Returns: The ID of the created stake

  • Access Control: Anyone can call this function

  • Events Emitted: Staked(uint256 indexed stakeId, address indexed owner, uint256 indexed poolId, uint256 amount, uint256 lockDuration)

function unstake(uint256 stakeId) external returns (uint256)

Unstakes tokens from a pool after the lock period has ended.

  • Parameters:

    • stakeId: The ID of the stake to unstake

  • Returns: The amount of tokens unstaked

  • Access Control: Only the stake owner can call this function

  • Events Emitted: Unstaked(uint256 indexed stakeId, uint256 amount)

function claimRewards(uint256 stakeId) external returns (uint256)

Claims rewards for a stake.

  • Parameters:

    • stakeId: The ID of the stake to claim rewards for

  • Returns: The amount of rewards claimed

  • Access Control: Only the stake owner can call this function

  • Events Emitted: RewardsClaimed(uint256 indexed stakeId, uint256 amount)

function extendLock(uint256 stakeId, uint256 newLockDuration) external returns (bool)

Extends the lock duration of a stake.

  • Parameters:

    • stakeId: The ID of the stake

    • newLockDuration: The new lock duration in seconds

  • Returns: bool indicating success

  • Access Control: Only the stake owner can call this function

  • Events Emitted: LockExtended(uint256 indexed stakeId, uint256 newLockDuration)

Vault Integration

function stakeFromVault(uint256 vaultId, uint256 poolId, uint256 amount, uint256 lockDuration) external returns (uint256)

Stakes tokens from a vault in a pool.

  • Parameters:

    • vaultId: The ID of the vault

    • poolId: The ID of the pool to stake in

    • amount: The amount of tokens to stake

    • lockDuration: The lock duration in seconds

  • Returns: The ID of the created stake

  • Access Control: Only vault owners or authorized users can call this function

  • Events Emitted: VaultStaked(uint256 indexed vaultId, uint256 indexed stakeId, uint256 indexed poolId, uint256 amount, uint256 lockDuration)

function unstakeToVault(uint256 vaultId, uint256 stakeId) external returns (uint256)

Unstakes tokens and sends them to a vault.

  • Parameters:

    • vaultId: The ID of the vault

    • stakeId: The ID of the stake to unstake

  • Returns: The amount of tokens unstaked

  • Access Control: Only vault owners or authorized users can call this function

  • Events Emitted: VaultUnstaked(uint256 indexed vaultId, uint256 indexed stakeId, uint256 amount)

function claimRewardsToVault(uint256 vaultId, uint256 stakeId) external returns (uint256)

Claims rewards for a stake and sends them to a vault.

  • Parameters:

    • vaultId: The ID of the vault

    • stakeId: The ID of the stake to claim rewards for

  • Returns: The amount of rewards claimed

  • Access Control: Only vault owners or authorized users can call this function

  • Events Emitted: VaultRewardsClaimed(uint256 indexed vaultId, uint256 indexed stakeId, uint256 amount)

Information and Metrics

function getStake(uint256 stakeId) external view returns (Stake memory)

Gets information about a stake.

  • Parameters:

    • stakeId: The ID of the stake

  • Returns: A Stake struct containing information about the stake

  • Access Control: Anyone can call this function

function getUserStakes(address user) external view returns (uint256[] memory)

Gets a list of all stake IDs for a user.

  • Parameters:

    • user: The address of the user

  • Returns: An array of stake IDs

  • Access Control: Anyone can call this function

function getEstimatedRewards(uint256 stakeId) external view returns (uint256)

Gets the estimated rewards for a stake.

  • Parameters:

    • stakeId: The ID of the stake

  • Returns: The estimated rewards

  • Access Control: Anyone can call this function

function getPoolTVL(uint256 poolId) external view returns (uint256)

Gets the total value locked in a pool.

  • Parameters:

    • poolId: The ID of the pool

  • Returns: The total value locked

  • Access Control: Anyone can call this function

function getTotalStaked() external view returns (uint256)

Gets the total value staked across all pools.

  • Returns: The total value staked

  • Access Control: Anyone can call this function

function getTotalRewardsDistributed() external view returns (uint256)

Gets the total rewards distributed across all pools.

  • Returns: The total rewards distributed

  • Access Control: Anyone can call this function

Emergency Operations

function emergencyUnstake(uint256 stakeId) external returns (uint256)

Performs an emergency unstake, which may incur a penalty.

  • Parameters:

    • stakeId: The ID of the stake to unstake

  • Returns: The amount of tokens unstaked

  • Access Control: Only the stake owner can call this function

  • Events Emitted: EmergencyUnstaked(uint256 indexed stakeId, uint256 amount, uint256 penalty)

function pause() external returns (bool)

Pauses all Stakinator operations.

  • Returns: bool indicating success

  • Access Control: Only administrators can call this function

  • Events Emitted: StakinatorPaused(address indexed admin)

function unpause() external returns (bool)

Unpauses all Stakinator operations.

  • Returns: bool indicating success

  • Access Control: Only administrators can call this function

  • Events Emitted: StakinatorUnpaused(address indexed admin)

Storage Layout

The Stakinator Facet uses the Diamond Storage pattern to store its state. The storage layout is defined in the LibStakinatorStorage library.

library LibStakinatorStorage {
    bytes32 constant STAKINATOR_STORAGE_POSITION = keccak256("stakinator.core.storage");

    struct StakinatorStorage {
        // Stake management
        mapping(uint256 => Stake) stakes;
        mapping(address => uint256[]) userStakes;
        uint256 nextStakeId;
        
        // Pool management
        mapping(uint256 => Pool) pools;
        uint256 nextPoolId;
        
        // Protocol metrics
        mapping(uint256 => uint256) poolTVL;
        uint256 totalValueLocked;
        uint256 totalRewardsDistributed;
        
        // Reentrancy guard
        uint256 reentrancyStatus;
        
        // Pause status
        bool paused;
    }
    
    struct Stake {
        uint256 id;
        address owner;
        uint256 poolId;
        uint256 amount;
        uint256 lockDuration;
        uint256 unlockTime;
        uint256 rewardsClaimed;
        uint256 createdAt;
        bool active;
    }
    
    struct Pool {
        uint256 id;
        address stakingToken;
        address rewardToken;
        uint256 apr;
        uint256 minStake;
        uint256 maxStake;
        uint256 lockDuration;
        uint256 totalStaked;
        bool active;
        uint256 createdAt;
    }
    
    function stakinatorStorage() internal pure returns (StakinatorStorage storage ds) {
        bytes32 position = STAKINATOR_STORAGE_POSITION;
        assembly {
            ds.slot := position
        }
    }
}

Events

The Stakinator Facet emits the following events:

event PoolCreated(uint256 indexed poolId, address stakingToken, address rewardToken, uint256 apr, uint256 lockDuration);
event PoolUpdated(uint256 indexed poolId, uint256 apr, uint256 minStake, uint256 maxStake, uint256 lockDuration);
event PoolDeactivated(uint256 indexed poolId);
event Staked(uint256 indexed stakeId, address indexed owner, uint256 indexed poolId, uint256 amount, uint256 lockDuration);
event Unstaked(uint256 indexed stakeId, uint256 amount);
event RewardsClaimed(uint256 indexed stakeId, uint256 amount);
event LockExtended(uint256 indexed stakeId, uint256 newLockDuration);
event VaultStaked(uint256 indexed vaultId, uint256 indexed stakeId, uint256 indexed poolId, uint256 amount, uint256 lockDuration);
event VaultUnstaked(uint256 indexed vaultId, uint256 indexed stakeId, uint256 amount);
event VaultRewardsClaimed(uint256 indexed vaultId, uint256 indexed stakeId, uint256 amount);
event EmergencyUnstaked(uint256 indexed stakeId, uint256 amount, uint256 penalty);
event StakinatorPaused(address indexed admin);
event StakinatorUnpaused(address indexed admin);

Security Considerations

Reentrancy Protection

The Stakinator Facet uses a reentrancy guard to prevent reentrancy attacks. All external functions that interact with other contracts are protected by the reentrancy guard.

modifier nonReentrant() {
    LibStakinatorStorage.StakinatorStorage storage s = LibStakinatorStorage.stakinatorStorage();
    require(s.reentrancyStatus != 2, "ReentrancyGuard: reentrant call");
    s.reentrancyStatus = 2;
    _;
    s.reentrancyStatus = 1;
}

Access Control

The Stakinator Facet uses role-based access control to restrict access to certain functions. The access control is implemented through the Vaultinator Facet.

modifier onlyAdmin() {
    require(LibVaultinatorStorage.vaultinatorStorage().hasRole(msg.sender, "admin"), "StakinatorFacet: not admin");
    _;
}

modifier onlyStakeOwner(uint256 stakeId) {
    require(LibStakinatorStorage.stakinatorStorage().stakes[stakeId].owner == msg.sender, "StakinatorFacet: not stake owner");
    _;
}

modifier onlyVaultOwner(uint256 vaultId) {
    require(LibVaultinatorStorage.vaultinatorStorage().vaults[vaultId].owner == msg.sender, "StakinatorFacet: not vault owner");
    _;
}

Pause Mechanism

The Stakinator Facet includes a pause mechanism that allows administrators to pause all operations in case of an emergency.

modifier whenNotPaused() {
    require(!LibStakinatorStorage.stakinatorStorage().paused, "StakinatorFacet: paused");
    _;
}

function pause() external onlyAdmin returns (bool) {
    LibStakinatorStorage.StakinatorStorage storage s = LibStakinatorStorage.stakinatorStorage();
    require(!s.paused, "StakinatorFacet: already paused");
    s.paused = true;
    emit StakinatorPaused(msg.sender);
    return true;
}

function unpause() external onlyAdmin returns (bool) {
    LibStakinatorStorage.StakinatorStorage storage s = LibStakinatorStorage.stakinatorStorage();
    require(s.paused, "StakinatorFacet: not paused");
    s.paused = false;
    emit StakinatorUnpaused(msg.sender);
    return true;
}

Lock Period Enforcement

The Stakinator Facet enforces lock periods for stakes. Users cannot unstake their tokens until the lock period has ended.

function unstake(uint256 stakeId) external nonReentrant whenNotPaused onlyStakeOwner(stakeId) returns (uint256) {
    LibStakinatorStorage.StakinatorStorage storage s = LibStakinatorStorage.stakinatorStorage();
    LibStakinatorStorage.Stake storage stake = s.stakes[stakeId];
    
    require(stake.active, "StakinatorFacet: stake not active");
    require(block.timestamp >= stake.unlockTime, "StakinatorFacet: lock period not ended");
    
    // Implementation details...
    
    return amount;
}

Emergency Unstake

The Stakinator Facet includes an emergency unstake function that allows users to unstake their tokens before the lock period has ended, but with a penalty.

function emergencyUnstake(uint256 stakeId) external nonReentrant onlyStakeOwner(stakeId) returns (uint256) {
    LibStakinatorStorage.StakinatorStorage storage s = LibStakinatorStorage.stakinatorStorage();
    LibStakinatorStorage.Stake storage stake = s.stakes[stakeId];
    
    require(stake.active, "StakinatorFacet: stake not active");
    
    // Calculate penalty
    uint256 penalty = 0;
    if (block.timestamp < stake.unlockTime) {
        uint256 timeRemaining = stake.unlockTime - block.timestamp;
        uint256 totalLockTime = stake.lockDuration;
        penalty = stake.amount * timeRemaining / totalLockTime / 10; // 10% penalty prorated by time remaining
    }
    
    uint256 amountToReturn = stake.amount - penalty;
    
    // Implementation details...
    
    emit EmergencyUnstaked(stakeId, amountToReturn, penalty);
    return amountToReturn;
}

Reward Calculation

The Stakinator Facet uses a time-based reward calculation to ensure that rewards are distributed fairly.

function calculateRewards(uint256 stakeId) internal view returns (uint256) {
    LibStakinatorStorage.StakinatorStorage storage s = LibStakinatorStorage.stakinatorStorage();
    LibStakinatorStorage.Stake storage stake = s.stakes[stakeId];
    LibStakinatorStorage.Pool storage pool = s.pools[stake.poolId];
    
    uint256 timeStaked = block.timestamp - stake.createdAt;
    uint256 apr = pool.apr;
    uint256 rewards = stake.amount * apr * timeStaked / (365 days * 10000); // APR is in basis points
    
    return rewards - stake.rewardsClaimed;
}

Integration with Other Facets

The Stakinator Facet integrates with other facets in the Vaultinator Protocol:

  1. Vaultinator Facet: For vault management and access control

  2. Yieldinator Facet: For staking yield-generating assets

  3. Collatinator Facet: For using staked assets as collateral

Vaultinator Integration

function stakeFromVault(uint256 vaultId, uint256 poolId, uint256 amount, uint256 lockDuration) external nonReentrant whenNotPaused onlyVaultOwner(vaultId) returns (uint256) {
    // Implementation details...
    // Transfer assets from vault to this contract
    // Create stake
    // Update vault balance
    // More implementation details...
    emit VaultStaked(vaultId, stakeId, poolId, amount, lockDuration);
    return stakeId;
}

Yieldinator Integration

function stakeYieldToken(uint256 poolId, address yieldAdapter, address asset, uint256 amount, uint256 lockDuration) external nonReentrant whenNotPaused returns (uint256) {
    // Implementation details...
    // Transfer yield token from Yieldinator to this contract
    // Create stake
    // More implementation details...
    return stakeId;
}

Collatinator Integration

function useStakeAsCollateral(uint256 stakeId, address protocol, address debtAsset, uint256 debtAmount) external nonReentrant whenNotPaused onlyStakeOwner(stakeId) returns (uint256) {
    // Implementation details...
    // Check if stake can be used as collateral
    // Create collateralized position
    // More implementation details...
    return positionId;
}

Staking Strategies

The Stakinator Facet supports multiple staking strategies:

Fixed APR Staking

Fixed APR staking provides a guaranteed return on staked assets, regardless of external factors.

function createFixedAPRPool(
    address stakingToken,
    address rewardToken,
    uint256 apr,
    uint256 minStake,
    uint256 maxStake,
    uint256 lockDuration
) external onlyAdmin returns (uint256) {
    // Implementation details...
    // Create pool with fixed APR
    // More implementation details...
    return poolId;
}

Variable APR Staking

Variable APR staking provides a return that varies based on external factors, such as protocol performance.

function createVariableAPRPool(
    address stakingToken,
    address rewardToken,
    address aprOracle,
    uint256 minStake,
    uint256 maxStake,
    uint256 lockDuration
) external onlyAdmin returns (uint256) {
    // Implementation details...
    // Create pool with variable APR
    // More implementation details...
    return poolId;
}

function updateVariableAPR(uint256 poolId) external onlyAdmin returns (uint256) {
    // Implementation details...
    // Update APR based on oracle
    // More implementation details...
    return newAPR;
}

Tiered Staking

Tiered staking provides different APRs based on the amount staked and the lock duration.

function createTieredPool(
    address stakingToken,
    address rewardToken,
    uint256[] memory tiers,
    uint256[] memory aprs,
    uint256[] memory lockDurations
) external onlyAdmin returns (uint256) {
    // Implementation details...
    // Create pool with tiered APRs
    // More implementation details...
    return poolId;
}

function getTierAPR(uint256 poolId, uint256 amount, uint256 lockDuration) external view returns (uint256) {
    // Implementation details...
    // Calculate APR based on amount and lock duration
    // More implementation details...
    return apr;
}

Boosted Staking

Boosted staking provides increased rewards for users who also stake a secondary token.

function createBoostedPool(
    address stakingToken,
    address rewardToken,
    address boostToken,
    uint256 baseAPR,
    uint256 maxBoost,
    uint256 minStake,
    uint256 maxStake,
    uint256 lockDuration
) external onlyAdmin returns (uint256) {
    // Implementation details...
    // Create pool with boost mechanism
    // More implementation details...
    return poolId;
}

function stakeWithBoost(uint256 poolId, uint256 amount, uint256 boostAmount, uint256 lockDuration) external nonReentrant whenNotPaused returns (uint256) {
    // Implementation details...
    // Calculate boosted APR
    // Create stake with boosted APR
    // More implementation details...
    return stakeId;
}

Conclusion

The Stakinator Facet is a powerful component of the Vaultinator Protocol that enables users to stake tokens and earn rewards. Its modular design, security features, and integration with other facets make it a flexible and robust solution for staking.