The Technicals

Primary functions

Stake shards

  • stakeFractions(...) Moves the caller’s slice tokens into the vault and updates the UserPosition PDA:

    • secondsDebt – snapshot of the caller’s slice-seconds balance.

    • rewardDebt[mint] – one entry per reward mint so newcomers don’t skim past earlier rewards.

Unstake shards

  • unstakeFractions(...) Mirrors the maths, returns the staked tokens, and shrinks both debts.

Claim rewards

  • claim(...) Computes the pending amount

    pending  =  (stakeSecondsnowsecondsDebt)×accRewardPerShare\text{pending} \;=\; \bigl(\text{stakeSeconds}_{\text{now}} - \text{secondsDebt}\bigr) \times \text{accRewardPerShare}

    CPI-transfers it from the reward vault, then resets secondsDebt to the current stake-seconds.


The Authority (Contract Owner)

  • Refill / withdraw inventory

    • depositFractionTokens(...) & withdrawFractionTokens(...) move slice tokens between the Authority and the slice-vault.

  • Change price or mints

    • updateSwapConfig(...) updates token mints or the fixed exchange-rate.

  • Inject rewards

    • depositRewards(...) deposits any SPL mint into the reward-vault, snapshots the epoch, calculates the stake-second value, and bumps the reward-drop counter.

  • Initialize

    • initialize(...) sets up the whole program (vault PDAs, config, etc.).


Buyers

Any address can call depositQuoteTokens(amount) to purchase slices (shards) in one atomic transaction the contract:

  1. CPI-transfers the quote tokens into the payment vault.

  2. Divides amount by the fixed exchange-rate, pulls slice tokens from the slice-vault, and sends them to the buyer.

  3. Writes/updates a BuyerRecord PDA for the audit trail: who paid, how much, when.


Mathematics Behind Reward Calculation

TLDR; the smart contract functions as one big clock that tallies how long every token has been staked, then splits each reward by that tally—so you automatically get your slice and any leftovers roll into the treasury.

The pool tracks a single global timer, accSecondsPerShare, which grows by “seconds since last update ÷ totalStaked” whenever someone stakes or unstakes, logging stake-seconds per token. Each reward drop snapshots its own accRewardPerShare[mint] (scaled 1e18 for integer math), so your payout is just your stake × (new-minus-old snapshot) ÷ 1e18. Any reward slices tied to tokens that weren’t staked at epoch close get swept into the protocol treasury, so unclaimed yield becomes protocol revenue instead of disappearing. Now, here are the technicals:

  • Global accumulator The pool keeps accSecondsPerShare (Q128.64 fixed-point). Each time stake changes, syncSeconds() adds ΔttotalStaked\frac{\Delta t}{\text{totalStaked}} to that value.

  • Per-reward accumulator Every reward drop stores an accRewardPerShare[mint], scaled by 1e18.

  • Treasury credits Unsold or unstaked slices at epoch close are treated as treasury credits: the rewards meant for “nobody” accumulate under the contract’s own key instead of disappearing.

Last updated