03 · TCG Miners documentation

Sealed cards, reveal and traits

Sealed cards page revised: the pack-colourway claim now states the code (colourway follows the card's tier palette, PackSprite.tsx:16-19; PackOpen.tsx:164-167; site/tcg.js:453-464), every rule carries


A minted card is sealed. Element, design and edition are readable from the id on the day you mint. Tier and stars are not. The seed decides them, and it stays secret until the table sells out or the reveal deadline passes. Then reveal(seed) opens every card at once.

Decks, shifts and merge wait for reveal, at least until sellout or the deadline, 14 days after deploy on the testnet build.

Before this page You know what a tranche is and that mint number and token id are the same number (The mint). Constants, id ranges and addresses live on Reference.

What it is #

Sealed means half the card is public and half is committed. The sha256 hash of the secret seed is on-chain from deploy, before the first table mint, and reveal is the one call that hands the seed over. Ids go out in order and traits are deterministic, so a seed known mid-mint would let anyone compute which unminted ids land Tier 9 and buy those. The deadline keeps reveal from deadlocking if the table never sells out. The reason for the wait is sniping, not theatre.

Rules #

  • SEED_COMMITMENT is the sha256 hash of the seed string, set in the constructor and never rewritten; the production seed is a 256-bit value kept off-chain (contracts/src/MinersNFT.sol:70-71,135; deploy/mainnet-config.md:30-33).
  • reveal(seed) is open to anyone who knows the seed, once, else AlreadyRevealed (MinersNFT.sol:271-272).
  • It runs only after all 5,040 table cards are minted or after REVEAL_DEADLINE, else RevealTooEarly (MinersNFT.sol:277-279).
  • A seed whose hash does not match reverts BadSeed (MinersNFT.sol:280).
  • After reveal the seed is public through revealedSeed() (MinersNFT.sol:294-296).
  • Until reveal, cardTraits(tokenId) and mergeCards(keepId, burnId) revert NotRevealed and bindDeck(cardIds) reverts NotRevealedYet (MinersNFT.sol:306,319; contracts/src/MinePool.sol:215).
  • The mint has no end time and does not stop at reveal (MinersNFT.sol:192-194).
  • Stars move only through merge, +1 capped at 6 (MinersNFT.sol:336-338).

Numbers #

Figure 3.1 Commit-reveal timeline
deploy         SEED_COMMITMENT on-chain; ids 5,041 to 5,050 minted to TREASURY     MinersNFT.sol:135,140-144
mint window    cards sealed; cardTraits and mergeCards revert NotRevealed          MinersNFT.sol:306,319
               bindDeck reverts NotRevealedYet                                     MinePool.sol:215
gate           5,040 minted OR block time >= REVEAL_DEADLINE, else RevealTooEarly  MinersNFT.sol:277-279
reveal(seed)   sha256(seed) == SEED_COMMITMENT, else BadSeed; once, else           MinersNFT.sol:271-285
               AlreadyRevealed; emits BatchMetadataUpdate(1, 5050)
after          traits, decks, shifts and merge live; the mint stays open           MinersNFT.sol:192-194

Every element owns 252 consecutive ids and the four editions of one design sit side by side, so element, design and edition are arithmetic on the id (contracts/src/CardTraits.sol:72-78). The odd part is that the star floor is public while the stars are not.

Table 3.3 What you know when
TraitBefore revealAfter revealSource
ElementFrom the id, (id - 1) / 252SameCardTraits.sol:25,​72-73
DesignFrom the id, ((id - 1) % 252) / 4SameCardTraits.sol:74
EditionFrom the id, (id - 1) % 4SameCardTraits.sol:75
Star floorEqual to the editionSameCardTraits.sol:31-32,​122
TierSealedSeed shuffle per element, 1 to 9CardTraits.sol:79,​89-109
Exact starsSealed, floor to floor + 3edition + hash % 4CardTraits.sol:80,​113-123

The tier census is fixed. Every element has 14 Tier 1 designs and one Tier 9 design; the seed only decides which design is which.

Table 3.4 Tier census
TierDesigns per elementCards per element (derived, x4 editions)Cards table-wide (derived, x20 elements)Share of table (derived)Base weightSource
T114561,12022.2%1CardTraits.sol:36; MinePool.sol:247
T2114488017.5%2CardTraits.sol:36; MinePool.sol:247
T393672014.3%4CardTraits.sol:36; MinePool.sol:247
T483264012.7%8CardTraits.sol:36; MinePool.sol:247
T572856011.1%16CardTraits.sol:36; MinePool.sol:247
T66244809.5%32CardTraits.sol:36; MinePool.sol:247
T74163206.3%64CardTraits.sol:36; MinePool.sol:247
T83122404.8%128CardTraits.sol:36; MinePool.sol:247
T914801.6%256CardTraits.sol:36; MinePool.sol:247
One-of-one (T9)1110outside the table256CardTraits.sol:30,​66-70

The ten one-of-ones are design 63 of elements 0 to 9 and sit outside the 5,040.

Edition e draws stars uniformly from e to e + 3, so a 6-star card straight from the mint exists only in edition 3.

Table 3.5 Star range by edition
EditionMin starsMax stars6 stars at mintSource
003NoCardTraits.sol:32,​122
114NoCardTraits.sol:32,​122
225NoCardTraits.sol:32,​122
336Yes, 1 in 4; expected 315 cards (derived, 1,260 x 1/4)CardTraits.sol:32,​122

The element order is permanent because the names feed the hash. Index 0 to 19 runs Void, Thunder, Moon, Sun, Bleeding, Defense, Heal, Cloud, Attack, Earth, Sound, Water, Soul, Fire, Plant, Poison, Demon, Light, Charm, Slime. Id ranges and one-of-one ids are in Table 10.4.

Packs come in 20 designs, one per element, each in 9 colourways (site/tcg.js:430-433,446-451). The colourway the app shows follows the card's tier palette, brown for Tier 1 to yellow for Tier 9 (app/web/src/components/PackSprite.tsx:16-19; app/web/src/components/PackOpen.tsx:164-167; site/tcg.js:453-464). Packs exist only in the app's opening animation.

Table 3.6 Derivation steps
StepFormulaSource
HashFirst 8 bytes of sha256 over ':'-joined strings, read as a big-endian uint64CardTraits.sol:173-178; app/api/seed/generate.py:58-66
ShufflePer element, Fisher-Yates over designs 0 to 62; step i swaps with j = hash(seed:element:fy:i) % (i + 1)CardTraits.sol:156-171
TierShuffled positions 0 to 13 are T1, the next 11 T2, then 9, 8, 7, 6, 4, 3, and the last position T9CardTraits.sol:36,​89-109; generate.py:44-45,​69-92
Starsedition + hash(seed:element:design:edition) % 4CardTraits.sol:113-123
Merge overlayA merged card's stored tier and stars replace the seed values; element, design and edition never changeMinersNFT.sol:351-364
ParitySolidity and Python are locked against test/vectors/traits.jsonCardTraits.sol:7-11
Table 3.7 Metadata by state
StateURIName and attributesSource
SealedtokenURI(tokenId) is {baseURI}/hidden.jsonTCG Miners - SealedMinersNFT.sol:371-374; app/api/routes/meta.py:56-62
RevealedtokenURI(tokenId) is {baseURI}/{id}.jsonTCG Miners #{id} - {Element} Card; Element, Tier, Stars, Star Class (0-2, 3, 4, 5, 6), Weight Class (Common to Mythic), Weight (number), Edition, 1 of 1, Design. Tier, Stars and the two classes are strings so OpenSea rarity counts them; the classes are ordinal buckets that make rank rise with stars and weightMinersNFT.sol:375; meta.py:84-130
Collection, either statecontractURI() is {baseURI}/contract.json (ERC-7572)MinersNFT.sol:383-386

The image is keyed by element and tier, so a merge changes the picture (meta.py:65-72). The contract advertises interface 0x49064906 (MinersNFT.sol:452).

Worked example #

Id 1 is Void, design 0, edition 0. Id 3,277 is the Fire card of worked example D. Take z = id - 1 and divide.

Figure 3.2 Id anatomy of id 3,277
z          = 3,277 - 1 = 3,276
element    = 3,276 / 252 = 13, remainder 0  ->  Fire, ids 3,277 to 3,528   CardTraits.sol:73,143
design     = 0 / 4 = 0                                                     CardTraits.sol:74
edition    = 3,276 % 4 = 0                                                 CardTraits.sol:75
star floor = 0, so 0 to 3 stars at mint                                    CardTraits.sol:122
tier       = sealed until reveal(seed)                                     CardTraits.sol:79

Id 3,301 sits 24 ids on, so design 24 / 4 = 6, edition 0, floor 0. After reveal both come up Tier 2, with 1 and 3 stars, and that pair is merged on Merge. Before reveal cardTraits(3277) reverts NotRevealed (MinersNFT.sol:306; CardTraits.sol:72-78).

Edge cases #

  • Ids 5,041 to 5,050 are the ten one-of-ones, minted to TREASURY in the constructor (MinersNFT.sol:140-144). They never merge, else UniqueNotMergeable (MinersNFT.sol:328-329), and carry mining weight but no cost weight (MinePool.sol:249). Distribution is to be announced.
  • REVEAL_DEADLINE is 14 days after deploy on the testnet build (contracts/script/Deploy.s.sol:103); the mainnet value is set at deploy.
  • Marketplaces show the sealed name until reveal. Reveal emits BatchMetadataUpdate(1, 5050) (MinersNFT.sol:285); a marketplace that ignores it keeps the old name until you refresh the item.

Verify it on-chain #

Verify it on-chain On the MinersNFT read-contract tab (Robinhood testnet 46630, deploy/testnet-addresses.md:3,13; all addresses) call SEED_COMMITMENT(), REVEAL_DEADLINE(), revealed(), revealedSeed(), totalMinted(), cardTraits(3277), tokenURI(3277) and contractURI() (MinersNFT.sol:70-76,258-261,289-309,371-386). After reveal, sha256 of revealedSeed() equals SEED_COMMITMENT() (MinersNFT.sol:280) and cardTraits(3277) returns element 13, design 0, edition 0 (CardTraits.sol:72-78).

Not in the game #

Not in the game "Each tier earns 3x the one below" (opensea-description.txt:5,19). 2x; base weight is 2^(tier-1) (MinePool.sol:247), matrix on Decks. "Stars are upgrades earned after minting" (site/index.html:298). Assigned at mint by the seed; merge adds one, capped at 6 (CardTraits.sol:113-123; MinersNFT.sol:336-338). "18 pack designs" (opensea-description.txt:3,19). 20, one per element; the 18 came from an incomplete sprite sheet (site/tcg.js:438-451).

Next #

Read this if you are here to... Top up a bond, read the bond rule. Claim, read Claiming. Merge two of a kind, read Merge. Sell a card, read Locked; a card in a deck cannot move.

Next page, Decks and weight, where revealed cards become one deck with one weight.