Rewards Engine (Technicals)
Cliques’ rewards layer is the on-chain brain that tracks, allocates, and distributes yield to activated participants with precision, transparency, and built-in early-mover alpha.
Core Concepts
1. Normalized Reward Accounting
Rewards deposited from games are normalized to a fixed decimal base (9 decimals) so disparate SPL tokens can be handled uniformly. Deposits update a global counter (total_rewards_deposited_normalized), and the engine safely calculates per-share value using fixed-point math (via MULT) to avoid precision loss or overflow.
2. Vault & Share Architecture
- Reward Vault State: Holds accumulated rewards-per-share (acc_rps), last known balance, and per-mint active share count (mint_shares).
- Reward Vault Config: Tracks the creator, associated token mint, and total staked metadata.
- First Activator Pot: A reserve of unallocated rewards that becomes the launch bonus for the first participant to activate a given token’s rewards (the “first activator bonus”). This creates strong early incentives—early activation gets all pending unallocated rewards before the distribution shifts to stake-weighted sharing.
3. Activation & Positioning
- UserRewardPosition: Each user has a position with share, debt, and last claim timestamp.
- Init Flow: On first activation (init_user_rewards), the vault syncs, first-activator bonuses are applied if appropriate (without contaminating their initial debt), and the user’s share is added to mint_shares while their debt is initialized to reflect accrual logic.
- Debt Model: Debt ensures users only claim incremental earnings; owed = share * acc_rps / MULT, and pending = owed − debt.
4. Dynamic Distribution
- When new rewards are deposited (deposit_game_rewards):
- If no active users (mint_shares == 0), amount accumulates in the first activator pot.
- Otherwise, acc_rps is bumped proportionally: reward × MULT / mint_shares.
- Vault syncing logic (sync_vault) keeps acc_rps up to date when vault balance grows outside of planned flows.
5. Claiming & Cleanup
- Claiming: Users call claim_user_rewards to realize pending rewards. Vault is synced, user share/debt is reconciled, and difference is transferred.
- Registry: Each user has a registry tracking which token mints they’ve activated, ensuring batched queries and cleanup.
- Cleanup: If no active users and cleanup period elapsed, unclaimed rewards can be burned (cleanup_unclaimed_rewards), resetting state and preventing stale accumulation.
6. First Activator Bonus
Designed as “alpha, not a bug”:
- Grants the entire unallocated pot to the first real staker/activator for a token, giving enormous upside to early engagement while seeding usage.
- Subsequent activations shift distribution to a stake-weighted model, preserving fairness once activity stabilizes.
7. Integration & Security
- PDA-safe flows: Vaults and state are derived deterministically with seeds (e.g., CFG_SEED, VAULT_SEED, USER_SEED) to avoid ambiguity.
- Authorization: Deposits require being from authorized actors (admin, game program, staking program, or valid PDAs).
- Overflow guards: All arithmetic uses safe checked math; helpers like mul_div_u128 and normalization routines guard against precision loss and overflow.
- Eventing: Cleanup, bonus grants, and share updates emit structured events for observability and indexing.
8. Rich Read APIs
- Batch and single-token getters expose pending rewards, vault health, user share status, and global normalized stats, enabling front ends to feed real-time dashboards without heavy client logic.
Staking Engine (Technicals)
The staking layer is the leverage point—“taking a long position on giveaway activation.” It aligns capital with protocol growth, enforces commitment via lockups, and feeds the rewards layer for systematic yield.
Core Mechanics
1. Lockup & Commitment
- Stake Lockup: When staking, tokens are locked for a minimum of 7 days before they can enter an unstake phase.
- Unstake Lockup: After requesting unstake, there is an additional 7-day redemption delay before the user can claim their tokens.
This two-step mechanism enforces time-based skin in the game, discouraging churn and aligning staker incentives with healthier long-term activation behavior.
2. Stake Info & Fee Vault
- StakeInfo: Per-user state contains amount staked, reward debt, pending unstake amounts, timestamps for last stake, and lockup windows.
- FeeVault: Global aggregator tracking total staked, accumulated reward-per-share (acc_reward_per_share), pending distributions, and global pending unstakes—serving as the source of truth for reward calculations.
3. Reward Debt / Accrual
- Users accumulate reward entitlement via acc_reward_per_share.
- On stake/unstake, pending rewards are settled (difference between accumulated and stored debt), then the stake amount and debt are updated.
- This ensures users only receive what they have legitimately earned according to their share and the global rate.
4. Integration with Rewards Program
- Dual coordination: After local stake updates, the system conditionally pushes updates into the rewards engine (CliquesRewards) using CPI:
- If the user already has an activated reward position, global share counters are adjusted via update_global_shares.
- The user’s per-token position is updated via update_user_position, ensuring upstream reward accounting reflects the current stake.
- New stakers defer formal activation until they call init_user_rewards, avoiding premature inflation of global counters.
- Stake-aware activation: This layered activation decouples raw staking from reward activation, giving builders control over when a staker “shows up” in yield calculus.
5. Unstaking & Claiming
- Requesting Unstake: Validates lockup expiry, updates pending unstake, and imposes redemption wait.
- Claiming Unstaked: After redemption window, tokens are returned; global share counters are updated only if the user was activated, avoiding stale impact.
- Admin Force Unstake: Emergency migration path that returns full amounts (stake + pending) and adjusts share state, with CPI calls to reconcile downstream reward state.
6. Reward Distribution
- Protocol-side rewards (e.g., SOL rewards) are injected into the fee vault.
- Rewards expand the acc_reward_per_share based on total staked:
increment = rewards × MULTIPLIER / total_staked - Stakers’ pending rewards are derived from their stake × acc_reward_per_share minus their debt.
- Claiming settles pending rewards and updates debt to current accumulated state.
7. Economics & Safety
- Fixed-point precision: Use of large multipliers prevents rounding drift during division-heavy reward math.
- Sanity checks: All flows validate ownership, lockup constraints, and arithmetic bounds.
- Paused state: Admin can pause the contract to halt new actions if needed (guardrail for abnormal conditions).
- Fallback / Migration: Configurable fallback wallet and force-unstake paths support resilient operations in edge cases.
8. Event & State Visibility
- Staking info, reward debt, pending amounts, and time-until-claimable are queryable via a dedicated getter that packages a user’s entire current position.
- Updates to global shares or user positions get propagated to the rewards layer, maintaining eventual consistency without trusting off-chain reconcilers.