Concept
We aim to build a secure, manipulation-resistant PvP arena using EVM-compatible smart contracts. All core actions such as betting, creating and joining games, opening/closing positions, and finalizing are performed on-chain. The backend plays a supporting role: validating on-chain activity, relaying messages between players, and tracking game states.
The current flow idea for on-chain gameplay is as follows:
Backend Responsibilities
Event Listenerβ
- Listens to smart contract events
- Tracks and syncs game states (pending, active, completed) with a centralized DB
- Keeps game data fresh for frontend UX (matchmaking, game history, rewards)
- Removes games if the creator goes offline
Game Status and Matchmaking APIβ
/api/game/create
validates initGame transactions/games/waiting-for-opponent
returns available games to join/api/game/start
validates joinGame transactions
Signature Relayβ
/api/position/signature
provides backend EIP-712 signature for opening positions- WebSocket or long polling is used to relay signature requests:
- Player 2 β Backend β Player 1 β (Signs) β Backend β Player 2
Game Watchdogβ
- Tracks whether the game creator (Player 1) is online
- Hides the game from the list if the creator is offline
- During
joinGame
, the contract validates Player 1βs short-TTL signature, proving they were online and approved the join
Frontend Responsibilities
Game Startβ
- User clicks βStart Fightβ and selects a fighter
- Frontend queries
/games/waiting-for-opponent
- If a game is found, it requests a join signature from Player 1 via backend
- If no game is found, frontend calls
initGame(poolAddress, betAmount)
- After the transaction is confirmed, backend validates it via
/api/game/create
Matchmaking UIβ
- Displays a waiting screen with a countdown
- Optionally subscribes to real-time updates via WebSocket
In-Game Tradingβ
openHashedPosition(gameId, directionHash, backendSignature)
is used to open a position (direction is hidden initially)- Signature is retrieved from
/api/position/signature
closePosition(gameId, direction, nonce)
is used to reveal and close the position
π Finalizationβ
- Once both players have closed their positions, the creator finalizes the game via
finalizeGame(gameId)
- If someone is unresponsive, backend triggers forced finalization after timeout
Smart Contract Core Functions
initGame(poolAddress, betAmount)
locks the bet and creates a gamejoinGame(gameId, poolAddress, betAmount, joinSignature)
lets the second player joinopenHashedPosition(gameId, directionHash, backendSignature)
opens a hashed positionclosePosition(gameId, direction, nonce)
reveals and settles the positionfinalizeGame(gameId)
settles rewards and closes the game
Game Pool Whitelist
- The contract stores a whitelist of allowed DEX pools for each network
- Only the owner can modify the list to prevent use of fake or manipulatable pools
- The contract uses these whitelisted pools to fetch real-time prices for PnL and trade validation
Open Questions
- How to efficiently track intermediate game state (e.g., opponentβs position, PnL) β will likely be handled off-chain via event polling
Fail-safes and Abuse Preventionβ
- Join TTL signatures required from Player 1
- First-come, first-serve enforcement on backend and contract
- Pool whitelist enforced in contract
Security Measures
- Only whitelisted pools are used
- All actions require confirmed on-chain transactions
- TTL-based join signatures ensure liveness and prevent stolen entries
- Backend signs position hashes to enable forced closure if needed