A retro pixel-art Web3 arcade racing game with daily tournaments, prize pools, and on-chain leaderboards. Built with Next.js, React, Solidity, and Supabase.
- Overview
- Features
- Tech Stack
- Project Structure
- Getting Started
- Environment Variables
- Smart Contracts
- Database Schema
- API Routes
- Game Mechanics
- Deployment
- Testing
- Contributing
- License
ARC CRYPTO RACE is a Web3 arcade racing game where players compete in daily tournaments. Players pay a 5 USDC entry fee to participate, race for 5 minutes, and compete for daily prizes distributed to the top 3 players.
- 🏁 Retro Pixel-Art Racing Game: Classic pseudo-3D racing experience
- 💰 Daily Tournaments: 5 USDC entry fee, prize pool distribution
- 🏆 On-Chain Leaderboards: Transparent, verifiable rankings
- 🔐 Web3 Integration: MetaMask wallet connection via Wagmi
- 📊 Real-Time Scoring: Distance, speed, and crash-based scoring system
- 🎵 Game Music: Retro racing soundtrack with mute/unmute controls
- ⏱️ 5-Minute Sessions: Timed gameplay with early stop option
- Progressive difficulty system (increases every minute)
- Dynamic traffic with AI-controlled cars
- Multiple billboard advertisements
- Smooth pseudo-3D rendering
- Responsive controls (arrow keys)
- EVM wallet connection (MetaMask)
- USDC token approval and payment
- Smart contract tournament entry
- On-chain checkpoint commits
- Transparent prize distribution
- Supabase PostgreSQL database
- Real-time leaderboard updates
- Score submission API
- Admin finalization endpoints
- Worker script for blockchain commits
- Framework: Next.js 16.0.7 (App Router)
- UI Library: React 19.2.0
- Styling: Tailwind CSS 4.1.9
- Components: shadcn/ui (Radix UI)
- Web3: Wagmi 3.1.0, Viem 2.41.2
- Icons: Lucide React
- Language: Solidity 0.8.20
- Framework: Hardhat 2.19.0
- Libraries: OpenZeppelin Contracts 5.4.0
- Network: ARC Testnet (Chain ID: 5042002)
- Database: Supabase (PostgreSQL)
- API: Next.js API Routes
- Worker: TypeScript (tsx)
- Language: Vanilla JavaScript
- Rendering: HTML5 Canvas
- Assets: Pixel-art sprites and music
arc-crypto-race/
├── app/ # Next.js App Router
│ ├── api/ # API Routes
│ │ ├── admin/ # Admin endpoints
│ │ ├── leaderboard/ # Leaderboard API
│ │ └── submit-score/ # Score submission
│ ├── game/ # Game page
│ ├── leaderboard/ # Leaderboard page
│ └── page.tsx # Landing page
│
├── components/ # React Components
│ ├── ui/ # shadcn/ui components
│ ├── GameCanvas.tsx # Game wrapper
│ ├── HUDScore.tsx # In-game HUD
│ ├── EnterTournamentButton.tsx # Tournament entry
│ └── WalletConnectButton.tsx # Wallet connection
│
├── contracts/ # Smart Contracts
│ ├── src/
│ │ └── Tournament.sol # Main contract
│ ├── scripts/
│ │ └── deploy.js # Deployment script
│ └── hardhat.config.js # Hardhat config
│
├── lib/ # Utilities
│ ├── contract.ts # Contract interactions
│ ├── supabase.ts # Supabase client
│ ├── scoring.ts # Score calculation
│ ├── wallet.ts # Wagmi config
│ └── dayId.ts # Day ID utilities
│
├── game/ # Game Engine (source)
│ ├── common.js # Core game logic
│ ├── game-wrapper.js # React integration
│ ├── stats.js # FPS counter
│ └── assets/ # Game assets
│ ├── images/ # Sprites
│ └── music/ # Audio files
│
├── public/ # Static Assets
│ └── game/ # Served game files
│
├── scripts/ # Utility Scripts
│ ├── worker.ts # Blockchain worker
│ ├── check-tournament-entry.ts # Entry verification
│ └── test-contract-interaction.ts # Contract tests
│
└── docs/ # Documentation
├── SUPABASE_SCHEMA.sql # Database schema
└── USDC_ADDRESS_UPDATE.md # USDC address info
- Node.js 18+ and npm/pnpm
- MetaMask or compatible EVM wallet
- ARC Testnet USDC (for testing)
- Supabase account (for database)
-
Clone the repository
git clone <repository-url> cd arc-crypto-race
-
Install dependencies
npm install cd contracts && npm install && cd ..
-
Set up environment variables
cp .env.example .env # Edit .env with your credentials -
Set up Supabase
- Create a Supabase project
- Run the schema:
docs/SUPABASE_SCHEMA.sql - Get your project URL and keys
-
Deploy smart contract (optional, for testing)
cd contracts npm run deploy:arc # Update NEXT_PUBLIC_TOURNAMENT_CONTRACT_ADDRESS in .env
-
Run development server
npm run dev
-
Start worker (in separate terminal)
npm run worker
Visit http://localhost:3000 to see the application.
Create a .env file in the root directory:
# Supabase
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
SUPABASE_DB_PASSWORD=your_db_password
# Blockchain - ARC Testnet
NEXT_PUBLIC_ARC_TESTNET_RPC_URL=https://rpc.testnet.arc.network
NEXT_PUBLIC_TOURNAMENT_CONTRACT_ADDRESS=0x...
USDC_ADDRESS=0x3600000000000000000000000000000000000000
# Admin
ADMIN_API_KEY=your_admin_api_key
# Contract Owner (for deploy and transactions)
PRIVATE_KEY=0x....env files. They contain sensitive information.
The main contract (contracts/src/Tournament.sol) manages:
- Entry Fees: 5 USDC per tournament entry
- Prize Distribution: 60% / 25% / 15% for 1st / 2nd / 3rd place
- Site Fee: 10% of total pool
- Checkpoints: Periodic leaderboard hash commits
- Finalization: Daily tournament closure and prize distribution
enterTournament(uint256 dayId): Enter a daily tournamentcommitCheckpoint(uint256 dayId, bytes32 hash): Commit leaderboard statefinalizeDay(uint256 dayId, address[3], uint256[3]): Finalize and distribute prizesgetDayInfo(uint256 dayId): Query tournament informationhasEntered(uint256 dayId, address): Check if wallet entered
cd contracts
npm run deploy:arcThe contract address will be printed. Update NEXT_PUBLIC_TOURNAMENT_CONTRACT_ADDRESS in .env.
The Supabase database uses the following tables:
scores: All raw score submissionsbest_scores: Best score per wallet per day (auto-updated via trigger)pending_commits: Queue for blockchain operationscommit_logs: Logs of completed blockchain transactions
See docs/SUPABASE_SCHEMA.sql for the complete schema.
-
POST /api/submit-score: Submit a game score{ "wallet": "0x...", "dayId": 20251211, "score": 12345 } -
GET /api/leaderboard?dayId=20251211: Get leaderboard for a day{ "dayId": 20251211, "leaderboard": [...], "checkpoints": [...], "onChainWinners": {...} }
POST /api/admin/finalize-day: Finalize a day's tournament- Requires
x-admin-api-keyheader - Body:
{ "dayId": 20251211 }
- Requires
Score = (distance × 10) + (maxSpeed × 2) - (elapsedTime × 5) - (crashes × 100)
- Duration: 5 minutes (300 seconds)
- Entry Fee: 5 USDC per day
- Difficulty: Increases every minute
- Controls: Arrow keys (left/right/up/down)
- Early Stop: "Stop Playing" button saves current score
- Level 1 (0-60s): Base difficulty
- Level 2 (60-120s): +20% more cars, +15% speed
- Level 3+: Further increases per minute
- Push to GitHub
- Import project in Vercel
- Add environment variables
- Deploy
Ensure all .env variables are set in Vercel dashboard.
The worker script (scripts/worker.ts) should run as a cron job or background service:
npm run workerOr use a service like:
- Vercel Cron Jobs
- Railway
- Render Cron Jobs
cd contracts
npm testnpx tsx scripts/test-contract-interaction.tsnpx tsx scripts/check-tournament-entry.ts [wallet_address]npx tsx scripts/check-transaction.ts [tx_hash]Comprehensive test for payment flow and prize distribution:
npx tsx scripts/test-payment-and-prize-distribution.ts [dayId]This script tests:
- Tournament entry payments (3 wallets)
- Prize pool accumulation
- Prize distribution (finalizeDay)
- Balance verification before/after
- Site fee distribution
See docs/TESTING_PAYMENT_AND_PRIZES.md for detailed guide.
All documentation is available in the /docs directory:
- Project Structure
- Integration Guide
- Testing Guide
- Testing Payment and Prizes
- Supabase Setup
- USDC Address Update
- Quick Start
- ARC Testnet Info
- Sprite Editing Guide
- Never commit
.envfiles - Use
.env.exampleas a template - Store private keys securely
- Use service role keys only on the server
- Contracts are audited (recommended before mainnet)
- Use OpenZeppelin contracts for security
- Owner-only functions are protected
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
This project uses the game engine from javascript-racer by jakesgordon, an excellent Outrun-style pseudo-3D racing game implementation in HTML5 and JavaScript.
The original game engine has been adapted and integrated into this Web3 application with:
- React/Next.js integration
- Web3 wallet connectivity
- On-chain tournament system
- Real-time leaderboards
- Prize pool distribution
Special thanks to jakesgordon for creating and open-sourcing the original racing game engine under the MIT license.
Original Project:
- Repository: jakesgordon/javascript-racer
- Website: jakesgordon.com/writing/javascript-racer/
See docs/LICENSE for license information.
- ARC Network Docs: https://docs.arc.network
- ArcScan Explorer: https://testnet.arcscan.app
- Supabase: https://supabase.com
- Next.js: https://nextjs.org
- Wagmi: https://wagmi.sh
For issues and questions:
- Open an issue on GitHub
- Check the documentation in
/docs - Review the test scripts in
/scripts
Built with ❤️ for the Web3 gaming community