SOL&Sandals Whitepaper

SOL&Sandals — Technical Whitepaper

A verifiable, sink-driven token economy for on-chain gladiator combat on Solana.

Version 1.0 — July 2026 Author: Lisman Razvan (SECFORIT SRL)


Abstract

SOL&Sandals is a turn-based gladiator combat game deployed on the Solana blockchain. It combines a deterministic, seedable client-side combat engine with three Anchor programs that govern asset ownership, a capped token economy, trustless PvP wagering, and an escrowed NFT marketplace. The GOLD token — the game’s sole unit of account — follows a 50-million-unit hard cap with halving-style emission eras, a suite of deflationary sinks (arena entry fees, exponential forge upgrades, cosmetic renames, and marketplace fees split 50/50 between burn and treasury), and staking-based reward bonuses gated by lockup periods and elite-tier appearance bonds. Every economic constant is implemented identically in Rust (on-chain) and TypeScript (client-side), covered by unit tests on both sides. This document describes the full technical architecture, the token economy design from first principles, the combat engine mathematics, and the trust model under which the system operates.


1. Introduction

1.1 Problem Statement

Blockchain games face a structural economic problem: every in-game action that creates tokens is a faucet, and without proportional sinks, supply grows monotonically. A token with unbounded supply and zero marginal demand converges to zero value. Most web3 games address this with deferred promises (future land sales, governance tokens, staking emissions that print more tokens). SOL&Sandals inverts the model: the sinks are not future work — they are the core loop. Every time a player fights above tier 0, forges gear, renames their gladiator, or trades on the marketplace, a measurable fraction of GOLD is permanently destroyed or locked.

1.2 Design Philosophy

1.3 Technology Stack

Layer Technology
Blockchain Solana (Agave 3.0.x, devnet)
Smart contracts Anchor 0.32.1 (Rust)
BPF toolchain Solana platform-tools v1.52 (rustc 1.89)
Frontend React 18, TypeScript, Vite 5
Wallet integration @solana/wallet-adapter (Phantom, Solflare)
NFT standard Metaplex Token Metadata
Token standard SPL Token (0 decimals)
Testing Mocha (Anchor), Vitest (frontend), cargo test (unit)

2. System Architecture

The system is divided into four layers:

┌──────────────────────────────────────────────────────┐
│                  React UI (Vite)                      │
│  ┌─────────────┐  ┌──────────────┐  ┌─────────────┐ │
│  │ Game Engine  │  │ Wallet Layer │  │ State/Store │ │
│  │ (pure TS)   │  │ (adapters)   │  │ (local)     │ │
│  └──────┬──────┘  └──────┬───────┘  └─────────────┘ │
└─────────┼────────────────┼───────────────────────────┘
          │                │
          ▼                ▼
┌──────────────────────────────────────────────────────┐
│              Solana Devnet                            │
│  ┌────────────┐ ┌───────────┐ ┌───────────────────┐  │
│  │ arena-core │ │arena-wager│ │   marketplace     │  │
│  │ GOLD mint  │ │ PvP escrow│ │ NFT escrow (GOLD) │  │
│  │ profiles   │ │ settle    │ │ list/buy/cancel   │  │
│  │ forge/rename│ │ timeout   │ │ 5% fee burn/treas│  │
│  │ staking    │ │           │ │                   │  │
│  └────────────┘ └───────────┘ └───────────────────┘  │
│  ┌────────────────────────────────────────────────┐  │
│  │        Metaplex Token Metadata (NFTs)           │  │
│  └────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────┘

2.1 Program Addresses

Program ID
arena-core Ghp3KCDNnzhUDF4VMGbZwnb7AtzX1yBzRW5tRAZ6YXmt
arena-wager 2Z8gJ1nB64qkkR6CLnygGj4muj6YN1cN6WSWYw76nqJ6
marketplace iRxYaR4KPcrxeBmqzrR45jWT5n4YzeEWjbWFfqHyvw1

2.2 Program Design

The three programs are loosely coupled — they share no direct CPI calls. They interact only through the shared GOLD SPL token. This means each program can be upgraded, redeployed, or replaced independently without breaking the others, as long as the GOLD mint and treasury account addresses remain stable.


3. The Combat Engine

3.1 Overview

The combat engine is 100% pure TypeScript with zero dependencies. It runs client-side in milliseconds, is fully deterministic (seeded PRNG), and is covered by 34 unit tests. The engine lives in app/src/game/ across seven modules: combat.ts, ai.ts, items.ts, progression.ts, rng.ts, types.ts, opponents.ts, and fight.ts.

3.2 The Six Attributes

Stat Role
Strength Raw melee damage multiplier. Each point adds 0.8 damage to successful hits (scaled by morale).
Attack Hit probability. The primary factor in the hit formula (2× attack).
Defense Damage reduction. Soaks 60% of the stat value from each hit; doubled effectiveness while blocking.
Vitality Hit points. HP = 40 + vitality × 6. Also boosts rest healing.
Charisma Powers taunting (a contest against the opponent’s charisma) and casting (bolt damage = charisma × 1.2). Also resists enemy taunts.
Agility Crit chance (+1% per point on top of 5% base) and dodge chance (1.5× multiplier in the evade score).

3.3 The Five Actions

Players choose one action per turn. The AI opponent chooses simultaneously via its personality-driven weight system.

Attack

The core action. Hit probability is a ratio contest:

hitChance = (atk×2 + agi) × moraleMult(A) / [(atk×2 + agi) × moraleMult(A) + (def×0.5 + agi×1.5) × moraleMult(D)]

Block

Brace until next turn. Damages taken are reduced to 35%. Restores 5 morale. The block stance lapses automatically when the blocker’s next turn begins. The AI notices blocking and shifts toward taunts and casts (which partially or fully ignore armor).

Cast

Spend 3 focus (built at +1/turn, capped at 3) to hurl an arcane bolt. Ignores armor entirely — defense does not reduce cast damage. Blocking still helps (damage reduced to 60% instead of 35%). Damage formula: roll(4,8) + charisma × 1.2. Erodes 6 morale. Casting without enough focus causes a fizzle: the caster loses 5 morale from the crowd’s jeers.

Taunt

The signature mechanic. A charisma contest:

power = charisma(A) × 2 + roll(0,10)
resist = charisma(D) + roll(0,10)

Rest

Recover roll(6,10) + vitality/4 HP and 10 morale. However, the resting fighter is exposed: incoming damage until their next turn is increased (the block multiplier of 0.35 does not apply, and the rest action does not set blocking = true — the fighter takes full damage).

3.4 Morale System

Morale ranges from 0 to 100 and is the combat differentiator. Below 50 (the “shaken” threshold), attack and defense are penalized by a linear multiplier from 1.0 down to 0.6 at 0 morale. At 0 morale, a fighter may flee (35% per taunt received). Morale is damaged by being hit, being taunted, and fizzling a cast. It is restored by blocking, resting, dodging an attack, and successfully taunting. This creates a second dimension of combat: you can win by depleting HP, or by breaking your opponent’s nerve.

3.5 AI Personalities

Five personality archetypes govern opponent decision-making via weighted random action selection:

Personality Attack Block Cast Taunt Rest
Aggressive 6 1 2 1 0.5
Defensive 3 4 1 1 1.5
Trickster 3 1 3 4 1
Berserker 8 0.2 0.5 1 0.2
Showman 3 1 2 5 1

These weights are dynamically modified by the fight state: - Low HP (<35%) doubles block weight and adds +3 to rest (except berserkers, who never rest). - Enemy morale ≤25: +4 to taunt weight (smell blood). - Enemy blocking: attack weight halved, +2 to taunt (chip morale instead of swinging into a shield). - Enemy HP <20%: +6 to attack, rest weight set to 0.1 (finish them). - Cast weight drops to 0.1 when focus < 3 (can’t cast yet).

3.6 Deterministic RNG

Combat uses the mulberry32 algorithm, a fast 32-bit PRNG:

a = a + 0x6D2B79F5
t = imul(a ^ (a >>> 15), 1 | a)
t = t + imul(t ^ (t >>> 7), 61 | t)
return ((t ^ (t >>> 14)) >>> 0) / 4294967296

Every fight is seeded. Given the same seed and action sequence, the fight replays identically. This is the foundation for on-chain fight verification: the seed can be committed before the fight, and the action sequence submitted afterward for a verifier contract to replay and confirm the outcome. Chance values are clamped to [0.02, 0.95] so nothing is probabilistically certain.

3.7 The Opponent Ladder

Ten opponents, tier 0 through 9, each with unique stats, equipment, personality, taunt lines, and flavor text:

Tier Name Personality Notable
0 Fyodor the Unfortunate defensive Tutorial opponent. Rusty gladius, cloth tunic.
1 Livia the Alley Cat trickster High agility (9), spear, sandals.
2 Brutus the Wall defensive Defense 11, tower shield, chainmail.
3 Cassia the Flame showman Charisma 12. The crowd favorite.
4 Ragnar the Red berserker Strength 13, vitality 12. Never rests.
5 Seneca the Serpent trickster Attack 12, agility 12, falx.
6 Octavia the Iron Rose aggressive Plate armor, champion helm. Elite tier (bond required).
7 Maximus the Mountain aggressive Strength 16, vitality 16, titan cleaver.
8 Nyx the Whisper trickster Charisma 15, agility 16, dreadlord cuirass.
9 Imperator Sol showman All stats 12–18, full legendary gear. Undefeated in 100 bouts.

4. The GOLD Token Economy

The GOLD token (SPL, 0 decimals, mint authority held by the arena-core PDA) is the sole medium of exchange, unit of account, and store of value within SOL&Sandals. Its supply policy is designed to transition from inflationary bootstrapping to deflationary maturity.

4.1 Supply Policy

Parameter Value
Hard cap 50,000,000 GOLD (lifetime)
Halving interval Every 10,000,000 GOLD minted
Emission era total_minted / 10,000,000
Era scaling base >> era (integer halving)
Registration stipend 150 GOLD (era-scaled)
Win reward (25 + 12×tier) GOLD (era-scaled)
Loss reward 5 GOLD (era-scaled)
Daily cap 600 GOLD per gladiator per slot-day
Slots per day 216,000 (~24h at 400ms slots)

The cap is on cumulative minted (total_minted), not on circulating supply. Burns do not reopen emission room — once minted, that unit counts against the cap forever. This prevents the classic “burn and re-mint” dilution attack.

Emission is demand-driven, not clock-driven. The faster the game grows, the sooner rewards halve. Early players are compensated for bootstrapping the economy; late-era players rely on the sink/velocity economy rather than issuance.

4.2 Sinks

Every sink splits its payment by TREASURY_SHARE_BPS = 5000: 50% burned, 50% routed to the treasury PDA (burn takes the rounding remainder).

Sink Cost Location
Arena entry fee 2 × tier GOLD per fight arena-core::record_fight
Forge upgrade 100 × 2^gear_tier GOLD arena-core::forge_upgrade
Rename 25 GOLD arena-core::rename_gladiator
Marketplace fee 5% of sale price marketplace::buy_item
Staking lockup Variable (not destroyed, removed from float) arena-core::stake_gold

Entry Fee Schedule

Tier Fee Net on win (era 0) Net on loss (era 0)
0 0 +25 +5
1 2 +35 +3
3 6 +49 −1
5 10 +73 −5
7 14 +97 −9
9 18 +121 −13

Above tier 2, a loss is a net GOLD loss. This makes reckless farming of high tiers self-defeating.

Forge Cost Schedule

Tier Cost to upgrade Cumulative cost Bonus
0 → 1 100 100 +2%
2 → 3 400 700 +6%
4 → 5 1,600 3,100 +10%
6 → 7 6,400 12,700 +14%
8 → 9 25,600 51,100 +18%
9 → 10 51,200 102,300 +20%

The full forge ladder costs 102,300 GOLD — approximately 170 days of capped daily income — and the total bonus it unlocks (+20%) generates far less GOLD over a player’s lifetime than the sink cost. Forge is a net-negative GOLD flow: it exists to give late-game players something to burn toward, not to print more tokens.

4.3 Staking and Bonds

Staking locks GOLD in a program vault, removing it from the circulating float. It serves two purposes:

  1. Reward bonus: +1% fight gold per 50 GOLD staked, capped at +20% (1,000 GOLD staked).
  2. Appearance bond: Required to enter elite arenas. Tier 6 requires 50 staked, tier 7 requires 100, tier 8 requires 150, tier 9 requires 200. Tiers 0–5 require no bond.

The combined forge + stake bonus is capped at +40%. Bonuses multiply the base reward after era scaling but before the daily cap. The daily cap is always enforced — bonuses increase how fast you reach the cap, not the cap itself.

Staked GOLD is subject to a minimum lock period (MIN_STAKE_LOCK_SLOTS = 300 on devnet, ~2 minutes; designed for days on mainnet). Each new stake refreshes the lock timer.

4.4 Treasury

The treasury PDA accumulates 50% of every sink payment. It is a program-owned token account with no direct withdrawal instruction in the current deployment — spend governance is deferred to Phase 3 (see §8). Planned uses: DEX liquidity seeding, tournament prize pools (recycled, not minted), buy-and-burn operations. The treasury also serves as an on-chain verifiable sink accumulator: anyone can read its balance and confirm the total GOLD drained from circulation.

4.5 Float Accounting

At any point, the effective circulating supply is:

float = total_minted - total_burned - treasury_balance - total_staked

All four values are readable from GameConfig and the treasury/stake vault token accounts. The economic claim is not that GOLD is scarce, but that its float is transparent and its supply-side drivers are fully constrained.


5. On-Chain Programs

5.1 arena-core

The central hub. Owns the GOLD mint authority (a PDA), manages gladiator identity, and records PvE fight results.

Accounts: - GameConfig — singleton PDA at ["config"]. Stores authority, gold_mint, aggregate counters. - GladiatorProfile — one PDA per NFT at ["gladiator", nft_mint]. Stores level, XP, W/L, rating, cooldown, gear tier, daily emission tracking. - StakeAccount — one PDA per owner at ["stake", owner]. Tracks staked amount and lock expiry.

Instructions: - initialize — creates GameConfig, GOLD mint (PDA authority), treasury, stake vault. One-time. - register_gladiator(name) — creates profile, mints era-scaled stipend. Requires holding the NFT. - record_fight(tier, won) — charges entry fee (burned + treasury), verifies cooldown, checks bond, awards XP/rating, mints GOLD (era-scaled, boosted by forge/stake, clamped by daily cap and supply cap). Emits FightRecorded event. - forge_upgrade — burns 100 × 2^tier GOLD, increments gear tier. Emits GoldSunk. - rename_gladiator(new_name) — burns 25 GOLD, updates name. Emits GoldSunk. - stake_gold(amount) — transfers GOLD to stake vault, records amount and lock. Emits GoldStaked. - unstake_gold(amount) — returns GOLD from vault after lock expires. Emits GoldUnstaked.

5.2 arena-wager

Trustless PvP betting. State machine: Open → Active → Settled/Canceled/Refunded.

Instructions: - create_match(match_id, stake) — challenger escrows stake into a match PDA vault. Any SPL mint accepted (client enforces GOLD). Match is Open. - join_match — opponent matches the stake. Match becomes Active. Clock restarts for timeout. - settle_cooperative(winner) — both players co-sign. Winner receives the pot (2× stake). Match is Settled. - settle_by_arbiter(winner) — the designated arbiter (a pubkey set at match creation, e.g., a game server key) signs. Fallback for when a loser refuses to co-sign. - cancel_match — creator reclaims stake from an Open match nobody joined. - timeout_refund — anyone can trigger a refund of both stakes after SETTLE_TIMEOUT_SLOTS (9000 slots, ~1h on devnet). Funds can never be permanently locked.

Security properties: - The vault authority is the match PDA — only the program can move escrowed funds. - Settle paths are gated by signature checks (cooperative: both players; arbiter: the designated key). - The timeout path is permissionless after the slot threshold: no signature is required from either player. This is the ultimate escape hatch.

5.3 marketplace

Escrowed NFT listings priced in GOLD. One listing per NFT mint.

Instructions: - init_market — one-time setup pinning the payment mint, treasury, and authority. - list_item(price) — seller escrows the NFT into a listing PDA vault. - buy_item — buyer pays the seller (price − fee), burns half the fee, routes half to treasury, receives the NFT. Listing and vault are closed; rent returns to the seller. Emits ItemSold. - cancel_listing — seller reclaims the NFT, closes the listing and vault.

Fee structure: 5% of sale price. Split 50/50 between burn and treasury (2.5% each). For a 100 GOLD sale: seller receives 95, 3 GOLD burned, 2 GOLD to treasury.


6. Frontend Architecture

6.1 Tech Stack

6.2 Screens

Screen Description
Home Wallet connect, gladiator summary, quick actions
Character Creation Name, 15 stat points allocation, SVG appearance (skin/hair/warpaint)
Ladder 10 opponents with stats preview, fight initiation
Fight Turn-by-turn animated combat, action buttons, combat log, HP/morale bars, SVG fighters
Stats Level, XP, stat allocation, equipment view
Shop 22 items across 5 slots, buy/sell, level-gated
Forge GOLD-burning gear upgrades with cost preview
PvP Create/join wager matches, simulate fight, settle
Marketplace NFT listings (list/buy/cancel), GOLD-denominated
Leaderboard On-chain ranking by Elo rating from getProgramAccounts

6.3 SVG Gladiator Rendering

The gladiator is rendered as an inline SVG with customizable appearance (skin tone, hair style, hair color, warpaint) set at character creation. Equipment changes the rendered character: each item carries a hue value that tints the corresponding body part (weapon in hand, shield on arm, armor on torso, helmet on head, boots on feet). This creates visible progression — a fresh gladiator in a cloth tunic looks fundamentally different from a tier-10 champion in Dreadlord Cuirass wielding a Titan Cleaver.


7. Security and Trust Model

7.1 Current State (Devnet Demo)

record_fight is self-reported by the NFT holder’s wallet. The program trusts the caller’s won: bool and tier parameters. This is acceptable for a devnet demo where GOLD has no real-world value. The following mitigations bound the damage of forged wins:

Mitigation Mechanism
Slot cooldown 150 slots (~60s) between fights per gladiator
Entry fees Claims above tier 0 cost real GOLD first
Daily cap Max 600 GOLD/day/gladiator, regardless of wins
Supply cap 50M lifetime; mass-farming accelerates halving, gutting margins
Elite bonds Tiers 6–9 require locked capital
Public audit total_minted, total_burned, total_staked on-chain

7.2 Production Path

The combat engine is already deterministic and seedable. The production verification path is:

  1. Commit-reveal: Before a fight, the player commits to a seed hash on-chain. After the fight, they submit the (seed, action_sequence). An on-chain verifier replays the engine, confirms the outcome, and only then mints rewards.

  2. Signed oracle: A game server (the same key that serves as arbiter in PvP wagers) signs fight results. The program verifies the signature against a known oracle pubkey.

Both approaches are compatible with the current engine without modification. The daily cap, entry fees, and bonds remain valuable as defense-in-depth even with verified fights.

7.3 Known Limitations


8. Economic Analysis

8.1 Why GOLD Can Hold Value

The economic claim is narrow and defensible: given organic demand for the game, GOLD can hold value, whereas an unbounded-faucet token provably cannot. The mechanisms:

  1. Utility demand. GOLD is required for: arena entry above tier 0, forge upgrades, elite bonds, renames, marketplace purchases, and PvP stakes. A player who wants to progress must acquire GOLD — either by playing or by buying from other players.

  2. Burn deflation. Every sink destroys ≥50% of what it collects. Once emission eras advance, net supply growth can turn negative while activity is high. Marketplace burn scales with trade volume, which grows with player count.

  3. Lockup constrains float. Bonds + staking remove a structural fraction of supply. Progression forces lockup — to farm the best arenas, you must first lock GOLD.

  4. Hard cap enables pricing. 50M lifetime units, verifiable on-chain. A market can price a capped asset; it cannot price an unbounded one.

  5. Honest assumptions. This holds only if (a) the game retains players — token design cannot rescue a game nobody plays; (b) fight verification becomes trust-minimized; (c) real liquidity exists (Phase 3). The system is designed to be compatible with value accrual, not to guarantee it.

8.2 Emission Era Projection

Era Minted range Faucet scale Registration Win (tier 0)
0 0 – 10M 100% 150 25
1 10M – 20M 50% 75 12
2 20M – 30M 25% 37 6
3 30M – 40M 12.5% 18 3
4 40M – 50M 6.25% 9 1
5+ ≥50M 0% 0 0

At era 5 the cap is reached and all emission stops. Fights still grant XP and rating — GOLD emission simply ends. The economy thereafter runs entirely on velocity: existing GOLD circulates through wagers, marketplace trades, entry fees, and forge upgrades.

8.3 Sink/Faucet Balance (Era 0, per gladiator per day)

Maximum faucet: 600 GOLD/day (hard cap). Maximum sink (theoretical): 18 GOLD/fight × 5 fights/day (cooldown-bounded) = 90 GOLD/day in entry fees. Forge at tier 9→10: 51,200 GOLD (one-time, ~85 days of capped income). Net daily: At the cap, even a 100%-win-rate tier-9 farmer (implausible without verification) nets at most 600 − 90 = 510 GOLD/day — but the forge curve ahead of them will consume it all. A realistic mid-tier player (tier 5, mixed W/L) nets 50–150 GOLD/day after entry fees.

The sink/faucet balance is not perfectly symmetric at launch — that would make the game unplayable. It is designed so that early eras are net-inflationary (bootstrapping) and late eras are net-deflationary (maturity), with the crossover determined by actual player activity rather than a calendar date.


9. Roadmap

Phase 1 — Implemented (Current)

Phase 2 — Trust + More Sinks (Planned)

Phase 3 — Market Infrastructure (Planned)


10. Conclusion

SOL&Sandals is not a token with a game attached. It is a game whose economic design makes the token necessary, scarce, and auditable. The combat engine is fast, deterministic, and fully testable. The on-chain programs are minimal — three contracts, one token, no oracles, no dependencies between programs. The sinks are the core loop: every time a player fights, forges, renames, or trades, GOLD leaves circulation. The emission schedule is capped and decaying. The treasury exists but cannot print.

The system makes no promises about price. It makes a verifiable promise about supply. The rest depends on whether people want to fight in the arena.


Appendix A: Program Constants Reference

Constant Value Location
MAX_GOLD_SUPPLY 50,000,000 arena-core, progression.ts
HALVING_INTERVAL 10,000,000 minted arena-core, progression.ts
STARTING_GOLD 150 (era-scaled) arena-core, progression.ts
BASE_WIN_GOLD 25 + 12·tier arena-core, progression.ts
LOSS_GOLD 5 arena-core, progression.ts
BASE_WIN_XP 40 + 20·tier arena-core, progression.ts
LOSS_XP 15 arena-core, progression.ts
FIGHT_COOLDOWN_SLOTS 150 arena-core
MAX_TIER 9 arena-core
ENTRY_FEE_PER_TIER 2 arena-core, progression.ts
DAILY_GOLD_CAP 600 arena-core, progression.ts
SLOTS_PER_DAY 216,000 arena-core
TREASURY_SHARE_BPS 5,000 (50%) arena-core, progression.ts
FORGE_BASE_COST 100 arena-core, progression.ts
MAX_GEAR_TIER 10 arena-core, progression.ts
GEAR_BONUS_PCT_PER_TIER 2% arena-core, progression.ts
MAX_GEAR_BONUS_PCT 20% arena-core, progression.ts
STAKE_BONUS_DIVISOR 50 GOLD per +1% arena-core, progression.ts
MAX_STAKE_BONUS_PCT 20% arena-core, progression.ts
TIER_BOND_FREE_TIERS 6 arena-core
TIER_BOND_STEP 50 GOLD arena-core
MIN_STAKE_LOCK_SLOTS 300 arena-core
RENAME_COST 25 arena-core, progression.ts
MARKET_FEE_BPS 500 (5%) marketplace, progression.ts
SETTLE_TIMEOUT_SLOTS 9,000 arena-wager
FOCUS_COST 3 combat.ts
MAX_MORALE 100 combat.ts
SHAKEN_THRESHOLD 50 combat.ts
FLEE_CHANCE_AT_ZERO 0.35 combat.ts
POINTS_PER_LEVEL 3 progression.ts
CREATION_POINTS 15 progression.ts
BASE_STAT 5 progression.ts
BASE_HP 40 progression.ts
HP_PER_VITALITY 6 progression.ts

Appendix B: XP Curve

Level n requires 50 × n × (n−1) total XP: - Level 1: 0 XP - Level 2: 100 XP - Level 3: 300 XP - Level 5: 1,000 XP - Level 10: 4,500 XP - Level 20: 19,000 XP - Level 50: 122,500 XP (max level)


Repository: github.com/navzar97/gladiator-arena License: MIT