The Tokens page (M6's "Tokens" and M7's "transaction building" together): create a fungible token, send it, and for a managed-supply token grant, revoke and hand the issuer role on. Everything is read back by an index over the ledger chain; nothing is inferred from what this machine sent.
| Layer | File | Does |
|---|---|---|
| Engine | arcade/state.py |
Handlers for 55 (grant), 56 (revoke), 70 (change issuer), 200 (any data: valid, no-op) alongside 0, 4, 50, 54 |
| Index | arcade/ledger.py |
LedgerIndex: follows the ledger chain from its start block, one SQLite file per network, stops loudly on an unknown type, unwinds reorgs; queries for properties, holders, balances, history |
| Building | arcade/tokens.py |
TokenSender.prepare → signed, decoded, not broadcast; broadcast only after the caller has shown it. Payload builders that refuse what the engine would reject |
| Web | arcade/web/app.py, templates/tokens.html, token.html, _token_confirm.html |
The page; every action is prepare → confirm → broadcast the same bytes |
| Watcher | arcade/web/watcher.py |
Syncs the index when the ledger tip moves, 500 blocks a tick |
Rules and their sources are cited in the code; the ones not in M2 notes:
| Rule | Source |
|---|---|
| Grant: issuer only, managed only, cap so that total ever granted ≤ MAX | tx.cpp:2158-2257 |
| Revoke: any holder, from own balance | tx.cpp:2260-2326 |
| Change issuer: issuer only, needs a reference address | tx.cpp:2329-2390 |
| Grant with no reference lands on the issuer | tx.cpp:672-675 |
| Any data (200) is valid and changes nothing | tx.cpp:2735-2747 |
tests/test_state.py — each rule above, and that a grant and a change of
issuer roll back with their block.tests/test_ledger.py — the index against a regtest node: a token appears,
a send shows for both addresses, an unknown type (999) stops it at the
block and with the type in the reason, a reorg is unwound, a chain with no
start block is "disabled" rather than empty.tests/test_tokens.py — create / send / grant / revoke / hand-over built
through the wallet and read back by the index, with the engine's sender
and recipient; a long name goes Class B and reads back; every refusal
happens before anything is spent.tests/test_tokens_web.py — the page against a regtest node: the fee on the
confirm screen is the fee the node's mempool reports for that txid; prepare
does not broadcast; a confirmation for a transaction the server no longer
holds is shown again, not sent; a mainnet address is refused on testnet;
balances after a send are what the page shows for both wallet addresses.tests/test_web.py — every token page renders with no node, every token
POST is refused without the CSRF token, and the chain tag switches the
page and is remembered by a fresh AppState.The confirm screen was rendered in headless Firefox against the testnet node
before the first real transaction. It labelled every Class B data output
"change back to you": the wallet's decoder lists the sender's own key among a
multisig output's addresses, and the label keyed on that. The test that now
pins the labels (test_a_long_name_goes_class_b_and_reads_back) was written
after the screenshot, not before.
Open Tokens and click the red main tag: the page switches to testnet, and the green test tag switches back. Both chains are indexed all the time, so the switch shows a current index. The choice is remembered across restarts.
The first token created this way is Arcade Test, from
nYW2BPLENpu2nGa7WCExvzxD3hQYueULFa, transaction
4b902684ca547b2433d7963c4428bbeee67c45fd74c812e1e3910e0cbc8bb7b5, Class B
(the description pushed it past one OP_RETURN). 10,000 of it went to the
GX10's testnet address in
26904d43d8d45dbc5dda67bf1cc95f8af2561c758f530f8ecf313116c9814da1, Class C.
A second machine running the same version must show the same token, both
holders and both history entries — that is the cross-check.
The first airdrop was examples/airdrop.py through the bot RPC (D-017,
bot-rpc.md): 5 Arcade Test to every holder of #3 other than the sender —
one send, to the GX10, in
585f0bdec7ab055619b1b72977b785913ccc504936c6d73a91c084f768ffdffa, fee
0.00257 as the node's mempool reported it. GX10 should show 10,005.