OVERVIEW
MINECAT is a 10,000-piece NFT collection of ASCII cats in 3D pixel that cannot be bought at a mint button.
Every cat must be mined: your hardware grinds real keccak256 proof-of-work, and luck decides
whether you claim instantly or wait for the next round. Price per mint is $0.20, settled in ETH on
Robinhood Chain.
Three rigs are first-class citizens: CPU (Web Workers), GPU (a WGSL compute kernel on WebGPU), and AI agents (autopilot on the page, or any external bot speaking the same protocol).
GAME RULES
| rule | value |
|---|---|
| total supply | 10,000 β hard cap, no re-mints ever |
| round length | ~60 seconds on-chain (roundBlocks = 5 blocks) |
| lucky slots per round | 50, contested by all miners Β· tickets claimable on top |
| wallet cap | 5 mints per wallet (pending wins count toward the cap) |
| price | mintPriceWei β $0.20, paid at claim time |
| valid work | hash < ticketTarget β about 1 in 100,000,000 hashes on-chain (the arcade demo uses 1 in 4,000,000 so it stays playable) |
| lucky odds | 1 in 6 valid solutions β instant claim |
MINING
The proof-of-work message is exactly 60 bytes:
h = keccak256( roundSeed(32) β minerAddress(20) β nonce(8, big-endian) )
Because your address is inside the hash, nobody can steal your solution from the mempool β a copied nonce produces a different hash for a different sender.
Targets
ticketTarget = 2Β²β΅βΆ / diffTicket // any hash below = valid solution luckyTarget = ticketTarget / luckyDivisor // below this = LUCKY
Rigs
- CPU β spawns up to 6 workers, ~100β400 kH/s total on a typical laptop. Expected find: ~30 s.
- GPU β keccak-f[1600] in WGSL, hundreds of MH/s to GH/s. Expected find: instant-ish. The kernel self-tests against the CPU engine at startup and falls back to CPU if WebGPU is missing.
- AI AGENT β MEOW-9000 runs the whole loop: hardware pick, mining, claiming, ticket management, faucet top-ups, retirement at 5/5.
LUCK & TICKETS
Every valid solution triggers the gacha moment:
- π LUCKY (hash <
luckyTarget, slots remain): a slot is locked for you immediately; pay $0.20 to mint on the spot. - π BUST: you receive a ticket β a guaranteed mint claimable from the next round onward, same $0.20. One ticket held at a time.
- Slots gone while you were lucky? The win auto-converts to a ticket. Nothing is wasted.
Grinding extra hashes hunting a lucky one is not cheating β the luck roll is your own solution hash, so "more work = more luck" is the entire point of proof-of-luck.
GOING ON-CHAIN
Networks
| mainnet | testnet (dry-run) | |
|---|---|---|
| chainId | 4663 | 46630 |
| RPC | rpc.mainnet.chain.robinhood.com | rpc.testnet.chain.robinhood.com |
| explorer | robinhoodchain.blockscout.com | explorer.testnet.chain.robinhood.com |
| currency | ETH | test ETH |
Deploy with Remix (no toolchain)
- Open
remix.ethereum.org, createMINECAT.sol, paste the contract fromcontracts/MINECAT.sol. - Compile with Solidity
0.8.24. - Switch MetaMask to Robinhood Chain (the mint page can add the network for you).
- Deploy with constructor args:
_mintPriceWei : price of $0.20 in wei (check current ETH price) _diffTicket : 100000000 (live) _roundBlocks : 5 (~60 s β block.number tracks L1 at ~12 s on Arbitrum Orbit) _baseURI : ipfs://<metadata CID>/ (or "" and set later)
- Verify the source on Blockscout so miners can read it.
- You (deployer
0x25β¦) are owner + 5% royalty receiver. Keys stay in your wallet, always.
β Mainnet is real money. Deploy and drill the full flow on testnet first β same code, free gas.
MARKETPLACE
MINECAT has an official secondary market (market.html +
contracts/MinecatMarket.sol) with one sacred rule:
π The market is sealed until all 10,000 cats are mined. On-chain, every
list() and buy() checks totalMinted() >= MAX_SUPPLY β before sold-out
the only way to own a cat is to mine it. No paper hands during the gold rush.
How it works
- Approval-based listings β your cat never leaves your wallet until the second it sells.
Sellers call
setApprovalForAll(market, true)once, thenlist(tokenId, priceWei). - buy(tokenId) pays the exact listed price; the contract routes 5% royalty (ERC-2981) to the deployer wallet and the rest to the seller, in the same transaction (reentrancy-guarded, checks-effects-interactions).
- cancel(tokenId) anytime. Stale listings (owner changed / approval revoked) can't execute.
- Deploy order: MINECAT first, then
MinecatMarket(minecatAddress).
The demo market page mirrors all of this with fake balance β including the sold-out gate. It shows a locked screen with live supply progress; a dev shortcut lets you simulate sold-out to preview the exchange: floor price, listings from other miners, your sell/cancel flow, and the live activity feed.
OpenSea & enforced 5% royalty
Robinhood Chain is live on OpenSea, so MINECAT trades there out of the box: the contract speaks
standard ERC-721 metadata, ERC-2981, and contractURI() for the collection page.
Royalty is hardcoded at 5% (no setter exists) and enforced on-chain with an operator
allowlist: while royaltyEnforced is on, approve/setApprovalForAll and
operator-driven transfers only work for venues the owner has allowlisted via setOperator().
A marketplace that refuses to pay royalties simply cannot move your cats.
- After deploying, allowlist your venues:
setOperator(minecatMarket, true)andsetOperator(openSeaSeaportConduit, true)(grab the Seaport conduit address for Robinhood Chain from OpenSea's docs/explorer). - Set
setContractURI("ipfs://β¦/collection.json")β include"seller_fee_basis_points": 500and"fee_recipient"so OpenSea displays the fee. - Holders can always transfer their own tokens directly (gifts, wallet moves) β enforcement targets marketplace flows. OTC hand-offs are the one hole no NFT contract can close.
- Escape hatch:
setRoyaltyEnforced(false)if a future venue breaks; flip it back anytime.
USING THE MINT PAGE
- Open mint.html β CONNECT WALLET (adds/switches to Robinhood Chain).
- Paste the deployed contract address β load contract.
- If the round seed says NOT OPENED YET, hit OPEN ROUND (tiny tx, anyone may do it β the seed anchors to a blockhash so it can't be pre-mined).
- Pick CPU or GPU β START MINING. The page grinds against the live seed with your address.
- On a solution you review it first, then SUBMIT WORK (tx). Lucky = voucher; bust = ticket.
- CLAIM (tx, sends
mintPriceWei) β cat minted. Your on-chain cats render at the bottom.
Every transaction pops in your wallet for approval. The page holds no keys and can spend nothing on its own.
CONTRACT REFERENCE
Miner-facing
| function | what it does |
|---|---|
openRound() | sets the seed for the current round (once per round, anyone) |
submitWork(uint64 nonce) | verifies your PoW; luckyβvoucher (slot locked), elseβticket |
claim() payable | pays mintPriceWei, mints from voucher or matured ticket |
currentRound() / roundSeed(r) | round index (block-based) and its seed |
ticketTarget() / luckyTarget() | the two difficulty thresholds |
roundMinted(r) / totalMinted | slots used this round / global supply |
mintedBy(a) / voucher(a) / ticketFromRound(a) | your caps & pending wins |
Owner-only
| function | purpose |
|---|---|
setMintPrice(wei) | keep the price pegged near $0.20 |
setDifficulty(diff, luckyDiv) | tune for real network hashrate |
setBaseURI(uri) / setRoyaltyReceiver(a) | metadata + ERC-2981 (5%) |
withdraw() | pull mint proceeds to the owner wallet |
Events
RoundOpened(round, seed) WorkSubmitted(miner, round, nonce, hash, lucky) Claimed(miner, tokenId, viaTicket) Transfer / Approval / ApprovalForAll (ERC-721)
AGENT API
External AI agents mine through window.MINECAT on the arcade page, or straight against the
contract. Full protocol with an ethers.js sketch lives in agent/AGENT.md.
π€ Agents get their own skill file: skills/minecat-miner/SKILL.md β
a drop-in agent skill (YAML frontmatter + instructions) containing the PoW spec, the proven decision loop,
a revert decoder, and non-negotiable key-safety rules. Point your agent at it, or install it as a Claude Code
skill (~/.claude/skills/minecat-miner/) and it will know how to mine the moment MINECAT comes up.
MINECAT.job() // { round, seed, address, diffTicket, luckyDiv }
MINECAT.state() // slots, supply, wallet, hashrate, pendingHoki
MINECAT.start('gpu') // 'cpu' | 'gpu' | 'agent'
MINECAT.claim() // lucky win or matured ticket
MINECAT.submitNonce(hiHex, loHex) // bring-your-own-miner
ART & METADATA
- Every cat is a deterministic voxel ASCII cat: token id β xorshift RNG (seed
0xCA7C0DE) β 7 weighted trait categories (162,000 combos) β voxel model β canvas render. Same id, same cat, any machine. - Never change the seed after publishing β it IS the collection.
- Export the real files with export.html: writes
images/N.png+metadata/N.json(ERC-721 standard attributes) + a provenance hash, resumable. - Pin
images/to IPFS β put the CID in the metadata baseURI β re-export β pinmetadata/βsetBaseURI("ipfs://CID/"). Images first, metadata second. - Publish the provenance hash before mint opens β buyers can verify nobody reshuffled the rares.
FAQ & SECURITY
Can someone steal my solution from the mempool?
No. The hash commits to your address; a stolen nonce is worthless to anyone else.
Can the team pre-mine lucky rounds?
No. Seeds derive from a blockhash at openRound() time β unknowable in advance, for us too.
Is grinding for lucky hashes an exploit?
It's the game. Luck is priced in work.
What does the site do with my keys?
Nothing β it never sees them. All signing happens inside your wallet extension. No seed phrases, no key input fields, ever. Anyone asking you to paste a private key "to mine faster" is robbing you.
Why did my submitWork revert?
Usual suspects: the round rolled over (new seed β old solution invalid), the 50 slots were already gone (lucky path), you already hold a ticket, or the wallet cap (5, counting pending wins) is reached.
Demo mode on the arcade page is a 1:1 simulation with fake balance β perfect for feeling the game before spending real gas.
Mine from a terminal (CLI miner)
The website miner runs in your browser. If you would rather mine from a shell β on Windows, Linux, macOS or a VPS β use the CLI miner. Same proof-of-work, same contract, multi-core CPU or WebGPU, and it submits and claims for you.
β¬ download minecat-miner.zip
Quick start
# 1. unpack, then inside the miner folder:
npm install
# 2. benchmark first β no key needed, costs nothing
node minecat-miner.js --bench
# 3. give it a funded burner key
# Linux/macOS:
export MINECAT_PRIVATE_KEY=0xyour_key
# Windows PowerShell:
$env:MINECAT_PRIVATE_KEY="0xyour_key"
# 4. mine
node minecat-miner.js
node minecat-miner.js --mode gpu # WebGPU via headless Chrome
node minecat-miner.js --threads 8 # pick core count
Run it 24/7
# systemd
sudo systemctl enable --now minecat-miner
sudo journalctl -u minecat-miner -f
# pm2
MINECAT_PRIVATE_KEY=0xkey pm2 start minecat-miner.js --name minecat -- --threads 3
Prove it computes the same hash as the contract
node verify-preimage.js
Cross-checks the miner against
keccak256(abi.encodePacked(bytes32,address,uint64)) computed independently β
including uint64 max and the 232 boundary. If it fails, do not mine:
every solution would be rejected on-chain.
Full docs, systemd/pm2/screen recipes and a troubleshooting table are in
README.md inside the zip. Your private key never leaves your machine β
it signs locally and is never transmitted.