Разработка Keno на блокчейне: VRF, смарт-контракты, gas-оптимизация

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1305 услуг
Разработка Keno на блокчейне: VRF, смарт-контракты, gas-оптимизация
Средний
~3-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

Разработка игры Keno на блокчейне

Keno — лотерейная игра: игрок выбирает числа из диапазона 1–80, система случайно выбирает 20 чисел, выигрыш зависит от количества совпадений. Простая механика, но реализация на блокчейне требует решения нескольких нетривиальных задач: verifiable randomness для выбора 20 чисел, gas-эффективная проверка совпадений, математически корректная таблица выплат. Мы разрабатываем такие игры под ключ более 5 лет и успешно запустили 20+ проектов на Ethereum, Polygon и Arbitrum.

В отличие от Crash, Keno — игра с фиксированным исходом до cashout: числа вытягиваются, результат сразу известен. Это упрощает архитектуру, но требует особого внимания к качеству RNG и управлению банкроллом. Наша команда помогает клиентам избежать типичных ошибок — например, неверного расчёта house edge или перерасхода газа при розыгрыше. Свяжитесь с нами для бесплатной консультации по архитектуре контракта.

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

Основная техническая задача: из одного VRF random seed получить 20 уникальных чисел в диапазоне 1–80. Наивный подход (rand % 80 повторить 20 раз) создаёт коллизии — одно число может выпасть дважды. Мы используем Fisher-Yates shuffle на смарт-контракте:

function drawNumbers(uint256 seed) public pure returns (uint8[20] memory drawn) {
    // Инициализируем массив 1..80
    uint8[80] memory pool;
    for (uint8 i = 0; i < 80; i++) {
        pool[i] = i + 1;
    }
    
    // Fisher-Yates: тасуем первые 20 позиций
    for (uint8 i = 0; i < 20; i++) {
        // Получаем псевдо-random индекс из seed
        uint256 j = uint256(keccak256(abi.encodePacked(seed, i))) % (80 - i);
        
        // Swap pool[i] и pool[i + j]
        uint8 temp = pool[i];
        pool[i] = pool[i + j];
        pool[i + j] = temp;
        
        drawn[i] = pool[i];
    }
}

Fisher-Yates даёт гарантированно уникальные числа без reject-sampling. Для on-chain выполнения: 20 итераций × keccak256 ≈ 80,000–100,000 gas. На Arbitrum это ~$0.01 — приемлемо для большинства сценариев.

Почему Fisher-Yates предпочтительнее bitmap?

Bitmap-подход (запись занятых чисел в битовую маску) проще в реализации, но может потребовать больше вызовов keccak256 при выборе последних чисел из-за коллизий. Fisher-Yates гарантирует фиксированное число итераций — 20. Для предсказуемого газа мы рекомендуем именно его, особенно при массовых розыгрышах.

Проверка совпадений: gas-эффективная реализация

Игрок выбрал M чисел (1–10), нужно подсчитать, сколько совпадает с 20 drawn numbers. Вложенные циклы O(M×20) допустимы для small M, но мы используем bitmap для экономии газа:

function countMatches(
    uint8[] memory playerPicks,
    uint8[20] memory drawnNumbers
) public pure returns (uint8 matches) {
    // Строим bitmap drawn numbers
    uint256 drawnBitmap = 0;
    for (uint8 i = 0; i < 20; i++) {
        drawnBitmap |= (1 << (drawnNumbers[i] - 1));
    }
    
    // Проверяем picks игрока против bitmap
    for (uint8 i = 0; i < playerPicks.length; i++) {
        if (drawnBitmap & (1 << (playerPicks[i] - 1)) != 0) {
            matches++;
        }
    }
}

Битовые операции быстрее nested loops. Для типичных 1–10 picks: ≈ 3,000–5,000 дополнительного gas.

Таблица выплат и house edge

Keno выплаты — самая важная экономическая часть. Нужно балансировать house edge (обычно 20–35% в Keno) при разных количествах picks. Вот фрагмент таблицы множителей для популярных вариантов:

Количество picks Совпадения Множитель (X)
1 1 3.6
3 2 2
3 3 46
5 3 3
5 4 12
5 5 500
10 5 2
10 6 18
10 7 170
10 8 1000
10 9 2500
10 10 10000

House edge верифицируется математически: для каждого варианта picks рассчитывается Expected Value:

EV(5 picks) = Σ P(k matches) × payout(5, k) для k = 0..5
P(k matches) = C(20,k) × C(60, 5-k) / C(80, 5)

EV должен быть ≈ 0.70–0.80 (70–80% RTP, 20–30% house edge)

Например, для 1 pick: P(1 match) = 20/80 = 0.25, EV = 0.25 × 3.6 = 0.9, то есть RTP 90%, house edge 10%.

Полный игровой цикл on-chain

contract KenoGame is VRFConsumerBaseV2Plus {
    struct KenoRound {
        address player;
        uint256 betAmount;
        uint8[] playerPicks;
        uint8[20] drawnNumbers;
        uint8 matchCount;
        uint256 payout;
        RoundStatus status;
        uint256 vrfRequestId;
    }
    
    mapping(uint256 => KenoRound) public rounds;
    mapping(uint256 => uint256) public vrfToRound;
    uint256 public nextRoundId;
    
    function playKeno(uint8[] calldata picks) external payable {
        require(picks.length >= 1 && picks.length <= 10, "Invalid picks count");
        require(msg.value >= MIN_BET && msg.value <= maxBet(), "Invalid bet");
        
        // Валидируем picks (1-80, уникальные)
        _validatePicks(picks);
        
        uint256 roundId = nextRoundId++;
        rounds[roundId] = KenoRound({
            player: msg.sender,
            betAmount: msg.value,
            playerPicks: picks,
            drawnNumbers: [uint8(0),...], // заполнится в callback
            matchCount: 0,
            payout: 0,
            status: RoundStatus.PENDING,
            vrfRequestId: 0
        });
        
        // Запрашиваем VRF
        uint256 requestId = s_vrfCoordinator.requestRandomWords(
            VRFV2PlusClient.RandomWordsRequest({
                keyHash: s_keyHash,
                subId: s_subscriptionId,
                requestConfirmations: 1,
                callbackGasLimit: 300_000, // с запасом для drawNumbers
                numWords: 1,
                extraArgs: ""
            })
        );
        
        rounds[roundId].vrfRequestId = requestId;
        vrfToRound[requestId] = roundId;
        
        emit KenoRoundStarted(roundId, msg.sender, picks, msg.value);
    }
    
    function fulfillRandomWords(
        uint256 requestId,
        uint256[] calldata randomWords
    ) internal override {
        uint256 roundId = vrfToRound[requestId];
        KenoRound storage round = rounds[roundId];
        
        // Вытягиваем 20 чисел
        round.drawnNumbers = drawNumbers(randomWords[0]);
        
        // Считаем совпадения
        round.matchCount = countMatches(round.playerPicks, round.drawnNumbers);
        
        // Рассчитываем выплату
        uint256 multiplier = payoutTable[round.playerPicks.length][round.matchCount];
        round.payout = round.betAmount * multiplier / 100;
        round.status = RoundStatus.COMPLETED;
        
        // Выплачиваем победителю
        if (round.payout > 0) {
            require(address(this).balance >= round.payout, "Insufficient bankroll");
            payable(round.player).transfer(round.payout);
        }
        
        emit KenoResult(
            roundId,
            round.player,
            round.drawnNumbers,
            round.matchCount,
            round.payout
        );
    }
}

Что такое shared draw и как он экономит газ?

Для казино-стиля, где несколько игроков участвуют в одном draw раунда, мы реализуем shared draw. Один VRF-запрос на весь раунд делится между всеми участниками, что снижает gas на игрока с ~100,000 до ~15,000.

contract MultiPlayerKeno is VRFConsumerBaseV2Plus {
    struct DrawRound {
        uint8[20] drawnNumbers;
        uint256 drawTime;
        bool resolved;
        address[] participants;
    }
    
    // Раунды каждые N минут
    uint256 public roundInterval = 3 minutes;
    
    // Ставки привязываются к будущему draw раунду
    struct PlayerBet {
        uint8[] picks;
        uint256 amount;
        uint256 drawRoundId;
    }
    
    function getBetsOnNextDraw(address player) external view returns (PlayerBet[] memory) {
        uint256 nextDraw = (block.timestamp / roundInterval + 1) * roundInterval;
        return pendingBets[nextDraw][player];
    }
    
    // Один VRF запрос на весь draw — делится между всеми участниками
    // Gas cost per player: ~15,000 gas (vs ~100,000 для single player)
    function triggerDraw(uint256 drawRoundId) external {
        require(block.timestamp >= drawRoundId, "Too early");
        require(!drawRounds[drawRoundId].resolved, "Already drawn");
        
        uint256 requestId = s_vrfCoordinator.requestRandomWords(...);
        vrfToDrawRound[requestId] = drawRoundId;
    }
}

Как проверить результат игры?

  1. Получить VRF request и response из on-chain событий.
  2. Применить drawNumbers(vrfResult) — получить те же 20 чисел.
  3. Убедиться, что house не манипулировал.

Chainlink публично публикует cryptographic proof каждого VRF ответа — верификация возможна независимо от казино. Этот механизм основан на технологии Chainlink VRF.

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

  • Разработка смарт-контракта игры (single-player или multi-player)
  • Интеграция Chainlink VRF V2 Plus
  • Настройка таблицы выплат с математической верификацией house edge
  • Создание frontend-интерфейса (React + wagmi + RainbowKit)
  • Deploy на тестнет и mainnet (Polygon, Arbitrum или другая сеть)
  • Проведение аудита смарт-контракта (внутренний или сторонний)
  • Документация по интеграции и поддержке

Ориентировочные сроки

Фаза Срок
Контракты (single player, VRF, payout table) 3–4 нед
Multi-player shared draw 2 нед
Frontend + draw animation 2–3 нед
Bankroll + admin panel 1–2 нед
Audit + тестнет 3–4 нед

Итого MVP (single player Keno): 5–7 недель. Полная платформа с multi-player draw: 9–12 недель.

Наша команда имеет 5+ лет опыта в блокчейн-разработке и успешно реализовала 20+ игровых смарт-контрактов. Если вы планируете запустить собственную Keno-платформу с гарантированной прозрачностью и низким gas, получите консультацию — оценим ваш проект за один день.

Игровая экономика, контракты и 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 игр.