Skip to content

Repository files navigation

Beasts NFT Collection

Beasts is a fully onchain NFT collection featuring 75 unique monster species that integrate with the Loot Survivor game ecosystem on Starknet. Each Beast is dynamically generated with unique attributes, names, and artwork—all stored and rendered directly from the blockchain.

Overview

The Beasts are a collection of digital-native creatures, born onchain and built for battle. Across 75 species, each Beast combines naming, visual, and combat attributes to balance abundance and scarcity. Beasts carry two sets of traits: visual and combat.

  • Visual traits power collecting: Shiny and Animated forms activate pixel-perfect effects. Non-genesis Beasts receive live ranking within their species based on power and health, and those rankings update as new Beasts are minted.
  • Combat traits power play: On mint, a Beast includes level and health. Together with its type and tier, this defines a combat profile compatible with the Loot Survivor system that first brought Beasts into the world.
  • Live, credibly neutral traits such as Adventurers slain, last Adventurer who defeated a Beast, and timestamp of that defeat enable long-term growth systems without hardcoding game logic.
  • Beasts are earned by worthy Adventurers in the dungeons of Loot Survivor using verifiable randomness. Every step is etched onchain for permanent provenance. For collectors, Beasts offer verifiable scarcity and provenance; for players, they unlock endless onchain fun.

🎨 Example Beasts

🚀 Features

  • 🎨 Fully Onchain Artwork: Every Beast’s image data and metadata are generated onchain
  • 🎮 Born Onchain: Beasts emerge from the dungeons of Loot Survivor
  • ⚔️ Battle-Ready: Each Beast is minted with level and health and is compatible with the Loot Survivor combat system
  • 🏛️ 75 Unique Species: From mystical Warlocks to fierce Minotaurs, each with distinct visual traits
  • 📊 Tiered Rarity System: 5 tiers with visual indicators through border colors and effects
  • 🔐 Deterministic Token IDs: A Beast’s ERC721 token ID is the packed representation of its species, name parts, combat stats, and visual flags

📦 Installation

Prerequisites

Tool versions used by CI are pinned in .tool-versions.

Setup

  1. Clone the repository:
git clone https://github.com/Provable-Games/beasts.git
cd beasts
  1. Build contracts:
scarb build
  1. Run tests:
snforge test

🏗️ Architecture

Smart Contract Structure

src/
├── lib.cairo                  # Entry point; exposes modules and ERC721 contract (beasts_nft)
├── beast_definitions.cairo    # 75 species definitions and names
├── beast_manager.cairo        # Validation and uniqueness hashing
├── minting_coordinator.cairo  # Single and batch mint prep
├── beast_ranking.cairo        # Per-species live ranking
├── pack.cairo                 # Attribute packing (id, name parts, level, health, shiny, animated)
├── metadata_generator.cairo   # Onchain JSON metadata generation
├── beast_svg.cairo            # Dynamic SVG artwork generation
├── beast_images.cairo         # Shared image helpers
├── beast_png_*_data.cairo     # PNG image data provider contracts
├── beast_gif_*_data.cairo     # GIF image data provider contracts
├── encoding.cairo             # Encoding helpers
├── utils.cairo                # Shared utilities
└── interfaces.cairo           # External interfaces (image data providers, systems)

Beast Data Model

Each Beast is efficiently packed into 116 bits. The packed value is also the ERC721 token_id:

PackableBeast {
    id: u64,        // 64 bits - species (1–75 genesis, 76+ community)
    prefix: u8,     // 7 bits  - name prefix
    suffix: u8,     // 5 bits  - name suffix
    level: u16,     // 16 bits - level
    health: u16,    // 16 bits - health
    shiny: u8,      // 1 bit   - visual trait
    animated: u8,   // 1 bit   - visual trait
    tier: u8,       // 3 bits  - tier (1–5), static per species
    beast_type: u8, // 3 bits  - type (0 = Magic, 1 = Hunter, 2 = Brute)
}

Beast Token ID Design

Beasts do not use sequential token IDs. For fresh deployments, every token ID is deterministic:

token_id = encode_token_id(PackableBeast)

The bit layout is:

id
+ prefix     * 2^64
+ suffix     * 2^71
+ level      * 2^76
+ health     * 2^92
+ shiny      * 2^108
+ animated   * 2^109
+ tier       * 2^110
+ beast_type * 2^113

This keeps every valid Beast token ID below 2^116, so it fits in u128 (two 64-bit words for indexers and databases; JavaScript clients should use BigInt). The same format is used for genesis and non-genesis Beasts. Tier and type are resolved by the contract at mint time — never caller-supplied — so decoded values are trustworthy after an ERC721 existence check, and power = level * (6 - tier) is a pure function of the token ID.

Because the token ID is the source of the Beast attributes, contract reads such as get_beast(token_id), token_uri(token_id), and ranking comparisons decode the token ID after verifying ERC721 ownership/existence. There is no separate onchain map from token_id to PackableBeast.

Genesis Beasts are minted in the constructor to the owner with prefix = 0, suffix = 0, level = 1, health = 100, shiny = 1, and animated = 1. Genesis Beasts have rank 0 and are entered into the uniqueness map at construction. The (id, 0, 0) affix slot is reserved for the Genesis Beast of each species: non-genesis mints require prefix >= 1 and suffix >= 1, so genesis status is derived directly from the token ID and every species has a maximum supply of exactly 1,243 (69 prefixes × 18 suffixes + 1 genesis). Non-genesis Beast uniqueness is tracked by (beast_id, prefix, suffix), while ranking and metadata refresh state continue to index by packed token ID.

total_supply() is a count of minted NFTs, not the largest token ID.

🎮 Beast Types & Tiers

Beast Types

  • 🔮 Magical: Mystical creatures with arcane powers
  • 🏹 Hunter: Swift and agile predators
  • ⚔️ Brute: Raw strength and physical dominance

Tier System

  • Tier 1: Orange borders (Legendary) - Most powerful beasts
  • Tier 2: Purple borders (Epic)
  • Tier 3: Blue borders (Rare)
  • Tier 4: Green borders (Uncommon)
  • Tier 5: White borders (Common) - Entry level beasts

🧪 Testing

Run the test suite:

# Default local run
snforge test

# Match the CI test command
snforge test --max-n-steps 4294967295

# Generate local coverage
snforge test --coverage

# Summarize coverage locally
lcov --summary coverage/coverage.lcov

For a longer local pass, use snforge test --fuzzer-runs 500 --coverage. CI currently enforces formatting and tests, but not coverage thresholds; aim for >=80% overall coverage when feasible.

🚢 Deployment

  1. Configure environment variables:
cp .env.example .env
# Edit .env with your configuration

Required in .env (no defaults are assumed):

  • STARKNET_ACCOUNT, STARKNET_PRIVATE_KEY
  • RPC_URL (e.g., Sepolia or Mainnet endpoint)
  • NAME, SYMBOL
  • OWNER, ROYALTY_RECEIVER, ROYALTY_FRACTION (u128, denominator 10,000)

Optional in .env:

  • DEATH_MOUNTAIN_ADDRESS (ContractAddress, default 0): external systems integration address; set to 0 to disable.
  1. Deploy to Starknet:
bash scripts/deploy.sh

Notes:

  • The script declares and deploys the four image data provider contracts, then deploys the core NFT with their addresses passed to the constructor.
  • The script fails with a descriptive error if any required .env value is missing.
  • The deploy script reads DEATH_MOUNTAIN_ADDRESS and appends it to the constructor.

🛠️ Development

Code Style

  • Run formatter before committing:
scarb fmt
scarb fmt --check --workspace

CI/CD

The project uses GitHub Actions for:

  • Linting (scarb fmt --check --workspace)
  • Contract tests (snforge test --max-n-steps 4294967295)
  • Caching/installing coverage tools for local parity; coverage thresholds are not currently enforced in CI

🤝 Acknowledgments

🔗 Links

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages