Introduction
Proof of Coin starts from one rule: a coin isn't real until Bitcoin has seen it. Bitcoin blocks prove themselves, since each one carries the work that made it. Tokens on Solana carry nothing. Proof of Coin fixes that by binding coins to real Bitcoin blocks with proof-of-work, and keeping the books on Solana.
There are two ways a coin gets proven. New coins can be launched on the launchpad, already stamped. Coins that already exist sit on the Unproven Ledger until someone pays their hash on the prover, and that person is paid in $POC.
The Unproven Ledger
Every SPL token owes Bitcoin one hash from the moment it is minted. The ledger is where that debt is written down. Anyone can file any mint, which creates a ledger entry marked UNPROVEN with hash_owed = 1. When someone pays the hash, the entry flips to PROVEN and records the Bitcoin block, the nonce and the proof hash.
A coin can only be proven once. After that, the entry points at its Bitcoin block forever.
Launch status
The Proof of Coin program isn't deployed yet and $POC has no mint address, so the ledger counters read zero and the prover can't submit. The launchpad is different: it works today, by deploying through pump.fun. Launches are real mainnet transactions that spend real SOL.
Anyone selling $POC before a mint address appears on this site is not Proof of Coin.
Launching a coin
Until the program is live, the launchpad deploys real coins on pump.fun and writes the Bitcoin stamp into each coin's metadata.
- Run the launch server on your computer: double-click start.bat (Windows) or run node server.js, then open http://localhost:3000/launch.html. It needs Node.js 18 or newer.
- Fill in a name, ticker, description, image, links and an optional dev buy. pump.fun coins have a fixed 1,000,000,000 supply.
- Your browser makes a fresh mint keypair, pulls the current Bitcoin tip and checks its header, then mines a stamp bound to the new mint and your wallet.
- The image and metadata go to pump.fun's IPFS, with the stamp line added to the description. PumpPortal builds the create transaction.
- The mint key signs in your browser and your wallet signs the rest. The server only relays; it never sees a private key.
- When the transaction confirms you get links to the coin on pump.fun and Solscan.
Optional settings for the server: SOLANA_RPC_URL (defaults to the public mainnet endpoint, which is rate-limited), BTC_ESPLORA_URL (defaults to mempool.space) and PORT.
Birth stamps don't pay $POC. If they did, launching junk coins would be a cheaper way to farm rewards than paying real debts. Once the program is live, launch_stamped will do all of this on-chain in one instruction.
Proving an old coin
Paste any mint into the prover. If nobody has filed it yet, the same transaction files it. Your machine finds a proof against one of the six newest relayed Bitcoin blocks, the program checks it, the coin is marked PROVEN, and $POC is minted to your wallet.
The proof includes your wallet address. If someone copies your nonce out of a pending transaction, the hash comes out different for their wallet and the program rejects it with ProofMismatch.
$POC rewards
$POC has no presale, no team allocation and no admin mint. Its mint authority is a program address, and the only instruction that signs with it is prove. Every unit in existence has a proof receipt behind it.
| Bitcoin era | Bitcoin heights | $POC per proof |
|---|---|---|
| 0 | 0 – 209,999 | 50 |
| 1 | 210,000 – 419,999 | 25 |
| 2 | 420,000 – 629,999 | 12.5 |
| 3 | 630,000 – 839,999 | 6.25 |
| 4 (today) | 840,000 – 1,049,999 | 3.125 |
| 5 | 1,050,000 – 1,259,999 | 1.5625 |
Each Bitcoin block can back at most 21 proofs or stamps, so issuance can't outrun Bitcoin's pace.
The Bitcoin relay
Anyone can submit the next Bitcoin block header to relay_header. The program accepts it only if:
- it is exactly 80 bytes;
sha256d(header)is at or below the target encoded in itsbits;- its
prev_blockequals the hash of the current relay tip; - its timestamp isn't more than two hours before its parent's.
Each accepted header becomes a BtcAnchor account. The poc relay command fetches headers from any Esplora-compatible API, checks the work locally, and submits them in order.
# follow Bitcoin's tip, six blocks at a time
poc relay --api https://your-esplora-endpoint/api --max 6
The proof puzzle
Proofs and stamps solve the same puzzle. The input is 120 bytes:
message = "PROOF-OF-COIN:v1" // 16 bytes, domain tag || anchor // 32 bytes, Bitcoin block hash (internal order) || mint // 32 bytes, the coin || prover // 32 bytes, the wallet doing the work || nonce_le // 8 bytes, u64 little-endian proof = sha256(sha256(message)) valid = leading_zero_bits(proof) >= required_zero_bits(anchor.bits)
Zero bits are counted the way Bitcoin explorers display a hash, from the last byte backwards. The first 64 bytes never change during a search, so the prover compresses them once (the midstate) and each nonce costs three SHA-256 compressions instead of four. The page's prover, the SDK, the CLI and the Rust program all produce byte-identical hashes; the test suites check that against the same fixtures.
Difficulty Mirror
required_zero_bits = clamp(anchor_target_zero_bits / 4, 16, 32)
A 2009 Bitcoin block has a target with 32 leading zero bits, so a proof against it needs the minimum of 16 (about 65,000 hashes). A recent block has around 78, so a proof needs 19 (about 524,000 hashes). Because only the six newest relayed blocks can be used, proofs get harder exactly when Bitcoin does.
Program interface
| Instruction | Who | What it does |
|---|---|---|
| initialize | deployer, once | Creates the config, the $POC mint and a checkpoint Bitcoin header. |
| relay_header | anyone | Adds the next Bitcoin header after checking work and linkage. |
| file_unproven | anyone | Puts a mint on the Unproven Ledger. It owes one hash. |
| prove | anyone | Verifies a proof, marks the coin PROVEN, writes a receipt and mints $POC. |
| launch_stamped | anyone | Verifies a birth stamp, creates the coin with fixed supply and marks it PROVEN. |
| set_paused | admin | Stops relaying, proving and launching. Filing stays open. |
| transfer_authority | admin | Moves the admin key, or burns it by passing none. |
Errors
BrokenLink, InsufficientWork, TimeTravel, StaleAnchor, AnchorExhausted, AlreadyProven, ProofMismatch, NotEnoughZeros, RewardExhausted, BadMetadata, ZeroSupply, Paused, Unauthorized.
Accounts
| Account | Seeds | Holds |
|---|---|---|
| Config | ["config"] | Relay tip, counters, admin, pause flag. |
| $POC mint | ["poc-mint"] | 9 decimals; authority is the config PDA. |
| BtcAnchor | ["anchor", block_hash] | One verified Bitcoin header and how many proofs it has backed. |
| LedgerEntry | ["ledger", mint] | UNPROVEN or PROVEN, and which Bitcoin block proved it. |
| ProofReceipt | ["receipt", proof_hash] | The nonce, zeros and reward. One per proof hash, so work is never paid twice. |
| Launch | ["launch", mint] | Name, ticker, URI, supply and birth stamp of a launchpad coin. |
SDK and CLI
The TypeScript SDK builds every instruction by hand (no IDL step), decodes every account, and ships the same Bitcoin and proof code the site uses.
import { ProofOfCoinClient } from "@proof-of-coin/sdk"; const poc = new ProofOfCoinClient(connection); // launch a coin that's born proven const { mint, stamp } = await poc.launchStamped(wallet, { name: "Stamped Dog", symbol: "SDOG", supply: 1_000_000_000_000_000n, }); // pay an old coin's hash and get $POC await poc.proveCoin(wallet, someMint); // PROVEN, UNPROVEN, or not filed const verdict = await poc.check(someMint);
# the same from a terminal, hashing on every core poc launch --name "Stamped Dog" --symbol SDOG --supply 1000000000 poc prove --mint So11111111111111111111111111111111111111112 --threads 8 poc status
Limits and honesty
- It's a meme with real maths. The hashing is real and verifiable. The idea that a coin "owes" Bitcoin anything is the joke.
- The relay is a light client, not a full node. It checks each header's work and that it links to the last one, but not Bitcoin's difficulty retarget rules, and it trusts the checkpoint it starts from. Somebody with enough hash power could feed it a private chain. A production version would validate retargets and track cumulative work.
- Not audited. The program has unit tests and an integration test against a local validator. It has not been audited.
- Proofs say nothing about a coin's quality. PROVEN means someone did work tied to a Bitcoin block. It doesn't mean the coin is good, safe or worth anything.
Questions
Does this put anything on the Bitcoin blockchain?
Can I prove my own coin to get $POC?
Why can't I prove against the genesis block once it's live?
Why does my browser get hot?
When is launch?
References
- Bitcoin: A Peer-to-Peer Electronic Cash System (2008)
- “Bitcoin minting is thermodynamically perverse”, bitcointalk.org forum thread, August 2010
- Bitcoin block header reference
- Anchor framework
- SPL Token program