Program docs
The staking vault is a single Anchor program. One vault per mint, one position per staker per term. Principal never leaves the position — the program can move rewards, and nothing else.
Accounts
| Account | Seeds | Holds |
|---|---|---|
| Vault | ["vault", mint] | Reward accumulator, total weight, lifetime counters |
| Position | ["position", vault, owner, term] | Amount, term, reward debt, settled lamports, unlock slot |
| Treasury | ["treasury", vault] | SOL awaiting distribution, plus the LST float |
| Escrow | ["escrow", vault] | Token account holding staked principal |
Instructions
| Instruction | Signer | Effect |
|---|---|---|
| initialize_vault | creator | Creates the vault and locks the fee share. Callable once per mint. |
| stake | owner | Moves tokens to escrow, opens a position, adds amount × multiplier to total weight. |
| claim | owner | Pays out accrued lamports. Legal at any time, term or no term. |
| unstake | owner | Returns principal. Before unlock, forfeits accrued rewards into the accumulator. |
| distribute | keeper | Sweeps fees into the treasury and advances acc_reward_per_weight. |
| sweep_idle | keeper | Moves treasury float in and out of the liquid staking position. |
Reward accounting
Distribution is O(1) in staker count. The vault tracks a single monotonically increasing accumulator; a position stores the value it last settled against.
weight = amount * multiplier
pending = weight * (vault.acc_reward_per_weight - position.reward_debt)
+ position.settled_lamports
// distribute(lamports)
vault.acc_reward_per_weight += lamports / vault.total_weight
// unstake before unlock — forfeit accrued, never principal
recycled = pending * 0.9
vault.total_weight -= weight // exclude the leaver first
vault.acc_reward_per_weight += recycled / vault.total_weightNote the ordering in the forfeit branch: weight is removed before redistribution, so the leaver cannot claim a share of what they just forfeited.
Term multipliers
| Term | Multiplier | Tier |
|---|---|---|
| 7d | 1× | Flexible |
| 30d | 1.5× | Committed |
| 90d | 2.5× | Conviction |
| 365d | 5× | Founder |
Multipliers are stored in basis points on the position and cannot be changed after staking. Idle treasury float targets 7.8% via liquid staking.
Public API
Read-only, unauthenticated, cached at the edge. Amounts are decimal SOL unless the field name says lamports.
| Endpoint | Returns |
|---|---|
| GET /api/launches?sort=&limit= | All vaults with computed APY. sort: apy · marketCapUsd · volume24hUsd · stakedRatio · lifetimeFeesSol · createdAt |
| GET /api/launches/[mint] | A single vault with its APY breakdown |
| GET /api/stats | Protocol aggregates: average APY, staked value, 24h volume and yield |
| GET /api/positions?owner= | Open positions for a wallet |
Security notes
- — The program has no withdrawal authority over escrow beyond returning principal to the position owner. There is no admin path to staked tokens.
- —
distributeis keeper-signed but permissionlessly verifiable: it can only increase the accumulator, and only by lamports actually deposited in the same transaction. - — Rewards accrue against a snapshot, so staking immediately before a distribution earns nothing from it. This is what stops deposit-front-running of a known incoming sweep.
- — Not yet audited. Verify the deployed program on Solscan against this source before committing real size.