Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.parquet.exchange/llms.txt

Use this file to discover all available pages before exploring further.

If you have spent time around the staking overview, you already know Parquet has more than one staking pool. This page explains the two pool flavors, the curve that governs how emission pools pay out, the per-stake math that decides your share, and the atomic claim_and_restake instruction that turns one farm into a compounding flywheel.

Two pool types

Every StakingPool carries a reward_source field. That single byte discriminates two very different designs.
reward_sourceNameWhere rewards come fromBehavior
0BalanceBasedUSDC accumulated in reward_usdc (e.g. fee distributions)Per-staker payout = pro-rata share of the vault balance at claim time
1EmissionScheduleA fixed total_allocation of tokens vested over a finite durationPool starts empty in claimable terms, fills via the emission curve, drains via claims
BalanceBased pools are passive: someone (the fee distributor, an admin sweep, etc.) drops USDC into the vault, and every staker has a claim on a slice of it. EmissionSchedule pools are programmatic: the schedule is locked in at creation, and the contract decides how much of total_allocation has been “released” at any given second.

The concave decay curve

Emission-schedule pools release tokens via a quadratic concave decay, computed in u128 arithmetic on-chain:
released(total, elapsed, duration)
    = total * elapsed * (2 * duration - elapsed) / (duration * duration)
The key endpoints fall out cleanly:
  • At elapsed = 0, released = 0.
  • At elapsed = duration, released = total.
  • Past end_ts, the result is clamped at total.
In between, the curve is concave — releases land fast early, slow late. At the halfway point you are already at roughly 75% of total. The pool’s emitted_so_far field tracks the cumulative released amount and is updated lazily on every staker interaction.
If you are deciding when to enter an emission-schedule pool, remember that the first half of duration ships about three quarters of the rewards. Late entrants are competing for a much smaller absolute pool, even if APR quotes look superficially similar.

Per-stake reward math

The pool maintains an accumulator so each staker’s payout can be computed in O(1) at claim or unstake time. Conceptually, when you call claim:
your_reward = your_stake_amount
            * (emitted_so_far_now - emitted_so_far_at_your_last_interaction)
            / total_staked_at_the_time_of_the_delta
The accumulator handles the “at the time of the delta” piece for you — the contract folds every change in total_staked into a running per-token index, so your StakePosition only has to remember its snapshot of that index. Stake more, unstake, or claim, and the position’s snapshot updates.

Atomic compounding via claim_and_restake

This is the one v4 instruction worth memorizing if you are actively farming. claim_and_restake claims rewards from an LP reward_source = 1 pool and restakes them into a protocol reward_source = 0 pool in a single transaction. You pass:
  • Both StakingPool accounts (the emission source and the balance-based sink).
  • Both StakePosition PDAs (yours, one per pool).
  • Both reward vaults.
Doing this in one instruction saves you the two-transaction race between “claim” and “restake” — no slot in between where someone else’s deposit could dilute the index you were about to enter.
claim_and_restake does not mint or convert anything. The token coming out of the emission pool must be the same token the protocol pool accepts as its stake mint. Routing through swaps is the caller’s job, not the program’s.

Admin tail end: reclaim_unallocated

When an emission pool reaches end_ts, there is sometimes a gap between total_allocation and emitted_so_far. That gap is the leftover allocation: tokens that the curve would have released, except no one was staked during parts of the schedule, so the accumulator never advanced. After end_ts, the pool admin can call reclaim_unallocated to sweep that leftover back. In a healthy pool with steady stake throughout the schedule, this number is essentially zero. It is mostly a cleanup path for emission pools that finished underfilled relative to their advertised allocations.
reclaim_unallocated is gated on end_ts having passed. It cannot be used to claw back tokens mid-schedule, and it never touches your StakePosition — your already-earned rewards are claimable independently.