Flappy Robin docs
What it is
Flappy Robin is a one-button flying game. Its engine is a WebAssembly module stored as contract bytecode on Robinhood Chain (chain 4663). This site only ships the host: it reads the engine from the chain, checks every byte, then runs it.
The candles you fly through are real blocks, read live while you play.
Play
| Flap | Space, Arrow Up, W, Enter, click or tap |
|---|---|
| Pause | Escape or P. Leaving the tab pauses too. |
| Sound | M, or the speaker button |
| Best score | Kept in this browser only |
What lives on chain
One ROM contract and its chunks. The ROM is born sealed in a single transaction: its constructor stores every chunk, computes the Merkle root and body hash on chain, and it has no owner. Nothing can be added or replaced later.
Not published yet
| ROM contract | |
|---|---|
| Root | |
| Body hash | |
| Raw hash | |
| Chunks | |
| Bytes on chain | |
| Bundle bytes |
- Chunks are data contracts whose code is
0x00followed by up to 24,575 bytes. The ROM contract creates them itself. - The body is a gzipped CROM v1 bundle with two files:
flappy-robin.wasmandREADME.txt. - The read surface matches ChainRom, so ChainRom-style readers can load it too.
How your browser checks it
Every step must pass before anything runs. The expected root, body hash and raw hash are pinned in this site's code, so an RPC can fail to deliver the game but cannot swap it.
eth_chainIdmust be 4663.- The header comes in one batch.
sealed_()must be true, androot(),bodyHash()andrawHash()must equal the pins. pointers()lists the chunks, then oneeth_getCodebatch reads them. Each must start with0x00.- The Merkle root is rebuilt from the chunks and must equal the pinned root.
- The chunks are joined. Their keccak256 must equal the body hash, their length
bodyBytes(). - The body is inflated. Its keccak256 must equal the raw hash, its length
rawBytes(). - The first
.wasmin the bundle is compiled. It may import onlypresent,set_palette,poll_eventandnow_ms.
No JavaScript is ever loaded from chain. The engine runs in the WebAssembly sandbox and talks to the page only through those four imports and its own exports.
Merkle rule: leaf = keccak256(chunk), parent = keccak256(left ‖ right), an odd last node moves up unpaired. With one chunk, the root equals the body hash.
"Check a chunk on chain", behind the "Loaded from Robinhood Chain" button under the game, asks the contract's own verify(i, proof). That answer still travels through your RPC, so the pins above are what actually protect you.
Check it yourself
With Foundry's cast:
ROM=0x…
RPC=https://rpc.mainnet.chain.robinhood.com
cast call $ROM "sealed_()(bool)" --rpc-url $RPC
cast call $ROM "root()(bytes32)" --rpc-url $RPC
cast call $ROM "pointers()(address[])" --rpc-url $RPC
With one chunk, the root is the hash of the chunk's code minus its first byte:
P=$(cast call $ROM "chunks(uint256)(address)" 0 --rpc-url $RPC)
cast keccak $(cast code $P --rpc-url $RPC | sed 's/^0x00/0x/')
With plain curl (0xebf0c717 is root()):
curl -s $RPC -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":"'$ROM'","data":"0xebf0c717"},"latest"]}'
The full check, from a checkout of the source (Node 18 or newer):
node tools/verify-rom.mjs --rpc $RPC --rom $ROM
Live data
| Source | What | How often |
|---|---|---|
rpc.mainnet.chain.robinhood.com | Latest block: number, hash, gas used, transactions, base fee | 500 ms |
rpc.mainnet.chain.robinhood.com | Pons launches, graduations, and curve buys and sells of at least 0.1 in the quote token | 3 s |
api.coinbase.com | ETH spot price in USD | 60 s |
- Each candle takes the newest unused block. Its hash sets the gap, its gas used against the previous candle sets the colour. Hitboxes depend only on the gap.
- With no block newer than 3 s, the next candle is grey, labelled
----, and placed by a local random source. - The sky follows New York Stock Exchange hours, read from block timestamps.
- Launches count only from the Pons factory
0x7eD598BcEf8bd9Edd8C97A195C6d13f40801EC7e. Trades count only from curves whosefactory()is that address. - Token symbols are chosen by strangers. They are shown as plain text, reduced to letters, digits and emoji, and cut to 10 characters.
- Requests run one at a time, back off up to 30 s on errors, and stop while the tab is hidden. After two failures in a row they move to
rpc.ordofi.network. This site sets no cookies and runs no analytics.
Contract reference
FlappyRom, Solidity 0.8.30. Every function is read-only.
| Selector | Function | Returns |
|---|---|---|
06fdde03 | name() | string, "Flappy Robin" |
26d111f5 | note() | string |
8da5cb5b | owner() | address, always zero |
ebf0c717 | root() | bytes32, computed in the constructor |
ce57b4a0 | bodyHash() | bytes32, keccak256 of all chunks, computed |
e2332df3 | rawHash() | bytes32, keccak256 of the bundle, set by the publisher |
1ac27abc | bodyBytes() | uint32, computed |
1cb56a14 | rawBytes() | uint32, set by the publisher |
bf5390b9 | sealed_() | bool, always true |
f91f0937 | chunkCount() | uint256 |
5a2bc22d | chunks(uint256 i) | address of chunk i |
ed2e5a97 | read(uint256 i) | bytes, chunk i without its 0x00 |
a79a2a28 | verify(uint256 i, bytes32[] proof) | bool, chunk i is under the root |
71267571 | pointers() | address[], every chunk |
8c72c54e | publisher() | address that deployed the ROM |
Events: Wrote(uint256 indexed index, address pointer, uint256 length) once per chunk, then Sealed(bytes32 root, uint256 chunkCount, uint32 rawBytes) once.
Engine interface
The engine imports exactly ChainRom's four screen functions from module env, so it also boots on ChainRom's reader, keyboard only and silent.
| Import | Signature | Meaning |
|---|---|---|
present | (ptr, w, h, format, stride) → i32 | Show a 144×256 indexed frame |
set_palette | (ptr, count) → i32 | RGB triples, 8-bit |
poll_event | () → i32 | (type << 24) | code, 0 when empty |
now_ms | () → f64 | Milliseconds, monotonic |
Event types: 1 key down, 2 key up, 3 press (pointer or touch), 4 pause. Key codes: Arrow Up 1, Arrow Down 2, Arrow Left 3, Arrow Right 4, Control 5, Space 6, Enter 7, Escape 8, Alt 9, Shift 10, Tab 11. This page also sends W as 1 and P as 8.
| Export | Signature | Meaning |
|---|---|---|
memory, frame | frame() | Once per animation frame. Fixed 120 Hz steps inside |
feed_hash_ptr, feed_block | (number, gas_used, tx_count, timestamp) | Push a block; its 32-byte hash goes to the pointer first |
msg_ptr, push_msg | (len, tone) | Ticker message, up to 48 ASCII bytes |
set_seed | (hi, lo) | Seed for the offline candle generator |
set_best, set_flags | (n), (bits) | Restore best score; bit 0 reduced motion, bit 1 muted |
score, best, state | () → i32 | State 0 ready, 1 playing, 2 paused, 3 dying, 4 game over |
ui_take | () → i32 | Bits since last call: start, scored, died, new best, paused, resumed, ready |
sfx_take, sfx_rate, sfx_count | () → i32 | Sound bits since last call, sample rate 22,050, number of sounds |
sfx_ptr, sfx_len | (id) → i32 | Sounds synthesized by the engine: 16-bit mono PCM, length in samples |
Cost
Not published yet
| Publish transaction | |
|---|---|
| Gas used | |
| Gas price | |
| Fee | |
| Published |
Contract code costs L2 gas only on Robinhood Chain today, about 218 gas per stored byte.
Token
Only trust the address shown on this site.
Disclaimer
Fan-made game. Not affiliated with, endorsed by, or officially connected with Robinhood Markets, Inc.
Chain activity shown in the game is raw public data, not advice. The game and this site are provided as is.