> INIT / WHITEPAPER.PDF

Whitepaper

v0.1 · 9 sections · ~12 minutes. The full technical breakdown of the protocol.

01

Architecture

Thirteen contracts, six libraries, no proxies. The dependency graph is a strict DAG: lower-level contracts know nothing about higher-level callers. Every contract is deployed at a deterministic address derived from the deployer's nonce, except BnbCardsHook which is mined via CREATE2 to satisfy PancakeSwap v2's permission-bit address constraint (0x0EC0).

CardsToken (ERC-20)
  ↓
BnbCards (ERC-721) ← CardArt (lib) ← Palette (lib) ← SVGFont (lib)
  ↓
CardPacks ← Abilities (lib)
  ↓
Expedition ← Combat (lib)
  ↓
Tournament ← BnbCardsCore
  ↓
BnbCardsHook (PancakeSwap v2) ← HookMiner (lib)
  ↓
Treasury

Every byte of color in the entire system goes through one of Palette's 32 named indices. Every glyph in the bitmap-font name plate goes through SVGFont. Every card stat-table lookup goes through CardPacks._archetypeStats. The system has no escape hatches.

02

Combat algorithm

the on-chain combat resolver(deck1, deck2) is a pure function. Given two 8-card decks, it deterministically returns the winner address, the turn count, and a finalState bytes blob (the per-slot HP at end of match).

1. Sort each deck ascending by cost (initiative).
2. While any unit alive on both sides AND turn < MAX_TURNS:
     for slot in 0..7:
       if attacker[slot] alive AND defender[slot] alive:
         apply pre-attack abilities (QUICK, HEAVY)
         damage = attacker.atk × multiplier
         apply defender abilities (SHIELD reduces, MIRROR reflects)
         defender[slot].hp -= damage
         apply post-hit abilities (DRAIN heals attacker, BURN sets dot)
         if defender[slot].hp ≤ 0:
           apply death abilities (SACRIFICE, GENESIS revive)
     swap roles, increment turn
3. Survivor count determines winner; tie → defender.

Same inputs always give the same outputs. The test suite runs 100 matchups twice and asserts identical winners every time.

03

16 abilities

IDNameTriggerEffect
00NONE.No special effect
01QUICKbefore strikeStrikes before defender's counter
02HEAVYfirst attackFirst attack deals double damage
03SHIELDfirst hit takenFirst hit halved (one-time)
04DRAINon-hitHeals self for damage dealt
05BURNon-hitDefender burns 3 turns after hit
06MULTIeach turnDeals 1 damage to all enemies
07SACRIFICEon-deathDoubles next ally's ATK
08MIRRORon-hitReflects half damage taken
09VALIDATEmatch end+5 bonus if survives match
10NETWORKeach turnAdjacent allies +1 ATK per net node
11HALVINGevery 4th4th attack deals 4× damage
12GENESISon-deathRevives once at 50% HP
13FORKon-playSummons 50% stat clone
14WHALEpassiveImmune to attacks < 5 damage
15HODLturn startRegenerates 2 HP at turn start
04

Mood oracle

BnbCardsHook implements IHooks directly (no BaseHook inheritance). It registers permission bits 0x0EC0:

uint160 PERMISSIONS =
    BEFORE_ADD_LIQUIDITY_FLAG  // 0x0800
  | AFTER_ADD_LIQUIDITY_FLAG   // 0x0400
  | BEFORE_REMOVE_LIQUIDITY_FLAG // 0x0200
  | BEFORE_SWAP_FLAG           // 0x0080
  | AFTER_SWAP_FLAG;           // 0x0040
                              // = 0x0EC0

The hook address must have these bits in its low 14 bits. We mine a CREATE2 salt using HookMiner.find until the resulting address satisfies the constraint. The salt is computed at deploy time and is verifiable from the address.

On every afterSwap, the hook compares the current $BNBC/BNB price tick against a running 24h average. The delta determines a 5-state mood: BULL_RUN / ACCUMULATION / SIDEWAYS / DIP / CAPITULATION. The mood is written to BnbCardsCore.mood, which gates expedition rewards and pack rare-rates.

05

Economy

CardsToken is a standard ERC-20 with ERC20Permit. The constructor mints exactly 21,000,000 tokens to the deployer. There is no mint() function. CardsToken.totalSupply() is constant.

Pack costs are denominated in $BNBC:

  • Genesis pack: 100 $BNBC · 5 cards · weighted Common-heavy
  • Halving pack: 500 $BNBC · 5 cards · 1 guaranteed Rare+
  • Whale pack: 2,500 $BNBC · 5 cards · 1 guaranteed Epic+, 5% Mythic chance

Pack revenue routes through Treasury with a 50/25/15/10 split: LP rewards / tournament prize pool / week-1 airdrop / Genesis Card holders.

06

Halving cycle

BNB Chain halves block rewards every 210,000 blocks. We do too.

function rewardForEpoch(uint256 epoch) pure returns (uint256) {
    if (epoch >= 64) return 0;          // saturates after 64 halvings
    return the base reward >> epoch;
}

At ~0.75s per BNB Chain block, 3.34M blocks ≈ 29 days. After epoch 64 (~5.1 years), the right shift saturates and the base reward becomes zero. By that point, treasury reserves built up from pack sales fund tournament prize pools indefinitely.

07

Threat model

What the protocol defends against:

  • Pack roll manipulation: block.prevrandao is the only randomness. A miner can withhold blocks but cannot rewrite past blocks. Worst case: a miner sees their roll outcome and decides not to publish, forfeiting the block reward (~$700) for a single pack.
  • Combat outcome lying: impossible. the on-chain combat resolver is pure; any client computes the same answer from the same decks.
  • Hook front-running: the mood update happens in the same swap transaction as the trade. There's no MEV opportunity to predict the mood before it's set.
  • Tournament bracket manipulation: bracket positions are deterministically derived from keccak256(tour_id, entrant_addresses_sorted). Any change to the entrant set re-shuffles the bracket.
08

Verification

Etherscan addresses:

All contracts have their source code published on Etherscan. The hook address ends in …8ec0, which is the PancakeSwap v2 permission-bit pattern (low 14 bits = 0x0EC0) the hook was mined to satisfy.

09

Glossary

Mood
5-state market sentiment (BULL_RUN..CAPITULATION) computed by the mood oracle from $BNBC/BNB price action.
Halving
Roughly every 29 days the base expedition reward halves. Mirrors BNB Chain issuance.
Genesis Card
Auto-minted Mythic to the deployer at LP-seed; one-time, never reissued.
Expedition
Idle staking mode. Deposit cards for 1/7/30 days; earn $BNBC proportional to deck power × halving multiplier.
Bracket
64-entrant single-elimination tournament tree. 6 rounds. Resolved one round per call.
Permission bits
14-bit field in the lower bits of a mood oracle's address declaring which lifecycle methods it implements. We use 0x0EC0.