В традиционных онлайн-казино генераторы случайных чисел (RNG) остаются «чёрным ящиком» — игрок вынужден верить оператору на слово. Это подрывает доверие и вызывает сомнения в честности выплат. Provably fair решает проблему: каждый результат игры публикуется на блокчейне с криптографическим доказательством. Игрок может самостоятельно воспроизвести расчёт и убедиться, что бросок костей, спин или исход crash не подтасован. Мы внедряем provably fair с момента появления технологии и реализовали более 30 проектов в ончейн-гемблинге. Наш стек: смарт-контракты на Solidity 0.8.x, off-chain движок на Node.js и верификационный UI на React. Оценим ваш проект — свяжитесь для обсуждения.
Как работает provably fair на практике?
Существует два основных подхода: on-chain VRF и off-chain commit-reveal. Выбираем в зависимости от нагрузки, стоимости газа и требуемого уровня доверия.
Chainlink VRF — on-chain верифицируемая случайность
VRF генерирует случайное число вместе с криптографическим proof. Контракт запрашивает randomness, oracle возвращает число и proof, контракт верифицирует proof и принимает число. Результат публикуется в event — любой может проверить: requestId → blockHash → randomness. В одном из проектов мы использовали Chainlink VRF с subscription ID, что снизило газовые затраты на 30% по сравнению с pay-as-you-go.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {VRFConsumerBaseV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
import {VRFV2PlusClient} from "@chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";
contract ProvablyFairCasino is VRFConsumerBaseV2Plus {
struct BetRecord {
address player;
uint256 amount;
uint8 gameId;
bytes betParams;
uint256 vrfRequestId;
uint256 randomResult;
bool settled;
uint256 payout;
}
mapping(uint256 => BetRecord) public betRecords;
event BetPlaced(
uint256 indexed requestId,
address indexed player,
uint8 gameId,
uint256 amount,
bytes betParams
);
event BetSettled(
uint256 indexed requestId,
uint256 randomness,
uint256 payout,
bool isWin
);
function placeBet(uint8 gameId, bytes calldata betParams)
external payable returns (uint256 requestId)
{
require(msg.value >= MIN_BET[gameId], "Below minimum");
require(msg.value <= MAX_BET[gameId], "Above maximum");
require(msg.value <= getMaxBet(), "Exceeds bankroll limit");
requestId = s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: KEY_HASH,
subId: SUBSCRIPTION_ID,
requestConfirmations: 3,
callbackGasLimit: 200_000,
numWords: 1,
extraArgs: VRFV2PlusClient._argsToBytes(
VRFV2PlusClient.ExtraArgsV1({nativePayment: false})
)
})
);
betRecords[requestId] = BetRecord({
player: msg.sender,
amount: msg.value,
gameId: gameId,
betParams: betParams,
vrfRequestId: requestId,
randomResult: 0,
settled: false,
payout: 0
});
emit BetPlaced(requestId, msg.sender, gameId, msg.value, betParams);
}
function fulfillRandomWords(
uint256 requestId,
uint256[] calldata randomWords
) internal override {
BetRecord storage bet = betRecords[requestId];
require(!bet.settled, "Already settled");
bet.randomResult = randomWords[0];
bet.settled = true;
uint256 payout = _resolveGame(bet.gameId, bet.amount, bet.betParams, randomWords[0]);
bet.payout = payout;
bool isWin = payout > 0;
if (isWin) {
payable(bet.player).transfer(payout);
}
emit BetSettled(requestId, randomWords[0], payout, isWin);
}
}
Commit-reveal для off-chain казино
Классическая схема: казино публикует hash(server_seed) до игры, игрок добавляет client_seed, после игры казино раскрывает server_seed. Результат = HMAC-SHA256(server_seed, client_seed + nonce). Любой может воспроизвести. Chainlink VRF быстрее по времени верификации, но commit-reveal экономит газ.
class ProvablyFairEngine {
async createSession(userId: string): Promise<Session> {
const serverSeed = crypto.randomBytes(32).toString("hex");
const serverSeedHash = crypto
.createHash("sha256")
.update(serverSeed)
.digest("hex");
const session = await db.createSession({
userId,
serverSeed: this.encrypt(serverSeed),
serverSeedHash,
nonce: 0,
createdAt: new Date(),
});
return { sessionId: session.id, serverSeedHash };
}
async resolve(
sessionId: string,
clientSeed: string,
gameType: string,
betAmount: number
): Promise<GameResult> {
const session = await db.getSession(sessionId);
const serverSeed = this.decrypt(session.serverSeed);
const nonce = ++session.nonce;
const combinedSeed = `${serverSeed}:${clientSeed}:${nonce}`;
const hashHex = crypto
.createHmac("sha256", serverSeed)
.update(`${clientSeed}-${nonce}`)
.digest("hex");
const rawResult = parseInt(hashHex.slice(0, 8), 16);
const gameResult = this.applyGameLogic(gameType, rawResult);
await db.updateSession(sessionId, { nonce });
await db.saveGameRecord({
sessionId,
nonce,
clientSeed,
serverSeedHash: session.serverSeedHash,
gameType,
result: gameResult.outcome,
betAmount,
payout: gameResult.payout,
});
return gameResult;
}
async rotateSession(sessionId: string): Promise<void> {
const session = await db.getSession(sessionId);
const serverSeed = this.decrypt(session.serverSeed);
await db.revealServerSeed(sessionId, serverSeed);
await this.createSession(session.userId);
}
}
Верификация на стороне игрока
Каждый игрок может проверить результат через публичный verifier. Мы встраиваем UI на React, который по serverSeedHash и раскрытому serverSeed воспроизводит расчёт и сравнивает с результатом. Поддержка метрик: totalBetsCount, totalWagered, actualRTP. Прозрачность банкролла — дополнительный уровень доверия. Верификационный UI позволяет игроку экспортировать историю для собственного аудита.
Что входит в разработку под ключ?
| Компонент | Детали |
|---|---|
| Смарт-контракты | Solidity 0.8.x, Chainlink VRF v2.5, ERC-20 payout |
| Off-chain движок | Node.js + TypeScript, PostgreSQL, AES-256-GCM |
| Верификационный UI | React-компонент для проверки результатов |
| Аудит | Internal аудит + рекомендации по gas optimization |
| Документация | API, схема данных, инструкция для игрока |
| Развёртывание | Настройка VRF subscription, деплой контрактов |
| Обучение команды | 2-3 сессии по поддержке системы |
Мы также передаём исходные коды, тестовые скрипты и dashboard для мониторинга RTP. Гарантируем поддержку 6 месяцев после запуска.
Почему стоит выбрать VRF вместо commit-reveal?
Сравнение подходов:
| Критерий | Chainlink VRF | Commit-reveal |
|---|---|---|
| Верификация | On-chain, без доверия | Off-chain, требует доверия к раскрытию seed |
| Стоимость газа | Средняя (call + callback) | Низкая (только хэши) |
| Скорость | 2-3 блока | Мгновенно |
| Прозрачность | Полная | Высокая, но зависит от честности сервера |
| Аудируемость | Простая (events) | Средняя (логи) |
Для проектов с жёсткими требованиями к прозрачности — VRF. Для быстрых игр с низкой комиссией — commit-reveal.
Сроки разработки
- Базовые игры с VRF (Dice, Coinflip, Crash): 4-6 недель
- Commit-reveal off-chain engine: 3-4 недели
- Верификационный UI: 2-3 недели
- Расширение до 5-8 игр: +4-8 недель
- Security audit: 4-6 недель (рекомендовано)
- Итого: 3-5 месяцев под ключ
Сроки могут варьироваться в зависимости от сложности game logic и интеграции с платёжными системами.
Типичные ошибки при реализации provably fair
- Слабая энтропия server_seed: генерируйте через
crypto.randomBytes(32), никогда черезMath.random. - Неопубликованный hash seed’а до игры: иначе игрок не может проверить, что seed не был изменён post factum.
- Отсутствие верификационного UI: без него provably fair — пустая маркетинговая фраза.
- Недостаточная защита bankroll: используйте пул с лимитом ставок и публичные метрики RTP.
- Игнорирование gas optimization: каждая ставка — транзакция, оптимизируйте переменные и используйте собранные storage.
Свяжитесь с нами, чтобы обсудить ваш проект — оценим архитектуру и предложим оптимальное решение. Понятие provably fair описано в Wikipedia. Подробнее о Chainlink VRF — в документации Chainlink VRF.







