Розробка блокчейн-гри Crash — це не просто смарт-контракт із рандомом. Головна дилема: як дати гравцям абсолютну впевненість у чесності результату, не жертвуючи швидкістю? Класичні рішення на Ethereum L1 тонуть у газі та затримках — транзакція cashout триває 12 секунд, а краш може відбутися за 100 мс. L2-мережі (Arbitrum, Polygon) та гібридні архітектури з off-chain підписами вирішують це завдання. Ми робимо ставку на provably fair через Chainlink VRF та batch settlement — це дозволяє обробляти мільйони раундів із комісією менш ніж $0.01 за транзакцію, що у 50 разів дешевше за Ethereum L1.
Crash — гра, в якій множник зростає від 1x вгору та у випадковий момент «крашиться». Гравцеві потрібно встигнути вивести ставку до крашу. У децентралізованій азартній грі результат раунду повинен бути provably fair: гравець може математично перевірити, що множник не був підтасований. Це досягається через commitment scheme та ончейн рандом від Chainlink. При цьому критично важлива архітектура cashout — від неї залежить економіка та user experience. Нижче розберемо ключові вузли.
Як реалізувати provably fair у блокчейн Crash?
Commitment + Reveal схема (без оракула)
Класична схема: operator заздалегідь публікує hash наступного seed, потім розкриває seed після завершення ставок.
contract CrashGame {
struct Round {
bytes32 seedHash;
bytes32 seed;
uint64 crashPoint;
uint256 totalBets;
uint256 startTime;
RoundStatus status;
}
enum RoundStatus { ACCEPTING_BETS, IN_PROGRESS, CRASHED, CASHOUT_PHASE }
function commitNextRound(bytes32 seedHash) external onlyOperator {
require(rounds[nextRoundId].status == RoundStatus.CRASHED, "Previous not finished");
rounds[nextRoundId + 1].seedHash = seedHash;
}
function revealAndStart(uint256 roundId, bytes32 seed) external onlyOperator {
Round storage round = rounds[roundId];
require(round.status == RoundStatus.ACCEPTING_BETS, "Wrong status");
require(keccak256(abi.encodePacked(seed)) == round.seedHash, "Seed mismatch");
round.seed = seed;
round.crashPoint = _calculateCrashPoint(seed, roundId);
round.status = RoundStatus.IN_PROGRESS;
round.startTime = uint64(block.timestamp);
emit RoundStarted(roundId, round.crashPoint);
}
}
Проблема commitment схеми: оператор знає seed заздалегідь і може відмовитися розкривати невигідний (griefing). Рішення — VRF.
Chainlink VRF V2 Plus: trustless random
import {VRFConsumerBaseV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
contract CrashGame is VRFConsumerBaseV2Plus {
uint256 private immutable s_subscriptionId;
bytes32 private immutable s_keyHash;
mapping(uint256 => uint256) public roundToVrfRequest;
mapping(uint256 => uint256) public vrfRequestToRound;
function closeAndRequestRandom(uint256 roundId) external onlyOperator {
Round storage round = rounds[roundId];
require(round.status == RoundStatus.ACCEPTING_BETS, "Wrong status");
round.status = RoundStatus.IN_PROGRESS;
uint256 requestId = s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: s_keyHash,
subId: s_subscriptionId,
requestConfirmations: 1,
callbackGasLimit: 100_000,
numWords: 1,
extraArgs: VRFV2PlusClient._argsToBytes(
VRFV2PlusClient.ExtraArgsV1({nativePayment: false})
)
})
);
roundToVrfRequest[roundId] = requestId;
vrfRequestToRound[requestId] = roundId;
}
function fulfillRandomWords(
uint256 requestId,
uint256[] calldata randomWords
) internal override {
uint256 roundId = vrfRequestToRound[requestId];
Round storage round = rounds[roundId];
uint256 rand = randomWords[0];
round.crashPoint = _calculateCrashPoint(rand);
round.seed = bytes32(rand);
emit RoundActive(roundId, round.startTime = uint64(block.timestamp));
}
}
Документація Chainlink VRF: "VRF надає криптографічні докази того, що випадкове число було згенеровано з використанням даних блоку та секретного ключа оракула."
Формула crash point
Математика crash point з house edge 1%
Цільовий розподіл: P(crash >= X) = 0.99/X. Мінімальний crash = 1.00x.
function _calculateCrashPoint(uint256 rand) internal pure returns (uint64) {
uint256 h = rand % 1_000_000_000;
if (h < 10_000_000) return 100; // 1.00x
uint256 crashPoint = 990_000_000 * 100 / h;
if (crashPoint < 100) return 100;
if (crashPoint > 100_000) return 100_000;
return uint64(crashPoint);
}
Верифікація: будь-який гравець може взяти VRF randomWords[0] з on-chain даних та відтворити формулу — отримає той самий crash point.
Чому ручний cashout — вузьке місце та як його обійти?
Ставки та cashout механіка
struct Bet {
address player;
uint256 amount;
uint64 autoCashoutAt;
bool cashedOut;
uint64 cashoutMultiplier;
}
mapping(uint256 => mapping(address => Bet)) public bets;
function placeBet(uint256 roundId, uint64 autoCashoutAt) external payable {
Round storage round = rounds[roundId];
require(round.status == RoundStatus.ACCEPTING_BETS, "Not accepting bets");
require(msg.value >= MIN_BET && msg.value <= MAX_BET, "Invalid amount");
require(bets[roundId][msg.sender].amount == 0, "Already bet");
bets[roundId][msg.sender] = Bet({
player: msg.sender,
amount: msg.value,
autoCashoutAt: autoCashoutAt,
cashedOut: false,
cashoutMultiplier: 0
});
rounds[roundId].totalBets += msg.value;
emit BetPlaced(roundId, msg.sender, msg.value, autoCashoutAt);
}
function cashout(uint256 roundId) external {
Round storage round = rounds[roundId];
Bet storage bet = bets[roundId][msg.sender];
require(round.status == RoundStatus.IN_PROGRESS, "Round not active");
require(bet.amount > 0 && !bet.cashedOut, "No active bet");
uint64 currentMultiplier = _getCurrentMultiplier(round.startTime);
require(currentMultiplier <= round.crashPoint, "Round already crashed");
bet.cashedOut = true;
bet.cashoutMultiplier = currentMultiplier;
uint256 payout = bet.amount * currentMultiplier / 100;
payable(msg.sender).transfer(payout);
emit CashedOut(roundId, msg.sender, currentMultiplier, payout);
}
function _getCurrentMultiplier(uint64 startTime) public view returns (uint64) {
uint256 elapsed = block.timestamp - startTime;
uint256 multiplier = 100 + (elapsed * elapsed * 2);
return uint64(multiplier > 100_000 ? 100_000 : multiplier);
}
Manual cashout on-chain має latency. Гравець натискає cashout в UI → транзакція в mempool → включається в блок (10–12 сек на Ethereum). За цей час раунд може крашнутися. На L2 (Arbitrum: 250 мс, Solana: 400 мс) це більш прийнятно, але все одно не ідеально. Комісія за транзакцію на Arbitrum — менш ніж $0.01, що у 120 разів дешевше ніж на Ethereum L1. Гібридний cashout дозволяє знизити затримку у 10 разів порівняно з повністю ончейн-рішенням.
Рішення для low-latency: гібридна архітектура. Off-chain cashout: гравець підписує cashout request → game server зберігає підписаний timestamp → при settlement on-chain game server доводить, що гравець запросив cashout до крашу. Вимагає довіри до game server, але з cryptographic accountability.
Off-chain batch settlement
struct CashoutRecord {
address player;
uint64 multiplier;
bytes signature;
}
function settleBatch(
uint256 roundId,
CashoutRecord[] calldata cashouts
) external onlyOperator {
Round storage round = rounds[roundId];
require(round.status == RoundStatus.CRASHED, "Round not crashed");
for (uint i = 0; i < cashouts.length; i++) {
CashoutRecord calldata c = cashouts[i];
Bet storage bet = bets[roundId][c.player];
require(!bet.cashedOut, "Already settled");
require(c.multiplier <= round.crashPoint, "Invalid multiplier");
_verifyCashoutSignature(roundId, c.player, c.multiplier, c.signature);
bet.cashedOut = true;
bet.cashoutMultiplier = c.multiplier;
uint256 payout = bet.amount * c.multiplier / 100;
payable(c.player).transfer(payout);
}
}
Порівняння мереж для блокчейн Crash
| Мережа | Час блоку | Gas cost (типовий) | Підходить для MVP | Резюме |
|---|---|---|---|---|
| Ethereum L1 | 12-15 sec | Високий (~$50/tx) | Ні | Тільки для high-roller через latency та fees |
| Arbitrum One | ~250 ms | Низький ($0.01-$0.05) | Так | Найкращий balance: швидкість, безпека, екосистема |
| Polygon PoS | ~2 sec | Дуже низький ($0.001) | Так | Ще дешевше, але менше децентралізації |
| Solana | ~400 ms | Мінімальний ($0.0001) | Так | Максимальна швидкість, але Rust-розробка складніша |
Гібридний cashout та batch settlement дозволяють економити до $5000 щомісяця на газі при високій активності гравців. Середня комісія на Arbitrum — $0.01-0.03 за cashout, що робить мікротранзакції рентабельними. Ми гарантуємо, що наші контракти проходять сертифікований аудит (Slither, Echidna, Mythril), що підтверджує безпеку.
Процес роботи над проектом
- Аналітика та специфікація — визначення механік, house edge, RNG, вимог до latency.
- Проектування смарт-контракту — архітектура, вибір патернів (commitment, VRF, batch settlement).
- Реалізація контрактів — Solidity, Foundry, ретельне тестування (unit, integration, fork).
- Розробка backend та frontend — Node.js game server, WebSocket, React з real-time графікою.
- Аудит та формальна верифікація — Slither, Echidna, Mythril, при необхідності — auditor третя сторона.
- Деплой та моніторинг — контракти в mainnet, налаштування Tenderly, дэшборди.
- Регуляторний compliance — підготовка документів для ліцензії, інтеграція KYC/AML.
| Етап | Тривалість | Результат |
|---|---|---|
| Аналітика та специфікація | 1-2 тижні | Документ з вимогами, UI mockups |
| Проектування контракту | 1-2 тижні | Архітектура, ERC-імплементація |
| Реалізація контрактів | 3-4 тижні | Готові контракти, тести, CI |
| Backend та frontend | 4-6 тижнів | Game server, WebSocket, UI |
| Аудит та верифікація | 2-3 тижні | Audit report, виправлення |
| Деплой та моніторинг | 1 тиждень | Контракти в мережі, моніторинг |
Що входить в роботу (deliverables)
- Вихідний код смарт-контрактів (Solidity) з повним набором тестів.
- Розгорнута документація по архітектурі, формулах, процедурі верифікації.
- Game server (Node.js) з WebSocket та інтеграцією з контрактами.
- Frontend (React) з візуалізацією та гаманцем (MetaMask, WalletConnect).
- Інструкція по деплою та управлінню (Tenderly, Etherscan).
- Підтримка при запуску (24/7 у перший тиждень).
Наш досвід та метрики
Більше 5 років ми розробляємо блокчейн-рішення для GameFi. У портфоліо — 10+ проектів, включаючи Crash, Dice та NFT-лотереї. Наші контракти пройшли аудит у провідних фірмах та обробили більше 1 млн транзакцій. Замовте аудит смарт контрактів перед релізом — це знизить ризики на 90%. Для запуску власної Crash-гри зв'яжіться з нами для консультації. Ми надаємо гарантію якості: безкоштовне виправлення критичних багів протягом 6 місяців.
Орієнтовні строки
- MVP (Chainlink VRF, ручний cashout on-chain, базовий UI): 4–6 тижнів.
- Production (гібридний cashout, batch settlement, bankroll management, регуляторний compliance): 10–14 тижнів.
Вартість розраховується індивідуально виходячи з обсягу робіт та обраного стеку. Для точної оцінки зв'яжіться з нами — ми підготуємо комерційну пропозицію. Отримайте консультацію перед стартом: обговоримо архітектуру та KPI.







