In a world of sweets, only the fewest get the feast.
Thế giới ngọt ngào, ai ít hơn… ăn nhiều hơn!
🎮 Solana-powered minority game where players stake, commit their choice (Milk 🍼 or Cacao 🍫), reveal, and the minority side wins the pool!
📚 Documentation: DESIGN.md · SPRINT_PLAN.md · SPRINT_PLAN_S3.md
🔧 Scripts Guide: scripts/README.md
- Overview
- Prerequisites
- Quick Start
- Solana Program Development
- Frontend Development
- Deployment Guide
- Gameplay Flow
- Architecture
- Troubleshooting
- Contributing
ChocoChoco is an on-chain minority game built on Solana using the commit-reveal pattern to ensure fairness and prevent front-running.
Primary (Solana):
- 🦀 Program: Anchor Framework (Rust)
- ⚛️ Frontend: React 19 + TypeScript + Vite
- 🎨 Styling: Tailwind CSS 3.4
- 🔗 Web3: @solana/web3.js + @solana/wallet-adapter
- 🌐 Network: Devnet/Testnet/Mainnet-beta
Legacy (EVM - Optional):
- 📜 Solidity 0.8.x with Foundry
- 🌉 Base Sepolia, Polygon Amoy
- Commit Phase: Players stake SOL and submit a hidden commitment (hash of choice + salt)
- Reveal Phase: Players reveal their true choice (Milk 🍼 or Cacao 🍫) with the salt
- Settlement: Minority side wins! Winners split the pool minus a small crumb fee (3%)
- Claim: Winners claim their rewards; UI prevents double-claiming
Key Features:
- 🛡️ MEV-resistant (commit-reveal)
- ⚡ Efficient on-chain logic (no loops, pull-payment pattern)
- 🔐 Secure (reentrancy guards, CEI pattern)
- 📊 Indexable events for analytics
-
Node.js & Package Manager
# Node.js LTS (v20+) node --version # Should be v20+ # pnpm (recommended) npm install -g pnpm pnpm --version # Should be 8.0+
-
Solana CLI (for program deployment)
sh -c "$(curl -sSfL https://release.solana.com/stable/install)" solana --version # Should be 1.18+
-
Anchor CLI (for program development)
cargo install --git https://github.com/coral-xyz/anchor --tag v0.29.0 anchor-cli anchor --version # Should be 0.29.0+ -
Rust (if building from source)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh rustup --version
- Foundry for Solidity contracts:
curl -L https://foundry.paradigm.xyz | bash foundryup
- Solana: Phantom, Solflare, or any Solana wallet with devnet/testnet SOL
- EVM (legacy): MetaMask or WalletConnect-compatible wallet
# Clone repository
git clone https://github.com/NQKhaixyz/chocochoco.git
cd chocochoco
# Install all dependencies (monorepo)
pnpm install# Frontend environment
cd frontend
cp .env.example .env
# Edit .env with your values (see next section)Required Frontend Variables:
# Solana Configuration
VITE_SOLANA_CLUSTER=devnet # devnet | testnet | mainnet-beta
VITE_PROGRAM_ID=J5GgxY8zobKvjJovnENncHDLWVQ2gBPH2skhTKL8JuGz # Your deployed program
VITE_STAKE_LAMPORTS=1000000 # Default stake (0.001 SOL)
# Optional: Custom RPC
VITE_SOLANA_RPC_URL= # Leave empty to use cluster default💡 Tip: After deploying your program, scripts/solana-deploy.sh will automatically update VITE_PROGRAM_ID in your .env!
# Frontend only (connects to existing deployed program)
cd frontend
pnpm dev
# Open http://localhost:5173OR use task-based workflow:
# Automatically updates issue status (S1.11)
pnpm task:s1_11:start # Marks issue "in-progress" & starts dev server
# ... make changes ...
pnpm task:s1_11:done # Marks issue "done"The deploy scripts automatically read from your Solana CLI config (no .env needed for scripts!):
# Check current config
solana config get
# Set cluster (devnet recommended for testing)
solana config set --url devnet
# Check your balance
solana balance
# Airdrop SOL if needed (devnet/testnet only)
solana airdrop 2cd contracts # Or wherever your Anchor program is
# Build with Anchor
anchor build
# Output: target/deploy/your_program.so & target/idl/your_program.json# Unit tests
anchor test
# Integration tests (requires local validator)
anchor test --skip-local-validator # If validator already running# Terminal 1: Start local validator
solana-test-validator
# Terminal 2: Deploy to local
anchor deploy --provider.cluster localnet
# Terminal 3: Run tests
anchor test --skip-deployfrontend/
├── src/
│ ├── components/ # UI components
│ │ ├── SolanaConnect.tsx # Wallet connection
│ │ ├── SolanaClaimPanel.tsx # Claim rewards
│ │ ├── SolanaCountdown.tsx # Time countdown
│ │ └── RevealForm.tsx # Reveal screen
│ ├── hooks/ # Custom React hooks
│ │ ├── useSolanaTime.ts # Chain time sync
│ │ ├── useCountdown.ts # Countdown logic
│ │ └── useSolanaEvents.ts # Event subscriptions
│ ├── lib/ # Utilities
│ │ ├── time-format.ts # Time formatting
│ │ ├── player-round.ts # State parsing
│ │ └── pda.ts # PDA derivation
│ ├── solana/ # Solana integration
│ │ ├── program.ts # Program instance
│ │ └── instructions.ts # Instruction builders
│ └── styles/ # CSS & theming
└── public/assets/ # Static assets
cd frontend
# Development server (hot reload)
pnpm dev # Starts on localhost:5173
# Type checking
pnpm type-check # Or: tsc --noEmit
# Linting
pnpm lint
# Build for production
pnpm build # Output: dist/
# Preview production build
pnpm previewEach Sprint 1 task has dedicated scripts that auto-update issue status:
# S1.5 - FE Project Setup
pnpm task:s1_5:start # Mark in-progress, start dev
pnpm task:s1_5:done # Mark done
# S1.6 - Join/Commit Screen
pnpm task:s1_6:start
pnpm task:s1_6:done
# S1.7 - Reveal Screen
pnpm task:s1_7:start
pnpm task:s1_7:done
# S1.8 - Claim Screen
pnpm task:s1_8:start
pnpm task:s1_8:done
# S1.9 - Countdown & Helpers
pnpm task:s1_9:start
pnpm task:s1_9:done
# S1.10 - Env & Config
pnpm task:s1_10:start
pnpm task:s1_10:done
# S1.11 - Docs & Runbook (current)
pnpm task:s1_11:start
pnpm task:s1_11:doneThese scripts use scripts/update-issue-status.mjs to automatically update docs/issues/*.md files.
# From repo root, ensure you have SOL balance
solana balance # Should show at least 2 SOL on devnet
# Deploy program + publish IDL + update frontend/.env automatically!
chmod +x scripts/solana-deploy.sh
scripts/solana-deploy.sh devnetWhat happens:
- ✅ Builds program with
anchor build - ✅ Uses your
solana configfor cluster/wallet - ✅ Deploys program to devnet
- ✅ Publishes IDL on-chain (
anchor idl init) - ✅ Saves deployment info to
solana/deployments.json - ✅ Automatically updates
frontend/.env:- Sets
VITE_SOLANA_CLUSTER=devnet - Sets
VITE_PROGRAM_ID=<your_deployed_program_id>
- Sets
chmod +x scripts/solana-verify.sh
# Verify latest deployment (reads from solana/deployments.json)
scripts/solana-verify.sh devnet
# Or verify specific program ID
scripts/solana-verify.sh devnet YourProgramId111111...Output example:
🔍 Verifying program deployment on devnet...
Program Id: J5GgxY8zobKvjJovnENncHDLWVQ2gBPH2skhTKL8JuGz
Authority: YourWalletAddress11111111111111111
Balance: 0.5 SOL
Executable Data Length: 123456 bytes
✅ Program ID matches frontend/.env
✅ IDL found on-chain!
📊 Explorer: https://explorer.solana.com/address/J5GgxY8zobKvjJovnENncHDLWVQ2gBPH2skhTKL8JuGz?cluster=devnet
# Switch cluster
solana config set --url testnet
solana airdrop 2
# Deploy (same command, different cluster)
scripts/solana-deploy.sh testnet
scripts/solana-verify.sh testnet# Switch to mainnet
solana config set --url mainnet-beta
# Ensure you have real SOL for deployment (~1-2 SOL)
solana balance
# Deploy
scripts/solana-deploy.sh mainnet-beta
scripts/solana-verify.sh mainnet-betaAll deployments are logged in solana/deployments.json:
[
{
"program": "chocochoco_game",
"programId": "J5GgxY8zobKvjJovnENncHDLWVQ2gBPH2skhTKL8JuGz",
"network": "devnet",
"slot": 123456789,
"explorer": "https://explorer.solana.com/address/J5GgxY8zobKvjJovnENncHDLWVQ2gBPH2skhTKL8JuGz?cluster=devnet",
"timestamp": "2025-10-26T10:30:00.000Z"
}
]Publish/Update IDL manually:
# Initialize new IDL
anchor idl init <PROGRAM_ID> target/idl/your_program.json \
--provider.cluster devnet
# Update existing IDL
anchor idl upgrade <PROGRAM_ID> target/idl/your_program.json \
--provider.cluster devnet
# Fetch IDL from chain
anchor idl fetch <PROGRAM_ID> --provider.cluster devnetOr use the verify script:
scripts/solana-verify.sh devnet <PROGRAM_ID> target/idl/your_program.jsonPhase 1: Commit (Hidden Choice)
Players stake SOL and submit a commitment without revealing their choice:
// Frontend generates commitment
const salt = crypto.randomBytes(32);
const commitment = keccak256(abi.encodePacked(choice, salt));
// Submit to program
await program.methods
.commitMeow(commitment)
.accounts({
round: roundPda,
playerRound: playerRoundPda,
player: wallet.publicKey,
systemProgram: SystemProgram.programId,
})
.signers([wallet])
.rpc();During the reveal window, players reveal their actual choice + salt:
await program.methods
.revealMeow(choice, salt) // choice: 0 = Milk, 1 = Cacao
.accounts({
round: roundPda,
playerRound: playerRoundPda,
player: wallet.publicKey,
})
.signers([wallet])
.rpc();After reveal deadline, anyone can trigger settlement:
await program.methods
.settleRound()
.accounts({
round: roundPda,
treasury: treasuryPubkey,
})
.rpc();
// Program emits RoundMeowed event with winner dataWinners claim their share of the pool:
await program.methods
.claimTreat()
.accounts({
round: roundPda,
playerRound: playerRoundPda,
player: wallet.publicKey,
})
.signers([wallet])
.rpc();UI automatically:
- ✅ Checks if player is a winner
- ✅ Prevents double-claiming
- ✅ Shows payout amount
- ✅ Displays animations (win/lose)
┌─────────────────────────────────────────────────────────┐
│ Solana Program │
│ ┌──────────────────────────────────────────────────┐ │
│ │ ChocoChocoGame (Anchor) │ │
│ │ - commitMeow() │ │
│ │ - revealMeow() │ │
│ │ - settleRound() │ │
│ │ - claimTreat() │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
│ RPC calls & events
▼
┌─────────────────────────────────────────────────────────┐
│ Frontend (React) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Commit │→ │ Reveal │→ │ Claim │ │
│ │ Screen │ │ Screen │ │ Screen │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ Components: │
│ - SolanaConnect (wallet adapter) │
│ - SolanaCountdown (time sync) │
│ - SolanaClaimPanel (rewards) │
└─────────────────────────────────────────────────────────┘
│
│ Queries
▼
┌─────────────────────────────────────────────────────────┐
│ Indexer (Optional - Future) │
│ - The Graph / SubQuery │
│ - Leaderboards │
│ - Analytics │
└─────────────────────────────────────────────────────────┘
Round Account:
#[account]
pub struct Round {
pub status: RoundStatus, // Created | CommitOpen | RevealOpen | Settled
pub commit_deadline: i64, // Unix timestamp
pub reveal_deadline: i64, // Unix timestamp
pub stake_lamports: u64, // Required stake amount
pub milk_count: u32, // Players who chose Milk
pub cacao_count: u32, // Players who chose Cacao
pub milk_pool: u64, // Total lamports staked on Milk
pub cacao_pool: u64, // Total lamports staked on Cacao
pub winner_side: Option<Tribe>, // Milk | Cacao | None (tie)
pub bump: u8, // PDA bump seed
}PlayerRound Account:
#[account]
pub struct PlayerRound {
pub commitment: [u8; 32], // keccak256(choice || salt)
pub tribe: Option<Tribe>, // Revealed choice
pub revealed: bool, // Has player revealed?
pub claimed: bool, // Has player claimed reward?
pub bump: u8, // PDA bump seed
}Round Status Flow:
Created
│
│ initialize_round()
▼
CommitOpen ◄─┐
│ │ Players submit commitments
│ │ (within commit window)
▼ │
RevealOpen │
│ │ Players reveal choices
│ │ (within reveal window)
▼ │
Settled │
│ │ Settlement triggered
│ │ Winners claim rewards
└───────────┘
New round starts
- Commit-Reveal Pattern: Prevents front-running by hiding choices until reveal phase
- Pull Payment: Winners call
claimTreat()themselves (no loops, gas-efficient) - PDA Architecture: Deterministic addresses for Round and PlayerRound accounts
- Event-Driven: Emits events for indexers and real-time UI updates
- CEI Pattern: Checks-Effects-Interactions to prevent reentrancy
Total Pool = Milk Pool + Cacao Pool
Crumb Fee (3%) → Treasury
Remaining Pool (97%) → Winner Pool
Per-Winner Payout = Winner Pool / Winner Count
Example:
- 10 players stake 0.01 SOL each (5 Milk, 5 Cacao)
- Total pool: 0.1 SOL
- Milk side wins (minority with 5 players)
- Crumb fee: 0.003 SOL → Treasury
- Winner pool: 0.097 SOL
- Each Milk player claims: 0.0194 SOL (1.94x return)
Developer Workflow:
1. Configure Solana CLI
$ solana config set --url devnet
$ solana config set --keypair ~/.config/solana/id.json
2. Build Program
$ cd contracts && anchor build
3. Deploy (auto-updates frontend/.env!)
$ scripts/solana-deploy.sh devnet
✅ Updates frontend/.env:
VITE_SOLANA_CLUSTER=devnet
VITE_PROGRAM_ID=<deployed_program_id>
4. Frontend Reads Config
const programId = new PublicKey(import.meta.env.VITE_PROGRAM_ID)
const cluster = import.meta.env.VITE_SOLANA_CLUSTER
The indexer service listens to Solana program events and provides REST APIs for leaderboards and analytics.
Location: indexer/
Features:
- Real-time event listening via WebSocket
- PostgreSQL storage with automatic deduplication
- REST APIs for leaderboard queries
- Automatic backfill on startup
- Helius Streams support (optional)
Quick Start:
cd indexer
# Install dependencies
pnpm install
# Configure
cp .env.example .env
# Edit PROGRAM_ID, DATABASE_URL, etc.
# Create database
createdb chocochoco_indexer
# Run migrations
pnpm migrate
# Start indexer
pnpm devAPI Endpoints:
# Health check
GET http://localhost:3001/health
# Top payout leaderboard
GET http://localhost:3001/leaderboard/top-payout?limit=50
# Weekly win rate
GET http://localhost:3001/leaderboard/weekly-winrate
# Get round by ID
GET http://localhost:3001/rounds/:id
# Get player rounds
GET http://localhost:3001/player/:address/rounds?limit=50See indexer/README.md for complete documentation.
Error: "Insufficient funds for deployment"
# Check balance
solana balance
# Airdrop SOL (devnet/testnet only)
solana airdrop 2
# Wait and check again
solana balanceError: "Invalid keypair"
# Generate new keypair
solana-keygen new --outfile ~/.config/solana/id.json
# Or use existing one
solana config set --keypair /path/to/your-keypair.jsonError: "Cluster RPC URL not responding"
# Try different RPC endpoint
solana config set --url https://api.devnet.solana.com
# Or use custom RPC (Helius, Alchemy, QuickNode)
solana config set --url https://your-custom-rpc.comError: "anchor: command not found"
# Install Anchor CLI
cargo install --git https://github.com/coral-xyz/anchor --tag v0.29.0 anchor-cli
# Verify installation
anchor --versionError: "Program ID mismatch"
# Check program address from keypair
solana address -k target/deploy/your_program-keypair.json
# Update Anchor.toml [programs.devnet] section
# Update lib.rs declare_id!() macro
# Rebuild
anchor buildError: "IDL account not found"
# Publish IDL manually
anchor idl init <PROGRAM_ID> target/idl/your_program.json \
--provider.cluster devnet
# Verify
anchor idl fetch <PROGRAM_ID> --provider.cluster devnetError: "Cannot find module '@solana/web3.js'"
cd frontend
pnpm install
# If still errors, try clean install
rm -rf node_modules pnpm-lock.yaml
pnpm installError: "Property 'env' does not exist on type 'ImportMeta'"
Solution: Restart TypeScript server
- VS Code:
Cmd/Ctrl + Shift + P→ "TypeScript: Restart TS Server" - Or check
frontend/src/vite-env.d.tsexists
Error: "Buffer is not defined"
Solution: Ensure @types/node is installed and tsconfig.json includes:
{
"compilerSettings": {
"types": ["vite/client", "node"]
}
}Error: "Wallet not connected"
- Ensure wallet extension is installed (Phantom, Solflare)
- Check wallet is unlocked
- Verify wallet is set to correct cluster (devnet/testnet/mainnet)
- Try refreshing page
Environment variables not updating:
Vite requires server restart after .env changes:
# Stop dev server (Ctrl+C)
# Edit .env
# Restart
pnpm devScripts not executable (Unix/Mac):
chmod +x scripts/*.shPowerShell syntax errors (Windows):
Use ; instead of && for command chaining:
cd c:\path\to\repo; scripts\solana-deploy.sh devnetError: "Transaction simulation failed: Blockhash not found"
Solution: Network congestion or old blockhash
// Add commitment level and retry logic
const { blockhash } = await connection.getLatestBlockhash('confirmed');
// Retry transaction with new blockhashError: "Transaction too large"
Solution: Program may be too large for deployment
# Optimize program size
cargo build-bpf --features "no-entrypoint"
# Or increase compute units in Anchor.tomlError: "Custom program error: 0x1"
Solution: Check program error codes in IDL or Anchor errors
# View program logs
solana logs <PROGRAM_ID>
# Or use anchor with verbose flag
anchor test --verboseError: "Cluster mismatch"
Ensure frontend cluster matches program deployment:
# Frontend .env
VITE_SOLANA_CLUSTER=devnet # Must match deployed cluster
# Verify program exists on cluster
solana program show <PROGRAM_ID> --url devnetError: "Wallet balance shows 0 SOL"
- Ensure wallet is set to correct cluster in extension
- Check airdrop limits (devnet: 2 SOL per request, max 10 SOL/day)
- Try alternative airdrop: https://solfaucet.com/
Error: "429 Too Many Requests"
Solution: Use paid RPC provider
# Frontend .env
VITE_SOLANA_RPC_URL=https://rpc.helius.xyz/?api-key=YOUR_KEY
# Or use Alchemy, QuickNode, Triton, etc.Error: "IDL not found for program"
# Check if IDL exists on-chain
anchor idl fetch <PROGRAM_ID> --provider.cluster devnet
# If not found, publish it
anchor idl init <PROGRAM_ID> target/idl/your_program.json \
--provider.cluster devnetError: "IDL version mismatch"
# Upgrade IDL after program update
anchor idl upgrade <PROGRAM_ID> target/idl/your_program.json \
--provider.cluster devnetError: "Reveal phase not started yet"
- Check countdown component (uses on-chain time)
- Ensure frontend uses
useSolanaTime()hook for accurate time sync - Verify round deadlines in program state
Error: "Reveal deadline passed"
- Commitment window closed, cannot reveal
- Wait for next round
- Check if admin can extend deadline (if implemented)
Hash mismatch error:
// Ensure commitment matches reveal
const commitment = keccak256(abi.encodePacked(choice, salt));
// Salt must be exactly the same bytes used in commit
// Store salt securely in localStorage or stateError: "Already claimed"
UI should prevent this, but if error occurs:
- Check
playerRound.claimedflag - Ensure UI refetches state after claim transaction
Error: "Not a winner"
- Check
round.winner_sidematchesplayerRound.tribe - Handle tie case (tie may refund or roll over)
Logs & Debugging:
# View program logs in real-time
solana logs <PROGRAM_ID>
# View recent logs
solana logs <PROGRAM_ID> --before <SLOT_NUMBER>
# Get transaction details
solana transaction <SIGNATURE>Explorers:
- Devnet: https://explorer.solana.com/?cluster=devnet
- Testnet: https://explorer.solana.com/?cluster=testnet
- Mainnet: https://explorer.solana.com/
Community Support:
- GitHub Issues: https://github.com/NQKhaixyz/chocochoco/issues
- Solana Discord: https://solana.com/discord
- Anchor Discord: https://discord.gg/anchor
main- Stable production branchdev- Integration branch for testingfeat/*- Feature branchesfix/*- Bugfix branches
Use Conventional Commits format:
feat: add commit validation
fix: resolve double-claim bug
chore: update dependencies
docs: improve README deployment section
test: add unit tests for settle logic- Fork the repository
- Create your feature branch:
git checkout -b feat/amazing-feature - Commit your changes:
git commit -m 'feat: add amazing feature' - Push to the branch:
git push origin feat/amazing-feature - Open a Pull Request
# Install dependencies
pnpm install
# Create feature branch
git checkout -b feat/my-feature
# Make changes, run tests
anchor test # Program tests
cd frontend && pnpm lint # Frontend linting
# Commit with conventional format
git commit -m "feat: add new feature"
# Push and create PR
git push origin feat/my-feature- ✅ All tests must pass
- ✅ TypeScript strict mode (no
anytypes) - ✅ ESLint & Prettier formatting
- ✅ No console.log in production code
- ✅ Update documentation for API changes
- DESIGN.md - Complete game mechanics and architecture
- SPRINT_PLAN.md - Sprint 1-2 roadmap (MVP → Production)
- SPRINT_PLAN_S3.md - Sprint 3 GameFi expansion (NFTs, tokens, marketplace)
- scripts/README.md - Detailed script usage and environment guide
All sprint tasks are documented in docs/issues/:
Sprint 1 - Core MVP:
- S1.1 - Spec & Scaffolding
- S1.2 - Core Contract
- S1.3 - Unit Tests
- S1.4 - Deploy & Verify
- S1.5 - FE Setup
- S1.6 - Join/Commit Screen
- S1.7 - Reveal Screen
- S1.8 - Claim Screen
- S1.9 - Countdown & Helpers
- S1.10 - Env & Config
- S1.11 - Docs & Runbook ← You are here
Sprint 2 - Production Readiness:
- S2.1 - Forfeit Mode
- S2.2 - Admin & Pausable
- S2.3 - ERC20 Support
- S2.4 - Subgraph Setup
- S2.5 - Leaderboard UI
- Solana: https://docs.solana.com/
- Anchor Framework: https://www.anchor-lang.com/
- Solana Web3.js: https://solana-labs.github.io/solana-web3.js/
- Wallet Adapter: https://github.com/solana-labs/wallet-adapter
See LICENSE file for details.
Ready to play?
- Get devnet SOL:
solana airdrop 2 - Deploy program:
scripts/solana-deploy.sh devnet - Start frontend:
cd frontend && pnpm dev - Open http://localhost:5173
- Connect wallet, choose Milk 🍼 or Cacao 🍫, and may the minority win!
Questions? Open an issue: https://github.com/NQKhaixyz/chocochoco/issues
Built with ❤️ on Solana by the ChocoChoco team
- Commit: chọn Milk/Cacao, app tạo salt cục bộ, tính commitment, gửi commit kèm stake.
- Reveal: trong cửa sổ, gửi
reveal(choice, salt). - Settle: phe thiểu số thắng (hoà → hoàn stake); emit
RoundMeowed. - Claim: chỉ người thắng claim; UI chặn double‑claim.
Chi tiết: xem DESIGN.md.
- Env:
VITE_SUBGRAPH_URL - Route:
/leaderboard - Bảng:
- Top Payout: tổng claim theo
player(aggregate client‑side) - Weekly Win‑Rate: 7 ngày gần nhất từ
playerRounds(revealed=true), so sánhsidevớiround.winnerSide
- Top Payout: tổng claim theo
- Phân trang: Prev/Next theo
first/skip; Next chỉ bật khi đủpageSize.
- Pastel theme (CSS variables) tại
frontend/src/styles/theme.css; Tailwind tokens (brand, accent, card, border, win/lose) - Dark mode: thêm class
darkvào<html> - Cat icon:
frontend/public/assets/icons/cat.svg - Win/Lose animation: Lottie trong
frontend/public/assets/anim/ - Sound toggle:
src/context/sound.tsx(purr khi thắng, lưu trạng thái localStorage)
- Landing: route
/landing(CTA “Play on Testnet” →/app) - In‑app tips: bật/tắt tại Navbar (persist localStorage)
- Coach marks: 3 bước (Commit → Reveal → Claim)
Xem subgraph/README.md để cài đặt, build và deploy qua The Graph Studio.
- Chạy
forge buildđể tạo ABI tạicontracts/out/ChocoChocoGame.sol/ChocoChocoGame.json. - Cập nhật
subgraph.yamlvớinetwork,address,startBlocktrước khi deploy.
- Ví/mạng: đảm bảo
CHAIN_IDkhớp mạng ví; thử RPC public - Commit/Reveal bị từ chối: kiểm tra countdown (chain time). Hash mismatch → kiểm tra schema + salt
- Double‑claim: UI disable nếu
hasClaimed; refetch sau khi mined - Verify lỗi: đúng network/args; chờ indexer vài phút rồi thử lại
- Env không đọc: Vite yêu cầu biến
VITE_* - Countdown lệch: dùng hook chain time (
frontend/src/hooks/useChainTime.ts)
- Branches: main (stable), dev (integration), feature:
feat/*,fix/* - Commits: Conventional Commits (
feat:,fix:,chore:, …) - Env: giữ
.env.example, không commit secrets