Divi Games

Courses / Build a game

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.