$MIRROR whitepaper
v1.1 · launching on Solana mainnet-betaa permanent identity layer for tickers, and the launch handler built on top of it
This document describes what is actually built and running (program ID GXYdT8sPgrc63D3FbxmwiK8LomoC8DYnHFbEnonRGtoV on Solana devnet right now), not a roadmap dressed up as a spec. Every mechanism below is a real instruction in programs/mirror/src/lib.rs — nothing here is aspirational unless the "Roadmap" section says so explicitly. The mechanics are identical on devnet and mainnet; only the Program ID and the money at stake differ. Not financial advice.
Audit status: audited by Cyfrin — complete, no unresolved findings. Public report link pending; this page will be updated with it once it's up.
Folklore holds that a vampire casts no reflection. $MIRROR turns that into a protocol primitive: before a ticker is deployed anywhere — pump.fun, another launchpad, a raw SPL mint — its creator can claim a reflection, a permanent on-chain record binding a wallet to that ticker, first. Anyone can check a ticker before buying: a reflection that exists means someone put a wallet on the record and stood behind the name. No reflection, or one claimed suspiciously late, means there is nothing behind the identity at all.
On top of the registry sits Shield, an on-chain anti-vamp check-and-launch handler: it scores a new ticker/name/twitter/image against recent claims, writes a permanent verdict, and — only if the check passes — builds and sends the launch transaction to pump.fun itself. Every launch made through $MIRROR runs through this same handler, so every launch has a public, immutable check attached to it, pass or block.
Permissionless launchpads have no concept of identity. Anyone can deploy a ticker, including one that already belongs — visually, textually, or by reputation — to an existing project. The result is a predictable pattern: a real project gains attention, and within minutes near-identical tickers, names, and avatars appear to catch its overflow buying pressure. Buyers have no fast, trustless way to tell which one, if any, is "the original," because launchpads themselves don't track that — they only track mints.
This isn't a technology gap that needs a new blockchain or a new token standard. It's a missing registry: a place to prove priority (who showed up first) independent of which launchpad, if any, a project eventually deploys on.
A reflection is a Solana PDA (program-derived address) deterministically seeded from a normalized ticker: seeds = ["reflection", normalize(ticker)]. Normalization is uppercase, ASCII-alphanumeric only, truncated to 10 characters — so mirror, MIRROR, and $M.I.R.R.O.R! all resolve to the exact same account, closing the obvious "beat the claim with punctuation" loophole.
The entire trust model rests on one Solana primitive: an account created with init fails outright if it already exists. claim_reflection uses init on that PDA, which means a ticker can be claimed exactly once, by whoever's transaction lands first, full stop. There is no admin key, no upgrade authority over someone else's claim, and no instruction that can overwrite or delete a reflection. The guarantee doesn't depend on $MIRROR behaving honestly later — it's enforced by the Solana runtime itself.
| Field | Mutable after claim? |
|---|---|
| ticker | no — identity is permanent |
| claimed_at | no — priority is permanent |
| authority (owning wallet) | no — claims are not transferable |
| image_uri, twitter | yes, via update_socials — owner-only |
Claiming is free (rent-exempt deposit only, refundable if the account is ever closed).
Shield (run_shield_check) is the mechanism that makes the registry actionable at launch time instead of only after the fact. A creator submits a ticker/name/twitter/ image plus a list of existing reflections to compare against (capped at 20, enforced by the program itself); the instruction scores the submission and permanently records the result — on-chain, inside the program, not in a frontend that could quietly change its mind later.
| Signal | Risk score |
|---|---|
| exact ticker collision (post-normalization) | 100 — always blocked |
| twitter handle reused, case-insensitive | 90 |
| image URL byte-identical | 85 |
| ticker ≥ 80% edit-distance similarity | 70 |
| project name ≥ 85% edit-distance similarity | 60 |
The highest-scoring signal wins; a score ≥ 55 is recorded as blocked. Edit distance is a straight Levenshtein implementation, run in Rust inside the instruction.
The launch handler. On this site, one click drives both steps: submit the Shield check, and — only if it passes — immediately build pump.fun's own create_v2 instruction (via their official @pump-fun/pump-sdk) and send it as a second transaction. Every launch made through $MIRROR goes through this exact path; there is no separate "skip the check" launch button. The two transactions are kept deliberately unbundled: Solana rolls back every instruction in a failed transaction, so bundling the check and the launch atomically would mean a blocked verdict never actually got written anywhere. Keeping them separate means a blocked attempt is still a permanent public record, and the live feed shows real rejections, not just successful launches.
No smart contract on any chain can enumerate "every account under this program ID" from inside an instruction — that's a platform limit, not a shortcut this project took. The candidate set scored against is whatever the caller supplies, capped at the most recently claimed 20 reflections here. If the registry grows past that, a check only ever compares against the most recent claims. Because every check is its own transaction, the exact candidate list used is publicly auditable after the fact on any explorer — but it cannot be made complete up front, by construction.
Shield is also not a launch gate anywhere except this site's own UI. It calls pump.fun's public, permissionless program like any other client — there is no partnership, no special access, and nothing stops the identical ticker from being deployed directly on pump.fun's own site, bypassing Shield entirely. A passed check is a permanent record that a check was run and passed at that point in time, against that candidate set — not a guarantee of legitimacy, and not financial advice.
| Action | Program-required cost | This frontend's protocol fee |
|---|---|---|
| claim a reflection | rent-exempt deposit only | none |
| shield check | rent-exempt deposit + tx fee | 0.005 SOL |
| launch (if check passes) | pump.fun's own costs | 0.01 SOL |
The protocol fee is a plain SystemProgram.transfer this frontend adds alongside the program instruction in the same transaction — visible in your wallet's preview before you sign. run_shield_check itself has no idea this fee exists and works identically without it; calling the program directly, or through a different frontend, skips it entirely. Reflection claims have never had a fee, from any frontend.
The homepage's live activity feed and Shield's own feed are not a database this site controls — they are getProgramAccounts reads against the live program, decoded client-side and merged by timestamp. A claim, a passed check, a blocked check, and a launch are all the same kind of fact: an account that exists on Solana, readable by anyone, that nobody — including the people who built this site — can quietly edit or delete after the fact.
- No admin key. No upgrade authority over any user's claim or verdict.
- Uniqueness is enforced by Solana's
init-on-existing-account failure, not by application-level checks that could be bypassed or changed. - Ticker/name normalization is identical across the client, the program, and this document.
- Shield's scoring, thresholds, and verdict-writing all execute inside the program — a frontend operator cannot alter a recorded score after the fact.
- Candidate-set incompleteness (§4) is a disclosed, structural limitation, not a bug.
- Audited by Cyfrin, no unresolved findings. Public report link pending — see Roadmap.
The following are not yet built — listed here as direction, not claims about the current deployment:
- Publish the Cyfrin audit report link once it's available.
- Off-chain indexer so Shield candidate sets can reasonably approach "every claimed reflection," not just the most recent 20 — without changing the on-chain trust model.
- Optional cross-program invocation into pump.fun so check-then-launch can become a single atomic transaction, once their program surface is stable enough to audit for on-chain use.
- Extending the same claim/init-guard mechanic to additional launchpads beyond pump.fun.
Nothing in this document is financial, legal, or investment advice. $MIRROR is not affiliated with pump.fun or any project that claims a reflection. Program ID: GXYdT8sPgrc63D3FbxmwiK8LomoC8DYnHFbEnonRGtoV on Solana devnet — verify it yourself on an explorer rather than taking this page's word for it.