State and Ctx
A module sees the chain only through Ctx.
State
State is one sorted key-value map. Your module's keys are prefixed automatically: when tic-tac-toe calls
ctx.put("game/00ab…", bytes) the chain stores m/tictactoe/game/00ab…. You cannot clobber another
module's keys, and ctx.read_raw lets you read (never write) any key, including balances.
ctx.get(key) -> Option<Vec<u8>> ctx.put(key, bytes) ctx.delete(key)
ctx.scan(prefix, start_after, limit) // sorted, prefix removed from returned keys
ctx.get_json::<T>(key) / ctx.put_json(key, &value) // from CtxExt
Money
Tokens move through the module's escrow account module:<id>:
ctx.escrow_in(player, GOLD, amount)?; // player -> module:tictactoe
ctx.escrow_out(winner, GOLD, amount)?; // module:tictactoe -> winner
A module may also create_token, mint and burn tokens it issued itself. Because a failing apply is
rolled back as a whole, a game can take stakes first and fail later without losing anyone's money.
Facts about the block
ctx.height(), ctx.block_time_ms(), ctx.txid(), ctx.divi_checkpoint() and ctx.sequencer().
ctx.public_random(domain) is SHA256(divi_checkpoint.hash || txid || domain): nobody can choose it ahead
of time.
Events
ctx.emit("tictactoe.moved", json!({...})) adds an event to the transaction's receipt. The WebSocket stream
sends every block with its receipts, so a browser game redraws from events instead of polling.
Block-by-block isolation
Each transaction runs in its own child overlay that is discarded if the action fails; a block runs inside another overlay. That is why a failed action changes nothing except the sender's nonce.