mirror of
https://github.com/averel10/crypto_clash.git
synced 2026-03-12 19:08:11 +01:00
start fixing frontend
This commit is contained in:
267
docs/DUAL_MODE_IMPLEMENTATION.md
Normal file
267
docs/DUAL_MODE_IMPLEMENTATION.md
Normal file
@@ -0,0 +1,267 @@
|
||||
# Dual-Mode Frontend Implementation
|
||||
|
||||
This document describes the frontend implementation that supports both Classic and Minus One game modes.
|
||||
|
||||
## Overview
|
||||
|
||||
The frontend has been updated to seamlessly work with two different smart contracts:
|
||||
- **Game.sol** (Classic Mode): Standard Rock-Paper-Scissors with 2 phases
|
||||
- **GameMinusOne.sol** (Minus One Mode): Squid Game-inspired variant with 6 phases
|
||||
|
||||
## Game Modes Comparison
|
||||
|
||||
### Classic Mode
|
||||
**Phases:**
|
||||
1. Commit Phase (both players submit encrypted moves)
|
||||
2. Reveal Phase (both players reveal their moves)
|
||||
|
||||
**Functions:**
|
||||
- `register(gameId, nickname)` - Join/create game
|
||||
- `play(gameId, encrMove)` - Submit encrypted move
|
||||
- `reveal(gameId, clearMove)` - Reveal move
|
||||
- `commitTimeLeft()` / `revealTimeLeft()` - Check timeouts
|
||||
- `getOutcome(gameId)` - Get game result
|
||||
|
||||
**Data Structure:**
|
||||
```typescript
|
||||
Player {
|
||||
addr: string;
|
||||
nickname: string;
|
||||
encrMove?: string; // Classic only
|
||||
move?: string; // Classic only
|
||||
}
|
||||
```
|
||||
|
||||
### Minus One Mode
|
||||
**Phases:**
|
||||
1. Registration
|
||||
2. Initial Commit (both players commit 2 moves)
|
||||
3. First Reveal (reveal both moves)
|
||||
4. Withdraw Commit (commit which move to withdraw)
|
||||
5. Withdraw Reveal (reveal withdrawal choice)
|
||||
6. Done
|
||||
|
||||
**Functions:**
|
||||
- `register(gameId, nickname)` - Join/create game
|
||||
- `commitInitialMoves(gameId, hash1, hash2)` - Submit 2 encrypted moves
|
||||
- `revealInitialMoves(gameId, clear1, clear2)` - Reveal both moves
|
||||
- `commitWithdraw(gameId, wHash)` - Submit withdrawal choice
|
||||
- `withdrawMove(gameId, clear)` - Reveal withdrawal
|
||||
- `getTimeLeft(gameId)` - Universal timeout checker
|
||||
- `getOutcome(gameId)` - Get game result
|
||||
|
||||
**Data Structure:**
|
||||
```typescript
|
||||
Player {
|
||||
addr: string;
|
||||
nickname: string;
|
||||
hash1?: string; // MinusOne only
|
||||
hash2?: string; // MinusOne only
|
||||
move1?: string; // MinusOne only
|
||||
move2?: string; // MinusOne only
|
||||
wHash?: string; // MinusOne only
|
||||
withdrawn?: string; // MinusOne only (1 or 2)
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Type System
|
||||
The frontend uses optional fields to support both modes:
|
||||
|
||||
```typescript
|
||||
interface Player {
|
||||
addr: string;
|
||||
nickname: string;
|
||||
// Classic fields
|
||||
encrMove?: string;
|
||||
move?: string;
|
||||
// MinusOne fields
|
||||
hash1?: string;
|
||||
hash2?: string;
|
||||
move1?: string;
|
||||
move2?: string;
|
||||
wHash?: string;
|
||||
withdrawn?: string;
|
||||
}
|
||||
|
||||
interface GameDetails {
|
||||
playerA: Player;
|
||||
playerB: Player;
|
||||
initialBet: string;
|
||||
outcome: number;
|
||||
isActive: boolean;
|
||||
returnGameId: number;
|
||||
gameMode?: string; // "classic" or "minusone"
|
||||
}
|
||||
```
|
||||
|
||||
### Component Updates
|
||||
|
||||
#### GameModal.tsx
|
||||
- **Purpose:** Manages game state and routes to correct phase component
|
||||
- **Changes:**
|
||||
- Added `gameMode` field to GameDetails
|
||||
- Phase detection logic checks `gameMode` and appropriate fields
|
||||
- Storage structure expanded for MinusOne state (move1/2, secret1/2, withdrawChoice)
|
||||
|
||||
#### Commit.tsx
|
||||
- **Purpose:** Handles all commit phases (move commits and withdrawal commits)
|
||||
- **Changes:**
|
||||
- Added MinusOne-specific props: `selectedMove1`, `selectedMove2`, `secret1`, `secret2`, `withdrawChoice`
|
||||
- Hash generation adapts based on mode:
|
||||
- Classic: `keccak256(move-password)`
|
||||
- MinusOne initial: Two hashes `keccak256(move1-password1)`, `keccak256(move2-password2)`
|
||||
- MinusOne withdrawal: `keccak256(1-password)` or `keccak256(2-password)`
|
||||
- UI conditionally renders:
|
||||
- Classic: Single move selector
|
||||
- MinusOne initial: Two move selectors with different colored borders
|
||||
- MinusOne withdrawal: Choice between withdrawing move 1 or 2
|
||||
- Contract calls:
|
||||
- Classic: `play(gameId, encrMove)`
|
||||
- MinusOne initial: `commitInitialMoves(gameId, hash1, hash2)`
|
||||
- MinusOne withdrawal: `commitWithdraw(gameId, wHash)`
|
||||
|
||||
#### Reveal.tsx
|
||||
- **Purpose:** Handles all reveal phases (move reveals and withdrawal reveals)
|
||||
- **Changes:**
|
||||
- Added MinusOne-specific props: `selectedMove1`, `selectedMove2`, `secret1`, `secret2`, `isWithdrawPhase`
|
||||
- State checks adapted:
|
||||
- Classic: Check `move` field
|
||||
- MinusOne initial: Check `move1` field
|
||||
- MinusOne withdrawal: Check `withdrawn` field
|
||||
- Reveal function calls:
|
||||
- Classic: `reveal(gameId, clearMove)`
|
||||
- MinusOne initial: `revealInitialMoves(gameId, clear1, clear2)`
|
||||
- MinusOne withdrawal: `withdrawMove(gameId, clear)`
|
||||
- UI conditionally displays:
|
||||
- Classic: Single move display
|
||||
- MinusOne initial: Both moves displayed side-by-side
|
||||
- MinusOne withdrawal: Withdrawal choice (1️⃣ or 2️⃣)
|
||||
- Final outcome shows:
|
||||
- Classic: Final move from each player
|
||||
- MinusOne: All 4 moves with withdrawn ones marked (grayed out with "Withdrawn" label)
|
||||
|
||||
#### GameList.tsx
|
||||
- **Purpose:** Lists available games and shows their status
|
||||
- **Changes:**
|
||||
- `getGamePhase()` function checks `gameMode` and appropriate fields:
|
||||
- Classic: Returns "Commit", "Reveal", or "Outcome"
|
||||
- MinusOne: Returns "Initial Commit", "Initial Reveal", "Withdraw Commit", "Withdraw Reveal", or "Done"
|
||||
- Game mode badge displays "Classic" or "Minus One"
|
||||
- Phase colors:
|
||||
- Yellow: Commit phases
|
||||
- Blue: Initial reveal
|
||||
- Amber: Withdraw commit
|
||||
- Orange: Withdraw reveal
|
||||
- Purple: Complete/Done
|
||||
|
||||
### Hash Generation
|
||||
|
||||
All hashing uses Keccak256 with Web3.js:
|
||||
|
||||
```typescript
|
||||
// Classic mode
|
||||
const hash = web3.utils.keccak256(web3.utils.utf8ToHex(`${move}-${secret}`));
|
||||
|
||||
// MinusOne initial moves
|
||||
const hash1 = web3.utils.keccak256(web3.utils.utf8ToHex(`${move1}-${secret1}`));
|
||||
const hash2 = web3.utils.keccak256(web3.utils.utf8ToHex(`${move2}-${secret2}`));
|
||||
|
||||
// MinusOne withdrawal
|
||||
const wHash = web3.utils.keccak256(web3.utils.utf8ToHex(`${withdrawChoice}-${secret}`));
|
||||
```
|
||||
|
||||
### Session Storage
|
||||
|
||||
Game state persists across page refreshes using sessionStorage:
|
||||
|
||||
**Classic Mode:**
|
||||
```typescript
|
||||
{
|
||||
selectedMove: string,
|
||||
secret: string,
|
||||
expiresAt: number
|
||||
}
|
||||
```
|
||||
|
||||
**MinusOne Mode:**
|
||||
```typescript
|
||||
{
|
||||
selectedMove1: string,
|
||||
selectedMove2: string,
|
||||
secret1: string,
|
||||
secret2: string,
|
||||
withdrawChoice: string,
|
||||
expiresAt: number
|
||||
}
|
||||
```
|
||||
|
||||
Storage expires after 1 hour to prevent stale data.
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Classic Mode
|
||||
- [ ] Create new game with "Classic Mode"
|
||||
- [ ] Join game as Player B
|
||||
- [ ] Both players commit moves
|
||||
- [ ] Both players reveal moves
|
||||
- [ ] Verify correct outcome (win/lose/draw)
|
||||
- [ ] Check ETH payouts
|
||||
|
||||
### Minus One Mode
|
||||
- [ ] Create new game with "Minus One Mode"
|
||||
- [ ] Join game as Player B
|
||||
- [ ] Both players commit 2 initial moves
|
||||
- [ ] Both players reveal initial moves
|
||||
- [ ] Verify both moves displayed correctly
|
||||
- [ ] Both players commit withdrawal choice
|
||||
- [ ] Both players reveal withdrawal
|
||||
- [ ] Verify final moves (1 active, 1 withdrawn per player)
|
||||
- [ ] Check correct outcome based on remaining moves
|
||||
- [ ] Check ETH payouts
|
||||
|
||||
### Edge Cases
|
||||
- [ ] Timeout handling in Classic mode
|
||||
- [ ] Timeout handling in MinusOne mode (different phases)
|
||||
- [ ] Page refresh preserves state (both modes)
|
||||
- [ ] GameList shows correct phase for both modes
|
||||
- [ ] Switching between active games (mixed modes)
|
||||
|
||||
## Deployment Steps
|
||||
|
||||
1. **Deploy GameMinusOne contract:**
|
||||
```bash
|
||||
cd crypto_clash_contract
|
||||
npx hardhat run scripts/deploy.ts --network <network>
|
||||
```
|
||||
|
||||
2. **Update config.json:**
|
||||
```json
|
||||
{
|
||||
"GAME_CONTRACT_ADDRESS": "0x...",
|
||||
"GAME_MINUSONE_CONTRACT_ADDRESS": "0x...",
|
||||
"NETWORK_NAME": "...",
|
||||
"CHAIN_ID": ...
|
||||
}
|
||||
```
|
||||
|
||||
3. **Copy config to frontend:**
|
||||
```bash
|
||||
cd crypto_clash_frontend
|
||||
node copy_config.js
|
||||
```
|
||||
|
||||
4. **Build and deploy frontend:**
|
||||
```bash
|
||||
npm run build
|
||||
npm run start
|
||||
```
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- Contract mode detection could be automatic (read contract bytecode or interface)
|
||||
- Support switching contracts without page reload
|
||||
- Statistics tracking for each mode separately
|
||||
- Leaderboard for each mode
|
||||
- Tournament mode supporting both game types
|
||||
Reference in New Issue
Block a user