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
Stakinator Facet: The main contract that implements the staking functionality
Pool Registry: A registry of staking pools that the Stakinator manages
Staking Adapters: Standardized adapters for interacting with different staking protocols
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 stakerewardToken: The address of the token used for rewardsapr: The annual percentage rate in basis points (1/100 of a percent)minStake: The minimum amount that can be stakedmaxStake: The maximum amount that can be stakedlockDuration: 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 updateapr: The new annual percentage rate in basis pointsminStake: The new minimum stake amountmaxStake: The new maximum stake amountlockDuration: The new minimum lock duration in seconds
Returns:
boolindicating successAccess 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:
boolindicating successAccess 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
Poolstruct containing information about the poolAccess 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 inamount: The amount of tokens to stakelockDuration: 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 stakenewLockDuration: The new lock duration in seconds
Returns:
boolindicating successAccess 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 vaultpoolId: The ID of the pool to stake inamount: The amount of tokens to stakelockDuration: 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 vaultstakeId: 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 vaultstakeId: 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
Stakestruct containing information about the stakeAccess 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:
boolindicating successAccess Control: Only administrators can call this function
Events Emitted:
StakinatorPaused(address indexed admin)
function unpause() external returns (bool)Unpauses all Stakinator operations.
Returns:
boolindicating successAccess 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:
Vaultinator Facet: For vault management and access control
Yieldinator Facet: For staking yield-generating assets
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.