PepecoinArcade
@ Create a new account

What it does

docs/features.md

Jump to: The shape of it · Messages · The public board · The feed · Tokens · NFTs · Collections · Shops and swaps · The exchange · Prize pools · Games · Approvals · @tags · The address book · Running your own · Two ways to run it · The bot RPC · Node-to-node messages · Joining a node · Wallets, keys and backup · What it does not do

Everything the application can do, what each thing costs, and what it deliberately does not do. Written against the code; where a number appears it was measured rather than estimated.

The shape of it

DogecoinArcade is a sidecar: it runs beside Pepecoin Core on your own machine and reads the chain the node already has. It never holds your keys — the node's wallet does — and it never asks for a passphrase. Everything it knows, it learned by replaying blocks, which means two installations reading the same chain agree without talking to each other.

Interfacehttp://127.0.0.1:8420, bound to loopback because it can spend
ChainsPepecoin main and Pepecoin test — the two this arcade moves coins on, and the only two
MessagingPepecoin test only, by design
Tokens, inscriptions, @tagsPepecoin main and Pepecoin test
Storageyour node's datadir for the chain; ~/.dogecoinarcade for what the app remembers

Messages

Private messages between two people, encrypted end to end and carried on the chain.

Costs a transaction fee, and for a file, dust in the outputs that carry it. Both are shown before anything is sent.

The public board

Red means somebody said something. A channel with posts you have not seen carries a red count in the list, and so does a conversation with unread messages. Opening one clears its own count and nobody else's — reading #trading does not silence #releases. Your own posts are never news to you.

Channels anybody can read. Nothing here is encrypted and nothing can be deleted — that is the point of them.

Every page that shows one chain says which, and switches. The Pepecoin main / Pepecoin test tag is on the Wallet, its NFTs and Tokens tabs, the Tokens page and the Exchange, and pressing it stays where you are. A fresh wallet opens on the chain its own identity is on, not on Pepecoin main.

The address book is @tags. Everybody in it has claimed a name on the chain, and that is the only way in — search for a tag and add it. What gets saved is what the name points at: both addresses and their key, read from their own announcement. A contact shows as their @tag with any notes you made; addresses and key are one click away, because addresses are for machines. Somebody who has not claimed a tag cannot be added, and a key announced under no name is not offered.

Publishing your tag publishes three things: your Pepecoin test address, your Pepecoin main address and your messaging key, all under that name. One search then gives somebody everything they need to pay you on either chain and write to you. The Pepecoin main address is chosen once and kept, so republishing says the same thing.

A new wallet is asked what it is called. The first page a wallet with no @tag opens asks for one: it is how everybody else sees you — in their address book, on anything you sell, beside every offer — instead of a 34-character address. Claimed on the chain, first claim wins, and claiming it publishes your key at the same time so people can write to you. While the claim is on its way the card says so instead of asking again.

The feed

Every tag has a page, and everything they post is on it. The Feed is everybody's posts, newest first, ten at a time with more as you scroll. Clicking a @tag anywhere goes to that person's page.

A profile is a name, a face, a line and a link — all published in the one announcement your tag makes, so a single lookup gives somebody everything. Set the bio, the link and the picture in the address book; each publishes on the same tag without changing it. The picture is any inscription you hold on either chain, shown only while the chain says you still hold it, and changing it updates every post you have ever made.

@tag's wallet is a page of its own: their coins, tokens and inscriptions on both chains, read from the chain for the addresses their tag names. Nothing there is private — a chain is public — and nothing there can be spent.

Tokens

An Omni-style token ledger, indexed from the chain.

A token can wear an inscription as its icon. Give the inscription's id when creating a token, or pick one of your own pictures from the list beside the field. Inscribe the picture from the NFTs page first if it is not on the chain yet — one thing at a time, and the id is what the token needs. The icon is shown wherever the token is — the markets table, its market page, the token list. A token without one gets a plain mark, never its initials: two letters of a name read as a ticker symbol, and nothing here has one. The picture is an inscription on the same chain, so it is served by whichever node is looking at it rather than by a website that can go away.

An Omni issuance has five strings and no sixth, so the icon rides in data: the inscription's id alone when there is nothing else to say, and {"about": "...", "icon": "<id>"} when there is. A link pasted into the link field is read as an icon too. A token with an icon is carried as Class B — an id is 64 characters and a Class C payload is 76 for the whole issuance, name and all, so no shortening would fit it into one OP_RETURN. That costs the multisig encoding and some dust you can sweep back; the confirm screen shows it before anything is paid. None of it is consensus — the property on the chain is exactly what it always was, and a node that never heard of the convention shows the same token with the JSON as its description. It costs about thirty bytes of the issuance, and an issuance cannot be corrected afterwards, so an icon that is not an inscription on this chain is refused before it is paid for.

NFTs

A file written onto the chain in full and uncompressed, owned by an address. Called an inscription in the code and on the wire, and NFTs in the interface, because that is the word people arrive with.

What it costs, measured:

FileTransactionsChainFeeDustTotalAfter sweeping
100 KB14205 KB2.118.120.12.1
1 MB1382.0 MB20.2178.0198.320.2
5 MB68810.1 MB100.9887.5988.4100.9

The dust is not lost: every data output is a 1-of-3 including your own key, so it is spendable again by whoever made the inscription.

What an inscribed page can call

An inscription can be an HTML page, and that page can ask this node questions — the same idea as ordinals' recursive endpoints, with the same paths where the meaning matches.

EndpointReturns
/content/<id>the bytes, with the type its creator gave it
/r/inscription/<id>owner, creator, number, type, length, hash
/r/metadata/<id>the JSON field, parsed
/r/inscriptionsa page of them, newest first
/r/inscriptions/<address>what an address owns
/r/collectionsthe collections on the chain, newest first
/r/collection/<creator>/<name>one collection: its items in edition order, and its traits
/r/blockheight, /r/blocktimewhere the chain is
/r/balances/<address>token balances at any address
/r/tag/<name>, /r/address/<address>who holds a @tag, and the other way
/r/walletwhich wallet is looking — can be switched off
/r/sendasks the wallet to send coins, tokens or an inscription; the person approves. Give it a ref and a retry is never a second send
/r/tx/<txid>whether a transaction is confirmed, and how deep
/r/storage.jsarcade.storage: what the wallet remembers for the page, since a sandbox has no localStorage
/r/contacts.jsarcade.contacts: the signed-in player's address book. list() (asked once per game), and add({address, name}) / remove(address), each only after the player taps yes on a card
/r/node.jsarcade.node: node-to-node messages sent as the wallet the page runs in, and the replies to them
/r/swap.jsarcade.swap: a shop page's listings, and a buy button that ends in one transaction both sides signed
/r/owner.jsarcade.owner: a page sending, unasked, from the wallet that created it and holds it
/r/token/<id>, /r/tokens?ids=a token's issuance fields, its issuer, and its data parsed when it is JSON
/r/holders?token=<id>, /r/holders?creator=&collection=who holds a token now, or who owns a collection's pieces — addresses only. An empty list means nobody holds it; a call that names nobody, or a node that cannot look, answers an error instead
/r/realtime.jsarcade.realtime: rooms where players on any node meet, over the node mesh
/r/escrow.jsarcade.escrow: a player's NFTs and tokens held for a game, released by its judge
/r/state.js, /r/state/<piece>arcade.state: a game's own record on each NFT (condition, charges, counts)
/r/verified/<judge>every inscription carrying a result that judge verified

An inscription is <id> by its number or its creating transaction id.

Why this is safe to serve. Inscribed code is written by strangers and runs in the browser of a wallet that can spend. It is loaded into a frame with sandbox and no allow-same-origin, so it has an opaque origin: no cookies, no access to the page around it, no navigating the top window. These endpoints are the only URLs in the application that answer a cross-origin request — every other page says nothing about CORS, so an inscription cannot read them. Content is served with sandbox in its own policy and nosniff, and a type we will not render is handed over as a download rather than guessed at.

The file is on the chain, and only there. This node does not keep a copy of an inscription's bytes: it keeps which transactions carried them, and reads them back when somebody asks — reassembling, checking the result against the manifest's own sha256, and handing it over. That is a stronger guarantee than a stored copy, not a weaker one: a stored blob is trusted, a reassembled one is proved. Recently-served files are cached briefly so a wall of a hundred tiles is not a hundred trips to the node. A node that would rather hold the bytes — a gallery answering strangers — can say so.

Collections

A set of inscriptions that belong together — a HashLips build, or anything that names itself the same way.

A set can describe itself, on its #1. The first piece is the one a collection is known by, so that is where collection-level details are read from. The collection wizard asks for them at step 2 — description, website, Twitter, and a thumbnail — and writes them onto that piece; or put them there yourself, in a collection object beside the name:

``json {"name": "Space Frogs #1", "edition": 1, "collection": {"name": "Space Frogs", "description": "100 hand drawn space frogs", "url": "https://spacefrogs.example", "twitter": "@spacefrogs", "supply": 100}} ``

description, url (or website, or external_url), twitter, discord, telegram, supply, artist and icon are read — field by field, the object first and the item's own top level after, so adding one of them never hides another. (A HashLips description at the top level is still read when the wizard writes an object for the thumbnail.) Everything else is ignored, text is capped, and a link that is not http(s) is not shown as a link — an inscription is written by anybody and this ends up on a page. A HashLips build already writes description and external_url at the top level of every item, and those are read whether or not there is an object.

The set's face is its #1 unless it says otherwise. A marketplace shows the first piece, because that is the one people recognise — but icon names another inscription to use instead, and the wizard takes its id at step 2. It is an inscription on this chain either way: a face on somebody's website is a face that disappears when the hosting does. One this node cannot draw falls back to #1 rather than to a broken image.

Membership is not decided by any of that. A piece belongs to the set its own name or a collection STRING says — an object is not a string, so a set that describes itself is still filed by its name. Two nodes cannot disagree about what is in a collection because one of them understood a richer JSON.

The IPFS pointer is not inscribed. A HashLips build writes "image": "ipfs://NewUriToReplace/1.png" into every item. Here the picture IS the inscription — its content, in full, served by every node that has it — so that field names a place the art is not, at about fifty bytes an item that somebody pays for and nobody can follow. It is dropped before the set is priced. An image that is a real http(s) link is left alone: that resolves, and it is the creator's decision. Everything else is inscribed exactly as the build wrote it, including edition and attributes, which is what membership, numbering and rarity are read from.

Shops and swaps

A shop is an inscription. Its JSON lists what it sells and where its owner's node listens; the page inscribed with it is the storefront.

Pepecoin test only, like the messages it travels on. Swaps are read from a starting height per chain (swaps_from), so a swap in a block before it is recorded as unread rather than as an old transaction meaning something new.

A token can sell itself from a launchpad. On the token's own page, its issuer can inscribe a page that sells a fixed lot of the token for a fixed price — the mintpad's idea with the wall taken out. It is an inscription whose JSON names a shop, so any node can read it and buying is one swap carrying both sides; it is written from the address that holds the tokens, and it runs out when that address no longer holds a lot to sell. Previewed before it is inscribed, with the token's own icon and the price you typed, so what you pay for is what you already looked at. It appears under Mintpads in the Exchange by itself.

The exchange

Everything for sale on a chain, read from the chain itself. Four tabs, four questions.

Charts of what actually traded. Tokens and NFTs both get candles, built from the swaps on the chain — there is no order book to draw, so what is drawn is what people paid. A day with no trade is a dot on the axis, never a line ruled to the next price. One chart per collection, drawn on that collection's own page beside the pieces it prices, and one currency to a chart: what a Space Frog goes for says nothing about what a Doge Punk goes for, and coins and tokens on one axis is adding pounds to metres. Drawn as SVG in the page: no charting library is fetched from anywhere, and the page's CSP would stop it if it tried.

Putting a price on one. Sell it — on any NFT of yours, from Wallet → NFTs, from its tile in a collection, or from the piece's own page — asks a price and broadcasts an ask: one OP_RETURN saying "this piece, for this much" (47 bytes of payload; about 250 as a whole transaction, with its inputs, change and signature). It is the other half of an offer, and the two are symmetrical: an offer is a buyer's word about somebody else's piece, an ask is the holder's about their own, and both are said on the chain rather than kept anywhere, so every node has the same book and a seller's wallet can be switched off without withdrawing the price.

Asks are read from a starting height per chain, like every other newer record type; before it, an ask made on that chain would be read by nobody, and the page says so before the fee.

An offer is made with what you have. The form lists the tokens this wallet holds with their balances, and the coin balance beside the coins option; it does not draw a token picker when the wallet holds none. The door checks again when the form comes back. Offering something you do not hold is a fee spent to be told no by somebody else, a block later, for something your own wallet could see at once.

A wallet is told before it pays. A buyer's side of a swap is two transactions — the half they sign and the message that carries it — so it needs two confirmed outputs. A wallet with one is refused before the order goes out, not three transactions later, and told to split the address.

An offer can be made on any NFT, listed or not: open one you do not own and press Make offer. It is said on the chain, not sent to anybody: somebody holding an NFT never asked to be reachable, and most have published no key at all, so the holder's own wallet finds the offer by watching its own things — any address, no announcement needed. It fits one OP_RETURN, so it costs a flat fee and locks nothing.

An offer already accepted says so, and says who it is waiting for and until when, rather than offering to accept it again.

It is not a PSBT — the buyer cannot build the transaction, because the seller's own output has to be its first input. The holder's Accept builds the seller's half and sends it to the buyer, whose wallet signs its half automatically only if it names the same item and the same price that was offered. Which is why making an offer needs your key published: you are the one asking for an answer. An offer on something of yours is never answered without you.

Prize pools

How a game, a puzzle or any other page pays out tokens to whoever wins, without its owner being there. The Prize pools guide has the details.

Games

The Games tab, between Your arcade and Messages, lists every game on the chain, and everything a game needs from the arcade is built for any game, not one in particular. Each piece has its own guide.

Approvals

A page or a program can ask this wallet to send something. It cannot send.

@tags

The @ is punctuation. Anywhere a name can be typed — a send, a message, a search — it works with or without it: @alice and alice are the same name. What tells a name from an address is the shape, not the sigil: a tag is at most 24 characters of a-z, 0-9 and _, and an address is 34 of mixed-case base58.

Case is free when you search, and with an @ it is free everywhere — @ALICE is plainly a name. A bare name in a field that spends is read as a name only if it is written as one, in lower case, so that a mistyped address is still answered with "that is not a valid address" rather than "nobody holds that name".

A handle that belongs to an address, such as @alice.

A claim is 13 bytes, so it fits an OP_RETURN: naming yourself costs one small transaction. Claim or change one from the address book, in two steps like a send: the transaction is shown before anything is broadcast, and a name that is malformed, reserved, already yours or already somebody's is refused before it costs anything.

The address book

Who is who, and where to pay them. Stored only on this computer — not published, not on any chain, and not visible to anyone you message.

Running your own

Every instance serves the next one. /clone on any node hands over the program and a copy of that node's index, so a network of arcades does not depend on one website staying up.

The realtime mesh. Nodes also link to each other directly, for game rooms. It is on by default and finds its peers on the chain: each node that can be reached announces where, and works that out by itself. A node behind a router with nothing forwarded still plays by dialling out. Setting up a node has the port and the options.

Two ways to run it

As a wallet — the default. One person's machine, everything in it theirs, reachable from where it is served.

As a public arcade — --public, which is what a node on a real domain runs. Only the public surface is served: the feed, somebody's profile and holdings, the collections, the chain views, inscribed content, the page API, the guide and the sign-up page. Every form is refused, because every form in this application spends the NODE's wallet and none of it is per-account yet. Holding a seat says who you are; it does not make the operator's wallet yours, and no combination of cookies changes that, because the refusal is about the route rather than the visitor.

It is an allowlist, not a blocklist: a route added tomorrow is refused until somebody names it, which is a visible one-line mistake, where the other way round the mistake is somebody's coins. It says which of the two it is at startup, every time.

A door for the pagesAn inscribed page runs in a sandbox that can carry no cookie, so it is framed from a second hostname of its own (pages_host): that name serves the page's content and the page API (/content/, /r/) and answers 404 to everything else. With no second name configured, pages are framed from here — the same sandbox, minus the extra origin.
No bot RPC/rpc/* is never served publicly. It has its own key, in a file beside the node, and it can spend.

The bot RPC

A JSON-RPC endpoint for programs: airdrop bots, faucets, leaderboards, anything that reads balances or moves tokens without a person clicking.

See Bot RPC for the full method list.

Node-to-node messages

An encrypted channel for one arcade to talk to another — for marketplaces, bots and anything built on top.

On the chain rather than peer-to-peer because a custom P2P message does not propagate — Pepecoin tolerates an unknown command and drops it without forwarding. The mempool already gossips to every node in seconds.

Joining a node

Wallets, keys and backup

What it says in the journal. The interface logs what it did at info -- blocks read, a store rebuilt, a sale answered, a release announced -- so journalctl -u arcade-web is a record of the wallet's own decisions rather than only of its complaints. --log-level warning quiets it; debug is for when something is wrong.

What it does not do

← all docs