Блокчейн-рулетка на Solidity: VRF, смарт-контракты и React-интерфейс

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1305 услуг
Блокчейн-рулетка на Solidity: VRF, смарт-контракты и React-интерфейс
Средний
~5 дней
Часто задаваемые вопросы

Направления блокчейн-разработки

Этапы блокчейн-разработки

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1351
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1247
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    951
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1186
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    642
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    922

Архитектура блокчейн-рулетки

Обеспечить непредсказуемость результата в on-chain рулетке — нетривиально. Наивные реализации используют block.prevrandao или хэши блоков, но это открывает путь для MEV-атак: валидатор может переупорядочить транзакции, чтобы повлиять на исход. Мы разрабатываем смарт-контракты с проверяемой случайностью — либо через Chainlink VRF, либо по commit-reveal схеме. Первый дороже, но полностью децентрализован; второй быстрее, но требует доверия к серверу. Ниже — полная архитектура с кодом, выбором сети и анимацией.

За 5+ лет разработки рулеток на блокчейне мы запустили более 20 проектов: от казуальных игр до крупных DeFi gaming платформ. Опыт с разными L1/L2 позволяет выбрать оптимальный стек под бюджет и аудиторию.

Как обеспечить честную случайность?

Нельзя использовать block.timestamp, block.prevrandao, или хэши блоков как источник случайности. Miner/validator может влиять на эти значения — это называется miner extractable value (MEV) атака на randomness. Для рулетки это означает, что нода может выбирать, включать или нет транзакцию в зависимости от того, выигрышный ли блок.

Chainlink VRF v2.5

Cryptographically secure, verifiable random numbers от децентрализованного оракула. Chainlink VRF гарантирует, что ни казино, ни игрок не могут повлиять на результат, а доказательство верифицируется в контракте.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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 RouletteGame is VRFConsumerBaseV2Plus {
    uint256 public subscriptionId;
    bytes32 public keyHash; // зависит от сети

    enum BetType { Number, Red, Black, Even, Odd, Low, High }

    struct Bet {
        address player;
        BetType betType;
        uint8 number;   // для Number ставки (0-36)
        uint256 amount;
        bool settled;
    }

    struct Round {
        uint256 requestId;
        uint8 result;   // 0-36
        bool fulfilled;
        mapping(uint256 => Bet) bets;
        uint256 betCount;
    }

    mapping(uint256 => Round) public rounds; // requestId → Round
    mapping(uint256 => uint256) public requestToRound;

    uint256 public currentRoundId;
    uint256 public constant MAX_BET = 1 ether;
    uint256 public constant HOUSE_EDGE = 270; // 2.7% (европейская рулетка)

    event BetPlaced(uint256 roundId, address player, BetType betType, uint8 number, uint256 amount);
    event RoundStarted(uint256 roundId, uint256 requestId);
    event RoundSettled(uint256 roundId, uint8 result);
    event WinningPaid(address player, uint256 amount);

    constructor(
        address vrfCoordinator,
        uint256 _subscriptionId,
        bytes32 _keyHash
    ) VRFConsumerBaseV2Plus(vrfCoordinator) {
        subscriptionId = _subscriptionId;
        keyHash = _keyHash;
        currentRoundId = 1;
    }

    function placeBet(
        BetType betType,
        uint8 number
    ) external payable {
        require(msg.value > 0 && msg.value <= MAX_BET, "Invalid bet amount");
        if (betType == BetType.Number) {
            require(number <= 36, "Invalid number");
        }

        Round storage round = rounds[currentRoundId];
        uint256 betId = round.betCount++;
        round.bets[betId] = Bet({
            player: msg.sender,
            betType: betType,
            number: number,
            amount: msg.value,
            settled: false
        });

        emit BetPlaced(currentRoundId, msg.sender, betType, number, msg.value);
    }

    // Закрыть раунд и запросить случайность
    function spinWheel() external returns (uint256 requestId) {
        requestId = s_vrfCoordinator.requestRandomWords(
            VRFV2PlusClient.RandomWordsRequest({
                keyHash: keyHash,
                subId: subscriptionId,
                requestConfirmations: 3,
                callbackGasLimit: 300_000,
                numWords: 1,
                extraArgs: VRFV2PlusClient._argsToBytes(
                    VRFV2PlusClient.ExtraArgsV1({ nativePayment: false })
                )
            })
        );

        requestToRound[requestId] = currentRoundId;
        rounds[currentRoundId].requestId = requestId;
        currentRoundId++;

        emit RoundStarted(currentRoundId - 1, requestId);
    }

    // Callback от Chainlink VRF
    function fulfillRandomWords(
        uint256 requestId,
        uint256[] calldata randomWords
    ) internal override {
        uint256 roundId = requestToRound[requestId];
        Round storage round = rounds[roundId];

        // 0-36 включительно = 37 значений
        uint8 result = uint8(randomWords[0] % 37);
        round.result = result;
        round.fulfilled = true;

        emit RoundSettled(roundId, result);
        _settleAllBets(roundId);
    }

    function _settleAllBets(uint256 roundId) internal {
        Round storage round = rounds[roundId];
        uint8 result = round.result;

        for (uint256 i = 0; i < round.betCount; i++) {
            Bet storage bet = round.bets[i];
            if (bet.settled) continue;
            bet.settled = true;

            uint256 payout = _calculatePayout(bet, result);
            if (payout > 0) {
                payable(bet.player).transfer(payout);
                emit WinningPaid(bet.player, payout);
            }
        }
    }

    function _calculatePayout(
        Bet memory bet,
        uint8 result
    ) internal pure returns (uint256) {
        bool win = false;
        uint256 multiplier = 0;

        if (bet.betType == BetType.Number) {
            win = (bet.number == result);
            multiplier = 35; // 35:1
        } else if (bet.betType == BetType.Red) {
            win = _isRed(result);
            multiplier = 1;  // 1:1
        } else if (bet.betType == BetType.Black) {
            win = (!_isRed(result) && result != 0);
            multiplier = 1;
        } else if (bet.betType == BetType.Even) {
            win = (result != 0 && result % 2 == 0);
            multiplier = 1;
        } else if (bet.betType == BetType.Odd) {
            win = (result % 2 == 1);
            multiplier = 1;
        } else if (bet.betType == BetType.Low) {
            win = (result >= 1 && result <= 18);
            multiplier = 1;
        } else if (bet.betType == BetType.High) {
            win = (result >= 19 && result <= 36);
            multiplier = 1;
        }

        if (!win) return 0;
        return bet.amount + (bet.amount * multiplier); // stake + profit
    }

    // Красные числа европейской рулетки
    function _isRed(uint8 n) internal pure returns (bool) {
        uint256 redNumbers = 0x3A4A5251412C2B1A191009080706;
        return (redNumbers >> n) & 1 == 1;
    }

    // Пополнение пула призов
    receive() external payable {}

    // Emergency: вывод призового пула (timelock + multisig в production)
    function withdrawHouseBalance(uint256 amount) external onlyOwner {
        payable(owner()).transfer(amount);
    }
}

Почему стоит выбрать Chainlink VRF?

Chainlink VRF предоставляет доказательство случайности, которое можно проверить в контракте. Это значит, что игрок может самостоятельно убедиться в честности раунда. Альтернативные решения, такие как commit-reveal с серверным seed, не предоставляют ончейн-верификации, но значительно дешевле. Для low-stake игр или тестнетов их можно рассматривать.

Комиссия и ликвидность

Казино должно иметь достаточный резерв для выплаты крупных выигрышей. Распространённые подходы:

Fixed house reserve — контракт держит фиксированный резерв, максимальная ставка ограничена процентом от резерва.

Liquidity pool модель — LP провайдеры депонируют ETH, получают долю house edge как доходность. Игроки играют против пула. Смарт-контракт вроде Liquidity.lp token. Эта модель популярна в крупных блокчейн-гемблинг проектах (Rollbit, Stake Casino).

Фронтенд и анимация

Сама рулетка — DOM canvas (React + Pixi.js или Three.js). Ключевые моменты:

  • Анимация колеса запускается при вызове spinWheel(), ещё до получения результата от VRF
  • Реальный результат приходит через ~30–60 секунд (время VRF callback)
  • Колесо "случайно вращается" несколько полных оборотов, затем плавно тормозит на нужном секторе
  • Event RoundSettled от контракта — триггер для финальной позиции колеса
// Подписка на событие результата
const unwatch = publicClient.watchContractEvent({
  address: ROULETTE_ADDRESS,
  abi: rouletteAbi,
  eventName: "RoundSettled",
  args: { roundId: currentRoundId },
  onLogs: (logs) => {
    const result = logs[0].args.result;
    rouletteWheel.stopAt(result); // анимация финального положения
    showResult(result);
    checkWinnings(result);
  },
});

Сети и стоимость

Основной параметр выбора сети — стоимость VRF callback. На Ethereum mainnet это дорого. Использование L2 сетей снижает затраты на VRF в 10-20 раз. Рекомендуемые сети:

Сеть VRF стоимость Время ответа Рекомендация
Arbitrum ~$0.30–0.80 ~30–60 сек Оптимально
Polygon ~$0.01–0.05 ~30–60 сек Бюджетный вариант
Avalanche ~$0.10–0.30 ~30–60 сек Хорошо
Base ~$0.05–0.20 ~30–60 сек Растущая аудитория

Для high-frequency вращений (несколько в минуту) — рассматриваем alternative VRF: Pyth Entropy (значительно дешевле Chainlink), или commit-reveal с server seed (менее decentralized, но мгновенно).

Процесс разработки под ключ

Мы работаем по чёткому плану:

  1. Аналитика и проектирование — фиксируем правила игры, виды ставок, house edge, механику выплат.
  2. Разработка смарт-контракта — пишем контракт на Solidity 0.8.x с поддержкой VRF, multiple bet types, payout math.
  3. Тестирование и аудит — unit тесты (Foundry), fuzzing (Echidna), статический анализ (Slither, Mythril). Сертифицированные Solidity-разработчики проводят ревью кода.
  4. Фронтенд интеграция — React + wagmi + RainbowKit, анимация колеса на Pixi.js/Three.js, подписка на события контракта.
  5. Деплой и запуск — развертывание в выбранной сети, настройка Chainlink VRF subscription, финальный smoke test.

Что входит в работу

Компонент Описание
Смарт-контракт Solidity, ERC-20/ETH, VRF, все ставки
Анимация колеса React + Pixi.js, синхронизация с событием контракта
Бэкенд (опционально) Event sourcing, лидерборд, админка
Документация API контракта, инструкция по запуску
Поддержка 2 недели после запуска, обучение команды

Сроки и стоимость

Сроки разработки — от 4 до 8 недель в зависимости от сложности. Точная стоимость рассчитывается индивидуально после анализа ваших требований. Готовы обсудить проект? Свяжитесь с нами для предварительной оценки. Закажите разработку рулетки под ключ — получите готовый продукт с гарантией качества.

Игровая экономика, контракты и on-chain механика

Мы видели этот сценарий не раз. Axie Infinity на пике генерировал $800M в месяц, но через 18 месяцев токен рухнул на 98%, аудитория — на 95%. Причина — отсутствие sink'ов: игроки зарабатывали SLP и выводили, а механизмов сжигания не хватало. Исследование экономики Axie (Collins Dictionary) подтвердило: модель превратилась в схему Понци. Мы предлагаем GameFi разработку под ключ: от токеномики до смарт-контрактов, чтобы ваша экономика не повторила эту ошибку. Оценим ваш проект на meetup или онлайн.

Где ломается Play-to-Earn экономика

Инфляционная токеномика без sink'ов. Игрок получает токены за геймплей. Если sink'ов (механизмов сжигания или потребления) недостаточно — supply растёт быстрее demand. Цена падает. Доход игрока в fiat уменьшается. Игроки уходят. Смертельная спираль.

Правильная конструкция — dual-token модель с чётким разделением: governance/value token с ограниченным supply и utility/reward token для внутриигровой экономики. Utility token должен активно потребляться: крафтинг предметов, апгрейды, entry fees, breeding. Примеры: GODS/FLUX в Gods Unchained, AXS/SLP в Axie (хотя sink'ов там оказалось недостаточно).

Какие sink-механизмы реально работают?

  • Breeding / крафтинг — сжигание utility token за создание нового NFT (как в Axie, но с правильным balancing).
  • Апгрейды персонажей — каждая эволюция требует сжигания токена.
  • PvP entry fee — вход в турнир сжигает токены, часть идёт в призовой пул.
  • Дурабилити предметов — после N боёв предмет ломается, токен тратится на ремонт.
  • Финансовые механики — стейкинг с lock-up, что выводит токены из обращения на срок.

On-chain vs Off-chain: где проходит граница

Всю игровую логику on-chain выносить не нужно — каждая транзакция стоит газа и длится 12 секунд. Игровой цикл — миллисекунды. Баланс:

Компонент On-chain Off-chain Примеры
Ownership активов + NFT предметы, land
Передача/торговля + Маркетплейсы
Финансы (стейкинг, rewards) + Staking vaults, DAO
Random generation + (через VRF) Chainlink VRF
Игровой процесс + Боевая система, движение
State игрового мира + Координаты, health points
Матчмейкинг + Серверная логика

Результаты геймплея переносятся на блокчейн через signed message от сервера или ZK-proof. Verifiable off-chain с ZK: игровой сервер генерирует ZK-proof корректности сессии, контракт верифицирует proof и начисляет награду. Реализации: Cartridge (Starknet), zkSync game rollups.

Реализация NFT игровых предметов

Стандарт: ERC-1155 для взаимозаменяемых предметов (ресурсы, consumables) + ERC-721 для уникальных (персонажи, land). ERC-1155 даёт до 60% экономии на газе при batch transfer.

Как реализовать динамические NFT без перегрузки блокчейна?

Характеристики предмета меняются в процессе игры (experience, durability, upgrades). Два подхода:

  • Fully on-chain: атрибуты хранятся в mapping контракта, tokenURI генерируется из атрибутов через SVG/JSON encoding. Дорого по газу при частом обновлении. Используется для land и ключевых активов.
  • Hybrid: атрибуты хранятся off-chain, в tokenURI — hash состояния. Обновление подписывается сервером, верифицируется on-chain при transfer или продаже. Дешевле, но требует доверия к серверу или ZK.

Breeding и crafting. Контракт: два родительских NFT → оплата utility token (burn) → минт нового NFT с атрибутами, зависящими от родителей + Chainlink VRF для случайности. Без VRF майнеры могут манипулировать рандомом через выбор блока.

// Simplified breeding with Chainlink VRF
function breed(uint256 parent1Id, uint256 parent2Id) external {
    require(ownerOf(parent1Id) == msg.sender);
    require(ownerOf(parent2Id) == msg.sender);
    require(breedingToken.burnFrom(msg.sender, BREEDING_COST));

    uint256 requestId = vrfCoordinator.requestRandomWords(...);
    pendingBreeds[requestId] = BreedRequest(parent1Id, parent2Id, msg.sender);
}

function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
    BreedRequest memory req = pendingBreeds[requestId];
    uint256 childAttributes = deriveAttributes(req.parent1Id, req.parent2Id, randomWords[0]);
    _mintWithAttributes(req.requester, childAttributes);
}

Маркетплейс и роялти

Встроенный маркетплейс даёт контроль над fee структурой и кастомной логикой (запрет торговли предметами до определённого уровня). Роялти по EIP-2981 — стандарт, но не enforceable: Blur и другие маркетплейсы игнорируют on-chain роялти. Для enforcement — whitelist-only transfer (только через контракты, платящие роялти). Жертвуем composability ради защиты прав.

Staking и rewards distribution

Staking NFT — механика для удержания игроков. Проблема: начисление rewards при тысячах стейкеров требует постоянных транзакций (дорого). Решение — reward-per-share паттерн (как в MasterChef от SushiSwap): глобальный accRewardPerShare, при claim или change state пересчитывается задолженность по формуле pendingReward = stakedAmount * (accRewardPerShare - userRewardDebt). O(1) сложность независимо от числа стейкеров. Экономия газа — до 70% по сравнению с поэлементным начислением.

Процесс и сроки

Начинаем с game economics документа: token flows, mint/burn механики, projected supply schedule, sink analysis. До написания кода экономика моделируется (Cadence, Python simulation).

Как мы строим GameFi: 5 этапов

  1. Экономическое моделирование — 1-2 недели. Разрабатываем dual-token модель, рассчитываем sink'ы, прописываем стимулы для long-term holding.
  2. Разработка токен-контрактов — 2-3 недели. ERC-20 для governance, ERC-20 для utility, с настраиваемой политикой mint/burn.
  3. Смарт-контракты NFT — 3-5 недель. ERC-721 / ERC-1155 с dynamic metadata, breeding/crafting, Chainlink VRF.
  4. Staking + rewards — 2-3 недели. Контракт на базе reward-per-share, интерфейсы для frontend.
  5. Маркетплейс (опционально) — 2-4 недели. Кастомный маркетплейс с enforced royalty.

Что входит в работу

  • Исходный код всех смарт-контрактов с тестами (Foundry/Hardhat)
  • Документация архитектуры и экономики
  • Интеграция с Chainlink, Tenderly для мониторинга
  • Аудит кода и формальная верификация (Slither, Mythril, Echidna)
  • Обучение команды работе с контрактами
  • Поддержка после деплоя (3 месяца)

Базовый GameFi стек (токены + NFT + staking + маркетплейс) — от 8 до 16 недель. Полная игра с on-chain рандомом, breeding, dynamic NFT — 4-8 месяцев. ZK-based verifiable gameplay — отдельный проект от 6 месяцев.

Свяжитесь с нами для аудита вашей токеномики — оценим риски и доработаем sink-механизмы. Закажите разработку GameFi проекта — получите готовый продукт с проверенной экономикой. Гарантируем стабильность контрактов и прозрачность кода. Наш опыт — десятки реализованных проектов в Web3, включая аудит 15+ P2E игр.