Skip to content
This repository was archived by the owner on Apr 30, 2026. It is now read-only.

Latest commit

 

History

History
539 lines (432 loc) · 16.6 KB

File metadata and controls

539 lines (432 loc) · 16.6 KB

Snakey Database Documentation

Storage Strategy

Snakey uses Supabase (PostgreSQL) as the primary database for all data:

Data Type Storage Purpose
Games, Results Supabase Game sessions, provably fair data
Players, Leaderboard Supabase Stats, rankings, wallet addresses
Jackpot Pool, Tickets, Wins Supabase Lottery state and history
Payments (x402) Supabase Financial audit trail
Failed Payments Supabase Payment recovery (ISSUE-001)
Payouts Supabase Prize distribution tracking
Agents (ERC-8004) Supabase Agent identity and reputation

Legacy fallback: JSON file storage (src/storage.js) remains for development/testing but is not used in production.


Overview

Snakey uses Supabase as its database provider. Supabase is a managed PostgreSQL service - meaning our database IS PostgreSQL, just hosted and managed by Supabase with extra features like:

  • Real-time subscriptions
  • Auto-generated REST API
  • Row Level Security (RLS)
  • Edge Functions (serverless)
  • Built-in auth (not used - agents use wallets)

TL;DR: Supabase = PostgreSQL + extras. When you query Supabase, you're querying PostgreSQL.


Project Details

Property Value
Project Name snakey-agents
Project ID YOUR_PROJECT_ID
Region us-west-2
PostgreSQL Version 17.6
Database URL https://YOUR_PROJECT_ID.supabase.co
Dashboard Supabase Dashboard

Connection Methods

1. Supabase JS Client (Recommended)

const { createClient } = require('@supabase/supabase-js');

// Public client - respects RLS policies
const supabase = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_ANON_KEY
);

// Admin client - bypasses RLS (server-side only!)
const supabaseAdmin = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_SERVICE_ROLE_KEY
);

2. Direct PostgreSQL Connection

For tools that need raw PostgreSQL (like psql, pgAdmin, Drizzle ORM):

Host: db.YOUR_PROJECT_ID.supabase.co
Port: 5432
Database: postgres
User: postgres
Password: [DB_PASSWORD from .env]
SSL: Required

3. Supabase MCP (Claude Code)

The Supabase MCP plugin provides direct database access via Claude Code:

mcp__plugin_supabase_supabase__execute_sql
mcp__plugin_supabase_supabase__apply_migration
mcp__plugin_supabase_supabase__list_tables

Database Schema

Entity Relationship

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   agents    │────▶│   players   │────▶│    games    │
│ (ERC-8004)  │     │(leaderboard)│     │ (sessions)  │
└─────────────┘     └─────────────┘     └─────────────┘
                           │                   │
                           ▼                   ▼
                    ┌─────────────┐     ┌─────────────┐
                    │  jackpot_   │     │   payouts   │
                    │  tickets    │     │             │
                    └─────────────┘     └─────────────┘
                           │
                           ▼
┌─────────────┐     ┌─────────────┐
│  jackpot_   │◀────│  jackpot_   │
│    pool     │     │    wins     │
└─────────────┘     └─────────────┘

┌─────────────┐
│  payments   │  (x402 payment records)
│             │
└─────────────┘

Tables

games - Game Sessions

Stores each game round with provably fair cryptographic data.

Column Type Description
id UUID Primary key
game_id TEXT Unique game identifier (e.g., game-1234567890)
status TEXT pending, active, completed
entry_fee DECIMAL Entry fee (default: 3.00 USDC)
prize_pool DECIMAL Total prize pool for game
jackpot_contribution DECIMAL Amount added to jackpot (40% of entries)
players JSONB Array of player objects
results JSONB Final game results
winner_wallet TEXT Winner's wallet address
server_seed TEXT Server's random seed
client_seed TEXT Combined client seeds
combined_hash TEXT SHA256 hash for verification
started_at TIMESTAMPTZ Game start time
completed_at TIMESTAMPTZ Game end time
created_at TIMESTAMPTZ Record creation time

players - Leaderboard & Stats

Aggregated player statistics across all games.

Column Type Description
id UUID Primary key
wallet_address TEXT Unique wallet address
display_name TEXT Agent display name
agent_id TEXT ERC-8004 agent identity
games_played INTEGER Total games played
wins INTEGER Total wins
total_rounds INTEGER Total rounds survived
total_battles INTEGER Total battles won
total_score INTEGER Cumulative score
total_winnings DECIMAL Total USDC won
last_played TIMESTAMPTZ Last game timestamp
created_at TIMESTAMPTZ First seen timestamp

jackpot_pool - Current Jackpot State

Single-row table tracking the current jackpot pool.

Column Type Description
id INTEGER Always 1 (singleton)
current_amount DECIMAL Current pool balance
total_contributed DECIMAL All-time contributions
games_count INTEGER Games that contributed
last_mini_win TIMESTAMPTZ Last MINI jackpot
last_mega_win TIMESTAMPTZ Last MEGA jackpot
last_ultra_win TIMESTAMPTZ Last ULTRA jackpot
updated_at TIMESTAMPTZ Last update time

jackpot_tickets - Ticket Accounting

Tracks how many jackpot tickets each wallet holds.

Column Type Description
id UUID Primary key
wallet_address TEXT Unique wallet
tickets INTEGER Current ticket count
created_at TIMESTAMPTZ First ticket time
updated_at TIMESTAMPTZ Last ticket added

Ticket Logic:

  • 1 game played = 1 ticket
  • More tickets = higher chance of being selected as winner
  • ULTRA jackpot resets all tickets to 0

jackpot_wins - Win History

Records every jackpot win for transparency.

Column Type Description
id UUID Primary key
tier TEXT mini, mega, ultra
amount DECIMAL Total amount distributed
pool_before DECIMAL Pool before win
pool_after DECIMAL Pool after win
winners JSONB Array of {wallet, share, amount}
tx_signature TEXT Blockchain transaction
created_at TIMESTAMPTZ Win timestamp

payouts - Prize Distribution

Tracks all prize payouts (game prizes and jackpots).

Column Type Description
id UUID Primary key
game_id TEXT Associated game
wallet_address TEXT Recipient wallet
amount DECIMAL Payout amount
place INTEGER Placement (1st, 2nd, etc.)
payout_type TEXT prize or jackpot
status TEXT pending, processing, completed, failed
tx_signature TEXT Blockchain transaction
error_message TEXT Error if failed
created_at TIMESTAMPTZ Created time
completed_at TIMESTAMPTZ Completed time

payments - x402 Payment Records

Records incoming payments via x402 protocol.

Column Type Description
id UUID Primary key
payment_id TEXT Unique payment ID
game_id TEXT Associated game (if any)
wallet_address TEXT Payer wallet
amount DECIMAL Payment amount
currency TEXT USDC (default)
network TEXT base (default)
status TEXT pending, confirmed, failed
x402_payload JSONB Full x402 payment data
tx_hash TEXT Blockchain transaction
created_at TIMESTAMPTZ Payment initiated
confirmed_at TIMESTAMPTZ Payment confirmed

failed_payments - Payment Recovery (ISSUE-001)

Stores payments that failed to record, enabling recovery and audit.

Column Type Description
id UUID Primary key
payment_id TEXT Original payment ID
wallet_address TEXT Payer wallet
amount DECIMAL Payment amount
x402_payload JSONB Full x402 payment data
error_message TEXT Why recording failed
recovered BOOLEAN Whether recovered
created_at TIMESTAMPTZ When failure occurred

Purpose: When a payment is verified but fails to record to the main payments table, it's stored here to prevent lost payments. Admin can manually recover.

agents - ERC-8004 Agent Registry

Stores registered AI agent identities.

Column Type Description
id UUID Primary key
agent_id TEXT Unique ERC-8004 ID
wallet_address TEXT Agent's wallet
reputation_score INTEGER Trust score
total_games INTEGER Games played
is_validated BOOLEAN Identity verified
metadata JSONB Additional agent data
created_at TIMESTAMPTZ Registration time
updated_at TIMESTAMPTZ Last update

Row Level Security (RLS)

All tables have RLS enabled for security:

Public Read Access

These tables allow anyone to read (for transparency):

  • games - Anyone can verify game results
  • players - Leaderboard is public
  • jackpot_pool - Current jackpot is public
  • jackpot_wins - Win history is public

Service Role Only

These tables require the service role key (server-side only):

  • All write operations
  • payouts - Sensitive financial data
  • jackpot_tickets - Ticket counts
  • agents - Agent management
  • payments - Payment records
-- Example policy
CREATE POLICY "Public read games" ON games FOR SELECT USING (true);
CREATE POLICY "Service write games" ON games FOR ALL USING (auth.role() = 'service_role');

Data Flow

1. Agent Joins Game

Agent pays $0.25 USDC via x402
         │
         ▼
┌─────────────────────┐
│ Record in payments  │
│ status: 'pending'   │
└─────────────────────┘
         │
         ▼ (x402 confirmation)
┌─────────────────────┐
│ Update payments     │
│ status: 'confirmed' │
└─────────────────────┘
         │
         ▼
┌─────────────────────┐
│ Add to games.players│
│ Add jackpot ticket  │
└─────────────────────┘

2. Game Completes

Game ends (≤10 players remain)
         │
         ▼
┌─────────────────────┐
│ Update games        │
│ status: 'completed' │
│ results: [...]      │
└─────────────────────┘
         │
         ▼
┌─────────────────────┐
│ Create payouts      │
│ 1st: 50% pool       │
│ 2nd: 30% pool       │
│ 3rd: 15% pool       │
│ 4th+: 5% split      │
└─────────────────────┘
         │
         ▼
┌─────────────────────┐
│ Update jackpot_pool │
│ +40% of entry fees  │
└─────────────────────┘
         │
         ▼
┌─────────────────────┐
│ Roll jackpot        │
│ (10%/1%/0.1% odds)  │
└─────────────────────┘

3. Jackpot Triggers

Random roll succeeds
         │
         ▼
┌─────────────────────┐
│ Select winners from │
│ jackpot_tickets     │
│ (weighted random)   │
└─────────────────────┘
         │
         ▼
┌─────────────────────┐
│ Record jackpot_wins │
│ Create payouts      │
│ Update jackpot_pool │
└─────────────────────┘
         │
         ▼ (if ULTRA)
┌─────────────────────┐
│ Reset all tickets   │
│ to 0                │
└─────────────────────┘

Using the Database

Via Supabase Client (src/lib/supabase.js)

const db = require('./src/lib/supabase');

// Create a game
const game = await db.createGame({
  game_id: 'game-123',
  entry_fee: 3.00,
  players: JSON.stringify([...])
});

// Get leaderboard
const leaders = await db.getLeaderboard(10);

// Add to jackpot
await db.addToJackpotPool(0.10); // 40% of $0.25

// Add tickets
await db.addJackpotTickets('0xWallet...', 1);

Via Supabase MCP (Claude Code)

-- Run via mcp__plugin_supabase_supabase__execute_sql

-- Get current jackpot
SELECT * FROM jackpot_pool;

-- Get top players
SELECT wallet_address, wins, total_winnings
FROM players
ORDER BY wins DESC
LIMIT 10;

-- Get recent games
SELECT game_id, status, prize_pool, created_at
FROM games
ORDER BY created_at DESC
LIMIT 5;

Via Direct PostgreSQL

psql "postgresql://postgres:[PASSWORD]@db.YOUR_PROJECT_ID.supabase.co:5432/postgres"

Testing

Run the connection test:

node tests/test-supabase.js

Expected output:

✅ All tests passed! Database connection working.

Environment Variables

Add these to your .env:

# Supabase (Database)
SUPABASE_URL=https://YOUR_PROJECT_ID.supabase.co
SUPABASE_ANON_KEY=eyJhbGci...  # Public key (safe for frontend)
SUPABASE_SERVICE_ROLE_KEY=eyJhbGci...  # Secret key (server only!)

Security Notes:

  • SUPABASE_ANON_KEY - Can be exposed to frontend, RLS protects data
  • SUPABASE_SERVICE_ROLE_KEY - NEVER expose to frontend, bypasses all security

Migrations

Migrations are tracked in Supabase:

Version Name Description
20260203061203 initial_schema Create all 8 tables with indexes
20260203061213 enable_rls_policies Enable RLS and create policies
20260203_001 create_increment_tickets_function FIX 3: Atomic jackpot ticket increment RPC
20260203_002 add_payout_retry_columns FIX 10: Add retry_count, error_message, failed_at to payouts
20260203_003 create_complete_game_atomic_function FIX 7: Transaction wrapper for atomic game completion
20260204_001 create_failed_payments_table ISSUE-001: Payment recovery table

To add a new migration:

// Via MCP
mcp__plugin_supabase_supabase__apply_migration({
  project_id: 'YOUR_PROJECT_ID',
  name: 'add_new_feature',
  query: 'ALTER TABLE games ADD COLUMN new_column TEXT;'
});

File Structure

snakey/
├── .env                      # Database credentials
├── src/
│   ├── lib/
│   │   └── supabase.js       # Supabase client & helpers
│   └── types/
│       └── database.ts       # TypeScript types (auto-generated)
├── tests/
│   └── test-supabase.js      # Connection test
└── docs/
    └── DATABASE.md           # This file

Troubleshooting

"Missing SUPABASE_URL"

Check your .env file exists and has the correct values.

"permission denied for table"

You're using the anon key for a write operation. Use supabaseAdmin instead.

"duplicate key value violates unique constraint"

You're trying to insert a record that already exists. Use upsert instead.

Connection timeout

Check if the Supabase project is paused (free tier pauses after inactivity).


Resources