Guidance for Claude Code (and any AI agent) working in this repository.
An agentic-commerce battle royale: AI agents pay $0.25 USDC via Coinbase's x402 protocol (HTTP 402) to enter an auto-playing battle on a 25×25 grid. Top 10 split 60% of the pot; 40% feeds a perpetual bonus-pool lottery.
Currently Base Sepolia testnet. Mainnet is the eventual target once the full loop is validated end-to-end with zero open bugs.
Core loop:
Agent → POST /join → 402 Payment Required → Pay x402 → Queue → Game → Rewards
Entry flows:
POST /join— single entryPOST /join/bulk— up to 100 entries per x402 signature- SDK:
@snakey/sdk(client.join(...)/client.joinBulk(...)/SnakeyClient.quickPlay(...))
snakey/
├── src/ # Backend (Node.js + Express + ws)
│ ├── server.js # HTTP + WebSocket + settlement observer + auto-payout cron
│ ├── game.js # Game engine (grid, snakes, clash RNG)
│ ├── jackpot.js # Bonus-pool contributions and tiered lottery rolls
│ ├── payout.js # CDP SDK v2 USDC transfers (gasless on Base)
│ ├── faucet.js # Testnet USDC faucet (CDP SDK)
│ ├── webhooks.js # Phase 10: HMAC-signed game-end webhooks
│ └── lib/
├── packages/snakey-sdk/ # Public SDK (@snakey/sdk)
├── frontend/ # SvelteKit spectator UI (Vercel)
├── scripts/ # Utility scripts
├── supabase/migrations/ # DB migrations
└── docs/
├── architecture.md
├── playing.md
└── reference/ # api, economics, x402, security, erc8004, database
- Payments:
@x402/expressv2.2.0. The middleware does NOT honor{ hooks }as a 3rd positional arg — that slot ispaywallConfigin v2.2.0. We use an explicitattachSettlementObserver(res, ...)insrc/server.jsthat hooksres.on('finish')to record payments on 200 and roll back queue + jackpot on 402. - Payouts:
@coinbase/cdp-sdkv2.CdpClient.getOrCreateAccount({ name })+account.transfer({ to, amount, token: 'usdc', network, gasless: true }). Coinbase sponsors gas on Base — no ETH balance required on the payout wallet. - Amount encoding for CDP: cents-first BigInt:
BigInt(Math.round(amount * 100)) * 10_000n(USDC is 6 decimals, socents × 10^4 = micros). - Network: Base Sepolia (
eip155:84532). USDC contract0x036CbD53842c5426634e7929541eC2318f3dCF7e. - Facilitator:
https://x402.org/facilitator(testnet). Expect occasional settlement failures — the observer handles rollback. - Randomness:
crypto.randomInteverywhere. SeesecureRandom()and game seed logging insrc/game.js/src/jackpot.js. - Jackpot tiers (Phase 11): MINI 3% / 5% payout · MEGA 0.3% / 18% · ULTRA 0.04% / 92% + full ticket reset.
- Auto-payout cron: every
AUTO_PAYOUT_INTERVAL_MS(default 2 min; production 30 s) with a guard flag to prevent overlap.
- No comments describing what code does — well-named identifiers already do that. Only add a comment when the why is non-obvious: a hidden invariant, a workaround for a specific bug, behavior that would surprise a reader.
- No scaffolding beyond the task. Don't add features, abstractions, or error handling for scenarios that can't actually occur.
- No backwards-compat shims unless the caller is external. Delete unused code; don't leave
// removedbreadcrumbs. - Validate at boundaries only — user input, external API responses. Trust internal helpers.
- Test:
npm testrunsnode --test scripts/**. The suite has to stay green; add regression tests when you fix a bug. - Lint SDK:
npm run lintinsidepackages/snakey-sdk(runstsc --noEmit).
Be autonomous on routine work. Fix bugs, run tests, update docs. Research via Context7 or web before guessing. Try multiple approaches when the first fails.
When you hit an error: read the actual message, search official docs for it, try a fix, test, iterate.
Ask only for:
- Architecture or product decisions
- Anything that changes external contracts (SDK public API, DB schema, payment flow)
- Destructive ops (force-push, reset, deleting branches or tables, sending real money)
npm install
npm run dev # watches src/ on port 3000
npm test # test suite
npm run frontend # SvelteKit dev serverDev mode bypasses x402 payments when X402_WALLET_ADDRESS is empty or NODE_ENV !== 'production'.
See .env.example. Required for production: X402_WALLET_ADDRESS, X402_NETWORK, CDP_API_KEY_ID, CDP_API_KEY_SECRET, CDP_WALLET_SECRET, SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, ADMIN_API_KEY.
Anything under .local/, private/, scratch/, notes/ — they're gitignored for a reason. Internal ops, project pitches, research scratch, historical archives. If you're writing something that isn't either (a) production code or (b) docs an external developer would actually read, route it to .local/.
The public/private split is enforced by .gitignore at the file level, not by branches. Everything tracked on main should be safe to make public whenever the maintainer flips repo visibility.