← Back to home
TapeRNG SDK

Add verifiable randomness to your app

The SDK wraps contract calls, canonical specs, Merkle roots, drand verification, TAP-02 circuit replay and proof checks behind a viem-first API.

@taperng/sdk is a private workspace package in this repo; it is not published to npm. The web demo keeps an equivalent derivation for inline verification. Both are checked against contracts/deployments/parity.json.
Features

What is in the SDK

  • createTapeRNG: a viem publicClient/walletClient wrapper for publish, finalize and proof.
  • buildDraw: deterministic canonical JSON, participant Merkle root, prizeSpecHash and specHash.
  • createDrawAndReveal: one transaction for create + reveal, with a future drand quicknet round and a fixed revealBlock.
  • getProof / verifyProof: recompute the pre-seed, circuit output, final seed, Fisher-Yates winners and prize slots.
  • TAP-02 interpreter: decode/encode/load/beat/pack/unpack/evalNetlist/stepNetlist, a strict port of the official reference.py.
  • drand: multi-endpoint fetchBeacon, offline BLS12-381 verifyBeacon, and sha256(signature) validation.
  • CLI: verify proof.json with exit code 0/1 for CI or third-party replay.
Quick start

Create a client

chainId 1952 uses the testnet MockCPU with real drand; on mainnet 196 set circuitMode to tapeout and use the real Keccak IP Core.

client.ts
import { createTapeRNG } from "@taperng/sdk";
import { createPublicClient, http } from "viem";

const publicClient = createPublicClient({
  transport: http("https://testrpc.xlayer.tech/terigon"),
});

// For writes: createWalletClient({ account, chain, transport: custom(window.ethereum) })
const taperng = createTapeRNG({
  publicClient,
  walletClient,                 // optional for read-only
  addresses: { lottery, tapeRng },
  chainId: 1952,
  beaconMode: "drand",          // or "none"
  circuitMode: "mock",          // "tapeout" on mainnet
});
Publish

Create and reveal a draw

participantCount and prizes are exactly the form inputs; the SDK generates Participant 001…N, the canonical spec and the flattened prize slots.

publish.ts
const draw = await taperng.createDrawAndReveal({
  name: "Annual Party Lottery",
  participantCount: 100,
  prizes: [
    { name: "50 CNY Red Packet", quantity: 3 },
    { name: "100 CNY Red Packet", quantity: 2 },
    { name: "Xiaomi Laptop", quantity: 1 },
    { name: "iPhone 17", quantity: 1 },
  ],
  locale: "en",
});

console.log(draw.drawId, draw.built.participantsRoot);
console.log(draw.built.specHash, draw.revealBlock, draw.beaconRound);
Finalize

Finalize and verify

finalizeDraw reads the draw's beaconRound, fetches the 48-byte signature from drand and submits it. verifyProof is a pure replay of the proof JSON.

finalize.ts
await taperng.finalizeDraw(draw.drawId);

const proof = await taperng.getProof(draw.drawId);
const result = taperng.verifyProof(proof);

if (!result.ok) {
  throw new Error("proof failed");
}
console.log(result.checks);
CLI

Independent verification

terminal
cd taperng/packages/sdk
./node_modules/.bin/tsx src/cli.ts verify proof.json
TAP-02

Replay the TapeOut circuit offline

The proof carries the netlist. In mainnet tapeout mode the SDK loads/evals the circuit locally and compares the output to circuitMix. Testnet MockCPU skips TAP-02 replay explicitly.

netlist.ts
import { load, evalNetlist } from "@taperng/sdk";

// TAP-02: NAND / LATCH / REF, LSB-first packing
const circuit = load(netlist, nIn, nOut);
const output = evalNetlist(circuit, input);
drand

Verify the beacon without RPC

verifyBeacon uses @noble/curves for BLS12-381; the message is sha256(uint64_be(round)) and randomness must equal sha256(signature).

drand.ts
import { fetchBeacon, verifyBeacon } from "@taperng/sdk";

const beacon = await fetchBeacon("latest");
const { ok, randomness } = verifyBeacon(beacon);

// randomness === sha256(beacon.signature)
Replay

What the proof verifies

  • Context hash: chainId, lottery, participantsRoot, counts, prizeSpecHash and specHash.
  • Organizer commitment: seed, context, organizer, chainId and TapeRNG address.
  • Pre-seed: requestId, blockHash, beaconRandomness, CPU/circuit/netlistHash and every fallback flag.
  • TapeOut circuit output and circuitMix; mainnet tapeout mode replays the netlist locally.
  • finalSeed and the partial Fisher-Yates winners / prizeSlots.
  • sha256(drand signature) equals beaconRandomness.
Boundaries

Security assumptions and limits

  • The contracts and the vendored BLS library are unaudited; this is a hackathon demo, not a production security claim.
  • TapeOut is not deployed on testnet, so the public demo uses MockCPU; only mainnet/fork uses the real Keccak IP Core.
  • Organizer + X Layer sequencer collusion remains a residual risk; a fixed revealBlock and drand reduce it but do not eliminate it.
  • drand relies on the League of Entropy threshold assumption; blockhash is only an additional/fallback source.
  • Prizes are metadata only; there is no on-chain escrow, claim or settlement.
  • The proof embeds participants, prizes and the netlist; privacy use cases need an extra disclosure/hashing strategy.
Web

How the demo relates to the SDK

The home form demonstrates the same flow: buildDraw → createDrawAndReveal → finalizeDraw → proof. The web app keeps an equivalent derivation for inline verification without a backend; third-party apps should reuse packages/sdk directly.

Demo only: no assets are escrowed or settled on-chain. Contracts and the BLS library are unaudited. No trustless claim.