Reference

Provenance and chain

The boring facts, which are the important ones. What the provenance hash covers, which steps cannot be undone, and how to check a claim about a season placement without asking us.

The provenance hash

One sha256 over every metadata document in the collection, concatenated in token ID order. It is committed on chain before any token can be minted, which is what makes the shuffle unriggable after the fact: the art and the trait assignment are published as a single fingerprint while the supply is still zero.

FieldValue
Algorithmsha256 over lines "<id>|<trait_type>=<value>;..." for id 1..3000 ascending, trait order as declared in the metadata, each line newline-terminated
Current hashce8553bfbe83bd5a2d15f50a955d1694bbc555463bc64205cf8f664642637fef
Computed2026-08-15
Covers3,000 metadata documents, which pin every trait of every token
Committed on chainNot yet
generator
npm run provenance --workspace generator
# -> output/provenance.json + the hash on stdout

The contract double-locks the commitment. Setting it reverts if it has already been set, and reverts if any token has been minted, so it cannot be set once, gamed mid-mint, and set again.

The order that cannot be undone

Several of these steps are one-way. Doing them out of sequence either wastes a deployment or locks in something wrong.

  1. Generate and look at it

    npm run generate then npm run preview. Once the hash is on chain the art is settled.
  2. Pin, then recompute

    Pin images and metadata, substitute the real CID into the image fields, then run npm run provenance again. The hash changing here is correct behaviour.
  3. Dry run on testnet

    Chain 46630. Deploy and walk the whole mint flow with a real wallet. A testnet mistake costs nothing.
  4. Deploy to mainnet

    The deploy script asserts block.chainid against CHAIN_ID before broadcasting, so a wrong RPC fails fast instead of deploying to the wrong network.
  5. setProvenanceHash, while supply is zero

    One shot. The contract refuses a second call, ever.
  6. setBaseURI, reserveMint, configureMint

    In that order. reserveMint takes the 60 chase pieces plus any treasury allocation, then configureMint opens the public lane with the real price and per-wallet limit.
  7. freezeMetadata, last and only after checking

    Irreversible. Leave it alone until after reveal and a careful read of several token URIs.

Network facts

Robinhood Chain is an Arbitrum Orbit L2 with ETH gas. Mainnet went live 2026-07-01. The values below were verified on 2026-08-12 against Robinhood's own chain docs, the ethereum-lists entry, and live eth_chainId probes.

MainnetTestnet (Sepolia L2)
Chain ID466346630
RPChttps://rpc.mainnet.chain.robinhood.comhttps://rpc.testnet.chain.robinhood.com
Explorerhttps://robinhoodchain.blockscout.comhttps://explorer.testnet.chain.robinhood.com
Verifier URLhttps://robinhoodchain.blockscout.com/api/https://explorer.testnet.chain.robinhood.com/api/
  • robinscan.io is a live third-party explorer UI, but its API returns not found. Do not point a verifier at it.
  • https://rpc.arrowrpc.com is a third-party fallback RPC listed in ethereum-lists.
  • Gas on Robinhood Chain is subsidized until roughly late September 2026. Transaction and active-address figures across the whole ecosystem are inflated until then, including any we would quote.

The contracts

Two contracts, Solidity 0.8.26, optimizer at 200 runs, 35 Foundry tests passing. Deliberately minimal: no upgradeability, no proxy, no allowlist phase, no ERC-6551 hooks.

RHGrandPrixRacers

PropertyValue
StandardERC-721A, with ERC-2981 royalties and Ownable2Step
Name and symbolRH Grand Prix Racers / NFCR, placeholder quality and unchangeable after deployment
Cap3,000. reserveMint bypasses pause, price and the wallet limit, and never the cap.
PaymentExact. msg.value must equal price × quantity, and overpayment reverts rather than being quietly kept.
Per-wallet limitDefault 5, counted against lifetime mints, so transferring tokens out does not reset it.
Royalty5% default to the owner, adjustable through setDefaultRoyalty
tokenURIbaseURI + id + ".json"
Metadata freezefreezeMetadata() is irreversible
Deploy statePaused with price 0. Real values are set in one configureMint call.

SeasonRegistry

Stores commitments, never results. commitBatch appends a daily hash, postSeasonRoot writes a season root exactly once, and verifyResult checks a proof. It is Ownable2Step so the owner key can be handed to an authorized game-server signer later without redeploying.

Verifying a season placement

A career claim is either provable or false. Here is the whole check, with no trust in this server anywhere in it.

  1. Build the leaf

    keccak256(abi.encode(tokenId, rank, points)), all three as uint256.
  2. Take the proof

    From the published season proof file, which is written before the root is broadcast, or rebuild the tree yourself from the published standings.
  3. Ask the contract

    verifyResult(seasonId, proof, leaf) returns a boolean against the immutable root.
foundry
# token 42 finished rank 3 with 60 points in season 1
LEAF=$(cast keccak $(cast abi-encode "f(uint256,uint256,uint256)" 42 3 60))

cast call $SEASON_REGISTRY \
  "verifyResult(uint256,bytes32[],bytes32)(bool)" \
  1 "[$PROOF_ELEMENTS]" $LEAF \
  --rpc-url https://rpc.mainnet.chain.robinhood.com
viem
import { keccak256, encodeAbiParameters } from "viem";

const leaf = keccak256(
  encodeAbiParameters(
    [{ type: "uint256" }, { type: "uint256" }, { type: "uint256" }],
    [42n, 3n, 60n],
  ),
);

const ok = await client.readContract({
  address: SEASON_REGISTRY,
  abi,
  functionName: "verifyResult",
  args: [1n, proof, leaf],
});

Pairs are sorted before hashing, which is OpenZeppelin's convention and the reason a proof carries no position bits. If you rebuild the tree with a library that pairs unsorted, every proof will fail.

Nothing is deployed yet

No contract address exists on mainnet or testnet, no deployer key has been used, no provenance hash has been committed, and no season has been settled. The mint page shows the cold garage state because NEXT_PUBLIC_CONTRACT_ADDRESS is unset, which is the correct thing for it to display right now.

When that changes, the address will be verified on the mainnet Blockscout instance and linked from the Paddock Pass. Until then, treat any contract address claiming to be this collection as false. Every name in this ecosystem has dozens of verified clone contracts, and a green checkmark on an explorer is not proof of anything except that source was uploaded.