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.
| Field | Value |
|---|---|
| Algorithm | sha256 over lines "<id>|<trait_type>=<value>;..." for id 1..3000 ascending, trait order as declared in the metadata, each line newline-terminated |
| Current hash | ce8553bfbe83bd5a2d15f50a955d1694bbc555463bc64205cf8f664642637fef |
| Computed | 2026-08-15 |
| Covers | 3,000 metadata documents, which pin every trait of every token |
| Committed on chain | Not yet |
npm run provenance --workspace generator
# -> output/provenance.json + the hash on stdoutThe 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.
Generate and look at it
npm run generatethennpm run preview. Once the hash is on chain the art is settled.Pin, then recompute
Pin images and metadata, substitute the real CID into the image fields, then runnpm run provenanceagain. The hash changing here is correct behaviour.Dry run on testnet
Chain 46630. Deploy and walk the whole mint flow with a real wallet. A testnet mistake costs nothing.Deploy to mainnet
The deploy script assertsblock.chainidagainstCHAIN_IDbefore broadcasting, so a wrong RPC fails fast instead of deploying to the wrong network.setProvenanceHash, while supply is zero
One shot. The contract refuses a second call, ever.setBaseURI, reserveMint, configureMint
In that order.reserveMinttakes the 60 chase pieces plus any treasury allocation, thenconfigureMintopens the public lane with the real price and per-wallet limit.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.
| Mainnet | Testnet (Sepolia L2) | |
|---|---|---|
| Chain ID | 4663 | 46630 |
| RPC | https://rpc.mainnet.chain.robinhood.com | https://rpc.testnet.chain.robinhood.com |
| Explorer | https://robinhoodchain.blockscout.com | https://explorer.testnet.chain.robinhood.com |
| Verifier URL | https://robinhoodchain.blockscout.com/api/ | https://explorer.testnet.chain.robinhood.com/api/ |
robinscan.iois a live third-party explorer UI, but its API returns not found. Do not point a verifier at it.https://rpc.arrowrpc.comis 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
| Property | Value |
|---|---|
| Standard | ERC-721A, with ERC-2981 royalties and Ownable2Step |
| Name and symbol | RH Grand Prix Racers / NFCR, placeholder quality and unchangeable after deployment |
| Cap | 3,000. reserveMint bypasses pause, price and the wallet limit, and never the cap. |
| Payment | Exact. msg.value must equal price × quantity, and overpayment reverts rather than being quietly kept. |
| Per-wallet limit | Default 5, counted against lifetime mints, so transferring tokens out does not reset it. |
| Royalty | 5% default to the owner, adjustable through setDefaultRoyalty |
| tokenURI | baseURI + id + ".json" |
| Metadata freeze | freezeMetadata() is irreversible |
| Deploy state | Paused 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.
Build the leaf
keccak256(abi.encode(tokenId, rank, points)), all three as uint256.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.Ask the contract
verifyResult(seasonId, proof, leaf)returns a boolean against the immutable root.
# 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.comimport { 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.