Scribblehood
Run it yourself

Run it yourself

Clone, install, run. Then the environment variables, the contracts, the pool probe, the sprite pipeline and the weekly drop script.

$ git clone https://github.com/oggyfoxy/scribblehood && cd scribblehood
$ npm i
$ vercel env pull
  # optional: writes .env.local with the vars below
$ npm run dev
  # http://localhost:3000
From nothing to a running sandbox. Without any env vars the app still runs: the store is in memory and the resolver is the dictionary.

The site

  1. Clone and npm i. Node 22 and Next 16 with the App Router, TypeScript and Tailwind v4.
  2. vercel env pull if you have the Vercel project, or write .env.local by hand from the table below. Nothing is required to run; the model, Redis, sign in and the contracts each switch on when their variable exists.
  3. npm run dev and open localhost:3000. npm run build then npm start for a production build. npm run lint runs ESLint.
VariableMeaning
SCRIBBLEHOOD_MODELModel id for the resolver through the Vercel AI Gateway. Defaults to openai/gpt-5-mini. Gateway auth is the Vercel OIDC token, which vercel env pull writes and Vercel sets automatically.
KV_REST_API_URL, KV_REST_API_TOKENUpstash Redis. Optional: without them the store is an in-memory map that forgets on restart.
STORE_PREFIXKey prefix in Redis. Defaults to sh:. Mandatory on a shared instance.
AUTH_SECRETHex, at least 32 characters. Signs the session cookie. Required for sign in; without it verify throws.
FREE_SUMMONS_PER_DAYFree summons per visitor per UTC day. Defaults to 15.
SCRIBBLE_THRESHOLDWhole SCRIBBLE tokens for the holder tier. Defaults to 50000.
NEXT_PUBLIC_SCRIBBLE_TOKENSCRIBBLE token address. Unset until launch; while unset everyone is free tier and nothing touches the chain for balances.
NEXT_PUBLIC_SCRIBBLE_BASKETScribbleBasket address. Unset: Own it swaps go straight to SwapRouter02, no fee.
NEXT_PUBLIC_SCRIBBLE_OBJECTSScribbleObjects address. Unset: the Mint button is hidden and /api/nft answers 404.
NEXT_PUBLIC_SCRIBBLE_REWARDSScribbleRewards address. Unset: the weekly drop card says no round yet.
NEXT_PUBLIC_PONS_URLPons launchpad URL. When set, the Summon as token button appears.
NEXT_PUBLIC_WC_PROJECT_IDWalletConnect project id. Optional; injected wallets always work.
NEXT_PUBLIC_SITE_URLAbsolute origin for share links and OG images. Falls back to the Vercel production URL, then localhost.
DEPLOYER_FILE, TREASURY, SCRIBBLE_TOKEN, RPC_URLScripts only: the deployer key file (cast wallet new --json format, default ~/.scribblehood/deployer.json), the fee treasury (default the deployer), the token to bake into ScribbleRewards (default zero), and the RPC.

Contracts

The contracts are a Foundry project in contracts/ with OpenZeppelin vendored under lib/. Tests cover the basket, the objects, the rewards, and a fork test against mainnet pools that only runs when you pass a fork URL.

$ cd contracts && forge test
$ forge test --fork-url https://rpc.mainnet.chain.robinhood.com --match-contract Fork
$ npx tsx scripts/publish-rewards.ts --selftest
  # the merkle vector both sides check

Deploying is a shell script around a Foundry script. It dry runs by default and prints the gas and ETH needed; the key never leaves the shell.

$ cast wallet new --json > ~/.scribblehood/deployer.json
  # then fund that address with ETH on Robinhood Chain
$ scripts/deploy-contracts.sh
  # dry run: simulate, print gas
$ scripts/deploy-contracts.sh --broadcast
  # deploys, verifies on Blockscout, writes contracts/deployments/4663.json, prints the three NEXT_PUBLIC_ vars
$ scripts/transfer-ownership.sh 0xNewOwner
  # starts the two-step handover on all three

Pools

node scripts/probe-pools.mjs asks the Uniswap factory on mainnet for a USDG pool behind every Stock Token in src/lib/chain.ts, at every fee tier, quotes 100 USDG through each, and writes src/lib/pools.json. That file is the ground truth for what Own it can buy; rerun it when Robinhood lists more tokens or pools appear.

The sprite pipeline

Detailed sprites are optional and live in public/sprites. The pipeline is three Python scripts in scripts/sprites (numpy, Pillow, scipy).

  1. build_manifest.py writes one image prompt per dictionary concept into manifest.json, keyed by the concept's slug, with a fixed 16-bit style preamble.
  2. Generate the images however you like and list them in jobs.json as slug and url (a local path works).
  3. process.py jobs.json downloads each once, floods the white background to alpha from the corners, trims, detects the generator's pixel grid, samples block centers, fits into 96 by 96 with nearest neighbour, quantizes to 48 colors and writes a PNG-8 with alpha plus public/sprites/manifest.json. Idempotent; --force rewrites, --only a,b limits.
  4. sheet.py renders a contact sheet at 3x for review.
$ python3 scripts/sprites/build_manifest.py
$ python3 scripts/sprites/process.py scripts/sprites/jobs.json --only rocket,gaming-pc
$ python3 scripts/sprites/sheet.py
  # scripts/sprites/sheet.png

The weekly drop

$ npx tsx --env-file=.env.local scripts/publish-rewards.ts --pool 10000 --dry
  # prints the round without writing
$ npx tsx --env-file=.env.local scripts/publish-rewards.ts --pool 10000
  # writes the round and proofs to the store
$ npx tsx --env-file=.env.local scripts/publish-rewards.ts --pool 10000 --broadcast --round 3
  # also sends publishRound; the contract must already hold the tokens

Flags: --pool in whole tokens (required), --round (default latest plus one), --top (default 10), --decimals (read from the token when it is set on chain, else 18). The details of who gets what are on Free tier and SCRIBBLE.