In blockchain PvP games, the central challenge is ensuring fairness with hidden moves and low latency for real-time gameplay. Our smart contract PvP solutions ensure fair play. On-chain transactions are expensive ($0.1–$1 per move) and slow (>12 seconds). Simply leaving logic on the server means giving up trustless properties. We develop blockchain PvP games with smart contracts that ensure fair matchmaking, hidden moves via commit-reveal, and lightning-fast off-chain moves through state channels. Our experience: over 5 years in Web3 gaming, 50+ successful projects, a team of senior engineers focused on game mechanics. Our turnkey solution includes everything from architecture to deployment within 4-6 weeks for basic games. Typical development costs start at $15,000 for a simple 1v1 game, with state channels adding $10,000. Gas costs on Arbitrum are ~$0.01 per transaction, making state channel closures extremely affordable. We guarantee security and fair play with audited contracts. Get a blockchain architect consultation: tell us about your game, we'll pick the optimal stack and estimate the cost.
How commit-reveal solves the hidden move problem
In card games and strategies, each opponent's move must remain hidden until applied. On blockchain, all data is public, so the commit-reveal pattern is used. Players first send a hash of their action with a secret, then reveal it. If someone fails to reveal in time, they automatically lose. This eliminates peeking and back-running.
Example implementation in Solidity (using Foundry):
Click to expand Solidity example
contract PvPGame {
struct GameState {
address player1;
address player2;
bytes32 p1CommitHash; // hash(action + secret)
bytes32 p2CommitHash;
uint8 p1Action; // revealed after both commit
uint8 p2Action;
Phase phase;
uint256 commitDeadline;
uint256 revealDeadline;
}
enum Phase { WAITING, COMMIT, REVEAL, RESOLVED }
// Phase 1: both players send hash(action + secret)
function commitAction(uint256 gameId, bytes32 commitHash) external {
GameState storage game = games[gameId];
require(game.phase == Phase.COMMIT, "Not commit phase");
require(block.timestamp <= game.commitDeadline, "Commit deadline passed");
if (msg.sender == game.player1) {
game.p1CommitHash = commitHash;
} else if (msg.sender == game.player2) {
game.p2CommitHash = commitHash;
} else revert("Not a player");
// If both committed — transition to reveal
if (game.p1CommitHash != bytes32(0) && game.p2CommitHash != bytes32(0)) {
game.phase = Phase.REVEAL;
game.revealDeadline = block.timestamp + REVEAL_WINDOW;
}
}
// Phase 2: reveal actual actions
function revealAction(uint256 gameId, uint8 action, bytes32 secret) external {
GameState storage game = games[gameId];
require(game.phase == Phase.REVEAL, "Not reveal phase");
bytes32 expectedHash = keccak256(abi.encodePacked(action, secret));
if (msg.sender == game.player1) {
require(game.p1CommitHash == expectedHash, "Hash mismatch");
game.p1Action = action;
} else if (msg.sender == game.player2) {
require(game.p2CommitHash == expectedHash, "Hash mismatch");
game.p2Action = action;
}
// If both revealed — resolve
if (game.p1Action != 0 && game.p2Action != 0) {
_resolveGame(gameId);
}
}
// If player doesn't reveal in time — forfeit
function claimTimeout(uint256 gameId) external {
GameState storage game = games[gameId];
require(game.phase == Phase.REVEAL, "Not reveal phase");
require(block.timestamp > game.revealDeadline, "Deadline not passed");
// The player who didn't reveal loses
address winner;
if (game.p1Action == 0 && game.p2Action != 0) {
winner = game.player2;
} else if (game.p2Action == 0 && game.p1Action != 0) {
winner = game.player1;
} else {
// Both not revealed — refund stakes
_refundBothPlayers(gameId);
return;
}
_payWinner(gameId, winner);
}
}
Why state channels are optimal for real-time PvP
On-chain, each move costs gas and takes >12 seconds. State channels allow thousands of moves between two players without fees, with only the deposit at stake. After the game, the channel is closed with a single on-chain transaction. Our smart contracts optimize gas usage, achieving up to 90% savings. Typical gas savings with state channels amount to $0.5–$1 per move, which for 1000 moves equals $500–$1000 saved. Compare:
| Criteria | On-chain | State channel |
|---|---|---|
| Move time | 12+ seconds | <50 ms |
| Fee | $0.1–$1 | $0 (open/close only) |
| Throughput | ~15 tx/s | 1000+ moves/s |
| Trustlessness | Full | High (requires dispute slot) |
State channels are 1000x faster than on-chain transactions, critical for real-time strategies. Gas savings reach up to 90% when using state channels.
How we protect the game from cheaters
For on-chain PvP, all logic is in the smart contract — cheating is impossible. For off-chain + on-chain settlement, server-side validation is needed. We implement checks for turn order, move validity, timestamps, and replay protection. For hidden data, we use zk-SNARKs: a player proves the correctness of a move without revealing it.
class GameValidator {
async validateMove(
gameState: GameState,
move: Move,
playerId: string
): Promise<ValidationResult> {
// 1. Check turn order
if (gameState.currentTurn !== playerId) {
return { valid: false, reason: "Not your turn" };
}
// 2. Check move validity per game rules
const allowedMoves = this.getAllowedMoves(gameState, playerId);
if (!allowedMoves.includes(move.type)) {
return { valid: false, reason: "Invalid move" };
}
// 3. Check that move hasn't already been made (replay protection)
if (this.moveCache.has(move.id)) {
return { valid: false, reason: "Duplicate move" };
}
// 4. Check timestamp (move not older than N seconds)
if (Date.now() - move.timestamp > MAX_MOVE_AGE_MS) {
return { valid: false, reason: "Move too old" };
}
return { valid: true };
}
}
How the tournament system is implemented
contract PvPTournament {
struct Tournament {
uint256 entryFee;
uint256 maxPlayers;
address[] participants;
TournamentType tournamentType; // SINGLE_ELIMINATION, ROUND_ROBIN, SWISS
uint256 prizePool;
TournamentStatus status;
}
// Prize distribution (in basis points)
uint256[] public prizeDistribution = [5000, 3000, 1500, 500]; // 50%, 30%, 15%, 5%
function registerForTournament(uint256 tournamentId) external payable {
Tournament storage t = tournaments[tournamentId];
require(t.participants.length < t.maxPlayers, "Tournament full");
require(msg.value == t.entryFee, "Wrong entry fee");
t.participants.push(msg.sender);
t.prizePool += msg.value;
}
function distributePrizes(uint256 tournamentId, address[] calldata rankedPlayers)
external onlyAdmin
{
Tournament storage t = tournaments[tournamentId];
for (uint i = 0; i < prizeDistribution.length && i < rankedPlayers.length; i++) {
uint256 prize = (t.prizePool * prizeDistribution[i]) / 10000;
payable(rankedPlayers[i]).transfer(prize);
}
}
}
How matchmaking works
For high-load PvP games, we use Redis sorted sets. Each player enters a queue with a rating (MMR). When the rating matches another player, they are automatically paired into a smart contract. This blockchain-based matchmaking allows processing thousands of requests per second without bottlenecks. The queue is stored off-chain, but the match is confirmed by an on-chain transaction.
Development process
- Analysis — description of game mechanics and economy
- Design — smart contract architecture, data schema
- Development — writing contracts in Solidity, testing in Foundry
- Audit — security check, fuzzing, formal verification
- Deployment — publishing on chosen L2, infrastructure setup, frontend integration
What's included in the work
- Architectural documentation (diagrams, contract specifications)
- Source code of smart contracts (Solidity, Foundry)
- Full test suite (unit, integration, fuzz)
- Integration with Chainlink VRF for randomness
- State channel setup (if required)
- Deployment on chosen EVM L2 and monitoring setup
- Security audit (Slither, Mythril, fuzzing, manual review) by independent firms
- Scripts for contract verification in explorer
- Team training (1-2 sessions, documentation)
- Support for 1 month after deployment
Timelines
- 1v1 game (Coinflip, RPS, basic card): 4-6 weeks
- State Channel PvP: +3-4 weeks
- Tournament system: +3-4 weeks
- Complex card/strategy game: 3-5 months
- Security audit: mandatory, +4-6 weeks
Technology stack
| Component | Technology |
|---|---|
| Smart contracts | Solidity + Foundry, State Channels |
| Randomness | Chainlink VRF v2.5 |
| Real-time comm. | WebSocket (Socket.io) |
| Game server | Node.js + TypeScript |
| Matchmaking | Redis sorted sets |
| Frontend | React / Unity WebGL |
| EVM L2 | Arbitrum / Base / Polygon |
| Anti-cheat | Server-side validation + zkProofs |
| Gas optimization | Techniques like calldata packing, storage packing |
commit-reveal — standard protocol for hidden data submission.
Our turnkey solution includes everything from architecture to deployment within 4-6 weeks for basic games. Write us for a free project estimate. Get a blockchain architect consultation — tell us about your game, we'll pick the optimal stack and estimate the cost.







