How it works
The exact checks, in the order they run, each with the line of source that makes the sentence true. lib.rs is programs/fairproof/src/lib.rs, the on-chain program; verify.ts is web/verify/src/verify.ts, the verifier your browser runs; README.md under web/verify documents it.
1. The launch transaction: the program refuses, it does not annotate
One atomic instruction
A founder calls one instruction,
source: lib.rs:5-12, 239-244, 301-345create_fair_launch. Inside that single atomic transaction the 0.25 SOL fee is paid, the LP tokens move into a program-owned vault, and the proof record is written. If any step fails, nothing happens and no fee is taken.Live authorities are refused
The program refuses a launch whose mint authority or freeze authority is still live. It does not record the problem and continue; the transaction fails.
source: lib.rs:209-224At least 90% of the LP supply
At least 90% of the LP token’s current supply must be locked, or the launch is refused. A dust lock cannot get a proof.
source: lib.rs:226-237, 654A real unlock date
The unlock date must be in the future and within 100 years.
source: lib.rs:198-206, 648One withdrawal, by one person, after one date
Only the original founder can withdraw, only at or after the unlock date, and only once.
source: lib.rs:366-428, 1072-1083One proof per token, forever
There is exactly one proof per token mint. It cannot be re-locked, topped up or split, and the proof record is never deleted — it persists after withdrawal as a public record.
source: lib.rs:989-995, 1016-1026; docs/f5-audit-lite.md:66-68A soulbound receipt
The founder may claim one soulbound receipt token (Token-2022 NonTransferable, supply 1, authority renounced) that can never leave their wallet.
source: lib.rs:430-446, 547-613An emergency brake that cannot trap founders
FairProof can pause new launches in an emergency. Pausing never blocks a founder from withdrawing an expired lock.
source: lib.rs:196; docs/f5-audit-lite.md:123-132
2. What the verifier checks, every time someone looks
Locked & Fair only when all ten checks pass: the proof exists and is authentic, the vault is authentic and still holds the recorded LP, the mint account is real, the LP mint is real, the lock is still active by chain time, mint authority renounced, freeze authority renounced, no Token-2022 control surface, and the locked share meets the minimum.
source: verify.ts:12-25, 169-180Time comes from the chain
Time comes from the chain (Clock sysvar or block time), never from your computer’s clock. If only a local clock is available the result is Not Verified.
source: web/verify/README.md:130-147Renounce state is read now, not copied
Renounce state is read from the token mint at verification time, not copied from the proof. Both are shown; the read-time state decides. Authorities live on the token mint, not on the proof: a verdict is a snapshot as of the slot shown, not a promise about the future.
source: web/verify/README.md:186-200; web/page/src/render.ts:399The locked share is recomputed
The locked share is computed from the vault’s live balance against the LP mint’s live supply, using the program’s own integer math.
source: web/verify/README.md:124The proof account itself is checked
The proof must be owned by the program, carry the right discriminator and exact size, and sit at the address derived from the token mint. An edited or look-alike record is Not Verified.
source: verify.ts:454-495
3. Token-2022 policy: the table, not a summary
Renouncing both base authorities is not enough on Token-2022: a mint can keep a control surface in an extension. The program’s gate reads the base authorities; the verifier decodes every extension on the mint and refuses to badge one that keeps a post-launch control surface. FairProof will not badge such a launch.
| Extension | Verdict | Condition |
|---|---|---|
| PermanentDelegate | Not Fair | with a delegate set |
| TransferHook | Not Fair | with a program or authority |
| TransferFeeConfig | Not Fair | with a live authority, or an immutable 100% fee |
| Pausable | Not Fair | with an authority, or paused |
| MintCloseAuthority | Not Fair | set |
| DefaultAccountState | Not Fair | frozen |
| NonTransferable | Not Fair | present |
| InterestBearing / ScaledUiAmount / ConfidentialTransferFee | Not Fair | with an authority |
| Any account-only or unrecognised extension; any malformed payload | Not Fair | always |
| Metadata and group pointers; confidential-transfer configuration | Disclosed, not refused | shown on the page |
| Immutable transfer fee below 100% | Disclosed, not refused | shown with its basis points |
4. The four verdicts
Labels are code constants. Precedence: Not Verified beats Expired beats Not Fair beats Locked & Fair. No fallback can produce Locked & Fair.
source: verify.ts:149-154, 32-33Locked & Fair
All ten checks passed at the slot shown: the proof is authentic, the vault still holds the recorded LP, mint and freeze authority read as renounced, no Token-2022 control surface, lock still active by chain time, locked share at or above the minimum.
Lock Expired
The unlock time has passed, or the LP has been withdrawn. LP is withdrawable. This is never shown as Locked & Fair.
Not Fair
The proof is real and the lock is active, but a gate fails: a live authority, a Token-2022 control surface, a locked share below the minimum, or an “LP” that is the launch token itself.
Not Verified
No proof exists for this mint, or the proof is tampered or inconsistent, or something could not be read. Nothing about the launch is asserted — this is the absence of a proof, not a failure of the token.
Samples of the reference badge art, not live launches.
5. Expiry, honestly
Lock Expired means the unlock time has passed or the LP has been withdrawn, and the page says so: “LP is withdrawable.” It is never shown as Locked & Fair, and a badge pasted while the lock was active turns amber the moment chain time passes the unlock date — the embed points at the live badge, never at a state.
Two consequences worth reading twice. First, “LP locked” without a date and a share is not a FairProof statement; the page always shows both. Second, a lock does not stop the LP mint’s own supply from growing if the DEX that issued it keeps its mint authority — that is why the locked share is recomputed from live supply on every read, and why the scope statement exists.
6. The proof page and the badge
No backend
Your browser reads the RPC endpoint directly and runs the verifier locally. What you see is what the verifier returned for what the RPC served, as of the slot shown.
source: web/page/README.md:3-7; web/page/index.html:130-132Every value links to its evidence
Every value links to the account, transaction or slot it was read from.
source: web/page/src/render.ts:111-150, 491-524The creation transaction is found, not assumed
When the page looks up the creation transaction, it is located and verified as a successful
source: web/verify/README.md:218-235; web/page/src/render.ts:538create_fair_launchfor this mint; the oldest signature is never assumed. It is shown as evidence, not counted as a check: a verdict does not depend on it, and when it is not looked up the page says so.A link cannot choose what gets verified
The program id and RPC endpoint are pinned by the deployment; a URL that tries to override them is ignored and the page says so. The page never shows the green head unless the verdict is consistent, Locked & Fair, and computed under pinned configuration.
source: web/page/README.md:39-72; web/page/src/render.ts:285-294, 9-24, 259-268The badge fails closed
The badge is green only if the verifier says Locked & Fair. Anything else — including an edited or missing verdict — renders a different colour, text and mark, so it survives greyscale and red-green colour blindness.
source: web/badge/src/badge.ts:9-31, 128-139
Scope, glossary, verdict semantics and privacy in the docs →