Documentation
Everything FeeRelay does is in one small Anchor program. The website and API are conveniences on top of it.
Overview
A project stores 1–8 recipient wallets and their shares in basis points (total exactly 10,000). Each project has a vault: a data-less, system-owned address derived from the project. Fees sent to the vault are swept into the project account by distribute and credited to recipients. Each recipient withdraws with their own signature.
Accounts & PDAs
| Account | Seeds | Purpose |
|---|---|---|
| Config | ["config"] | Admin, pending admin, pause flags, project count. |
| Project | ["project", creator, project_id] | Recipients, shares, claimable balances, totals, dust, config nonce, lock. |
| Vault | ["vault", project] | Fee address. Pre-funded to its rent floor so small payouts land. |
Including the creator in the project seed prevents another wallet from squatting a project ID seen in the mempool.
Instructions
| Instruction | Who | Effect |
|---|---|---|
initialize | Upgrade authority | Creates Config. Can only run once. |
create_project | Any wallet | Validates recipients, creates Project, funds vault rent floor. |
distribute | Anyone | Sweeps vault balance above rent floor and credits recipients. |
withdraw(index, amount) | That recipient | Moves up to the recipient’s claimable lamports to their wallet. |
update_recipients(nonce, …) | Project authority | Distributes pending fees under the old split, carries balances, replaces the list. Rejects removing a recipient with a balance, and stale nonces. |
lock_recipients | Project authority | Permanently freezes the list. |
set_authority | Current + new authority | Both must sign. |
set_paused, propose_admin, accept_admin | Admin | Pause config/distribution and, separately, withdrawals; two-step admin rotation. Admin cannot move funds. |
Routing fees into a vault
The vault address is shown after you create a project and on the dashboard. Options:
- Manual (works now): send SOL to the vault from any wallet or program, then call
distribute. - pump.fun fee sharing (unvalidated): pump.fun's official fee-sharing config lets a coin creator split fees among shareholders by basis points. Adding the vault as a shareholder would route that share into FeeRelay.
- pump.fun creator address (unvalidated): launching a coin with the vault as
creator.
The pump.fun routes have not been reproduced on devnet by this project, so live launch is disabled. See docs/PUMPFUN_INTEGRATION.md in the repository for the validation plan. FeeRelay never hard-codes pump.fun fee percentages.
Split math & dust
For each distribution, pool = swept + dust; each recipient gets floor(pool × bps / 10000) using 128-bit intermediates; the remainder (always fewer lamports than recipients) becomes the new dust. Invariant: project lamports = rent + Σ claimable + dust.
HTTP API
All state-changing endpoints require a wallet session (Sign-In With Solana message, single-use nonce, 5-minute expiry) and same-origin requests. Create endpoints require an Idempotency-Key header. Full reference: docs/API.md.
GET /api/health
POST /api/auth/nonce { wallet }
POST /api/auth/verify { wallet, nonce, signature }
GET /api/projects (mine)
POST /api/projects { name, recipients[], launchConfig } + Idempotency-Key
GET /api/projects/:id
POST /api/projects/:id/onchain { signature } (server re-verifies on-chain)
POST /api/withdrawals { projectId, signature } + Idempotency-Key
GET /api/tx/:signatureKeeper & indexer
Long-running jobs do not run on Vercel. An external worker (worker/keeper.ts) calls /api/indexer/sync with a bearer secret to cache events, and can optionally call distribute for projects with pending fees. The dashboard always reads balances directly from the chain, so the cache is never the source of truth.