Reward System Contracts
The Harbor Protocol reward system consists of multiple contracts working together to distribute rewards efficiently and fairly, even when staking balances decrease unexpectedly.
Overview
The reward system is built on two main components:
- LinearMultipleRewardDistributor: Handles linear distribution of rewards over time periods
- MultipleRewardCompoundingAccumulator: Tracks and compounds rewards based on user shares
StabilityPool contracts inherit from MultipleRewardCompoundingAccumulator, which inherits from LinearMultipleRewardDistributor, combining both functionalities.
Architecture
StabilityPool_v1
↓ inherits
MultipleRewardCompoundingAccumulator
↓ inherits
LinearMultipleRewardDistributor
LinearMultipleRewardDistributor
The base contract that manages linear reward distribution over configurable time periods.
Key Features
- Multiple Reward Tokens: Supports multiple reward token types simultaneously
- Linear Distribution: Distributes rewards linearly over a period (or immediately if period is 0)
- Period Management: Configurable period length (0 = immediate, 1-28 days = linear)
- Token Registration: Active and historical reward token tracking
Immutable Configuration
- REWARD_MANAGER_ROLE: Role for managing reward tokens (register/unregister)
- REWARD_DEPOSITOR_ROLE: Role for depositing rewards
- REWARD_PERIOD_LENGTH: Length of reward distribution period in seconds
0: Immediate distribution (no linear ramp)1 dayto28 days: Linear distribution over period
Key Functions
Reward Token Management
registerRewardToken(address token)- Registers a new reward token (REWARD_MANAGER_ROLE)unregisterRewardToken(address token)- Unregisters a reward token (moves to historical)activeRewardTokens() returns (address[])- Returns list of active reward tokenshistoricalRewardTokens() returns (address[])- Returns list of historical reward tokens
Reward Distribution
depositReward(address token, uint256 amount)- Deposits rewards for distribution (REWARD_DEPOSITOR_ROLE)- Transfers tokens from caller to contract
- Distributes pending rewards from previous period
- Notifies new rewards (immediate or linear based on period length)
View Functions
rewardData(address token) returns (uint256 lastUpdate, uint256 finishAt, uint256 rate, uint256 queued)- Returns reward distribution datapendingRewards(address token) returns (uint256 distributable, uint256 undistributed)- Returns pending reward amounts
Linear Distribution Mechanism
When REWARD_PERIOD_LENGTH > 0, rewards are distributed linearly:
- Reward Rate:
rate = totalRewards / periodLength(rewards per second) - Distribution: Rewards accumulate at constant rate over the period
- Period Management:
- If new rewards ≥ 90% of current period's distributed amount → Start new period
- If new rewards < 90% → Queue for next period
- Queued Rewards: Rounding errors and small amounts are queued for next period
When REWARD_PERIOD_LENGTH == 0, rewards are distributed immediately.
MultipleRewardCompoundingAccumulator
Extends the linear distributor with compounding reward tracking that handles stake decreases.
Key Features
- O(1) Complexity: Reward calculations are constant time regardless of time elapsed
- Stake Decrease Handling: Correctly handles proportional stake decreases (e.g., from rebalancing)
- Multiple Reward Tokens: Supports multiple reward tokens simultaneously
- Precision Preservation: Uses floating-point representation to prevent precision loss
- Epoch System: Handles cases where total supply reduces to zero
- Custom Receivers: Users can set custom reward receiver addresses
Mathematical Model
The accumulator uses a sophisticated mathematical model based on Liquity's StabilityPool paper:
Key Variables:
s[i]: Total pool stakes after event iu[i]: User's personal stakes after event id[i]: Amount of total stake decrease in event ir[i]: Amount of rewards distributed in event ip[i]: Product factor tracking stake decreases
Stake Decrease Formula:
u[n] = u[0] * (1 - d[1]/s[0]) * (1 - d[2]/s[1]) * ... * (1 - d[n]/s[n-1])
Reward Accumulation:
g[n] = u[0] * (r[1] * p[0]/s[0] + r[2] * p[1]/s[1] + ... + r[n] * p[n-1]/s[n-1])
Storage Structures
RewardSnapshot
timestamp: When snapshot was takenintegral: Accumulated reward integral value