Contract Reference
Every contract, worker, and subsystem in Monsuta Core — what each does, how they compose, and where they live.
Contract Summary
| Contract | Lines | Purpose | Pattern |
|---|---|---|---|
FadedMonsutaTHC.sol | 370 | ERC-20 gameplay token with mint-on-claim / burn-on-teleport bridge semantics | Custom (Owned + Oracled) |
MonsutaNFT.sol | 274 | Upgradeable ERC-721 — batch minting, token locking, royalties, multi-minter | UUPS upgradeable |
CraftingRecipes.sol | 697 | Recipe-based item creation — trustless and server-gated, dual mint modes | UUPS upgradeable |
AchievementsRegistry.sol | 425 | On-chain achievement attestations — direct/batch/EIP-712 issuance, revocation | UUPS upgradeable |
NFTBridge.sol | 213 | Vault-based NFT bridge — threshold oracle signatures, bidirectional | UUPS upgradeable |
PrizePool.sol | 729 | Tournament prize escrow — fund, register, submit results, claim | UUPS + ERC1967 per-instance |
PrizePoolFactory.sol | 295 | Deploys PrizePool proxy instances — deployer whitelist, registry | UUPS upgradeable |
NFTStaking.sol | 257 | Non-custodial NFT staking — 3 reward models, Merkle epoch distribution | UUPS upgradeable |
teleporteos | — | EOSIO C++ token bridge on WAX — deposit, teleport, oracle signing | EOSIO (C++) |
nftbridge | — | EOSIO C++ NFT bridge on WAX — vault locking, oracle release | EOSIO (C++) |
Total Solidity: ~3,265 lines across 8 contracts
Off-Chain Workers
| Worker | Purpose |
|---|---|
oracle-eth.js | Watches Avalanche events, relays to WAX |
oracle-eos.js | Watches WAX events via Hyperion, signs attestations for Avalanche |
nft-oracle-wax.js | WAX NFT bridge — watches logteleport, queries AtomicAssets, signs EIP-712 |
nft-oracle-avax.js | Avalanche NFT bridge — watches Teleport, releases/mints on WAX |
| Metadata Router | Dynamic NFT trait generation — mathematical token ID decoding |
| Staking Distributor | Computes staking weights, builds Merkle trees, publishes epochs |
Identity Module
MonsutaNFT.sol (274 lines)
Features:
- UUPS upgradeable with
AccessControlUpgradeable MINTER_ROLE— allows multiple minters (bridge + crafting + achievements)LOCK_MANAGER_ROLE— token locking for staking and tournamentsmintBatch(address, uint256[])— gas-optimized batch mintingburn(uint256)— ownership-gated burning- ERC-2981 royalty support
PausableUpgradeableemergency switch
Token ID Architecture:
tokenId = typeId × 1,000,000 + serial
Enables mathematical trait decoding without any on-chain registry.
CraftingRecipes.sol (697 lines)
Two Crafting Modes:
| Mode | Requires Server Sig | Use Case |
|---|---|---|
| Trustless | ❌ No | Pure NFT↔NFT recipes — e.g., 4 Rocks → 1 Slab |
| Server-Gated | ✅ Yes | Off-chain conditions — XP thresholds, quests, resources |
Two Output Modes:
| Mode | How It Works |
|---|---|
MINT_ON_DEMAND | Contract mints a fresh NFT. Requires MINTER_ROLE on MonsutaNFT. |
PRE_MINTED | Deposited tokens queued and transferred FIFO on craft. |
Gas Costs:
- Trustless 4-NFT burn + mint: ~220k gas (~0.002 AVAX @ 10 gwei)
- Server-gated mode adds: ~+6k gas
AchievementsRegistry.sol (425 lines)
On-chain achievement attestation system — records that prove a player accomplished something, with admin-managed achievement types and flexible issuance.
Two Issuance Modes:
| Mode | How It Works |
|---|---|
TRUSTLESS | Direct issuance by authorized issuers or owner |
SERVER_GATED | Requires EIP-712 signature from authorized backend signer |
Key Features:
- Achievement type registry with stable keys, metadata URIs, and active/revocable flags
- Single + batch issue and revoke
- Issuer allowlist (owner can delegate)
- Nonce replay protection for signature-based issuance
- Pausable emergency stop
Metadata Router
Architecture:
GET /meta/{tokenId}.json— dynamic trait generation- Mathematical decoding:
typeId = tokenId / 1_000_000 - No database lookups for standard items
- IPFS-ready for complex or unique assets
Bridge Module
Token Bridge
| Direction | Contract | Oracle |
|---|---|---|
| WAX → AVAX | FadedMonsutaTHC.claim() | oracle-eos.js signs attestation |
| AVAX → WAX | FadedMonsutaTHC.teleport() | oracle-eth.js watches + relays |
NFT Bridge
The 100k NFT Problem: Faded Monsuta has 100,000+ NFTs on WAX. Bridging one at a time is a UX nightmare. Mapping each on-chain is a gas explosion. The solution: batched database translation with vault-based locking — NFTs are never burned, always recoverable.
How It Works:
WAX → Avalanche
─────────────────────────────────────────────────────────
Player transfers NFTs to WAX bridge with memo:
"teleport:<chain_id>:<destination>"
│
▼
NFTs locked in WAX vault (NOT burned — always recoverable)
│
▼
nft-oracle-wax.js detects logteleport event
Queries AtomicAssets for template IDs
Translates to Avalanche token IDs via PostgreSQL
Signs EIP-712 payload, stores signature on WAX
│
▼
Player calls NFTBridge.claim(tokenIds, waxTxId, signatures)
Contract verifies threshold sigs → MonsutaNFT.mintBatch()
Avalanche → WAX
─────────────────────────────────────────────────────────
Player calls NFTBridge.teleport(waxName, tokenIds, chainId)
NFTs locked in Avalanche bridge vault
│
▼
nft-oracle-avax.js detects Teleport event
Checks WAX vault for original NFTs
If in vault → RELEASE original (round-trip complete)
If not in vault → MINT new on WAX
Key Design Properties:
| Property | Implementation |
|---|---|
| Vault-based (not burning) | NFTs locked, never destroyed — always recoverable |
| Bitwise token IDs | avaxTokenId = (typeId << 128) | serial — unlimited serials per type |
| Atomic ID allocation | PostgreSQL RETURNING prevents ID collisions |
| EIP-712 threshold | Same security model as token bridge |
| Replay protection | waxTxId nonce prevents double-claim |
| Emergency stop | PausableUpgradeable on both contracts |
Economy Module
PrizePool.sol (729 lines) + PrizePoolFactory.sol (295 lines)
Pool Lifecycle:
Created → Registering → Active → Finalized → Completed
Security guarantee: No function exists for an admin to withdraw prize funds. Only verified winners (submitted via submitResults()) can call claim(). Sponsor and player deposits are fully protected by contract logic.
NFTStaking.sol (257 lines)
Architecture — Merkle-Based Epoch Distribution:
┌──────────────────────────────────────────────────────────────────────┐
│ OFF-CHAIN (Worker) │
│ │
│ Transfer Logs → Ownership State → Holdings Snapshot │
│ │ │ │ │
│ │ sync:ownership │ │ derive:weights │
│ ▼ ▼ ▼ │
│ Block Cursor Rates Engine Account Weights │
│ (incremental + (template + [{account, weight}] │
│ reorg safety) attr rules) │
│ │ │
│ build:epoch │
│ ▼ │
│ Merkle Tree + Epoch Artifact │
│ │
└──────────────────────────────────────────────────────────────────────┘
│
│ publish:latest
▼
┌──────────────────────────────────────────────────────────────────────┐
│ ON-CHAIN (NFTStaking.sol) │
│ │
│ publishEpoch(epochId, merkleRoot, totalWeight, totalReward, hash) │
│ claim(epochId, weight, amount, merkleProof[]) │
│ batchClaim(epochIds[], weights[], amounts[], proofs[][]) │
│ │
└──────────────────────────────────────────────────────────────────────┘
Why Merkle? Zero on-chain staking state. Worker computes all weights off-chain, publishes only a 32-byte merkle root. Each claimer provides their own proof — scales to any number of stakers without gas scaling linearly.
Full System Map
monsuta-core/
└── modules/
├── identity/
│ ├── nft/contracts/src/nft/MonsutaNFT.sol
│ ├── crafting/contracts/src/CraftingRecipes.sol
│ └── achievements/contracts/src/AchievementsRegistry.sol
├── economy/
│ ├── staking/
│ │ ├── contracts/src/staking/NFTStaking.sol
│ │ └── workers/distributor/
│ └── prize_pools/contracts/src/...
└── bridge/
└── nft/
├── contracts/src/NFTBridge.sol
├── contracts/wax/src/nftbridge.cpp
└── oracle/
├── nft-oracle-wax.js
└── nft-oracle-avax.js
wax-avax-bridge/
└── contracts/FadedMonsutaTHC.sol
bridge-oracle/
├── oracle-eth.js
└── oracle-eos.js