QENTRO

How it works

1. Two quantum sources

Qentro fetches 32 bytes from the vacuum-fluctuation generator of the Australian National University and 32 bytes from the public LfD QRNG service. Both raw response bodies are kept exactly as received. If one source is down, the launch can continue only with an explicit “1 of 2 quantum sources” warning. Non-quantum randomness is never substituted.

2. Derivation

A browser-side salt keeps the seed secret until you reveal it. Everything below is pure and deterministic.

anuBytes  = concat(hex data)                       // 4 × 8 bytes = 32 bytes
lfdrBytes = hex qrn                                // 32 bytes
salt      = crypto.getRandomValues(32 bytes)       // keeps the seed secret until reveal
seedBase  = SHA-256(anuBytes || lfdrBytes || salt)
seed_n    = SHA-256(seedBase || uint32BE(n))       // n = 0, 1, 2, ...
mint      = Keypair.fromSeed(seed_n).publicKey     // first n whose base58 ends with the chosen suffix
commit    = "qentro:v1 commit=<SHA-256(seedBase)> anu=<SHA-256(raw ANU body)> lfdr=<SHA-256(raw LfD body)> n=<n>"
reveal    = "qentro:v1 reveal mint=<mint> seedBase=<hex> salt=<hex>"

Suffix options are none, “q”, “Qu” and “Qnt” (case-sensitive). The expected number of tries is 58 to the power of the suffix length, ground in parallel Web Workers.

3. Commit before launch

The commit string is written as an SPL Memo inside the same transaction that creates the coin on pumpfun. The entropy is therefore fixed on-chain before anything is revealed.

4. Reveal

After the launch confirms, you sign a second tiny transaction that publishes the seed base and salt. The mint keypair has no authority over the coin after a pumpfun launch, so revealing its seed is safe.

Verify it yourself

import { Keypair } from '@solana/web3.js'

const hex = (b: Uint8Array) => [...b].map((x) => x.toString(16).padStart(2, '0')).join('')
const fromHex = (h: string) => Uint8Array.from(h.match(/../g)!.map((x) => parseInt(x, 16)))
const sha = async (d: Uint8Array | string) =>
  new Uint8Array(await crypto.subtle.digest('SHA-256', typeof d === 'string' ? new TextEncoder().encode(d) : d))
const cat = (...p: Uint8Array[]) => Uint8Array.from(p.flatMap((x) => [...x]))

export async function verify(proof: any) {
  const anu = fromHex(JSON.parse(proof.anuRaw).data.join(''))
  const lfdr = fromHex(JSON.parse(proof.lfdrRaw).qrn)
  const seedBase = await sha(cat(anu, lfdr, fromHex(proof.salt)))
  const n = new Uint8Array(4)
  new DataView(n.buffer).setUint32(0, proof.counter, false)
  const kp = Keypair.fromSeed(await sha(cat(seedBase, n)))
  return {
    seedBaseOk: hex(seedBase) === proof.seedBase,
    commitOk: hex(await sha(seedBase)) === proof.commitHash,
    mintOk: kp.publicKey.toBase58() === proof.mint,
  }
}