Docs

How Proof of Coin works.

From the idea down to the bytes. The first half is for holders and launchers, the second half is for anyone reading the program.

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.

  1. 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.
  2. Fill in a name, ticker, description, image, links and an optional dev buy. pump.fun coins have a fixed 1,000,000,000 supply.
  3. 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.
  4. The image and metadata go to pump.fun's IPFS, with the stamp line added to the description. PumpPortal builds the create transaction.
  5. The mint key signs in your browser and your wallet signs the rest. The server only relays; it never sees a private key.
  6. 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 eraBitcoin heights$POC per proof
00 – 209,99950
1210,000 – 419,99925
2420,000 – 629,99912.5
3630,000 – 839,9996.25
4 (today)840,000 – 1,049,9993.125
51,050,000 – 1,259,9991.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 its bits;
  • its prev_block equals 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

InstructionWhoWhat it does
initializedeployer, onceCreates the config, the $POC mint and a checkpoint Bitcoin header.
relay_headeranyoneAdds the next Bitcoin header after checking work and linkage.
file_unprovenanyonePuts a mint on the Unproven Ledger. It owes one hash.
proveanyoneVerifies a proof, marks the coin PROVEN, writes a receipt and mints $POC.
launch_stampedanyoneVerifies a birth stamp, creates the coin with fixed supply and marks it PROVEN.
set_pausedadminStops relaying, proving and launching. Filing stays open.
transfer_authorityadminMoves the admin key, or burns it by passing none.

Errors

BrokenLink, InsufficientWork, TimeTravel, StaleAnchor, AnchorExhausted, AlreadyProven, ProofMismatch, NotEnoughZeros, RewardExhausted, BadMetadata, ZeroSupply, Paused, Unauthorized.

Accounts

AccountSeedsHolds
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?
No. Bitcoin isn't written to. Proof of Coin reads Bitcoin's block headers and makes Solana coins do work that references them. Bitcoin is the clock, Solana keeps the books.
Can I prove my own coin to get $POC?
Yes, the same as anyone else, as long as nobody proved it first. Coins launched on the launchpad are already proven and can't be proven again.
Why can't I prove against the genesis block once it's live?
Proofs must use one of the six newest relayed blocks. Otherwise people would stockpile proofs against old, easy anchors.
Why does my browser get hot?
Because it's doing real proof-of-work. Lower the thread count on the prover if you'd rather it didn't.
When is launch?
When it's announced on this site, with the mint address. Not before.

References