Overview
Every DAMM v2 pool has two built-in reward slots (reward_infos[0] and reward_infos[1]). Any SPL token (including Token 2022) can be used as a reward. LPs earn rewards proportional to their total position liquidity — including vested and permanently locked liquidity.
No staking contract, no LP token wrapping. Positions earn rewards automatically while they hold liquidity.
Reward Distribution Model
Rewards use a reward-per-token-stored accumulator pattern (similar to Uniswap v3 staking):| Field | Description |
|---|---|
reward_per_token_stored | Cumulative rewards per unit liquidity (U256, stored as [u8; 32]) |
reward_rate | Tokens emitted per second (or slot) |
reward_duration_end | When the current campaign ends |
total_claimed_rewards | Lifetime claimed by this position |
Why U256?
The accumulator uses 256-bit math to avoid overflow over long reward campaigns with small reward rates or large liquidity values.Lifecycle
1. Initialize a Reward
The pool creator callsinitializeReward to set up one of the two reward slots:
reward_indexmust be 0 or 1 (InvalidRewardIndexif out of range)reward_durationmust be > 0 (InvalidRewardDuration)- Cannot re-initialize an already active slot (
RewardInitialized)
EvtInitializeReward
2. Fund the Reward
Transfer reward tokens into the reward vault:reward_rate = amount / remaining_duration and extends the campaign if called mid-campaign.
Events emitted: EvtFundReward with pre_reward_rate and post_reward_rate
3. LPs Accrue Rewards Passively
As time passes,reward_per_token_stored increases. Every time a position is touched (add/remove liquidity, claim fees, claim reward), the position’s checkpoint is updated:
4. Claim Rewards
reward_pendings[0] to the owner and resets the pending amount.
Event emitted: EvtClaimReward
Updating a Reward Campaign
| Action | Instruction | Notes |
|---|---|---|
| Change duration | updateRewardDuration | Can only extend, not shorten an active campaign |
| Change funder | updateRewardFunder | New funder becomes responsible for future fundReward calls |
| Withdraw ineligible | withdrawIneligibleReward | Reclaim tokens after campaign ends with unfunded remainder |
Reward Constraints
| Constraint | Details |
|---|---|
| Max reward slots | 2 per pool |
| Reward during locked liquidity | ✅ Vested + permanent locked liquidity earns rewards |
| Token 2022 rewards | ✅ Supported |
| Native SOL rewards | ❌ (use wrapped SOL) |
| Frozen vault | If reward vault is frozen, must skip reward (RewardVaultFrozenSkipRequired) |

