Why this matters for collections

Most generative collections pre-render every token off-chain and only reveal after mint. That's fine if collectors trust the team. Provable.io lets you derive traits from a committed seed instead — anyone can recompute a token's traits from public inputs.

Define the trait categories

const TRAITS = {
  background: [
    { name: "Sky",      weight: 50 },
    { name: "Sunset",   weight: 30 },
    { name: "Void",     weight: 15 },
    { name: "Aurora",   weight: 5  },
  ],
  body: [
    { name: "Robot",    weight: 60 },
    { name: "Ape",      weight: 30 },
    { name: "Alien",    weight: 10 },
  ],
  hat: [
    { name: "None",     weight: 50 },
    { name: "Cap",      weight: 30 },
    { name: "Crown",    weight: 15 },
    { name: "Halo",     weight: 5  },
  ],
};

One call per token, all traits at once

Each trait category needs one random integer. Compute the LCM (or just use a wide range and modulo per category) and batch them in a single request.

async function rollTraits(tokenId, collectionSeed) {
  const categories = Object.entries(TRAITS);
  const clientSeed = `${collectionSeed}:token:${tokenId}`;

  const url = new URL("https://api.provable.io/api/ints");
  url.searchParams.set("clientSeed", clientSeed);
  url.searchParams.set("count", String(categories.length));
  url.searchParams.set("min", "1");
  url.searchParams.set("max", "1000000");

  const res = await fetch(url, {
    headers: { "x-api-key": process.env.PROVABLE_KEY }
  });
  const { outcome, serverHash } = await res.json();

  const traits = {};
  categories.forEach(([category, options], i) => {
    const total = options.reduce((s, o) => s + o.weight, 0);
    const roll = (outcome[i] % total) + 1;
    let c = 0;
    for (const o of options) { c += o.weight; if (roll <= c) { traits[category] = o.name; break; } }
  });

  return { tokenId, traits, clientSeed, serverHash };
}

console.log(await rollTraits(1337, "myproject_v1_mainnet"));
// { tokenId: 1337, traits: { background: "Sunset", body: "Ape", hat: "Crown" }, ... }

The reveal flow

  1. Pre-mint: publish the collectionSeed prefix and the committed serverHash on-chain or in a signed announcement.
  2. Mint: users mint token IDs. No traits assigned yet.
  3. Reveal: run rollTraits(tokenId, collectionSeed) for each token, render the art, store the metadata.
  4. Audit: any holder can re-run the same call. If the result differs, the team is caught.

Tips

Next steps