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 uninitialised — faction == 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):
initmust be an object with exactly one fieldfaction(init.size() != 1is rejected, line 2403).factionmust 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:
- Ensure account row exists (auto-create if needed).
- Coin operations (
vc: mint/burn/transfer) — even for uninitialised accounts. Done first so explicit transfers get priority over implicit costs. - Gate: stop if the
GameStartfork is not active (only Cubit ops allowed before game start — see §7). - DEX operations (
x) — independent of initialisation. - Account update (
a) — e.g. faction init. - Gate: stop if account still uninitialised.
- Character updates (
c) — done before creation so a newly created character cannot be updated in the same move. - Character creation (
nc). - Building updates (
b). - 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_addr→paidToDev(used for character creation, §5.1). - WCHI sent to
burn_addr→burnt(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:
- Skip stages already fully sold (tracked by the
burnsalemoney-supply counter passed in asalreadySold). - In the current stage,
affordable = burntChi / price_sat(integer division, rounds down), capped by what's left in the stage. - Deduct
sold * price_satfromburntChi, addsoldto the result. - If the stage is exhausted, continue to the next; otherwise stop.
Key behaviours, all from src/burnsale_tests.cpp:
- Rounding down: burning
COIN + 9,999satoshi at stage-1 price (10,000) mints10,000vCHI and uses exactlyCOIN(the extra 9,999 sat is left over, testRoundingline 71). - Below minimum: burning
COIN/1000 - 1when only stage 4 remains mints0vCHI and uses0(line 72). - Crossing stage boundaries in one burn is handled (test
AcrossStageBoundaryline 64-67). - Sold out: once 50,000,000,000 vCHI have been minted, further burns mint
0and consume0WCHI (testSoldOutline 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.protofield 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
burnsalemoney-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 thefeeis 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).
- Fee is 0 if the operation is not in a building, in an ancient
building, or in the operator's own building
(
- 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; onlyownerandpayoutare 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.reservedin 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):
- 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 runningpaidToDev. - If the account is already at
character_limit(20), stop. - 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 withwith_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:
- Look up the victim's account fame and its level.
- For every distinct killer account:
- Increment its
killscounter by 1 (always, regardless of fame range —src/fame.cpp:100-101, testTrackingKills,fame_tests.cpp:160-176). - It is "in range" to receive fame iff
|killerLevel - victimLevel| <= 1(src/fame.cpp:107).
- Increment its
- If there are no in-range killers, no fame moves (the victim keeps its fame —
test case "out of range",
fame_tests.cpp:193). - 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
killsonce per owner (lines 160-176, 266-281). ExampleAccountsWithMultipleCharacters: 4 attacker characters across 3 owners →fameLost/3 = 100/3 = 33each (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_CHARACTERtargets,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):
- If all
numbercopies of that tier are already found (tracked globally viaItemCounts), skip it. - Roll
ProbabilityRoll(numerator, 100 * probability)whereprobabilityis the per-tier denominator (1/N odds) and the numerator is:100in normal zones,55in a low-prize zone — odds reduced by 45% (to 55% of normal).
- 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)#
- Wallet → game: Player burns 1,000,000 WCHI to
burn_addrwith{"vc":{"m":{}}}. At stage-1 price they receive 10,000,000,000 vCHI, credited tobalanceand toburnsale_balance(the protected, carry-over amount). Globalburnsalecounter += 10e9. - Join: Player chooses BLUE with
{"a":{"init":{"faction":"b"}}}(permanent). Account becomes initialised; starts with fame 100. - Build a roster: Player pays 10 WCHI to
dev_addrper 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.) - 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).
- Transfer/gift: Send vCHI to other players with
{"vc":{"t":{...}}}; recipients need not play. - PvP: Killing characters moves fame between accounts (±100, level-gated),
increments
kills. - Win prizes: Prospecting can award limited-supply partner prizes.
- Reset boundary: Only
burnsale_balance(minted) is documented as carrying over to the full game; airdropped/gifted vCHI may not.
Open questions#
- vCHI unit vs. satoshi cap mismatch.
MAX_COIN_AMOUNT(the per-move coin cap and stated "total cap on vCHI") is100,000,000,000satoshi (src/jsonutils.cpp:34-38), i.e. 1,000 vCHI. But the burnsale sells50,000,000,000units and mints them as raw integers added directly tobalance(src/moveprocessor.cpp:2440; test expectsbalance == 10,000,000,000after a single big mint,moveprocessor_tests.cpp:374). This strongly implies burnsale "amount_sold" units are counted in the same satoshi denomination asbalance— 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 callingMAX_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 rawvchinumber. - Airdrop / gift persistence at the full-game reset. Code comments say
burnsale_balanceis "carried over to the full game" and that theVCHI_AIRDROPand starter-inventory hacks must be removed before release, but the exact reset/migration policy for non-burnsalebalanceis not specified in the GSP source. - god_mode on mainnet. Config has
god_mode: true(params.pb.text:31) and thedev_addris described as the production mainnet multisig. Whethergod_mode/giftcoinsis 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. - The
cashprize tier.prizes: {name:"cash" number:10 probability:350000}sits inside theparams{}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. - 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-327andservices.cpp:1128-1145), but there is no explicit "burnt vCHI" money-supply counter analogous toburnsale/gifted, so the net circulating supply is not directly queryable viagetmoneysupply.