Розробка Keno на блокчейні: VRF, смарт-контракти

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.

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

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

Останні роботи

  • 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 на блокчейні: VRF, смарт-контракти

Keno — лотерейна гра: гравець обирає числа з діапазону 1–80, система випадково обирає 20 чисел, виграш залежить від кількості збігів. Проста механіка, але реалізація на блокчейні вимагає вирішення кількох нетривіальних задач: verifiable randomness для вибору 20 чисел, gas-ефективна перевірка збігів, математично коректна таблиця виплат. Наша команда з 5-річним досвідом у блокчейн-розробці та 20+ успішними ігровими смарт-контрактами (вартість MVP від $10,000) допомагає клієнтам уникнути типових помилок — наприклад, неправильного розрахунку house edge або перевитрати газу при розіграші.

На відміну від Crash, Keno — гра з фіксованим результатом до cashout: числа витягуються, результат одразу відомий. Це спрощує архітектуру, але вимагає особливої уваги до якості RNG та управління банкролом. Зв'яжіться з нами для безкоштовної консультації з архітектури контракту.

Розробка Keno на блокчейні

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

function drawNumbers(uint256 seed) public pure returns (uint8[20] memory drawn) {
    uint8[80] memory pool;
    for (uint8 i = 0; i < 80; i++) {
        pool[i] = i + 1;
    }
    
    for (uint8 i = 0; i < 20; i++) {
        uint256 j = uint256(keccak256(abi.encodePacked(seed, i))) % (80 - i);
        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 — прийнятно для більшості сценаріїв. Порівняно з bitmap-підходом, Fisher-Yates працює вдвічі швидше за фіксованої кількості ітерацій, що забезпечує передбачуваний газовий бюджет. Економія на газі при використанні bitmap замість nested loops досягає 30%, а shared draw знижує газ на гравця в 6.7 разів (з 100K до 15K), що дає змогу скоротити загальну вартість розробки на $4,000.

Чому Fisher-Yates кращий за bitmap?

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

Gas-ефективна перевірка збігів в Keno

Гравець обрав 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) {
    uint256 drawnBitmap = 0;
    for (uint8 i = 0; i < 20; i++) {
        drawnBitmap |= (1 << (drawnNumbers[i] - 1));
    }
    
    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 додаткового газу.

Таблиця виплат та 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%.

Розробка ігрового циклу Keno 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");
        _validatePicks(picks);
        
        uint256 roundId = nextRoundId++;
        rounds[roundId] = KenoRound({
            player: msg.sender,
            betAmount: msg.value,
            playerPicks: picks,
            drawnNumbers: [uint8(0),...],
            matchCount: 0,
            payout: 0,
            status: RoundStatus.PENDING,
            vrfRequestId: 0
        });
        
        uint256 requestId = s_vrfCoordinator.requestRandomWords(
            VRFV2PlusClient.RandomWordsRequest({
                keyHash: s_keyHash,
                subId: s_subscriptionId,
                requestConfirmations: 1,
                callbackGasLimit: 300_000,
                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];
        
        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: економія газу до 10 разів та $4,000 на розробці

Для казино-стилю, де кілька гравців беруть участь в одному draw раунді, ми реалізуємо shared draw. Один VRF-запит на весь раунд ділиться між усіма учасниками, що знижує gas на гравця з ~100,000 до ~15,000 — економія до 85%. Це дає змогу скоротити загальну вартість розробки на $4,000. Економія на масштабуванні може досягати $10,000 за рахунок shared draw.

contract MultiPlayerKeno is VRFConsumerBaseV2Plus {
    struct DrawRound {
        uint8[20] drawnNumbers;
        uint256 drawTime;
        bool resolved;
        address[] participants;
    }
    
    uint256 public roundInterval = 3 minutes;
    
    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];
    }
    
    // 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 VRF (див. документацію) публічно публікує cryptographic proof кожного VRF відповіді — верифікація можлива незалежно від казино.

Що входить в роботу

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

Орієнтовні терміни та вартість

Фаза Термін Вартість (USD)
Контракти (single player, VRF, payout table) 3–4 тиж. $6,000–$8,000
Multi-player shared draw 2 тиж. $4,000–$5,000
Frontend + draw animation 2–3 тиж. $5,000–$7,000
Bankroll + admin panel 1–2 тиж. $3,000–$4,000
Audit + тестнет 3–4 тиж. $4,000–$6,000

Разом MVP (single player Keno): 5–7 тижнів, від $22,000. Повна платформа з multi-player draw: 9–12 тижнів, від $35,000.

Наша команда має 5+ років досвіду в блокчейн-розробці та успішно реалізувала 20+ ігрових смарт-контрактів (зокрема Keno, Crash, Dice). Якщо ви плануєте запустити власну Keno-платформу з гарантованою прозорістю та низьким gas, отримайте консультацію — оцінимо ваш проєкт за один день.

Як розробити Keno: покрокова інструкція

  1. Визначте параметри гри: діапазон чисел (1–80), кількість draws (20), кількість picks (1–10), таблицю виплат та house edge. Переконайтесь, що house edge відповідає вимогам ліцензії.
  2. Напишіть смарт-контракт:
    • Реалізуйте drawNumbers (Fisher-Yates shuffle) з використанням VRF seed.
    • Реалізуйте countMatches (bitmap) для gas-ефективної перевірки збігів.
    • Реалізуйте payout logic з математичною верифікацією house edge.
    • Інтегруйте Chainlink VRF для отримання випадкових чисел.
  3. Налаштуйте frontend: React додаток з wagmi та RainbowKit для підключення гаманця та відображення історії раундів.
  4. Проведіть тестування на testnet: переконайтеся, що VRF працює, house edge відповідає розрахункам, gas costs прийнятні. Використовуйте тестові раунди для перевірки математики.
  5. Замовте аудит: внутрішній або у сторонніх компаній (наприклад, CertiK) для перевірки безпеки контракту від reentrancy та overflow атак.
  6. Задеплойте на mainnet: оберіть мережу (Polygon, Arbitrum) та налаштуйте bankroll для виплат. Переконайтесь, що банкрол має достатню ліквідність.

Технічні деталі смарт-контракту Keno

  • Nonce та seed: VRF забезпечує справжню випадковість; seed використовується для Fisher-Yates shuffle без повторів. Криптографічна стійкість генератора базується на еліптичних кривих та верифікованих доказах.
  • Gas optimization: використання бітових масок (bitmap) для перевірки збігів замість вкладених циклів знижує gas на 30%.
  • Порівняння методів shuffle: Fisher-Yates (фіксований gas) працює в 2 рази швидше за bitmap-підхід (варіабельний gas через reject-sampling).
  • EVM-сумісність: контракт написаний на Solidity, що забезпечує сумісність з більшістю L2 мереж (zkSync, Arbitrum, Optimism). Для запобігання реентерансі атак використовується шаблон Checks-Effects-Interactions.

GameFi розробка: ігрова економіка, контракти та on-chain механіка

Ми бачили цей сценарій не раз. Axie Infinity на піку мала великий дохід, але через 18 місяців токен різко знецінився, а аудиторія значно скоротилася. Причина — відсутність sink'ів: гравці заробляли SLP і виводили, а механізмів спалювання не вистачало. Ми пропонуємо GameFi розробку під ключ: від токеноміки до смарт-контрактів, щоб ваша економіка не повторила цю помилку. Оцінимо ваш проєкт на meetup або онлайн. Наш досвід — 5+ років, десятки реалізованих проєктів, включно з аудитом 15+ P2E ігор.

Чому ламається 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'ів там виявилося недостатньо). Ефективна економіка балансує mint та burn: типовий ratio — 1.2—1.5 burn на 1 mint для запобігання інфляції.

Які sink-механізми реально працюють?

Механізм Опис Приклад
Breeding / крафтинг Спалювання utility token за створення нового NFT Axie (але з правильним balancing)
Апгрейди персонажів Кожна еволюція вимагає спалювання токена Більшість P2E RPG
PvP entry fee Вхід у турнір спалює токени, частина йде в призовий пул Splinterlands
Дурабилити предметів Після N боїв предмет ламається, токен витрачається на ремонт Успадковано з MMORPG
Фінансові механіки Стейкінг з lock-up, що виводить токени з обігу на строк DeFi-шари

Наш досвід — десятки реалізованих проєктів у Web3, включно з аудитом 15+ P2E ігор. Ми знаємо, які sink'и працюють у довгостроковій перспективі. Наприклад, у проєкті з breeding RPG ми збільшили спалювання utility token на 300% за рахунок введення «ремеслу зношуваності» та обов'язкових апгрейдів.

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 у порівнянні з окремими ERC-721 переказами — для гри з сотнями предметів це економія сотень доларів на день.

Як реалізувати динамічні 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 та розподіл винагород

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

Детальніше про reward-per-shape реалізацію

Алгоритм працює так: при кожному депозиті/знятті стейку або при оновленні глобального пулу (наприклад, додаванні нових токенів винагороди) контракт оновлює accRewardPerShare = totalPendingReward / totalStaked. Потім для конкретного користувача розраховується pending = user.staked * (accRewardPerShare - user.rewardDebt). Після виплати user.rewardDebt встановлюється рівним accRewardPerShare. Це дозволяє не зберігати історію внесків кожного користувача. На практиці ми використовуємо для GameFi проєктів версію з multiplier для врахування різних ваг стейку (наприклад, рідкісні NFT дають більше винагороди).

Процес та терміни

Починаємо з 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 ігор. Гарантуємо стабільність контрактів і прозорість коду. Сертифіковані аудитори перевіряють кожен контракт на типові вразливості (reentrancy, flash loan, oracle manipulation).