Tokenomics activation
CPI-driven protocol economy that wraps the renounced mint: settlement-time fees, distribution rewards, buyback-and-burn, retro vesting. The architecture that replaces the aspirational TransferHook design.
Status: spec for the protocol-side economy wrapping the live SAEP mint.
Mint: see token2022-saep-mint.md.
Programs in scope: fee_collector, nxs_staking, treasury_standard, task_market, governance_program, template_registry, retro_distributor (when scaffolded).
Goal
Define how value flows through the SAEP protocol when the mint itself carries no transfer-time hooks — the economy is CPI-driven across the program set, and this spec is the public record of the architecture.
Why CPI-driven
The live SAEP mint is fully renounced and has no TransferHook, TransferFee, PermanentDelegate, InterestBearing, or Pausable extensions (see token2022-saep-mint.md). Pump.fun-style mints publish that posture deliberately. Rather than treat the absence as a defect, the protocol economy is wired entirely at program boundaries:
- Fees are captured at task-settlement time, not transfer time
- Staker rewards are distributed from a vault, not rebased on the mint
- Burns are executed via standard
burn_checkedagainst a protocol-held balance - Retro and incentive payouts use
treasury_standardstreaming claims
This is the same design pattern most Solana protocols converge on regardless of mint extension support.
Value flow overview
┌─────────────────────────────────────────────────┐
│ task_market │
│ release: payment_amount = agent_payout │
│ + protocol_fee ──────┐ │
│ + solrep_fee ──────┼──┐ │
└──────────────────────────────────┼──┼───────────┘
│ │
┌──────────────────────┘ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ fee_collector │ │ solrep pool │
│ EpochAccount │ │ (treasury) │
│ total_collect │ └────────────────┘
└───────┬────────┘
│ commit_distribution (per-epoch)
▼
┌────────────────────────────────────────────┐
│ StakerClaim PDAs (per staker, per epoch) │
└───────┬────────────────────┬───────────────┘
│ claim_staker │ execute_burn (buyback path)
▼ ▼
┌──────────────┐ ┌──────────────────────┐
│ stakers │ │ burn (supply down) │
└──────────────┘ └──────────────────────┘
Fee capture — task_market::release
When a task is verified and the dispute window expires, task_market::release splits payment_amount three ways before transfer:
agent_payout→ agent's operator wallet (the bulk of the payment).protocol_fee=payment_amount * MarketGlobal.protocol_fee_bps / 10_000. CPI-transferred to afee_collectorintake account.solrep_fee=payment_amount * MarketGlobal.solrep_fee_bps / 10_000. CPI-transferred to asolrep_poolATA.
The fee_collector::record_intake ix is the entrypoint — it is invoked by task_market (a registered slasher / fee source per FeeCollectorConfig) and adds the amount to the open EpochAccount.total_collected. No mint extension required.
fee_collector also accepts CPI fees from:
nxs_staking(slash receipts on operator misbehaviour)agent_registry(collateral forfeits onslash_agent)dispute_arbitration(dispute resolution penalties)governance_program(governance-routed allocations)
Each path uses the same record_intake family (slash_handler, forfeit_handler) gated on the caller program's pubkey matching a registered slasher in FeeCollectorConfig.
Distribution — fee_collector::commit_distribution → nxs_staking::claim_staker
When an epoch closes (process_epoch after epoch_seconds + grace_seconds have passed):
EpochAccount.statustransitions fromOpentoReadyToCommit.commit_distributionis called by anyone (gas paid by caller, no privilege required) and:- Reads
EpochAccount.total_collected. - Splits per
FeeCollectorConfig.distribution_split_bps(rewards bucket) vsburn_split_bps(burn bucket). - Allocates the rewards bucket across staker shares from
nxs_staking::Pool.total_stakedsnapshot at epoch close. - Creates one
StakerClaimPDA per eligible staker with(epoch_id, staker_pubkey, amount).
- Reads
- Each staker calls
claim_stakerto drain theirStakerClaimPDA into their ATA. PDA closes on claim, rent refunded.
Stakers see APY as a derived metric — not a native rebase but real distribution per epoch.
Burn — fee_collector::execute_burn
Two paths feed the burn bucket:
Direct burn from fee revenue
commit_distribution allocates burn_split_bps / 10_000 of total_collected to the burn bucket. If the bucket holds SAEP directly (e.g., from a Token-2022 SAEP-denominated fee path), execute_burn is called with a SAEP source account and burns it via burn_checked.
Buyback-then-burn (primary path for USDC fees)
Most fees accrue in USDC (payment_amount is typically USDC). The services/buyback-bot/ worker (deployed separately):
- Reads the USDC balance of the FeeCollector PDA's USDC vault.
- If above a configurable threshold (e.g., $50), routes the full balance through Jupiter v6 to swap USDC → SAEP. Slippage capped at 200 bps.
- Calls
fee_collector::execute_burnwith the SAEP balance just acquired. - Publishes
BurnExecuted { epoch_id, amount, signature }.
The buyback bot's keypair is restricted: it can call execute_burn and Jupiter-swap on its own ATAs only. No general SAEP transfer authority. No SAEP custody beyond the in-flight swap.
Staking — nxs_staking
- Stake mint: SAEP (
HEKVx7cxn4afiDKW56sWJGxzJe7wVBmhZhFzdqjApump). - Pool reward authority: FeeCollector PDA. Distributes through
commit_distribution. - Lockup: minimum 7 days at launch, raised by governance once a stable staker base exists.
- Slash authority:
governance_program. Only on operator misbehaviour, ratified by 6-of-9. - APY: surfaced on the portal
/stakingpage aslast_epoch_distribution / total_staked— a real moving number derived from on-chain state, not a configured rate.
Retro distribution — retro_distributor
Per retro-airdrop.md:
- 10–15% of supply allocated to a retro pool (TGE-snapshot, governance-funded from a treasury allocation).
- Trailing 6-epoch eligibility window.
- Operators with
PersonhoodAttestation::Verifiedclaim 100%; without, 50%. - Wash filter (self-task, circular settlement, burst, min-payment) excludes suspect fees.
- Claim flow: 50% cliff at TGE, 50% via a
treasury_standard::PaymentStreamlinear over 24 months.
The retro_distributor program is the on-chain claim ix; the eligibility table is materialised by the indexer (retro_rollup job consuming fee_collector::SlashReceived + intake events). Public claim surface lives at /retro/check on the portal.
Governance authority surface
MarketGlobalparameters (fee bps, deadlines, mint allowlist): governance 6-of-9.FeeCollectorConfigparameters (epoch length, splits, registered slashers): governance 6-of-9.nxs_staking::Poolparameters (lockup, slash threshold): governance 6-of-9.- Emergency pause on dependent programs (
fee_collector::set_paused,task_market::pause): ratified by emergency authority surface (smaller threshold, separate signer set perops-squads-multisig.md). - Treasury allocations from
governance_program::TreasuryVault(retro pool, bounty pool): 6-of-9.
What's not possible (vs the original aspirational design)
| Aspirational lever | Live posture |
|---|---|
| Halt all SAEP transfers in an incident | Not possible — no Pausable on mint. Programs pause; secondary trading continues. |
| Apply 10 bps fee on every transfer regardless of context | Not possible — settlement-time fees only. |
| Reclaim lost / stolen SAEP via PermanentDelegate | Not possible — fully renounced. |
| Show stakers a native rebase number | Not possible — APY is computed from realised distributions. |
| Block specific addresses from holding SAEP | Not possible — no freeze authority. |
These are protocol-level constraints, documented openly. Holders should know what the mint does and does not enforce.
Public verification
Anyone can independently verify the protocol-side flow:
MarketGlobalconfig: read on-chain, matches portal display.FeeCollectorConfigparameters: read on-chain.EpochAccounthistory: queryable via discovery (/v1/discovery/fee-epochsonce wired) or directly via RPC.BurnExecutedevents: indexed; cumulative supply burn surfaced on the portal/tokenomicspage.StakerClaimdistributions: per-staker, per-epoch, on-chain.- Retro eligibility table: indexer matview, public read.
Activation sequencing
This spec describes the architecture once activated. The activation order is captured in the substitute-package security work plus a separate sprint plan (not in this repo) and is gated on the security-review and bounty-pool prerequisites being live.
Roughly:
fee_collector::init_configvia 6-of-9 — sets epoch length, splits, registered slashers.nxs_staking::init_pool— establishes the SAEP staking pool.- Buyback bot deploy — daily USDC → SAEP → burn.
- Retro distribution — TGE snapshot, pool funded, claim flow live.
/tokenomicsportal page rewritten to surface live values.
Out of scope (this spec)
- Cross-chain SAEP (LayerZero / Wormhole) — separate spec, future work.
- ZK proofs of buyback authenticity — overkill at this scale.
- Pre-launch tokenomics governance (the mint is already launched; this spec describes operating economy, not launch ceremony).
- Re-mint to a fully-extended Token-2022 token — explicitly rejected; community sentiment + liquidity tied to v1.