Demo · simulated stocks · no real assets move

IRL STOCKS
Documentation

How it works. All of it.

Overview

IRL STOCKS is a location game where the loot is real. Fragments of tokenized stocks are attached to the physical places of the companies behind them: the store, the office, the HQ. You open the map, walk to the place, tap, and the fragment transfers to your wallet.

Three parts make it work: a spawn table (where, how much, when), a claim server (checks you are really there and signs a one-time voucher), and a vault contract (holds the tokens and only releases them against a valid voucher). Nothing is minted. Every fragment on the map is funded before it spawns.

This deployment runs in DEMO mode. Every spawn, balance and claim is simulated and labelled as such. No token, vault or chain is involved until the live gate is passed.

Spawns

A spawn is one amount of one stock at one location during one window. It carries a rarity, a claim radius, a daily maximum and a funding status. A spawn is only visible when the vault can pay it.

FieldMeaning
amountFragment paid per claim, in the token's 18-decimal units
rarityCOMMON · RARE · EPIC · LEGENDARY, a property of the spawn
claim_radius_mDefault 40 m; legendaries 25 m
max_claimsPer day, default 8; counters reset at 00:00 UTC
funding_statusFUNDED only when available inventory ≥ amount
  • One claim per spawn per wallet per day.
  • Four claims per wallet per day.
  • Cooldowns between claims per wallet and per spawn.
  • Location safety and public access are reviewed before a spawn goes live.

Claim flow

The client buffers a short GPS log while you stand at the spot, then asks the server to verify. The server re-checks distance and accuracy, inspects the log, applies limits, reserves inventory atomically, and only then signs a voucher. Your wallet submits the voucher to the vault, which transfers the fragment.

UNKNOWN → LOCATION_REQUIRED → TRACKING → TOO_FAR | NEARBY | IN_RANGE
IN_RANGE → VERIFYING → VERIFIED → VOUCHER_READY → AWAITING_WALLET → SUBMITTING → CONFIRMING → CLAIMED

Failures (named, never generic):
INACCURATE_LOCATION · PRESENCE_REJECTED · COOLDOWN · DROP_EXHAUSTED · WALLET_LIMIT · DROP_LIMIT
TOKEN_UNFUNDED · REGION_INELIGIBLE · VOUCHER_EXPIRED · TRANSACTION_FAILED · ALREADY_CLAIMED

Presence & privacy

GPS alone is not proof of presence. The claim server combines several signals, each with a configurable threshold: distance to the spot, reported accuracy, at least four fixes over the observation window, natural jitter (mock providers repeat the same coordinates), timestamp consistency, travel plausibility between claims, and, in later rounds, network geolocation, timezone and motion checks.

What we collect during a claim: wallet address, the buffered fixes for the observation window, accuracy, timestamps, a session id, and a hashed connection identifier. Raw fix paths are kept for 24 hours, summaries for 90 days. There is no background tracking and no location collection outside an active claim attempt or an explicit near-me request.

We do not claim hardware attestation and we do not claim perfect spoof prevention. The controls make abuse expensive and bounded.

Vault contract

The vault holds pre-funded tokens and exposes one user function: claim(voucher, signature). It reverts if paused, if the deadline has passed, if the nonce was already used, if the signer does not match, or if the balance is insufficient. Owner functions: setSigner, pause, unpause, withdraw, two-step ownership.

Claim {
  address to;        // recipient wallet
  address token;     // stock token contract
  uint256 amount;    // 18 decimals
  bytes32 spawnKey;  // keccak256(spawn id)
  uint256 nonce;     // single use
  uint256 deadline;  // unix seconds, 15 min TTL
}
EIP-712 domain: IRLStocksVault / 1 / chainId / vault address

The chain never trusts the app. It trusts one signing key, and that key only signs after the physical checks. In demo mode a throwaway signer produces the same typed data so the whole path is exercised without a contract.

Proof of reserves

The vault address is public. In live mode every balance on the site is a balanceOf call refreshed every minute, with an explorer link per token. Allocated inventory (what active spawns could still pay today) and outstanding reservations are subtracted to compute what is available. A spawn is hidden the moment it cannot be paid.

In demo mode the snapshot is simulated and says so on every surface.

Legendaries

A legendary is one whole share, at one place, during one window, with exactly one winner. Announced at least 24 hours ahead. A 25 m radius, an optional code at the spot, inventory reserved before the window opens, and a winner row written atomically before any voucher is signed. Past winners stay listed.

Tokens & chain

Target chain and token issuer are configured per environment and published here when live. Demo mode uses no chain. Supported assets, transfer restrictions and region eligibility are documented before any live inventory is funded.

Add a point

Anyone can suggest a place. Submissions pass duplicate check, location validation, safety review, company relationship review and admin review before they can be funded. A submission alone never creates inventory.

Brands

Brands can sponsor spawns at their own locations and pay per verified visit: a claim that passed every check above. Not per impression, not per click.

API

GET  /api/v1/spawns?lat=&lng=&limit=&city=   → { mode, cities[], items[] } sorted by distance
GET  /api/v1/spawns/:id                       → spawn
GET  /api/v1/claims?address=&limit=           → public feed / one wallet
GET  /api/v1/vault                            → per-asset balance, allocated, available, payable
GET  /api/v1/legendary                        → next event, past winners
POST /api/v1/claim   { spawnId, address, fixes[] }  → { state, voucher, signature, chainId, contract } | { ok:false, code, message }
POST /api/v1/claim/:id/submitted { txHash }
POST /api/v1/claim/:id/confirm                (demo only)
POST /api/v1/points  { place, symbol, city, lat, lng, reason, contact }

Errors come back as { ok: false, code, message } with a 4xx status. Codes are the failure states above. The claim endpoint is the only one that signs anything.

Demo vs live

Demo mode runs every check for real and records the claim under your demo wallet, but no voucher is submitted to any chain and nothing moves. Live mode requires the vault, an isolated signer, chain RPC, and an explicit human approval recorded in configuration. The server refuses to boot in live mode without them. The switch is never automatic.

Status

Round 1: demo functional clone. Homepage, app, docs, map, spawns, GPS lifecycle, demo claims, admin basics. Live financial behaviour: disabled.