Skip to main content

Contract Reference

Every contract, worker, and subsystem in Monsuta Core — what each does, how they compose, and where they live.


Contract Summary

ContractLinesPurposePattern
FadedMonsutaTHC.sol370ERC-20 gameplay token with mint-on-claim / burn-on-teleport bridge semanticsCustom (Owned + Oracled)
MonsutaNFT.sol274Upgradeable ERC-721 — batch minting, token locking, royalties, multi-minterUUPS upgradeable
CraftingRecipes.sol697Recipe-based item creation — trustless and server-gated, dual mint modesUUPS upgradeable
AchievementsRegistry.sol425On-chain achievement attestations — direct/batch/EIP-712 issuance, revocationUUPS upgradeable
NFTBridge.sol213Vault-based NFT bridge — threshold oracle signatures, bidirectionalUUPS upgradeable
PrizePool.sol729Tournament prize escrow — fund, register, submit results, claimUUPS + ERC1967 per-instance
PrizePoolFactory.sol295Deploys PrizePool proxy instances — deployer whitelist, registryUUPS upgradeable
NFTStaking.sol257Non-custodial NFT staking — 3 reward models, Merkle epoch distributionUUPS upgradeable
teleporteosEOSIO C++ token bridge on WAX — deposit, teleport, oracle signingEOSIO (C++)
nftbridgeEOSIO C++ NFT bridge on WAX — vault locking, oracle releaseEOSIO (C++)

Total Solidity: ~3,265 lines across 8 contracts


Off-Chain Workers

WorkerPurpose
oracle-eth.jsWatches Avalanche events, relays to WAX
oracle-eos.jsWatches WAX events via Hyperion, signs attestations for Avalanche
nft-oracle-wax.jsWAX NFT bridge — watches logteleport, queries AtomicAssets, signs EIP-712
nft-oracle-avax.jsAvalanche NFT bridge — watches Teleport, releases/mints on WAX
Metadata RouterDynamic NFT trait generation — mathematical token ID decoding
Staking DistributorComputes 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 tournaments
  • mintBatch(address, uint256[]) — gas-optimized batch minting
  • burn(uint256) — ownership-gated burning
  • ERC-2981 royalty support
  • PausableUpgradeable emergency 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:

ModeRequires Server SigUse Case
Trustless❌ NoPure NFT↔NFT recipes — e.g., 4 Rocks → 1 Slab
Server-Gated✅ YesOff-chain conditions — XP thresholds, quests, resources

Two Output Modes:

ModeHow It Works
MINT_ON_DEMANDContract mints a fresh NFT. Requires MINTER_ROLE on MonsutaNFT.
PRE_MINTEDDeposited 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:

ModeHow It Works
TRUSTLESSDirect issuance by authorized issuers or owner
SERVER_GATEDRequires 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

DirectionContractOracle
WAX → AVAXFadedMonsutaTHC.claim()oracle-eos.js signs attestation
AVAX → WAXFadedMonsutaTHC.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:

PropertyImplementation
Vault-based (not burning)NFTs locked, never destroyed — always recoverable
Bitwise token IDsavaxTokenId = (typeId << 128) | serial — unlimited serials per type
Atomic ID allocationPostgreSQL RETURNING prevents ID collisions
EIP-712 thresholdSame security model as token bridge
Replay protectionwaxTxId nonce prevents double-claim
Emergency stopPausableUpgradeable 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