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
whitelistStatusisMerkleorMerkleAndTrustedDistributors,merkleRootmust be non-zero - If
whitelistStatusisDisabledorTrustedDistributors,merkleRootmust be zero
Token Configuration:
stakingTokenmust be a valid ERC20 contract address- Cannot be zero address
Time Configuration:
startStakingDatemust be in the futureendStakingDatemust be afterstartStakingDatemaxStakeDurationmust be > 0unbondingPeriodmust be ≤ MAX_UNBONDING_PERIOD (5 years)
Tier Configuration:
tierLowerBoundmust be ≤tierUpperBound- Both bounds must be > 0
APY Configuration:
apyPercentageis 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
maxStakeDurationfrom 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