Skip to content
TAURION
11Economy & Accounts

Economy & Accounts

vCHI, the burnsale, account state, fame and the fee sinks.

In plain language (start here if you're a player)#

Taurion has two kinds of money. The first is WCHI — a real cryptocurrency that lives in your wallet and has actual value. The second is vCHI (shown in the UI as "vCHI", sometimes called "Cubit" in the code) — an in-game currency you can only get by permanently destroying ("burning") some of your real WCHI in the one-time burnsale. Early buyers get more vCHI per WCHI than later ones, and once a fixed amount has been sold there is no more.

To play, you pick a faction (RED, GREEN, or BLUE) once and forever, then pay 10 WCHI to the developers for each character you create (up to 20). Your first character spawns with a starter vehicle — RED gets a Raider, GREEN a Scarab, BLUE a Barracuda — and a basic gun. You then spend vCHI in the world on repairs, crafting, and trading, and earn (or lose) fame — a reputation score — by killing other players' characters. A lucky few find limited-edition prizes while prospecting. The rest of this document is the precise, code-backed reference for all of that.

(Terms in bold above are defined in detail in the sections below. "GSP" = Game State Processor, the server program that computes the official game state from on-chain moves; a "move" = a chunk of JSON the game reads from a blockchain transaction.)


This document is the authoritative reference for Taurion's in-game economy and the account/identity system. Everything here is derived from the Game State Processor (GSP) source tree, which is the single source of truth for consensus rules. Human-readable display names come from the official web client.

Taurion runs on the Xaya platform on Polygon. The native wrapped coin is WCHI (an ERC-20 on Polygon). The in-game currency is vCHI (also called "Cubit" in some code/UI). Player identities are Xaya names (NFT-style on-chain names), and every game action is a Xaya move — a JSON blob attached to a name update transaction.


1. Currency primer: WCHI vs. vCHI (Cubit)#

Currency What it is Where it lives
WCHI The real, on-chain Xaya coin (ERC-20 on Polygon). Has actual market value. Player's Polygon wallet / Xaya name.
vCHI ("Cubit") The in-game soft currency. Created by burning real WCHI in the burnsale. Tracked entirely inside the GSP database. The accounts.balance field per account.

Both are represented internally as satoshi (int64_t), where 1 coin = COIN = 100,000,000 satoshi (database/amount.hpp:28,31).

The client labels the in-game balance "vCHI" (the official web client's profile view shows { label: 'vCHI', value: ... }). Internally and in the on-chain JSON state the currency is referred to as Cubit in several places (e.g. trading/DEX code, and the getaccounts comment at gamestatejson.cpp:722 "Cubit balances reserved in open bids"). They are the same thing. This document uses vCHI throughout.

1.1 Hard cap on vCHI#

MAX_COIN_AMOUNT = 100,000,000,000 satoshi = 1,000 vCHI (src/jsonutils.cpp:38). This is the maximum amount accepted in a single coin-operation field of a move. The comment notes it is "actually the total cap on vCHI" — and indeed the burnsale schedule sells exactly 50,000,000,000 units of vCHI ("coins" = whole vCHI, see §3), which is a separate denomination from the satoshi cap above (see Open Questions for the unit caveat).


2. Accounts & identity#

2.1 Account creation (implicit, free)#

Any Xaya name that has ever sent any Taurion move automatically gets an account row, even before it joins the game. In MoveProcessor::ProcessOne (src/moveprocessor.cpp:1299-1303):

if (accounts.GetByName (name) == nullptr)
  {
    LOG (INFO) << "Creating uninitialised account for " << name;
    accounts.CreateNew (name);
  }

A freshly created Account is uninitialisedfaction == INVALID — and starts with fame = 100 set in the constructor (database/account.cpp:24-31). Balance starts at 0.

Accounts can also be auto-created as coin-transfer recipients (src/moveprocessor.cpp:2465-2472) or as god-mode loot/gift recipients (src/moveprocessor.cpp:2224-2226, 2261-2263).

The on-chain account state proto (proto/account.proto):

Field # Meaning
kills 1 Total characters killed by this account.
fame 2 Account fame (see §6). Defaults to 100 on creation.
balance 3 vCHI balance (satoshi).
burnsale_balance 4 vCHI ever minted via burnsale (the portion carried into the full game).

2.2 Account initialisation — choosing a faction (permanent)#

To actually play, an account must be initialised by choosing a faction. This is done with the a.init command. Once set, the faction can never be changed (database/account.cpp:69-78 CHECK-fails on a second SetFaction; MaybeInitAccount also rejects re-init, src/moveprocessor.cpp:2375-2378).

Move schema:

{"a": {"init": {"faction": "<r|g|b>"}}}

Rules (MoveProcessor::MaybeInitAccount, src/moveprocessor.cpp:2366-2413):

  • init must be an object with exactly one field faction (init.size() != 1 is rejected, line 2403).
  • faction must be the string "r", "g", or "b".
    • "a" (ANCIENT) is explicitly rejected (line 2395-2397).
    • Any other string is INVALID and rejected (line 2391-2393).
  • If the account already has a faction, the command is ignored (line 2375-2378).

Real example (from src/moveprocessor_tests.cpp:186-188) — extra sibling fields are tolerated, only init matters:

{"name": "domob", "move": {"a": {"x": 42, "init": {"faction": "b"}}}}

The three faction codes and their display identities:

Code Faction Spawn building ID Starter vehicle
r RED 6 rv st ("Raider")
g GREEN 4 gv st ("Scarab")
b BLUE 5 bv st ("Barracuda")

Spawn building IDs from proto/roconfig/params.pb.text:33-47; starter vehicles from src/spawn.cpp:127-160; vehicle display names from the official client.

2.3 Why initialisation gates the game#

In ProcessOne (src/moveprocessor.cpp:1333-1339), after attempting an account update, if the account is still uninitialised the move processing stops — no character creation, building updates, service ops, etc. happen. Note that coin operations and DEX operations are exempt (see §2.4 and §4): they are processed before this gate so vCHI behaves like a real currency that is not tied to game participation.

2.4 Ordering of operations within a single move#

MoveProcessor::ProcessOne (src/moveprocessor.cpp:1288-1359) processes a move in this strict order:

  1. Ensure account row exists (auto-create if needed).
  2. Coin operations (vc: mint/burn/transfer) — even for uninitialised accounts. Done first so explicit transfers get priority over implicit costs.
  3. Gate: stop if the GameStart fork is not active (only Cubit ops allowed before game start — see §7).
  4. DEX operations (x) — independent of initialisation.
  5. Account update (a) — e.g. faction init.
  6. Gate: stop if account still uninitialised.
  7. Character updates (c) — done before creation so a newly created character cannot be updated in the same move.
  8. Character creation (nc).
  9. Building updates (b).
  10. Service operations (s).

Because account update (step 5) runs before character creation (step 8), a single move can both choose a faction and create a character (src/moveprocessor_tests.cpp:226-242).

2.5 Character limit#

character_limit = 20 (proto/roconfig/params.pb.text:4). An account may own at most 20 characters at any time. Enforced on:

  • Creation (src/moveprocessor.cpp:156-163).
  • Receiving a character via transfer (src/moveprocessor.cpp:1422-1429).
  • A slow-consistency invariant validates this every block when enabled (src/logic.cpp:372-386).

3. How vCHI enters the economy: the burnsale#

The only way new vCHI is minted (outside god-mode gifting, §5.4) is the burnsale: a player burns real WCHI by sending it to the burn address, and receives newly minted vCHI in return according to a fixed, staged price schedule.

3.1 The burn address & dev address#

From proto/roconfig/params.pb.text:24-29:

dev_addr:  "0xb960bECa8623165a3094a629d6A4775857a14d28"   # multisig, Xaya team (mainnet)
burn_addr: "0x2659deaa71B51124bA6e6493b795486b3027Dbc9"   # WCHI sent here is bridged to ETH and burnt

When a Xaya move includes an out section paying these addresses, the GSP reads the amounts (BaseMoveProcessor::ExtractMoveBasics, src/moveprocessor.cpp:95-107):

  • WCHI paid to dev_addrpaidToDev (used for character creation, §5.1).
  • WCHI sent to burn_addrburnt (consumed by the burnsale mint, below).

3.2 Burnsale stages and exact CHI→vCHI rates#

From proto/roconfig/params.pb.text:49-52:

burnsale_stages: { amount_sold: 10000000000 price_sat: 10000  }
burnsale_stages: { amount_sold: 10000000000 price_sat: 20000  }
burnsale_stages: { amount_sold: 10000000000 price_sat: 50000  }
burnsale_stages: { amount_sold: 20000000000 price_sat: 100000 }

amount_sold is the number of vCHI units available in that stage; price_sat is the price in WCHI satoshi per 1 vCHI unit. Converting to whole-WCHI per vCHI (divide by COIN = 1e8):

Stage vCHI available Price (satoshi / vCHI) Price (WCHI / vCHI) Effective vCHI per 1 WCHI
1 10,000,000,000 10,000 0.0001 10,000
2 10,000,000,000 20,000 0.0002 5,000
3 10,000,000,000 50,000 0.0005 2,000
4 20,000,000,000 100,000 0.001 1,000
Total 50,000,000,000

So the total vCHI that can ever be minted via burnsale is 50 billion units, and minting all of it costs:

10e9 * 10000 + 10e9 * 20000 + 10e9 * 50000 + 20e9 * 100000
= 1e14 + 2e14 + 5e14 + 2e15 = 2.8e15 satoshi = 28,000,000 WCHI

This exact total is verified by the test BurnsaleTests.AllInOne (src/burnsale_tests.cpp:81-84): burning 30,000,000 * COIN WCHI mints all 50,000,000,000 vCHI and only uses up 28,000,000 * COIN WCHI (the remainder is unused/overpaid).

3.3 Minting algorithm#

ComputeBurnsaleAmount (src/burnsale.cpp:26-64) walks the stages in order:

  1. Skip stages already fully sold (tracked by the burnsale money-supply counter passed in as alreadySold).
  2. In the current stage, affordable = burntChi / price_sat (integer division, rounds down), capped by what's left in the stage.
  3. Deduct sold * price_sat from burntChi, add sold to the result.
  4. If the stage is exhausted, continue to the next; otherwise stop.

Key behaviours, all from src/burnsale_tests.cpp:

  • Rounding down: burning COIN + 9,999 satoshi at stage-1 price (10,000) mints 10,000 vCHI and uses exactly COIN (the extra 9,999 sat is left over, test Rounding line 71).
  • Below minimum: burning COIN/1000 - 1 when only stage 4 remains mints 0 vCHI and uses 0 (line 72).
  • Crossing stage boundaries in one burn is handled (test AcrossStageBoundary line 64-67).
  • Sold out: once 50,000,000,000 vCHI have been minted, further burns mint 0 and consume 0 WCHI (test SoldOut line 75-79).

3.4 The mint move#

Schema (BaseMoveProcessor::ParseCoinTransferBurn, src/moveprocessor.cpp:489-495) — the m (mint) command must be an empty object:

{"vc": {"m": {}}}

This is combined with an out payment to burn_addr carrying the WCHI to burn. Realistic example (from test helper ProcessWithBurn, src/moveprocessor_tests.cpp:105-115) — burning 1,000,000 WCHI:

[
  {
    "name": "domob",
    "move": {"vc": {"m": {}}},
    "out": {"0x2659deaa71B51124bA6e6493b795486b3027Dbc9": "1000000.0"}
  }
]

At stage-1 price, 1,000,000 WCHI mints 10,000,000,000 vCHI (src/moveprocessor_tests.cpp:363-378).

3.5 Effects of a successful mint#

In TryCoinOperation (src/moveprocessor.cpp:2437-2444):

if (op.minted > 0) {
  a->AddBalance (op.minted);                                  // credit balance
  a->MutableProto ().set_burnsale_balance (old + op.minted);  // track for full game
  moneySupply.Increment ("burnsale", op.minted);              // global counter
}
  • The minted vCHI is credited to the player's balance.
  • It is also accumulated into the per-account burnsale_balance (proto/account.proto field 4), described as "the balance that will be carried over to the full game" — i.e. burnsale-minted vCHI is the portion guaranteed to survive a reset, whereas other balance (airdrops, gifts) may not.
  • The global burnsale money-supply counter increments so future mints know how much has been sold (and which stage to price from).

burnsale_balance is exposed in the account JSON as minted (src/gamestatejson.cpp:313), and tracked in the BurnsaleBalance test (src/moveprocessor_tests.cpp:380-392): note that burning vCHI later does not reduce minted.


4. vCHI sinks & flows (where it goes)#

vCHI satoshi is conserved by Account::AddBalance (database/account.cpp:80-87), which CHECK-fails on negative balances. Here is every flow:

4.1 Explicit coin operations (vc)#

The vc command bundles mint / burn / transfer (ParseCoinTransferBurn, src/moveprocessor.cpp:475-557; executed in TryCoinOperation, src/moveprocessor.cpp:2425-2475). Full schema:

{"vc": {
  "m": {},                              // mint via burnsale (empty object)
  "b": 100,                             // burn 100 satoshi vCHI (sink)
  "t": {"alice": 50, "bob": 25}         // transfer satoshi vCHI to others
}}

Processing order is mint → burn → transfer (src/moveprocessor_tests.cpp:394-427), so newly minted vCHI can be spent in the same move. Rules:

  • Burn (b): an integer in [0, MAX_COIN_AMOUNT] (CoinAmountFromJson, src/jsonutils.cpp:127-135). The burnt amount is removed from the balance permanently — a true sink. If it exceeds the (running) balance the whole burn is dropped to 0 (src/moveprocessor.cpp:504-516).
  • Transfer (t): a map of recipient name → amount. Invalid entries are skipped individually; transfers that would exceed the remaining balance are skipped (src/moveprocessor.cpp:536-542). Recipients are processed in key order (src/moveprocessor_tests.cpp:429-447). Transfers to self are no-ops but don't consume budget (src/moveprocessor.cpp:544-552). Non-existent recipient accounts are auto-created uninitialised (src/moveprocessor.cpp:2465-2472).

vCHI works without an initialised account, so it can be sent/held by names that never join the game (src/moveprocessor.cpp:1305-1312).

4.2 Service fees (building services) — vCHI sink/redistribution#

Service operations (s command) cost vCHI. Cost = base + fee (ServiceOperation::GetCosts, src/services.cpp:985-1016):

  • Base cost is paid by the operator and is effectively burnt (removed from the operator's balance with no recipient; Execute, src/services.cpp:1128-1145 — only the fee is paid out, the base is not).
  • Service fee (a percentage on top of base) is paid to the building owner — a redistribution, not a sink:
    fee = (base * cfg.service_fee_percent () + 99) / 100;   // rounded up
    
    • Fee is 0 if the operation is not in a building, in an ancient building, or in the operator's own building (src/services.cpp:991-1009).
    • The owner's service fee percent is capped at MAX_SERVICE_FEE_PERCENT = 1000 (= 1000%, src/moveprocessor.cpp:48, 264-272).
  • Validity requires base + fee <= balance (src/services.cpp:1091-1100).

Base-cost constants (all from proto/roconfig/params.pb.text, multiplied by item-specific factors in src/services.cpp):

Service Base cost formula Constant Source
Armour repair ceil(missingHp * armour_repair_cost_millis / 1000) armour_repair_cost_millis = 100 (1 vCHI per 10 HP) params.pb.text:12, services.cpp:377-388
Refining steps * refData.cost item-specific services.cpp:129-131
Reverse engineering num * revEngData.cost item-specific services.cpp:471-473
Blueprint copy bp_copy_cost * complexity bp_copy_cost = 1 params.pb.text:13, services.cpp:631-633
Construction construction_cost * complexity construction_cost = 1 params.pb.text:15, services.cpp:791-794

Armour repair also has timing params: armour_repair_hp_per_block = 100 (params.pb.text:11). Blueprint copy / construction durations: bp_copy_blocks = 1, construction_blocks = 1 (params.pb.text:14,16).

The client can preview these costs via the getserviceinfo RPC (src/pxrpcserver.cpp:627-641), which returns cost: {base, fee} (src/services.cpp:1116-1125).

4.3 DEX (decentralised exchange) fees — vCHI sink + redistribution#

Trading items between players in a building via the x command. Sellers pay a fee out of their proceeds (NewOrderOperation::PayToSellerAndFee, src/trading.cpp:291-328):

totalBps = base_dex_fee_bps + building_owner_dex_fee_bps;
total = (cost * totalBps + 9999) / 10000;   // rounded UP, max 1 Cubit/fill effect
owner = (cost * ownerBps) / 10000;          // rounded DOWN
payout = cost - total;                       // seller receives this
  • Base DEX fee: dex_fee_bps = 300 = 3% (params.pb.text:19). This 3% base portion (total - owner) is burnt (removed from the system; only owner and payout are paid out — src/trading.cpp:325-327).
  • Building-owner DEX fee: configurable per building, capped at MAX_DEX_FEE_BPS = 3000 = 30% (src/moveprocessor.cpp:54, 279-293). Paid to the building owner.
  • Ancient buildings must have ownerBps == 0 (src/trading.cpp:304-305).
  • Buyers' vCHI for open bids is held in reserve and reported as balance.reserved in the state (src/gamestatejson.cpp:722-739).

The DEX fee is shown in building config JSON as dexfee (percent, src/gamestatejson.cpp:335-336).

4.4 Character creation — paid in WCHI to the dev, not vCHI#

Character creation does not spend vCHI; it spends real WCHI paid to the dev address. See §5.1.

4.5 Summary of value flow#

            burn real WCHI (burn_addr)
                      │
                      ▼
   ┌──────────  burnsale mint  ──────────┐   (only vCHI faucet, capped at 50e9)
   │                                       │
   ▼                                       ▼
account.balance  ◄── transfers ──►  other accounts
   │     ▲
   │     └── service fee / DEX owner fee (redistribution to building owners)
   │
   ├──► vc burn            → permanent sink
   ├──► service base cost  → permanent sink
   └──► DEX base 3% fee    → permanent sink

5. Dev payments (real WCHI) & character creation#

5.1 Character creation cost: 10 WCHI#

character_cost = 10 (proto/roconfig/params.pb.text:3). The cost in satoshi is character_cost * COIN = 10 * 100,000,000 = 1,000,000,000 sat = 10 WCHI (src/moveprocessor.cpp:144). This WCHI must be paid to the dev address in the move's out section.

The new-character command nc is an array of empty objects, one per character to create (TryCharacterCreation, src/moveprocessor.cpp:110-169):

{"nc": [{}]}            // create 1 character (needs 10 WCHI to dev_addr)
{"nc": [{}, {}, {}]}    // create 3 characters (needs 30 WCHI to dev_addr)

Each entry must be an empty object (extra fields → that entry is skipped, src/moveprocessor.cpp:137-141).

Realistic full move (account init + create 1 character in one move, src/moveprocessor_tests.cpp:226-235), with the 10-WCHI dev payment in out:

[
  {
    "name": "domob",
    "move": {
      "a": {"init": {"faction": "b"}},
      "nc": [{}]
    },
    "out": {"0xb960bECa8623165a3094a629d6A4775857a14d28": "10.0"}
  }
]

Per-creation rules (src/moveprocessor.cpp:143-167):

  1. If paidToDev < 10 WCHI, stop creating (remaining entries also can't afford it). Money is consumed greedily: each successful creation deducts 10 WCHI from the running paidToDev.
  2. If the account is already at character_limit (20), stop.
  3. Otherwise spawn the character (PerformCharacterCreation).

Any leftover paidToDev or burnt at the end of the move is logged as a warning ("user overpaid due to a frontend bug", src/moveprocessor.cpp:1354-1358) — it is not refunded (the WCHI has already left the player's wallet on-chain).

5.2 What a newly created character gets#

SpawnCharacter (src/spawn.cpp:117-160):

  • Faction-appropriate starter vehicle: rv st (Raider) / gv st (Scarab) / bv st (Barracuda).
  • One lf gun ("Light Rail Gun") fitment.
  • Placed inside the faction's spawn building (RED→6, GREEN→4, BLUE→5).
  • Stats derived from vehicle + fitments (DeriveCharacterStats).

5.3 TESTING-ONLY vCHI airdrop on character creation#

PerformCharacterCreation (src/moveprocessor.cpp:1361-1371) currently also airdrops VCHI_AIRDROP = 1,000 satoshi vCHI (= 0.00001 vCHI) to the account on each character creation. This is explicitly flagged as a temporary testing hack to be removed for the full game:

static constexpr Amount VCHI_AIRDROP = 1'000;   // src/moveprocessor.cpp:57
...
/* FIXME: For the full game, remove this. */
acc.AddBalance (VCHI_AIRDROP);

This airdropped vCHI is not tracked in burnsale_balance, so it would not be part of the "carried over to full game" amount.

5.4 Testing-only starting inventory hack#

PXLogic::InitialiseState (src/logic.cpp:177-202) seeds three named test accounts (snailbrain, johnv5, johnagent) with large starting inventories in the Reubo starter city (building 5). The exact quantities per item type are:

  • 100,000,000 units of each ore (items with refines).
  • 1 blueprint original (<item> bpo) of each item with with_blueprint.
  • 2 of each upgrade/fitment (items with fitment).
  • 2 of each vehicle (items with vehicle).
  • 100 of each artefact (items with reveng).

Also marked as a hack not to ship.


6. Fame & kill mechanics#

Fame is per-account reputation (proto/account.proto field 2). It drives PvP matchmaking ranges and is the basis for "levels". Logic in src/fame.cpp, tested in src/fame_tests.cpp.

6.1 Constants#

Constant Value Source
Starting fame (new account) 100 database/account.cpp:31
MAX_FAME 9,999 src/fame.cpp:35
FAME_PER_KILL 100 src/fame.cpp:38
Fame level min(fame / 1000, 8) src/fame.cpp:58-63
Damage list retention damage_list_blocks = 100 params.pb.text:9

Fame levels (FameUpdater::GetLevel): level = fame/1000, capped at 8. So 0-999 = level 0, 1000-1999 = level 1, …, 8000-9999 = level 8 (src/fame_tests.cpp:61-71).

6.2 Who gets fame on a kill#

When a character is killed, FameUpdater::UpdateForKill (src/fame.cpp:65-126) runs. The killers are the distinct account owners of the attackers in the victim's damage list (DamageLists, retained for 100 blocks). Algorithm:

  1. Look up the victim's account fame and its level.
  2. For every distinct killer account:
    • Increment its kills counter by 1 (always, regardless of fame range — src/fame.cpp:100-101, test TrackingKills, fame_tests.cpp:160-176).
    • It is "in range" to receive fame iff |killerLevel - victimLevel| <= 1 (src/fame.cpp:107).
  3. If there are no in-range killers, no fame moves (the victim keeps its fame — test case "out of range", fame_tests.cpp:193).
  4. Otherwise:
    • fameLost = min(victimFame, 100) (can't go below zero from a single kill; src/fame.cpp:116).
    • famePerKiller = fameLost / owners.size()integer division over ALL distinct killer owners (not just in-range ones), so fame can be partly lost to rounding (src/fame.cpp:119).
    • Each in-range killer gains famePerKiller.
    • The victim loses fameLost.

6.3 Edge cases (from fame_tests.cpp)#

  • Self-kills (your own character kills your own character) net zero fame — the deltas cancel (SelfKills, lines 231-264).
  • Multiple characters of one owner among the killers count as one owner for fame purposes but each kill still increments kills once per owner (lines 160-176, 266-281). Example AccountsWithMultipleCharacters: 4 attacker characters across 3 owners → fameLost/3 = 100/3 = 33 each (lines 266-281).
  • Floor at 0 / cap at 9,999 are applied only at the end, after summing all deltas in a block — intermediate values may temporarily exceed the cap and still resolve correctly (TemporarilyBeyondCap, lines 299-325).
  • All in-range decisions use the original (start-of-block) fame, so a chain of kills in one block is consistent (BasedOnOriginalFame, lines 327-345).
  • Killing a building is ignored for fame (only TYPE_CHARACTER targets, src/fame.cpp:128-136).

6.4 Exposure in state JSON#

Per account: kills, fame (only present once the account is initialised) and minted (= burnsale_balance) — src/gamestatejson.cpp:307-327.


7. The GameStart fork (testnet/mainnet timing)#

vCHI/burnsale operations are live before the rest of the game. The rest of gameplay only activates at the GameStart fork (src/forks.cpp, src/moveprocessor.cpp:1314-1322):

Chain GameStart activation height Source
MAIN / Polygon 80,528,098 src/forks.cpp:71
REGTEST / Ganache 0 (always active) src/forks.cpp:73

The chain names are conflated above because ForkHandler translates the live chain to its base before looking up the height (TranslateChain, src/forks.cpp:81-101, called from the constructor src/forks.hpp:70-72): POLYGON → MAIN, GANACHE → REGTEST, MUMBAI → TEST. Note FORK_HEIGHTS only defines GameStart heights for MAIN (80,528,098) and REGTEST (0); a TEST/MUMBAI deployment would CHECK-fail at src/forks.cpp:114-118 unless the override flag is supplied.

Override flag --fork_height_gamestart exists for testing (src/forks.cpp:31, 74; applied in IsActive, src/forks.cpp:111-112). Before GameStart, only vc coin operations are processed (burnsale + transfers); everything else (DEX, account init, characters, buildings, services) is skipped.

The GSP's initial sync block for Polygon is height 88,343,264 (src/logic.cpp:141-144).


8. Prizes (prospecting rewards)#

Prizes are special items awarded probabilistically when a character finishes prospecting a region (FinishProspecting, src/prospecting.cpp:126-179). They are partner-game collectibles, not currency. There are 55 prize tiers defined in proto/roconfig/params.pb.text:54-110 (54 "exported from the sheet" plus the special cash tier on line 54).

8.1 Award mechanics#

For each prize tier in config order (src/prospecting.cpp:155-178):

  1. If all number copies of that tier are already found (tracked globally via ItemCounts), skip it.
  2. Roll ProbabilityRoll(numerator, 100 * probability) where probability is the per-tier denominator (1/N odds) and the numerator is:
    • 100 in normal zones,
    • 55 in a low-prize zone — odds reduced by 45% (to 55% of normal).
  3. On success, the prize item "<name> prize" is added to the character's inventory, the global found-count increments, and no further tiers are rolled this prospect (break).

8.2 Low-prize zone#

Params::IsLowPrizeZone (src/params.cpp:30-49): any position within L1 distance 1,250 of a faction safe-zone centre is a low-prize zone. This discourages farming prizes from safety near spawn.

8.3 Prize catalog (selected)#

number = total copies in existence; probability = 1/N base odds per prospect; lower N = more common. Internal item code is "<name> prize". Display names from the official client.

Internal name (<name>) Display name number probability (1/N)
cash (special: 10 cash prizes) 10 350,000
Temporal Disrupter Temporal Disrupter (Taurion) 200 1,374
Syphon Syphon (Taurion) 200 1,374
Graviton Spectrometer Graviton Spectrometer (Taurion) 200 1,374
Tritanian Drill Tritanian Drill (Taurion) 200 1,374
100 TKT (5,000 TKT family) 250 1,109
TBD Common 125 2,150
Prison Key Card Prison Key Card (Age of Rust) 100 2,655
1% Player Shares 1% Player Shares (Soccer Manager Elite) 100 2,655
Package of 10 Training Tickets + 1 Trinity Boost 100 2,655
Medium Transport Medium Transport (Taurion) 50 5,063
Thermal Implant Thermal Implant (Age of Rust) 50 5,063
200 DIO 200 DIO (Decimated) 50 5,063
Dungeon Key Dungeon Key (Ethernal) 20 11,582
Starter Pack Starter Pack (Nine Chronicles) 20 11,582
U.S.S. Enterprise NCC-1701-D + 3 Founders Supply Crates + Steam key 1 130,289
Silver Tier P1 Pistol Silver Tier P1 Pistol (Dissolution) 1 130,289
3 Spaceships 3 Limited Edition Spaceships (Dissolution) 1 130,289
F1 Legendary Crate F1 Legendary Crate (F1 Deltatime) 1 130,289

(Full list of all 55 tiers in proto/roconfig/params.pb.text:54-110.) The probability value is the odds denominator, so a larger number means rarer. The rarest catalog tiers (probability = 130,289) are 1-of-1; the most common prize (probability = 1,109) is the 250-copy 100 TKT tier. The special cash tier has the largest denominator of all (probability = 350,000, see Open Questions).

8.4 Prize stats RPC#

getprizestats (src/pxrpcserver.cpp:592-601, src/gamestatejson.cpp:692-714) returns per-tier {number, probability, found, available}.


9. Money-supply accounting & state APIs#

9.1 The money_supply table#

Two global counters (MoneySupply::GetValidKeys, database/moneysupply.cpp:92-101), both initialised to 0 (InitialiseDatabase, lines 75-90):

Key Meaning
burnsale Total vCHI ever minted via the burnsale (drives stage pricing).
gifted Total vCHI ever created via god-mode giftcoins (testing/regtest only; CHECK-asserted 0 when god_mode is off, gamestatejson.cpp:653-657).

Increment requires a positive value and a valid key (database/moneysupply.cpp:56-73).

9.2 god-mode (testnet/regtest)#

god_mode = true in config (params.pb.text:31). God-mode admin commands (HandleGodMode, src/moveprocessor.cpp:2346-2364) include giftcoins which directly credits balances and increments the gifted counter (MaybeGodGiftCoins, lines 2249-2276). The official client wraps this as a godGiveCoins(amount) helper. For mainnet this would normally be disabled (see Open Questions).

9.3 RPCs the client consumes#

RPC Returns Source
getmoneysupply {total, entries:{burnsale, gifted?}, burnsale:[{stage,price,total,sold,available}]} pxrpcserver.cpp:582-590, gamestatejson.cpp:643-690
getprizestats per-tier {number,probability,found,available} pxrpcserver.cpp:592-601
getserviceinfo service op preview incl. cost:{base,fee} pxrpcserver.cpp:627-641
getbootstrapdata bootstrap data pxrpcserver.cpp:616-625
full state accounts[] per account {name, minted, balance:{available,reserved,total}, faction?, kills?, fame?} gamestatejson.cpp:305-327, 716-739

The getmoneysupply.burnsale array reports, per stage: stage (1-based), price (WCHI per vCHI, as a double = price_sat / COIN), total (stage size), sold, and available (gamestatejson.cpp:664-687).


10. Full lifecycle of value (worked example)#

  1. Wallet → game: Player burns 1,000,000 WCHI to burn_addr with {"vc":{"m":{}}}. At stage-1 price they receive 10,000,000,000 vCHI, credited to balance and to burnsale_balance (the protected, carry-over amount). Global burnsale counter += 10e9.
  2. Join: Player chooses BLUE with {"a":{"init":{"faction":"b"}}} (permanent). Account becomes initialised; starts with fame 100.
  3. Build a roster: Player pays 10 WCHI to dev_addr per character via {"nc":[{}]} (up to 20 characters). Each gets a starter Barracuda (bv st) + Light Rail Gun (lf gun) inside spawn building 5. (Currently also gets a 1,000-sat vCHI test airdrop.)
  4. Spend vCHI in-game: Repairs (1 vCHI / 10 HP base, burnt), refining, blueprint copies, construction, reverse engineering (base burnt + optional service fee to building owner). Trade items on the DEX (3% base fee burnt + owner fee).
  5. Transfer/gift: Send vCHI to other players with {"vc":{"t":{...}}}; recipients need not play.
  6. PvP: Killing characters moves fame between accounts (±100, level-gated), increments kills.
  7. Win prizes: Prospecting can award limited-supply partner prizes.
  8. Reset boundary: Only burnsale_balance (minted) is documented as carrying over to the full game; airdropped/gifted vCHI may not.

Open questions#

  1. vCHI unit vs. satoshi cap mismatch. MAX_COIN_AMOUNT (the per-move coin cap and stated "total cap on vCHI") is 100,000,000,000 satoshi (src/jsonutils.cpp:34-38), i.e. 1,000 vCHI. But the burnsale sells 50,000,000,000 units and mints them as raw integers added directly to balance (src/moveprocessor.cpp:2440; test expects balance == 10,000,000,000 after a single big mint, moveprocessor_tests.cpp:374). This strongly implies burnsale "amount_sold" units are counted in the same satoshi denomination as balance — so the realised vCHI cap is 50,000,000,000 sat = 500 vCHI total ever minted, which is below the per-move cap of 1,000 vCHI. The comment calling MAX_COIN_AMOUNT "the total cap" appears stale/approximate. Designers should confirm the intended human denomination (is 1 displayed "vCHI" = 1 satoshi-unit, or = 1 COIN?). The official client simply shows the raw vchi number.
  2. Airdrop / gift persistence at the full-game reset. Code comments say burnsale_balance is "carried over to the full game" and that the VCHI_AIRDROP and starter-inventory hacks must be removed before release, but the exact reset/migration policy for non-burnsale balance is not specified in the GSP source.
  3. god_mode on mainnet. Config has god_mode: true (params.pb.text:31) and the dev_addr is described as the production mainnet multisig. Whether god_mode/giftcoins is intended to remain enabled on the production Polygon deployment, or is to be flipped off in a release config, is not determinable from the source alone.
  4. The cash prize tier. prizes: {name:"cash" number:10 probability:350000} sits inside the params{} block (params.pb.text:54) separate from the "exported from the sheet" prizes, and has no display-name mapping in the client. Its in-game effect (does it award vCHI? a placeholder?) is not implemented in the prospecting code beyond adding a "cash prize" item.
  5. Where exactly burnt DEX base fee and service base cost go. They are removed from the payer with no crediting recipient (confirmed sinks in trading.cpp:315-327 and services.cpp:1128-1145), but there is no explicit "burnt vCHI" money-supply counter analogous to burnsale/gifted, so the net circulating supply is not directly queryable via getmoneysupply.