UnbondingStakingFactory
Overview
The UnbondingStakingFactory contract deploys staking contracts with built-in unbonding periods. These contracts implement a two-phase withdrawal process where users must initiate unbonding and wait for a specified period before completing withdrawals.
Contract Specification
contract UnbondingStakingFactory is IUnbondingStakingFactory, FactoryFeeManager
Inheritance:
IUnbondingStakingFactory
: Unbonding factory interface specificationFactoryFeeManager
: Fee management functionality
Constructor
constructor(
address feeCollector_,
uint256 defaultGasFee_,
uint256 defaultTokenFee_,
uint8 defaultFeeFunctions_
) FactoryFeeManager(feeCollector_, defaultGasFee_, defaultTokenFee_, defaultFeeFunctions_)
Parameters:
feeCollector_
: Address designated to receive collected feesdefaultGasFee_
: Default gas fee amount in weidefaultTokenFee_
: Default token fee in basis points (1-10000)defaultFeeFunctions_
: Bitmask defining which functions have fees applied
Requirements:
feeCollector_
must not be zero addressdefaultFeeFunctions_
must be ≤ 7 (valid bitmask)
Functions
newStakingWithUnbonding
function newStakingWithUnbonding(UnbondingStakingParams calldata params) external returns (address)
Deploys a new StakingWithUnbonding contract instance with the provided parameters.
Parameters:
struct UnbondingStakingParams {
bytes32 merkleRoot; // Merkle root for whitelist verification
ITypes.WhitelistStatus whitelistStatus; // Whitelist configuration mode
address stakingToken; // ERC20 token contract address
uint256 poolMaxCap; // Maximum total tokens stakeable
uint256 tierUpperBound; // Maximum tokens per individual staker
uint256 tierLowerBound; // Minimum tokens per individual staker
uint64 unbondingPeriod; // Unbonding period duration in seconds
uint64 maxStakeDuration; // Maximum duration for reward accrual in seconds
uint64 apyPercentage; // Annual percentage yield in basis points
uint80 startStakingDate; // Staking operations start timestamp
uint80 endStakingDate; // Staking operations end timestamp
}
Returns:
- Address of the newly deployed staking contract
Fee Application: The factory applies fees based on the caller's configuration:
- If custom fees are set for the caller, those are used
- Otherwise, default factory fees are applied
Events Emitted:
event StakingWithUnbondingCreated(
address indexed stakingContract,
bytes32 merkleRoot,
address token,
ITypes.FeeType feeType,
uint256 fee,
uint8 feeFunctions
);
Parameter Validation
The factory validates deployment parameters:
Whitelist Configuration:
- If
whitelistStatus
isMerkle
orMerkleAndTrustedDistributors
,merkleRoot
must be non-zero - If
whitelistStatus
isDisabled
orTrustedDistributors
,merkleRoot
must be zero
Token Configuration:
stakingToken
must be a valid ERC20 contract address- Cannot be zero address
Time Configuration:
startStakingDate
must be in the futureendStakingDate
must be afterstartStakingDate
maxStakeDuration
must be > 0unbondingPeriod
must be ≤ MAX_UNBONDING_PERIOD (5 years)
Tier Configuration:
tierLowerBound
must be ≤tierUpperBound
- Both bounds must be > 0
APY Configuration:
apyPercentage
is in basis points (1-10000)- Represents annual percentage yield for reward calculations
Unbonding Mechanism
Two-Phase Withdrawal Process:
-
Initiate Unbonding: User calls
initiateUnbonding(amount)
- Tokens move from staked to unbonding state
- Unbonding timestamp is recorded
- Rewards stop accruing for unbonded tokens
-
Complete Unbonding: User calls
completeUnbonding()
after period expires- Tokens are transferred from contract to user
- Fee is applied if configured
Unbonding Period Options:
0
: Immediate withdrawal (no unbonding delay)> 0
: Delay period in seconds before withdrawal can complete- Maximum:
MAX_UNBONDING_PERIOD
(1,825 days / 5 years)
Reward Calculation
UnbondingStakingWithUnbonding uses fixed APY-based rewards:
Formula:
reward = (stakedAmount × timeStaked × apyPercentage) / (365 days × BASIS_POINTS)
Key Characteristics:
- Rewards accrue from staking timestamp to current time
- Rewards are capped at
maxStakeDuration
from first stake time - No rewards accrue on tokens in unbonding state
- APY is fixed per contract deployment (immutable)
Maximum Stake Duration: Rewards stop accruing after the maximum stake duration:
maxEndTime = timeOfFirstStake + maxStakeDuration
if (currentTime > maxEndTime) {
currentTime = maxEndTime; // Cap for reward calculation
}
Fee Management Integration
The factory integrates with the FactoryFeeManager system:
Default Fees: Applied to all deployments unless overridden by custom fees.
Custom Fees: Set per campaign creator address with specific fee types and amounts.
Fee Functions: Bitmask determines which contract functions have fees applied:
- Bit 0 (1): Stake functions
- Bit 1 (2): Withdraw/complete unbonding functions
- Bit 2 (4): Claim functions
Deployed Contract Configuration
Each deployed staking contract receives:
Immutable Parameters:
- Staking token address
- Fee type and amount
- Fee collector address
- Fee functions bitmask
- Tier bounds
- Staking date range
- Unbonding period
- Maximum stake duration
- APY percentage
Configurable Parameters:
- Pool maximum capacity
- Whitelist status and merkle root
- Unbonding period (owner-controlled)
Contract State Management
Staker Information:
struct UnbondingStaker {
uint256 amountStaked; // Currently staked tokens
uint256 unclaimedRewards; // Accumulated unclaimed rewards
uint128 timeOfFirstStake; // Timestamp of first stake
uint128 timeOfLastUpdate; // Last reward update timestamp
uint128 unbondingTimestamp; // When unbonding was initiated
uint256 unbondingAmount; // Amount currently unbonding
uint64 stakersArrayIndex; // Index in stakers array
}
State Transitions:
- Initial: No tokens staked
- Staking: Tokens actively earning rewards
- Unbonding: Tokens waiting for unbonding period completion
- Withdrawn: Tokens removed from contract
Error Conditions
The factory may revert with these errors:
From StakingErrors:
InvalidAddress()
: Zero address providedInvalidFeeFunctionsMask()
: Invalid fee functions bitmaskInvalidTierBounds()
: Invalid tier bound configurationInvalidStakingDates()
: Invalid staking date rangeStartDateInPast()
: Start date is in the pastInvalidMerkleRoot()
: Invalid merkle root for whitelist statusInvalidFeePercentage()
: Fee percentage > BASIS_POINTSInvalidMaxStakeDuration()
: maxStakeDuration is zeroInvalidUnbondingPeriod()
: unbondingPeriod > MAX_UNBONDING_PERIOD
From FactoryFeeManager:
FeeTooHigh()
: Token fee > BASIS_POINTSCustomFeeNotSet()
: Attempting to use non-existent custom fee
Constants
uint64 public constant MAX_UNBONDING_PERIOD = 365 days * 5; // 5 years maximum
uint256 public constant BASIS_POINTS = 10_000; // 100% = 10,000 basis points