01The thesis
Most game NFTs are a picture with a promise attached: buy the art now, and one day a game will use it. Arcanox inverts that order. The game exists first, and the token is a character inside it.
An Arcanox Hero is not a portrait of a hero. It is the seed the game reads to build one. The same function that draws the card draws the character that walks into the dungeon — there is no second art pipeline that could drift from the game, because there is no art pipeline at all. The images are screenshots of the real renderer.
The practical consequence: if the collection's art and the game ever disagree, that is a bug with a
stack trace, not a broken promise. One function, nftTraitsFor(tokenId), is the only
source of truth, and it is a pure function — token id in, twelve traits out, no randomness anywhere.
02What a hero is
Each token rolls twelve traits — Lineage, Aura, Cloak, Armor, Eyes, Sigil, Crown, Companion, Weapon Finish, Trail, Realm and Blessing — plus a Rank derived from them.
Lineage is the class
A Mage token plays the Mage rig with Mage abilities. This is why choosing a hero on the create screen locks the class picker: the token already decided. It is the trait that makes the NFT a character rather than a skin.
Rank is derived, not assigned
A hero's rank comes from the summed information content of its own traits, measured in bits. Nothing is stored, nothing is looked up — a wallet can rank a hero offline, without ever fetching the collection. Over 1000 tokens the distribution lands here:
Rank is rarity, not power. A Mythic hero hits exactly as hard as a Common one. Rank describes how unusual the combination of traits is — nothing else.
03Why a free player is never out-scaled
Eleven of the twelve traits are purely cosmetic. Blessing is the only trait that touches numbers,
and it is capped at a few percent. The cap is not a guideline in a document — it is an assertion
in the test suite: tests/nft.test.js fails the build if any blessing exceeds 6%.
That number was chosen so a minted hero is an identity and a look, not a power tier. Sanctuary's End is a complete game that is free to play, with no token gate on any content. A player who never touches the mint can clear everything a holder can.
This is a deliberate design constraint, and it is the reason the collection has no staking, no yield, no play-to-earn loop, and no in-game currency of its own. Systems like those only pay out if the game punishes the people who don't buy in.
04Provenance: proving nothing was re-rolled
The oldest trick in NFT minting is deciding rarity after the fact — seeing who bought which id, then assigning the good traits to friendly wallets. Provenance closes that door.
Before the mint opens, the builder hashes all 1000 metadata files, in token order, into one SHA-256 digest, and that digest is written into the contract at deploy as an immutable value. Change a single trait on a single token afterwards and the hash no longer matches.
loading…To verify it yourself, with no trust in us at all:
- Fetch
nft/metadata/1.jsonthrough1000.json. - Concatenate them in that order and take the SHA-256.
- Compare against
PROVENANCEon the contract.
Or skip the download entirely and regenerate the collection from scratch:
node scripts/nft-build.js re-derives all 1000 files from the trait engine. Because the
engine is deterministic, a clean run produces byte-identical output — and the same hash.
05The contract
ArcanoxHeroes.sol is a plain ERC-721 with the Enumerable extension, on OpenZeppelin. It is deliberately small — there is no proxy, no upgrade path, and no delegatecall anywhere in it.
| Property | Value | Mutable? |
|---|---|---|
| MAX_SUPPLY | 1000, hard-capped | No — constant |
| PROVENANCE | SHA-256 of the metadata | No — immutable, set at deploy |
| Token ids | Sequential from 1, never reused | No |
| price | see the mint page | Owner |
| maxPerWallet | see the mint page | Owner |
| mintOpen | see the mint page | Owner |
| baseURI | Where metadata is served | Owner |
The owner keys can pause the mint, change the price and the wallet limit, and repoint the base URI — the last one matters if the metadata ever has to move hosts. The owner cannot mint past 1000, alter a token's traits, change the provenance hash, or take a token back.
Enumerable is a requirement, not a preference
The game lists your heroes with balanceOf plus tokenOfOwnerByIndex against an
ordinary RPC endpoint. No indexer, no subgraph, no backend of ours in the path. If our servers vanish,
a wallet still reads your heroes from chain — and the game is static files that can be served by anyone.
06How it plugs into the game
The integration is one classic script, js/28-nft.js, loaded last. It wraps the game's existing functions rather than editing them. Delete that single script tag and Sanctuary's End is exactly the game it was before — the NFT layer is strictly additive, and old saves without a token still load.
In co-op, peers carry their token id over the relay, so the hero you see another player wearing is the one they actually own. Nothing is transferred but the id: the same seed rebuilds the same hero on your machine. A shared world of verified characters costs a handful of bytes per player.
The relay itself is a dumb pipe — it forwards presence, chat and world sync between party members and verifies wallet signatures. It never simulates the game and never holds your save.
07Check it yourself
Nothing here needs to be taken on faith. Every one of these runs on your machine:
- Read the engine — js/28-nft.js holds the weight tables, the roll order, the scoring and the rank thresholds. The mint page reads its displayed odds from that same file, so the odds cannot disagree with the generator.
- Rebuild the collection —
node scripts/nft-build.jsregenerates all 1000 metadata files and prints the provenance hash. It should match the one above exactly. - Re-render the art —
node scripts/nft-render.jsdrives the real game renderer in headless Chrome to produce the images. Roughly 8–10 seconds per token. - Run the tests —
node --test tests/nft.test.jschecks determinism, pinned tokens and the blessing cap.node scripts/nft-selftest.jsgoes further: it boots the actual game, seeds a wallet holding two heroes, walks the create screen like a player, and asserts the character that comes out is the token — 27 checks in all.
08What this is not
A whitepaper is usually where promises go. This section is the opposite, and it is the most important part of the document.
- There is no fungible token, no presale, no allocation, no vesting schedule, and no plan for any of those.
- There is no revenue share, yield, staking or play-to-earn. Holding a hero pays nothing.
- There is no DAO or governance right. A hero is not a vote and not a share of anything.
- There is no roadmap of unreleased features being sold here. The game is finished and playable today; what you can do with a hero is what you can do with it today.
- A hero grants no power advantage. See §03 — the numbers cap is enforced by tests.
- This is not an investment, and nothing here should be read as a projection of value. NFTs are widely illiquid and can become worth nothing.
Risks, stated plainly. Smart contracts can contain bugs; this one is unaudited, which is part of why it is kept small and unupgradeable. Metadata and images are served from ordinary web hosting, so they depend on that host staying up — the contract's base URI can be repointed if it doesn't. Only ever mint from a URL you typed yourself, and read the transaction your wallet shows you.
09Current status
Reading the live configuration…
Until a contract address is configured, the mint page runs in vault mode: a claim is recorded in your browser's local storage so the whole flow — mint, own, play — works end to end before anything is deployed. Vault mode is not ownership, and the page says so on its face. When the contract goes live, the same page mints for real against it.