White paper

ARCADE Technical White Paper

Adaptive Reciprocating Chain-linked Auxiliary Distributed Ecosystem

By the ARCADE team · updated 2 November 2026 · Print or save as PDF

Abstract

ARCADE is a mesh of self-hosted servers ("arcades") that serve browser apps, games, a social layer and an exchange over eight proof-of-work and proof-of-stake chains. No arcade holds a user's keys unless the user hands one over for a capped order, and no single arcade is required for anything to keep working. Each arcade is one Python program run beside unmodified coin nodes. It keeps no account a user depends on: identity, names, messages, posts, tokens and inscriptions are all written to public chains, and every arcade reads them back the same way.

This paper describes how ARCADE is built and why: the node's process model, how chains are run locally or borrowed from peers, how identity is anchored on Pepecoin's test network, how arcades find and talk to each other, how inscriptions are indexed, and how each exchange method settles and who, if anyone, holds funds along the way. It closes with the security model, what an operator controls, and the open problems as of the date above.

The software ships as DogecoinArcade, PepecoinArcade and LitecoinArcade. These are faces of one codebase and speak one protocol; this paper calls the whole ARCADE. Facts here are taken from the code on its main branch at commit 110e55c8 and from arcades running today.

Background and motivation

Most blockchain apps put a company's server between the user and the chain. That server holds the account, often the keys, the order book and the app itself; when it goes away, so does the user's access. ARCADE removes that server by making every piece of state either on chain or reproducible from the chain.

Three problems shaped the design:

  • Custody by default. Hosted wallets and exchanges hold keys for convenience. Thefts and shutdowns on small-chain marketplaces show what that costs. ARCADE seals keys in the browser and settles trades by transactions both parties sign.
  • Small chains lack infrastructure. Pepecoin, Bellscoin and Luckycoin have few explorers, indexers or wallets. A full node plus an inscription index is too heavy for most people. ARCADE lets one operator run a chain and lend it to many others.
  • Identity tied to a site. A username on a hosted service is a row in its database. ARCADE writes names to a chain, so any arcade can resolve them and none can take them away.

The result is an arcade anyone can run on a spare computer, that a player can leave at any time with everything they own, and that keeps working when any single arcade, including the first one, is gone.

Design principles

Eight rules recur through the codebase. When a feature breaks one, the code marks the exception, and so does this paper.

  1. Keys stay with the user. A wallet is generated and sealed in the browser. The arcade stores only the sealed copy and signs nothing on a user's behalf unless the user hands it a bounded authority: a pre-signed lot, or a key for one capped order.
  2. The chain is the database. Names, messages, posts, tokens and inscriptions are transactions. An arcade's own databases are caches and indexes that can be rebuilt from the chains.
  3. Two-party settlement. A trade is one transaction both sides sign, or a pair of hash-locked contracts that either both pay or both refund. Anything custodial is opt-in and labelled, and most of it is capped.
  4. Borrow, then check. An arcade without a chain borrows it from a peer, and checks what it can (proof of work, headers, its own signatures) instead of taking answers on trust. Where it cannot fully check, the paper says so.
  5. Cheap identity on a test chain. Names, messages and the mesh's own records ride Pepecoin's test network, so social actions cost a fraction of a coin with no market value.
  6. Unmodified nodes, no root. Coin nodes run as published by their projects, with signatures checked at install. The arcade runs as an ordinary user service.
  7. Operators are sovereign. Each operator chooses chains, fees, limits, models and look. Some rules that protect users are fixed in code: two-party token and NFT swaps always wait a confirmation, trades never ask for a SIGHASH_NONE signature, and no lock is built without a time-lock check.
  8. Any arcade can start the next. Each arcade serves its own program and inscription index to new arcades, so the network does not depend on one website.

System architecture

An arcade is one Python program that runs as an ordinary user service beside the coin nodes. A small supervisor holds the listening socket, a single worker does all the work, and a reload swaps workers without dropping a connection. The stack is FastAPI 0.115 on uvicorn 0.34 with Jinja2 templates; the core needs only two libraries, requests and PyNaCl.

One worker does the work; the supervisor only holds the socketpasses the socketBrowsersigns, then sendsSupervisorholds port 8420, swaps workers on HUPWorker, one at a timeWeb appFastAPI + uvicornabout 560 routesBlock watcherblocks every 5 smempool 1.5 sOrd indexinscriptionsone per chainMeshTCP port 8421own event loop~/.dogecoinarcadeSQLite per chainsettings.jsonCoin nodeslocal RPC,or borrowedOther arcadeslend chainsshare orders
An arcade's processes and what they talk to

Swipe sideways to see the whole diagram.

Process model

  • Supervisor (arcade/web/supervisor.py). Started as arcade-web, it binds the socket itself with a backlog of 2,048, writes web-supervisor.pid, and starts a worker that inherits the socket's file descriptor.
  • Reload on SIGHUP. If the supervisor's own source changed, it re-executes itself first. Otherwise it starts a new worker and waits for it to report "warm" (up to 600 s). Then it sends SIGTERM to the old worker, which drains requests for up to 5 s and is killed after 60 s. Connections that arrive meanwhile wait in the socket backlog.
  • One worker at a time. A new worker takes the file lock web.lock before it starts any background work, so two workers never write the same databases.
  • Crash recovery. A crashed worker is restarted after 1 s, doubling to 60 s, with the delay reset after 300 s of health. systemd restarts the supervisor itself (Restart=always, NoNewPrivileges, PrivateTmp).
  • Who sends the HUP. The updater after installing a release, Admin's restart button, and supervisor.reload().

Background work in the worker

After it takes the lock, the worker starts:

Component What it does Cadence
Block watcher 23 phases per pass: confirmations, message and ledger scans, release notices, the shop, cross-chain swaps, the trader, faucets, auto-update Blocks every 5 s, up to 500 per pass; mempool every 1.5 s
Ord index runner Walks or helps scan inscriptions for each chain set to walk or help Continuous
Mesh Peer links, rooms, chain lending, order sharing, on its own event loop Pings every 15 s
Sites search Full-text index of on-chain sites, on chains set to host Continuous, per chain
Standing-order filler Fills pre-signed orders that meet their price On each matching order
LoRa gateway Bridges Meshtastic handhelds When enabled
Stall watch Logs every thread's stack when a request runs over 45 s At most every 120 s

Web layer

  • Size. About 560 routes in the main app, plus the model API, mesh, real-time rooms and LoRa. The largest areas are /account (191 routes in the main app), /admin (59) and the app runtime /r (55).
  • Binding. The default is 127.0.0.1:8420. A request counts as public if its Host is a configured public host, it carries an edge header (cf-ray, x-forwarded-for and similar), its TCP peer isn't loopback, or its Host isn't a local name. Public requests are served from an allowlist of pages and a short list of POSTs.
  • Machine interfaces. /rpc/{chain} is an Omni-style JSON-RPC for bots, authenticated by a cookie file (mode 0600). /v1/chat/completions, /v1/images and /v1/models follow the common chat API shape. /realtime serves game rooms over server-sent events, and /mesh is the WebSocket door for peers.

Storage

Everything lives in ~/.dogecoinarcade (mode 0700). Settings are one JSON file, written atomically. The rest is SQLite, split by purpose so each can be rebuilt or moved alone.

Files Holds
settings.json Every operator setting
<chain>-ledger.sqlite, <chain>-inscriptions.sqlite Arcade tokens and NFTs read from each chain
ord-<chain>.sqlite with .values and .seen The inscription index, coin values seen by the walk, and its coin filter
.snap, .part Index snapshots received from peers
sites-<chain>.sqlite Full-text search of on-chain sites
test.sqlite, test.key Messages and the messaging key
accounts.sqlite, admin.sqlite Seats, sessions, sealed wallets, admin sessions
swaps, swap-trades, trades, trade-orders, xchain, escrows (each a .sqlite file) Exchange state
mesh.key, mesh-peers.json, trade.key, referee.key The node's own keys and peer list

Chain data stays in each coin node's own folder; the arcade never modifies it.

Chains and backends

ARCADE works with six coins on eight networks today, and more can be added by pull request. Pepecoin's test network is the only one an arcade must run itself; every other chain can run locally, be borrowed from a peer, or be switched off (Pepecoin main can be borrowed but not switched off).

Supported networks

Network Id Default RPC port Activation height Swaps Escrow
Pepecoin test test 44873 1,496,133 Test only Yes
Pepecoin main main 33873 1,208,000 Yes Yes
Dogecoin doge 22555 6,415,000 Yes Yes
Litecoin ltc 9332 3,197,300 Yes Yes
Litecoin test ltctest 19332 4,926,300 Test only Yes
DigiByte dgb 14022 24,450,000 Yes Yes
Luckycoin lky 9918 1,153,000 Allowed by code; not announced Yes
Bellscoin bel 19918 1,289,000 No No

The activation height is the block where the arcade's own records begin on that chain; the ord index walks from the chain's first inscription instead. A local regtest network exists for development. Messaging is restricted in code to Pepecoin test and regtest.

Why Bellscoin can't swap

The swap and escrow scripts need features some chains lack, and the code refuses rather than build an unsafe script. Any chain added later is checked the same way.

  • Bellscoin uses P2SH version byte 30, the same as a Dogecoin "D" address, so a script address can't be told apart safely.

Three ways to reach a chain

Each chain has a setting core:<chain> of here, borrow or off, and a setting share:<chain> that defaults to on. With no local node found, the default is to borrow.

  1. Local node. The arcade reads the node's cookie, or falls back to the RPC user and password in its conf file. It checks the chain name, and the genesis hash where one is configured, before using it. Pepecoin has no genesis hash set yet, so a Dogecoin node on a Pepecoin port would pass the name check.
  2. Borrowed node. RPC calls travel over the mesh as cr frames to an arcade that shares the chain. A sharer answers a fixed list of read-only calls, sendrawtransaction, two arcade scan calls (arcade_utxoset, arcade_coinscan), and on test chains getblocktemplate and submitblock. Wallet calls are never served.
  3. Catching up. A local node that is still syncing reads from a sharer until it reaches the tip.

Checking what a peer says

A borrowing arcade checks the blocks it's given. Raw blocks are re-hashed, their merkle roots recomputed, and their proof of work checked against the chain's algorithm (scrypt with N = 1,024, or SHA-256d). DigiByte has no work check yet, because it mixes five algorithms. On that chain a borrowed block rests on the sharer's word. On every chain, answers about which block sits at a height, unspent coins and the mempool also rest on the sharer's word.

Limits on lending

Limit Value
Bandwidth per borrower 512 KB/s
Calls running at once across all borrowers (4 per borrower) 2
Calls per batch 500
Node-to-node block relay, optional 256 KB/s per link, up to 4 peers per chain

An arcade that lends a chain receives 20% of the operator fee on swaps and inscriptions its borrowers make on that chain, at least the dust line. Changes to share: apply within a minute; changes to core: apply at the next start.

Identity and accounts

An ARCADE account is a 12-word seed that never leaves the browser unsealed. Every key a user needs is derived from it, and the arcade stores only an encrypted copy, public keys and hashed session tokens.

Key derivation

The browser draws 128 bits from crypto.getRandomValues and encodes them as a BIP39 phrase. The seed is PBKDF2-HMAC-SHA512 over the phrase (2,048 rounds, salt "mnemonic"). Coin keys follow hardened BIP32 paths. The login and messaging keys use the same HMAC-SHA512 chain under purpose 24946', but aren't standard BIP32 keys. The browser code and arcade/seed.py match byte for byte.

Key Path Curve Used for
Login m/24946'/1'/0' Ed25519 Signing in to any arcade
Messaging m/24946'/0'/0' X25519 Encrypted messages
Coins, main chains m/44'/3'/0'/0/i secp256k1 Spending real coins
Coins, test chains m/44'/1'/0'/0/i secp256k1 Pepecoin test, Litecoin test
Swap orders m/44'/1'/7'/0/i secp256k1 One key per delegated order

Purpose 24946 is 0x6172, "ar". Litecoin test also derives BIP49 and BIP84 addresses.

Sign-up and the sealed wallet

  1. The user picks a name and a password of at least 12 characters. The browser refuses passwords on public leak lists, using the k-anonymity range API: only the first 5 hex digits of the password's SHA-1 leave the device. If the check can't reach the list, sign-up goes ahead.
  2. The browser seals the phrase with AES-256-GCM under a key from PBKDF2-HMAC-SHA512 (600,000 iterations, 16-byte salt, 12-byte nonce). The parameters travel with the blob so they can be raised later; the code itself notes Argon2id would be stronger.
  3. The browser posts the name, public keys, addresses per chain and the sealed blob to /signup. The arcade refuses a name the chain already gives to another address, a name another arcade holds under a different key (when the chain names no owner, it asks peers for up to 4 s and goes ahead if none answers), or a key that already has an account.
  4. The arcade takes a seat and stores the blob in its vault table: vault(tag, pubkey, address, blob, made, claimed). The account table holds no password and no name.

The password never reaches the arcade, so the 12-character rule is enforced in the browser. Only the operator's remote admin password is checked server-side (scrypt, N = 2^15, r = 8, p = 1).

Signing in

  1. GET /auth/challenge returns a nonce, good once for 120 s.
  2. The browser signs DogecoinArcade login\n{origin}\n{nonce} with the login key. The arcade checks that the origin is one of its own names, which stops a lookalike site from replaying the signature elsewhere.
  3. The arcade issues an arcade_session cookie (HttpOnly, SameSite=Lax), stored only as a SHA-256 hash and good for 14 days, renewed by use.

Sign-in is limited to 20 attempts per 5 minutes per IP and per key.

Moving between arcades

If an arcade has no vault entry for a name, it asks up to 16 known arcades over HTTPS for /signin/<name>?here=1 (the flag stops the request being passed on). If the chain names an owner for that @name, only a blob for that address is accepted. The browser warns that the copy came from elsewhere, checks the public key, and files the blob locally. Imported-wallet records follow over the mesh, accepted only when signed by the same login key and newer than the copy held.

Backup and recovery

The Backup page saves the sealed blob as a file; the arcade keeps no extra copy. /restore is the only page that asks for the 12 words. It signs in with the derived login key, reseals under a new password, uploads only the new blob, and ends the account's other sessions.

The real-coin gate

The browser, not the arcade, decides which networks hold real coins: main, doge, ltc, bel, lky and dgb, plus any name it doesn't recognise. Every real-coin signature shows a review card, except within a capped coin allowance that a batch inscription grants for its own tab, for up to 180 minutes. Test chains sign with the coin-type-1 key, so an arcade that lies about a chain's network gets a signature from a key holding no real coins.

@names

Names live on Pepecoin test and are read the same way by every arcade.

  • Format. 2 to 24 characters of a-z, 0-9 and _, with 20 names reserved (admin, arcade, support and others).
  • On chain. INSC | version 1 | kind | length | name, with kind 3 for a claim and 4 for a transfer, carried in one zero-value OP_RETURN output (bare multisig as a fallback).
  • Rules. The first claim in chain order wins. Each address holds at most one name and each name one address. A new claim frees the old name. A transfer must come from the holder and is refused if the recipient already has a name.
  • Cost. One transaction at 0.01 PEP test per kB; at most 5 claims an hour per account.

The operator and the node

An arcade has two identities. The node has its own keys (mesh.key for the mesh, trade.key, referee.key) and an address on each chain. The operator is one ordinary account, named only from the machine itself. The arcade's displayed @name is the operator account's name if one is set, otherwise the name held by the node's own address.

The mesh

Arcades link over an encrypted, authenticated peer-to-peer protocol of their own, not HTTP. A node's identity is the Ed25519 key it proves in the handshake. Messages between arcades are signed, so a forwarding peer can drop or delay one but can't forge it.

Transport and handshake

The mesh runs on raw TCP, port 8421 by default. Where no public address can be proved, an arcade opens a Cloudflare quick tunnel and speaks the same protocol over a WebSocket at /mesh, which carries only ciphertext. Nodes behind NAT on IPv6 meet with a simultaneous open every 15 s, and STUN runs on UDP 3478.

  1. HELLO. Each side sends ARCM | version 2 | network name | X25519 ephemeral key. The network name is arcade-<ledger>, so arcades on different ledgers don't link.
  2. Keys. Each direction gets its own key from BLAKE2b over the shared secret and the transcript, personalised arcm-dialer or arcm-listener.
  3. Frames. Every frame is sealed with XChaCha20-Poly1305 under a counter nonce. A frame is a 4-byte length and a body of at most 64 KB.
  4. AUTH. Each side signs arcade-mesh-auth-v1, its role and the transcript with its long-term mesh key. That key, kept in mesh.key, is the node's id.
  • On-chain announcements. An arcade writes arcade-mesh <ip> <port> <key> (or arcade-mesh-ws wss://…/mesh <key>) to Pepecoin test every 24 h. Only global IP addresses are accepted, never hostnames, and announcements older than 14 days are ignored.
  • Its own address. The public IP comes from the local node's getpeerinfo, and needs at least 2 peers to agree.
  • Other sources. --mesh-peer on the command line, and peer exchange. Peers are remembered for 30 days.
  • Link budget. 6 outbound links and up to 32 inbound. Every 5 minutes the node tries a new peer, and swaps out its slowest outbound link if the newcomer's round trip is at least 20% faster. Links ping every 15 s and are dropped after 45 s of silence.

Rooms

Rooms are publish-subscribe channels that flood across the mesh. A subscription carries 8-byte BLAKE2b fingerprints of room names and is signed by the node.

Limit Value
Payload per message 512 bytes
Rate per sender 5 a second, bursts of 10
Hops 16
Clock skew allowed 60 s

Players join a room through their arcade with a certificate signed by their coin key: arcade-realtime\n{network}\n{node}\n{address}\n{expires}, good for a day and checked by the arcade's own node with verifymessage. Anyone else is a guest.

What travels between arcades

Frame or room Carries Trust rule
cr Borrowed RPC calls and answers, block and transaction relay Blocks are re-checked, other answers rest on the sharer; a sharer caught lying is avoided
arcade-book room The shared swap order book: changes every 1.5 s, the full book every 30 s A node may speak only for its own orders
ix Index fingerprints, bisected to find the first block where two indexes differ At most 120 questions a minute
dx Inscription index work, leased out in chunks, plus snapshots Two arcades must agree before a chunk counts
kc Known collections, by fingerprint Agreement, not authority
iw Imported-wallet records Signed by the account's login key; newer wins
wl Welcome asks, payments and closings Signed by each arcade's mesh key
fr Handing a first @name to a new operator Unsigned offer and accept, trusted through the authenticated link
op Operator name, version, hosts, sites, disk Shown only; never used for payment or permission

Messages between people

Private messages are transactions on Pepecoin test, not mesh traffic, so they survive any arcade going offline.

  • Contact keys. A user publishes an X25519 key in a key announcement, with optional fields such as @name, picture, bio and link. A contact code reads arcade:<network>:<base58 key>:<checksum>.
  • Envelope. crypto_box_seal(recipient, sender_pk ‖ crypto_box(sender_sk, recipient_pk, header ‖ msg)), 126 bytes of overhead. The recipient can authenticate the sender, and the sender can later deny it. The envelope hides the sender's key, but the message transaction shows the sending wallet and pays the recipient, so who messaged whom is public.
  • Delivery. A message transaction pays the recipient 0.01 PEP test, so their wallet sees it at once. Web Push sends an empty wake-up, and the page then asks its arcade what's new.

Welcoming a new arcade

A new arcade needs a little Pepecoin test before it can claim a name or announce itself. The welcome protocol arranges that in the open.

  1. Ask. Once linked, an arcade whose wallet holds less than 1 PEP test gossips a signed ask record. Asks are rate-limited to 30 an hour per link.
  2. Send. Any operator can answer by sending 1 PEP test and gossiping a signed sent record with the transaction id.
  3. Close. The new arcade checks each named transaction with its own node. The first block that pays it closes the ask, listing every payment in that block. A closed ask never reopens.
  4. Score. Each arcade whose payment is in that block gets a point on the Welcomers board. If the coins came another way, such as mining, nobody scores.

The alternative is to mine a test block. The miner builds its own coinbase and, when it borrows the chain, pays the lending arcade a tenth of the reward in the same coinbase. Mining refuses real-coin and merge-mined chains.

Multiplayer

There are no game servers. A room name inside a game is the same room on every arcade, and a player's page picks the fastest arcade and re-checks every 10 minutes.

  • Seats. The operator joins as the node, players with their day certificate, everyone else as a guest. A dropped stream keeps its seat for 30 s.
  • Fast lane. Games can open WebRTC links: a full mesh of up to 20 players, or a star of up to 60 around a host, at up to 16 KB a message. A pair that can't connect directly falls back to the room, or to the operator's TURN relay for signed-in players.
  • Services. A program on the operator's machine can join a room as the node, to keep score, hold shared game state, or referee.

Games, inscriptions and the ord index

Everything a player sees, from a game to a collection, is an inscription read from a chain. ARCADE writes two inscription formats and reads a third, indexes them per chain, and serves inscribed web content inside a strict sandbox.

Three formats, two written

Format Chains How it's carried Owned by
ARCADE's own Pepecoin main and test Omni-style type-200 data with magic INSC, in bare-multisig outputs, about 7,650 bytes per transaction An address
Doginals-style ord envelope Dogecoin, Pepecoin main and test, Litecoin, Bellscoin, Luckycoin, DigiByte 240-byte pushes, up to 1,500 bytes of pushes per reveal transaction A coin (satoshi)
Taproot ord Litecoin, Litecoin test, Bellscoin Standard ordinals witness envelope; read, not written A coin (satoshi)

Ord inscriptions made through an arcade use postage of 0.001 coin, a maximum of 400 kB, and at most 20 unconfirmed at a time.

The ord index

Ord-style inscriptions belong to whichever coin carries them, so finding who owns what means walking every block from the chain's first inscription and tracking coins through every spend. That is the heaviest work an arcade does.

  • Modes per chain: walk (keep the full index), help (scan chunks for others), read (watch only: read another arcade's index), or off. The default is read on every chain.
  • Shared work. The chain is cut into chunks of 100 blocks. Arcades lease chunks to each other for 600 s, and a chunk counts once 2 arcades agree on its hash.
  • Reorgs. The index keeps 300 blocks of undo data and treats blocks as settled after 12 confirmations (2 for the walk itself).
  • Coin filter. A Bloom filter of possibly inscribed outpoints (7 hashes, about 12 bits per coin, about 0.3% false positives) lets the walk skip clean coins fast. It's saved beside the index as .seen.
  • Values. Coin values the walk has read are kept in .values, keyed by the first 12 bytes of the txid plus the output index. Only values read from bytes that hash to that txid are stored.
  • Checking each other. Arcades compare index fingerprints over the mesh and bisect to the first block where they differ.
  • Snapshots. A new arcade can download a peer's index as a snapshot rather than walk from scratch.

Collections

A collection is a root inscription (arcade-collection) plus parts of up to 500 items each, written to Pepecoin test. The first publisher of an item owns its entry, and only the same publisher can replace a root. Operators can also import a public list of known collections, off by default, agreed between arcades by fingerprint.

Sending an inscribed coin safely

The wallet keeps inscribed coins out of ordinary payments. Any coin of 0.01 or less is treated as possibly inscribed; coins up to 0.1 are traced back, 600 steps per lookup, before they're spent. To send an ord, the inscribed coin goes in as input 0 and out whole as output 0, with the fee paid only from clean coins. The send is refused unless the inscription sits at offset 0.

Games and apps

A game is a text/html inscription with a JSON card naming it a game or app.

Field Limit
Name (required) 60 characters
Description 280 characters
Version 20 characters
Players 20 characters
Genre 24 characters
Minimized timeout 900 s by default, up to 86,400 s

The creator is the address that inscribed it. The newest card per creator and name wins, so an update is a new inscription by the same creator, and a lookalike by someone else shows as a different game. Lookalike cards are hidden until the operator reviews them.

The sandbox

Inscribed content runs in an iframe without allow-same-origin, served with a content security policy of sandbox allow-scripts allow-pointer-lock, connect-src 'self' and form-action 'none'. Network requests can go only to the arcade itself, such as other inscriptions and the runtime scripts under /r/. Inline and evaluated scripts are allowed inside the sandbox. Each view gets a ticket for one page and one address, good for 12 hours and renewed while in use. Operators can serve content from a separate host name for stronger isolation.

A game reaches the wallet only through the runtime, and only if the player switched on Wallet access for that card. Every real-coin signature still shows a review card, with one narrow exception: the creator allowance lets a game send back to its own creator, without a card, a token they issued or a piece they inscribed, up to 0.05 coin per send within 20 coins per tab. On test chains, prize-pool claims (0.05) and inscribing to one's own address (2 coins) also sign without a card, under the same cap.

Exchange mechanisms

ARCADE has no matching engine and no exchange wallet. Trades on one chain settle as a single transaction both parties sign; trades across chains settle as two hash-locked contracts. Custodial options exist, are opt-in, and are capped.

Pre-signed trades are raw transactions with partial signatures, not PSBTs. The signature-hash code allows SIGHASH_ALL, ALL|ANYONECANPAY (0x81) and SINGLE|ANYONECANPAY (0x83). It refuses NONE, plain SINGLE, and SINGLE with an input index past the last output.

Same-chain trades

Kind How it's built Lifetime Where the funds sit
Exchange listing (tokens, NFTs) Seller signs 0x83: input 0, the piece, over the token payload; input 1, a seller coin, over the payment 24 h by default, up to 90 days, by the arcade's promise; the signature stays valid until its coins move Seller's own address
Ord listing Seller signs the inscribed coin as input 2, 0x83 over output 2, the price. The buyer adds two padding inputs and funding 30 days Seller's own address
Bid Buyer signs a payment 0x81, with every output fixed Until spent or cancelled Buyer's own address
Shop swap in an app Test chains only. Seller's arcade offers one of its outputs; the buyer signs, the arcade checks every buyer input, then countersigns 15 minutes Each side's own address until the one transaction confirms

Two rules came out of the October 2026 Doginal Dogs theft, in which 264 inputs signed with SIGHASH_NONE|ANYONECANPAY let an attacker move 201 pieces out of 194 wallets. A listing can only be re-listed cheaper, so an old, cheaper signature can't undercut a newer one. To cancel, the seller spends the coins: deleting a listing on the arcade doesn't revoke the signature.

The shared order book

Swap orders are relayed in the mesh room arcade-book: changes go out on the 1.5 s pass and the full book every 30 s. Each arcade may speak only for its own orders. An order listed from an open browser tab is shown only while the tab pings its arcade every 3 s, and drops out 12 s after the last ping; it costs nothing on chain. Orders can also be inscribed in batches of up to 50 that expire within 30 days.

Cross-chain swaps

One secret pays both sides, or both refundChain AinitiatorChain Bresponder1. Lock on Ahash of a secretrefund after 8 h2. Lock on Bsame hashrefund after 4 h3. Claim on Breveals the secretinitiator is paid4. Claim on Areads the secretresponder is paidIf step 3 never happens, nobody learns the secret: B refunds after 4 h, then A after 8 h.
Cross-chain swap

Swipe sideways to see the whole diagram.

Each side pays into a P2SH address with this script:

OP_IF
  OP_SHA256 <hash> OP_EQUALVERIFY <claimer> OP_CHECKSIG
OP_ELSE
  <locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP <refunder> OP_CHECKSIG
OP_ENDIF
  • Secret. 32 random bytes, hashed with SHA-256. Each hash is used for one swap only, because the script doesn't commit to an amount.
  • Lock times. Unix times, never heights. The initiator locks for 8 h, the responder for 4 h, apart by half the lock time and at least an hour. They were 2 h and 1 h until 2026-10-27, and were raised because a block may be stamped up to 2 h ahead of real time.
  • Margins. The responder's lock time is set when the swap is planned, 4 h ahead. The initiator may claim only until 2 h 20 min before it, leaving room for clock drift. A planned responder lock time less than 2 h 50 min away is refused.
  • The trade watcher. The browser pre-signs both the claim and the refund with SIGHASH_ALL and hands them to its arcade, which can only add the secret and choose when to send. It sends a refund once the chain's median time passes the lock time, and a claim once the lock has the agreed confirmations. A responder's claim reads the secret from the spend of its own lock.
  • Decided by the chain. The next step is a pure function of chain facts. Whether a swap is done is judged by which branch of the script was spent, not by where the outputs went.

Confirmations

Speed and safety are traded off per chain. Coin legs below a line act on unconfirmed payments; at or above the line, and on any leg that carries a token, NFT or inscription, they wait one confirmation. In trades the arcade makes for itself, its own real-coin side always waits one. The Pepecoin main swap service is the exception: it acts on unconfirmed deposits.

Chain Wait for a confirmation from Default cap on a delegated order
Pepecoin main 1,000,000 PEP 100,000 PEP
Dogecoin 1,000 DOGE 1,000 DOGE
Litecoin 2 LTC 1 LTC
DigiByte 1,000 DGB 1,000 DGB
Bellscoin 200 BEL 200 BEL
Luckycoin 20 LKY 20 LKY

Operators can move the line (swap_big:<chain>) and the cap (delegate_cap:<chain>). Delegated orders on test chains are never capped, and a swap whose legs are both test-chain coins never waits. The line for Bellscoin is set in code but unused, since that chain can't hold a swap lock.

Trust-based and custodial methods

These are the exceptions to two-party settlement. Each is labelled where it's offered.

  • Delegated swap orders ("fill while I'm away"). The browser derives a key for one order and hands it to its arcade, which watches the deposit address and trades for the user. The arcade's operator can spend that deposit. Only the coin side is capped. Withdrawal returns the deposit after any swap in flight settles; orders last up to 30 days.
  • Buy orders, away mode. The buyer pre-signs lots, each worth its own price plus fee, with 0x83 over their own change output. The signature doesn't cover the price, so whoever holds it can spend that lot; the arcade keeps only signatures that check out, and cancelling sweeps the lots back. In the other mode nothing is pre-signed and the buyer signs when notified.
  • Game escrow. Items sit at <tag> OP_DROP OP_IF <referee> CHECKSIG OP_ELSE <unlock> CLTV OP_DROP <owner> CHECKSIG OP_ENDIF. Before the unlock time (1 to 720 hours) only the referee can move them; after it, only the owner. The referee signs after a judge replays the game: a JavaScript function run in QuickJS with 5 s of CPU and 64 MB, with Date and Math.random removed, so every arcade reaches the same verdict.
  • Prize pools. A pool's lots are pre-signed 0x83 and sealed with AES-GCM under a SHA-256 key derived from a phrase hidden in the game. Whoever finds the phrase can claim. The pool address comes from the creator's own seed, so the creator can also sweep it at any time. Refereed pools add the referee's signature, binding the prize to the player who played.
  • Pepecoin main swap service. Test-chain assets for Pepecoin main, run by the arcade, which holds both deposits during the swap. A fixed 0.5% fee from the seller, a solvency check before every payout, 24 h for a deposit to arrive. It acts on unconfirmed deposits and has no per-order cap. Off unless the operator turns it on.
  • Model credit. Paid straight to the model seller, who keeps balances and counts usage.

Who holds what

Method Before settlement During If it fails
Same-chain trade Each party Nobody: one transaction Nothing moved
Cross-chain swap Each party Each party's own script lock Both refund on their time locks
Delegated swap order The listing arcade The listing arcade Withdraw at any time; orders stand up to 30 days
Buy order, away Buyer, with signed lots at the arcade Buyer's lots Cancel and sweep
Escrow Shared script Referee Owner after unlock
Prize pool Pool address Anyone with the phrase Creator can sweep it at any time
Pepecoin main swap service The arcade The arcade Arcade's solvency check
Model credit The seller The seller Seller's word

Fees and the network's own costs

Every action pays its chain's network fee. On top of that, an operator may charge for trades and inscriptions on main chains. Admin offers each operator fee from 0 to 2% (the code would accept more if set by hand), and the trade fee is off by default.

Network fees

Fees are priced per kilobyte of virtual size: the larger of the byte count and 20 times the signature operations, because a bare-multisig output counts as 20 sigops.

Chain Minimum fee per kB Dust line
Pepecoin, Dogecoin, Luckycoin 0.01 0.01, plus 0.01 extra for each output below it
DigiByte 0.01 0.0001
Litecoin, Bellscoin 0.0001 0.0001

A cross-chain lock also carries its own claim fee (a fixed 0.01 on Pepecoin, Dogecoin and Luckycoin; elsewhere the larger of the dust line and 400 bytes at the chain's rate), paid by the locker on top of the amount. Locks holding a token or an inscription carry a small reserve so the claim can move them.

Operator fees

Fee Setting Range Default Paid by
Trade cut on swaps trade_cut 0 to 2% Off In swaps, taken inside the lock and paid by the claim, at least the dust line; in shop sales and order fills, added to the price
Inscription fee mainnet_create_fee_permille, inscribe_fee:<chain> 0 to 2%, with a per-chain minimum 0.5% The inscriber, on main chains only, never on test chains
Pepecoin main swap service Fixed 0.5% Service off The seller

Lending is paid in kind

An arcade that lends a chain receives:

  • 20% of the operator fee on swaps and inscriptions made through the borrowing arcade on that chain, at least the dust line;
  • 10% of the reward of any test block mined with its work, paid in the same coinbase.

Social costs on the test chain

Names, posts and messages cost a fraction of a Pepecoin test coin, which has no market value. New accounts get 100 PEP test on many arcades (the operator sets the gift and daily top-up). On a main chain the faucet is refused unless the operator explicitly enables it, with a hard cap of 10 coins.

Limits that protect the commons

Hourly quotas per account keep any one user from flooding an arcade or a chain. Operators can raise or lower them up to a ceiling of 100,000 an hour and 50 MB a day.

Action Default per hour
Claim 600
React 60
Post, message, send, list, trade 30 each
Inscribe, issue a token 10 each
Change @name 5

Data is capped at 250,000 bytes a day, and an account may have at most 4 offers waiting at once.

Security model and threat analysis

The design assumes any arcade may be careless or hostile, including the one a user signs up on. For the two-party methods, the mitigations below hold against a hostile arcade. The custodial methods, unproved borrowed reads and the gaps at the end are where a user still has to trust one.

Trust boundaries

Party Trusted for Not trusted for
The user's browser Keys, signing, the real-coin gate Nothing beyond the user's own device
The user's arcade Serving pages, relaying signed transactions, keeping sealed blobs Holding keys; deciding which network is real; moving coins without a signature
A lending arcade Answering chain reads in time Correctness on chains with a work check; wallet calls of any kind
Other mesh peers Forwarding Forging messages; payments or permissions based on what they claim
The chains Final word on names, coins and inscriptions Nothing more: a reorg can undo recent blocks
Referees and judges Releasing escrow before unlock Holding items after unlock

Threats considered

Threat Mitigation
Arcade steals keys Phrase sealed in the browser with AES-256-GCM; the arcade sees no password
Weak or leaked password 12-character minimum for new passwords; leak-list check by k-anonymity, which goes ahead if the list can't be reached; 600,000 PBKDF2 rounds
Lookalike site replays a login The signed login message includes the page's origin, checked by the arcade
Arcade lies that a chain is a test chain The browser decides; test chains sign with a key that holds no real coins
Arcade substitutes a different trade The user signs the exact transaction; listings use 0x83 so the price output is fixed
Old cheap listing undercuts a new one Re-listing is allowed only at a lower price; cancelling spends the coins
SIGHASH_NONE drain, as in the Doginal Dogs theft NONE and plain SINGLE are refused for every signature the arcade asks for or stores; stored lot signatures are checked since 2026-10-27
Arcade signs an input it didn't offer Before countersigning, every buyer input is checked against the arcade's wallet
Lying peer for a borrowed chain Raw blocks re-hashed, merkle roots and proof of work checked; height, unspent-coin and mempool answers aren't proved; a sharer caught lying is avoided
Wrong node on a port Chain name checked before use, and the genesis hash where one is set (not yet for Pepecoin)
Block timestamps skewed to steal a swap 8 h and 4 h locks, 2 h drift allowance, 20 min claim margin, median-time refunds
Reused swap secret Each secret hash is accepted for one swap only
Unsafe scripts on weak chains Swaps and escrow refused where CLTV isn't enforced, P2SH bytes collide, or transactions carry timestamps
Spam and flooding Hourly quotas, daily byte caps, mesh rate limits, seat limits
Malicious game Sandboxed iframe without same origin, strict CSP, per-card wallet switch, review card for real coins
Remote takeover of Admin Operator account plus a 12-character scrypt password; 12 h sessions, SameSite=Strict; a custom header on every write, and a same-origin check whenever the browser sends an Origin; key settings only at the machine
Tampered update Release manifest signed with a pinned Ed25519 key; archive must match the signed hash; downgrades refused (archive installs; git installs aren't verified yet)

Operating a node

An arcade needs one always-on computer, Python 3.10 or newer, and a local Pepecoin test node. Everything else is optional and set per chain.

Install

curl -O https://dogecoinarcade.com/install.py && python3 install.py

The installer downloads Pepecoin Core and checks it by SHA-256, and by GPG signature when gpg and the signing key are present, creates a virtual environment in ~/.dogecoinarcade/venv, and registers arcade-web as a systemd service (a user unit unless installed system-wide). Coin nodes added later from Admin are checked against pinned SHA-256 hashes. No root access is needed for a user install.

Suggested hardware

The project doesn't publish requirements yet. These figures come from running arcades, measured on 2026-10-30.

Setup Runs locally CPU Memory Disk
Starter Pepecoin test; all else borrowed 2 cores 4 GB 32 GB SSD
Standard Pepecoin main and test, Pepecoin ord index 4 cores 8 GB 128 GB SSD
Full Adds Dogecoin, Litecoin, Bellscoin and their indexes 8 cores 32 GB 2 TB, indexes on SSD

Chain data on that date: Pepecoin test about 2 GB, Pepecoin main 15 GB, Dogecoin 224 GB, Litecoin 255 GB, Bellscoin 411 GB. The arcade itself uses about 0.6 GB of memory; its data folder holds 49 GB on a full arcade, mostly inscription indexes.

Network

Port Protocol Purpose Needed
8420 TCP, HTTP Player and admin pages, bound to 127.0.0.1 by default Local only unless published
8421 TCP Mesh Inbound helps; a Cloudflare quick tunnel is the fallback
3478 UDP STUN for NAT traversal Optional

To publish an arcade, an operator either runs the built-in wizard for a Cloudflare named tunnel (which sets up app. and pages. host names and a user service), forwards a port, or starts with --public.

Configuration surface

Area Settings Changed from
Identity Operator account; brand (Dogecoin, Pepecoin or Litecoin face); public and local host names Machine only, except brand
Access Remote admin password (12+ characters) Machine only
Seats and quotas seats (1 to 10,000); quota:<action>, quota:bytes Admin
Chains core:<chain> (here, borrow, off), share:<chain> Admin; core changes apply at the next start
Indexes ord_index:<chain> (walk, help, read, off); ord_cores; ord_share (10 to 100% of the machine); sites_index:<chain> (host, ask, off) Admin
Trading trade_cut, swap_big:<chain>, delegate_cap:<chain>, auto_sell, auto_fill, node_trades, xchain:enabled Admin
Fees mainnet_create_fee_permille (0 to 20 per mille), inscribe_fee:<chain> Admin
Faucet faucet, faucet_daily; real-coin faucet off unless enabled, capped at 10 coins Admin
Safety moderation (a local model's URL, name and key); flags:broadcast, flags:follow Admin
Extras turn (call relay key, apps, monthly GB); room for sale (boost:*); LoRa gateway; affiliates Admin
Updates auto_update, update_channel (live or test) Admin

Command-line flags cover the rest: --home, --port, --host, --public, --brand, the mesh flags (--no-mesh, --mesh-port, --mesh-peer, --mesh-tunnel, --mesh-core and others), and --<chain>-conf, --<chain>-datadir, --no-<chain> for each add-on chain.

Updates

  • Channels. "Main live" gets a release after a day without trouble; "Main test" gets every change on the test branch.
  • Checks. The watcher checks every 15 minutes. A signed release notice from the project's release account triggers an immediate check.
  • Verification. The release manifest {revision, sha256, published} is signed with Ed25519 and checked against a key pinned in the code. The archive must hash to the signed value, and a downgrade is refused. Verification fails closed.
  • Switch-over. After installing, the updater sends the supervisor a HUP and the new worker takes over without dropping connections. The update is judged by the revision now installed, not by the exit code.
  • Safety stops. An update is skipped while a send is in flight, and on a source checkout.

Monitoring

Admin shows each chain's sync state, disk cost and index progress, plus the Welcomers board and notifications. The stall watcher logs every thread's stack when a request runs over 45 s. Logs go to the systemd journal (journalctl --user -u arcade-web).

Limitations and open problems

ARCADE works today on real chains, but several problems are open. Some are engineering work in progress; others are trade-offs the design accepts.

Engineering work in progress

  • Index cost. Walking an ord index is single-threaded per chain and dominated by per-block SQLite work. On the team's own machines, Bellscoin's index held about 81 million inscriptions by block 88,000, and a full walk takes days. Batching commits across blocks gained only about 3% in a trial; the next step is likely a different storage layout.
  • Lane ordering. When many lanes trace old inscriptions back at once, the chunk the walk needs next can wait behind others. A fix that finishes the lowest chunk first is under review.
  • Verified pages. A player can't yet tell whether an arcade serves the standard, unmodified pages. A plan for signed page manifests, cross-checks between affiliated arcades, and badges is in progress.
  • Balances across arcades. An account used on several arcades can show different balances until each arcade has scanned for its coins. A shared per-account coin list is planned.
  • Platforms. Linux on x86-64 is tested, and Windows is being tested now. macOS and ARM are expected to work.
  • Documentation. Parts of the built-in guide trail the code; corrections are under review.

Accepted trade-offs

  • Test-chain identity. Names and messages ride Pepecoin test because it's cheap. A test network can in principle be reset, and its coins have no value that would deter spam beyond the quotas.
  • Unconfirmed swaps. Small coin swaps settle fast and risk a double-spend; large ones wait. The line is the operator's choice.
  • Custody where it's useful. Delegated orders and the Pepecoin main swap service hold user funds, within caps, because "fill while I'm away" can't work otherwise.
  • Seats. Each arcade caps its accounts (50 by default) so one machine isn't overwhelmed; more players means more arcades.

Open questions

  • Luckycoin swaps. The code allows them; the guides say they aren't open yet. The project maintainer is to decide.
  • Chains without a work check. How to prove DigiByte (five algorithms) answers from a lending arcade.
  • Stronger key stretching. When to move sealed wallets from PBKDF2 to Argon2id, and how to migrate existing blobs.
  • Signed git updates. Extending manifest verification to installs that update through git.
  • Merge mining. Whether operators should mine test chains together with spare computing power, and how to keep it within each host's terms of service.

Glossary and references

Glossary

Term Meaning in this paper
Arcade One running instance of the ARCADE program
Face A brand of the same program: DogecoinArcade, PepecoinArcade or LitecoinArcade
Operator The account that owns an arcade, named only at the machine
Mesh The authenticated peer-to-peer network between arcades
Core A coin's reference node software, run unmodified
Borrowing Using another arcade's Core over the mesh
Sealed blob A user's recovery phrase encrypted in the browser
@name A name claimed on Pepecoin test, one per address
Ord An inscription bound to a specific coin, as in Doginals and ordinals
Ord index The per-chain database of which coin carries which inscription
HTLC Hash time-locked contract: pays whoever shows the secret, or refunds after a time
CLTV OP_CHECKLOCKTIMEVERIFY, the opcode that enforces a time lock
0x83 SINGLE with ANYONECANPAY: signs one input and its matching output only
0x81 ALL with ANYONECANPAY: signs one input and all outputs
Big line The size at which a coin swap waits for a confirmation
Delegated order A swap order the user's arcade fills while the user is away, holding its deposit
Referee A key that co-signs escrow releases after a judge replays a game
Welcome The protocol by which operators send a new arcade its first test coin

References

  • ARCADE source code: the main branch, commit 110e55c8, is the basis for this paper.
  • Built-in documentation in arcade/web/docs/: how-it-works.md, swaps.md, escrow.md, exchange-safety.md, shared-cores.md, multiplayer.md, prize-pools.md, remote-access.md, and a page per chain.
  • Key source files: arcade/web/supervisor.py (process model), arcade/cores.py and arcade/mesh/coreshare.py (chains), arcade/mesh/ (mesh), arcade/htlc.py and arcade/trade.py (swaps), arcade/escrow.py, arcade/doginal_index.py (ord index), arcade/release.py (update signing).
  • BIP 39: mnemonic seed phrases.
  • BIP 32: hierarchical deterministic keys.
  • BIP 65: OP_CHECKLOCKTIMEVERIFY.
  • BIP 113: median time past as the lock-time reference.