Розробка PvP-ігор на блокчейні: смарт-контракти та чесність
У PvP-іграх на блокчейні центральна проблема — забезпечити чесність при прихованих ходах і низькі затримки для real-time геймплею. On-chain транзакції дорогі (0.1–1$ за хід) і повільні (>12 секунд). Просто залишити логіку на сервері — означає відмовитися від trustless-властивостей блокчейну. Ми розробляємо смарт-контракти, які забезпечують чесний матчмейкінг, приховані ходи через commit-reveal та блискавичні off-chain ходи через state channels. Наш досвід — понад 5 років у Web3, 50+ успішних проєктів, команда senior-інженерів з фокусом на ігрову механіку. Отримайте консультацію блокчейн-архітектора: розкажіть про свою гру, ми підберемо оптимальний стек і оцінимо вартість.
Як commit-reveal вирішує проблему прихованих ходів?
У карткових іграх та стратегіях кожен хід противника має бути прихованим до моменту його застосування. На блокчейні всі дані публічні, тому застосовується commit-reveal патерн. Гравці спочатку надсилають хеш своєї дії з секретом, а потім розкривають його. Якщо хтось не розкриває хід вчасно — він автоматично програє. Це виключає підглядання та back-running.
Приклад реалізації на Solidity (з використанням Foundry):
contract PvPGame {
struct GameState {
address player1;
address player2;
bytes32 p1CommitHash; // hash(action + secret)
bytes32 p2CommitHash;
uint8 p1Action; // розкривається після commit обох
uint8 p2Action;
Phase phase;
uint256 commitDeadline;
uint256 revealDeadline;
}
enum Phase { WAITING, COMMIT, REVEAL, RESOLVED }
// Phase 1: обидва гравці надсилають 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");
// Якщо обидва зробили commit — перехід до reveal
if (game.p1CommitHash != bytes32(0) && game.p2CommitHash != bytes32(0)) {
game.phase = Phase.REVEAL;
game.revealDeadline = block.timestamp + REVEAL_WINDOW;
}
}
// Phase 2: розкриваємо реальні дії
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;
}
// Якщо обидва розкрили — resolve
if (game.p1Action != 0 && game.p2Action != 0) {
_resolveGame(gameId);
}
}
// Якщо гравець не reveal вчасно — 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");
// Гравець який не reveal — програє
address winner;
if (game.p1Action == 0 && game.p2Action != 0) {
winner = game.player2;
} else if (game.p2Action == 0 && game.p1Action != 0) {
winner = game.player1;
} else {
// Обидва не revealed — повернення ставок
_refundBothPlayers(gameId);
return;
}
_payWinner(gameId, winner);
}
}
Чому state channels — оптимальний вибір для real-time PvP?
On-chain кожен хід коштує газу і займає >12 секунд. State channels дозволяють здійснювати тисячі ходів між двома гравцями без комісій, а на кону лише депозит. Після гри канал закривається однією on-chain транзакцією. Порівняйте:
| Критерій | On-chain | State channel |
|---|---|---|
| Час ходу | 12+ секунд | <50 мс |
| Комісія | $0.1–$1 | $0 (тільки відкриття/закриття) |
| Пропускна здатність | ~15 tx/s | 1000+ ходів/s |
| Trustlessness | Повна | Висока (потрібен слот для оскарження) |
State channels у 1000 разів швидші за on-chain транзакції, що критично для реал-тайм стратегій. Економія на газі досягає 90% при використанні state channels.
Як ми захищаємо гру від чітерів?
Для on-chain PvP вся логіка в смарт-контракті — чітерство неможливе. Для off-chain + on-chain settlement потрібна серверна валідація. Ми реалізуємо перевірку черговості, допустимості ходу, таймстампів та захист від повторень. Для прихованих даних використовуємо zk-SNARKs — гравець доводить коректність ходу, не розкриваючи його.
class GameValidator {
async validateMove(
gameState: GameState,
move: Move,
playerId: string
): Promise<ValidationResult> {
// 1. Перевіряємо черговість ходу
if (gameState.currentTurn !== playerId) {
return { valid: false, reason: "Not your turn" };
}
// 2. Перевіряємо допустимість ходу за правилами гри
const allowedMoves = this.getAllowedMoves(gameState, playerId);
if (!allowedMoves.includes(move.type)) {
return { valid: false, reason: "Invalid move" };
}
// 3. Перевіряємо що move не був уже зроблений (replay protection)
if (this.moveCache.has(move.id)) {
return { valid: false, reason: "Duplicate move" };
}
// 4. Перевіряємо timestamp (move не старше N секунд)
if (Date.now() - move.timestamp > MAX_MOVE_AGE_MS) {
return { valid: false, reason: "Move too old" };
}
return { valid: true };
}
}
Як реалізована турнірна система?
contract PvPTournament {
struct Tournament {
uint256 entryFee;
uint256 maxPlayers;
address[] participants;
TournamentType tournamentType; // SINGLE_ELIMINATION, ROUND_ROBIN, SWISS
uint256 prizePool;
TournamentStatus status;
}
// Prize distribution (в 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);
}
}
}
Як працює матчмейкінг?
Для високонавантажених PvP-ігор використовуємо Redis sorted sets. Кожен гравець стає в чергу з рейтингом (MMR). При збігу рейтингу з іншим гравцем — вони автоматично з'єднуються в смарт-контракт. Це дозволяє обробляти тисячі запитів на секунду без блокувань. Черга зберігається off-chain, але матч підтверджується on-chain транзакцією.
Процес розробки
- Аналіз — опис ігрової механіки та економіки
- Проектування — архітектура смарт-контрактів, схема даних
- Розробка — написання контрактів на Solidity, тестування в Foundry
- Аудит — перевірка безпеки, fuzzing, формальна верифікація
- Деплой — публікація на вибраній L2, налаштування інфраструктури, інтеграція з фронтендом
Що входить у роботу
- Архітектурна документація (схеми, специфікації контрактів)
- Вихідний код смарт-контрактів (Solidity, Foundry)
- Повний набір тестів (unit, integration, fuzz)
- Інтеграція з Chainlink VRF для рандому
- Налаштування state channels (якщо потрібно)
- Деплой на вибрану L2 та налаштування моніторингу
- Аудит безпеки (Slither, Mythril, fuzzing, manual review)
- Скрипти для верифікації контрактів в експлорері
- Навчання команди (1-2 сесії, документація)
- Підтримка протягом 1 місяця після деплою
Строки
- 1v1 гра (Coinflip, RPS, базова карткова): 4-6 тижнів
- State Channel PvP: +3-4 тижні
- Турнірна система: +3-4 тижні
- Складна карткова/стратегічна гра: 3-5 місяців
- Security audit: обов'язковий, +4-6 тижнів
Стек технологій
| Компонент | Технологія |
|---|---|
| Smart contracts | Solidity + Foundry, State Channels |
| Randomness | Chainlink VRF v2.5 |
| Real-time ком. | WebSocket (Socket.io) |
| Game server | Node.js + TypeScript |
| Matchmaking | Redis sorted sets |
| Frontend | React / Unity WebGL |
| L2 | Arbitrum / Base / Polygon |
| Anti-cheat | Серверна валідація + zkProofs |
commit-reveal — стандартний протокол для прихованої відправки даних.
Замовте консультацію сьогодні — ми проаналізуємо вашу ідею та запропонуємо архітектуру без передоплати. Наша гарантія: прозорий процес, фіксовані строки, повний супровід після деплою. Отримайте консультацію блокчейн-архітектора — розкажіть про свою гру, ми підберемо оптимальний стек та оцінимо вартість.







