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
The site
- Clone and
npm i. Node 22 and Next 16 with the App Router, TypeScript and Tailwind v4. vercel env pullif you have the Vercel project, or write.env.localby 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.npm run devand open localhost:3000.npm run buildthennpm startfor a production build.npm run lintruns ESLint.
| Variable | Meaning |
|---|---|
SCRIBBLEHOOD_MODEL | Model 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_TOKEN | Upstash Redis. Optional: without them the store is an in-memory map that forgets on restart. |
STORE_PREFIX | Key prefix in Redis. Defaults to sh:. Mandatory on a shared instance. |
AUTH_SECRET | Hex, at least 32 characters. Signs the session cookie. Required for sign in; without it verify throws. |
FREE_SUMMONS_PER_DAY | Free summons per visitor per UTC day. Defaults to 15. |
SCRIBBLE_THRESHOLD | Whole SCRIBBLE tokens for the holder tier. Defaults to 50000. |
NEXT_PUBLIC_SCRIBBLE_TOKEN | SCRIBBLE token address. Unset until launch; while unset everyone is free tier and nothing touches the chain for balances. |
NEXT_PUBLIC_SCRIBBLE_BASKET | ScribbleBasket address. Unset: Own it swaps go straight to SwapRouter02, no fee. |
NEXT_PUBLIC_SCRIBBLE_OBJECTS | ScribbleObjects address. Unset: the Mint button is hidden and /api/nft answers 404. |
NEXT_PUBLIC_SCRIBBLE_REWARDS | ScribbleRewards address. Unset: the weekly drop card says no round yet. |
NEXT_PUBLIC_PONS_URL | Pons launchpad URL. When set, the Summon as token button appears. |
NEXT_PUBLIC_WC_PROJECT_ID | WalletConnect project id. Optional; injected wallets always work. |
NEXT_PUBLIC_SITE_URL | Absolute origin for share links and OG images. Falls back to the Vercel production URL, then localhost. |
DEPLOYER_FILE, TREASURY, SCRIBBLE_TOKEN, RPC_URL | Scripts 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).
build_manifest.pywrites one image prompt per dictionary concept intomanifest.json, keyed by the concept's slug, with a fixed 16-bit style preamble.- Generate the images however you like and list them in
jobs.jsonas slug and url (a local path works). process.py jobs.jsondownloads 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 pluspublic/sprites/manifest.json. Idempotent;--forcerewrites,--only a,blimits.sheet.pyrenders 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.