Stability Pool Contract
The Stability Pool contract allows users to deposit pegged tokens (haTokens) to earn yield and participate in protocol rebalancing.
Overview
The Stability Pool (StabilityPool_v1) is a UUPS upgradeable contract that holds pegged tokens deposited by users. It uses a compounding balance system to track deposits and automatically distributes rewards and rebalance proceeds. The pool participates in protocol rebalancing when the collateral ratio falls below the threshold.
Contract Architecture
- Upgradeable: Uses UUPS (Universal Upgradeable Proxy Standard) pattern
- Storage: Uses ERC-7201 namespaced storage
- Reward System: Inherits from
MultipleRewardCompoundingAccumulator - Access Control: Uses BaoOwnableRoles for role-based access control
- Reentrancy Protection: Uses OpenZeppelin's ReentrancyGuardTransientUpgradeable
Immutable Addresses
Set during contract construction (cannot be changed):
- ASSET_TOKEN: The pegged token (haToken) that users deposit
- LIQUIDATION_TOKEN: The token received during liquidations (either wrapped collateral or leveraged token)
- MIN_TOTAL_ASSET_SUPPLY: Minimum total supply to prevent complete pool depletion
- MIN_DEPOSIT: Minimum deposit amount (equals MIN_TOTAL_ASSET_SUPPLY)
- WITHDRAWAL_START_DELAY: Delay before withdrawal window opens (in seconds)
- WITHDRAWAL_END_WINDOW: Duration of withdrawal window (in seconds)
Storage State
- totalAssetSupply: Current total supply of assets in the pool (using TokenBalance struct)
- assetBalances: Mapping of account addresses to their TokenBalance structs
- totalAssetSupplyHistory: Historical records of total supply over time
- lastAssetLossError: Error tracking for loss calculations (ensures fair distribution)
- withdrawalRequests: Mapping of accounts to their withdrawal request windows
- feePayment: Early withdrawal fee configuration (address + fee ratio)
Key Functions
Deposit and Withdrawal
deposit(uint256 assetAmount, address receiver, uint256 minAmount)
Deposits pegged tokens (haTokens) into the stability pool.
Parameters:
assetAmount: Amount of pegged tokens to deposit (usetype(uint256).maxfor all)receiver: Address to receive the deposit balanceminAmount: Minimum amount expected (slippage protection)
Returns:
assetsDeposited: Actual amount deposited
Effects:
- Transfers pegged tokens from sender to pool
- Updates receiver's balance using compounding system
- Updates total supply
- Records total supply history
- Cancels any active withdrawal request if depositing during withdrawal window
- Checkpoints receiver for reward distribution
Requirements:
- Deposit must be at least
MIN_DEPOSIT - Receiver cannot be zero address
withdraw(uint256 assetAmount, address receiver, uint256 minAmount)
Withdraws pegged tokens from the stability pool.
Parameters:
assetAmount: Amount to withdraw (usetype(uint256).maxfor all)receiver: Address to receive the withdrawn tokensminAmount: Minimum amount expected (slippage protection)
Returns:
assetsWithdrawn: Actual amount withdrawn (after fees)
Effects:
- Checkpoints sender to update balances and distribute rewards
- Calculates early withdrawal fee if applicable
- Updates total supply (maintains minimum)
- Updates sender's balance
- Transfers tokens to receiver
- Transfers fee to fee address if applicable
- Closes withdrawal request if one exists
Early Withdrawal Fee:
- Applied if withdrawal is outside the withdrawal window
- Not applied if:
- Withdrawal is within the withdrawal window (after
requestWithdrawal()) - Account has
EXEMPT_WITHDRAWAL_FEE_ROLE
- Withdrawal is within the withdrawal window (after
- Fee is deducted from withdrawal amount
- Fee cannot reduce total supply below
MIN_TOTAL_ASSET_SUPPLY
requestWithdrawal()
Creates a withdrawal request to establish a fee-free withdrawal window.
Effects:
- Sets withdrawal window:
[now + WITHDRAWAL_START_DELAY, start + WITHDRAWAL_END_WINDOW] - Withdrawals within this window are fee-free
- Emits
WithdrawalRequestedevent
Note: Depositing during an active withdrawal window cancels the request.
Rebalancing
notifyLiquidation(uint256 liquidated, uint256 returned) (Restricted)
Called by StabilityPoolManager during rebalancing to notify the pool of rebalance proceeds.
Parameters:
liquidated: Amount of pegged tokens liquidatedreturned: Amount of rebalance tokens returned
Access: Only addresses with REBALANCER_ROLE (typically StabilityPoolManager)
Process:
- Emits
Liquidatedevent - Checkpoints all accounts to distribute rewards on pre-loss balances
- Accumulates rebalance tokens as rewards
- Applies loss to total supply using
_notifyLoss()
Loss Distribution:
- Loss is distributed proportionally to all depositors
- Uses DecrementalFloatingPoint to adjust balances
- Maintains minimum total supply
- Tracks rounding errors to ensure fairness
Rewards
The contract inherits from MultipleRewardCompoundingAccumulator, providing:
- Automatic Compounding: Rewards compound into user balances
- Multiple Reward Tokens: Supports multiple reward token types
- Checkpoint System: Balances and rewards update on deposit/withdraw/claim
Reward Distribution
Rewards are distributed via:
depositReward(address token, uint256 amount)- Called by reward distributors (e.g., StabilityPoolManager during harvest)- Rewards compound automatically into user balances
- Users claim rewards by calling
claim()or through withdrawals
For detailed information about the reward system, see Reward System Contracts.
View Functions
Balance Queries
totalAssetSupply() returns (uint256)- Current total supply of assetsassetBalanceOf(address account) returns (uint256)- User's compounded balancetotalAssetSupplyHistory(uint index) returns (uint40 atDay, uint256 amount)- Historical supply records
Withdrawal Information
getWithdrawalRequest(address account) returns (uint64 start, uint64 end)- User's withdrawal windowgetWithdrawalWindow() returns (uint64 startDelay, uint64 endWindow)- Global withdrawal window config
Fee Information
getEarlyWithdrawalFee() returns (uint256)- Current early withdrawal fee ratiogetFeeAddress() returns (address)- Address receiving early withdrawal fees
Other Views
lastAssetLossError() returns (uint256)- Current loss calculation error trackerclaimable(address account, address token) returns (uint256)- Claimable rewards (from parent)
Balance System
The contract uses a sophisticated compounding balance system:
TokenBalance Struct
Each balance is tracked using:
- product: DecrementalFloatingPoint product (for loss distribution)
- amount: Current balance amount
- updatedAt: Timestamp of last update
Compounding Mechanism
- Balances compound automatically through the
_checkpoint()function - Rewards are added to balances (not separate tracking)
- Losses reduce balances proportionally via product factor
- All operations maintain precision through error tracking
Withdrawal Window System
Users can request a withdrawal window to avoid early withdrawal fees:
- Request: Call
requestWithdrawal() - Wait:
WITHDRAWAL_START_DELAYseconds - Window Opens: Fee-free withdrawals for
WITHDRAWAL_END_WINDOWseconds - After Window: Early withdrawal fee applies again
Note: Depositing during an active window cancels the request.
Early Withdrawal Fee
- Purpose: Discourage rapid deposit/withdraw cycles
- Application: Applied when withdrawing outside withdrawal window
- Exemptions:
- Withdrawals within requested window
- Accounts with
EXEMPT_WITHDRAWAL_FEE_ROLE
- Maximum: 1 ether (100%)
- Recipient: Configurable fee address
Minimum Supply Protection
- MIN_TOTAL_ASSET_SUPPLY: Prevents complete pool depletion
- Enforcement:
- Withdrawals cannot reduce supply below minimum
- Losses cannot breach minimum
- First deposit must be at least minimum
Dependencies
- ASSET_TOKEN: The pegged token (haToken) deposited by users
- LIQUIDATION_TOKEN: Token received during rebalancing (wrapped collateral or leveraged token) - Note: Contract constant name kept for compatibility
- MultipleRewardCompoundingAccumulator: Parent contract for reward distribution
- TokenHolder: Parent contract for token sweeping (used by StabilityPoolManager)
Roles
- REBALANCER_ROLE: Can call
notifyLiquidation()(typically StabilityPoolManager) - REWARD_MANAGER_ROLE: Can manage reward distribution (from parent)
- REWARD_DEPOSITOR_ROLE: Can deposit rewards (from parent)
- EXEMPT_WITHDRAWAL_FEE_ROLE: Exempt from early withdrawal fees
- Owner: Can upgrade contract and manage roles
Security Considerations
- All mutating functions are protected by
nonReentrantmodifier - Minimum supply prevents complete pool depletion
- Loss distribution uses error tracking to ensure fairness
- Early withdrawal fees discourage gaming
- Withdrawal windows provide fee-free exit mechanism
- Uses safe token transfers throughout
- Historical supply tracking for auditing