Розробка чесної лотереї на блокчейні під ключ

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

Доведена випадковість з Chainlink VRF

Традиційна лотерея — чорна скринька. Організатор може підтасувати результат, затримати виплати або зникнути з призовим фондом. Власники квитків залишаються ні з чим, а довіра до платформи руйнується. Смарт-контракти вирішують цю проблему кардинально: кожна дія записана в блокчейні, випадковість генерується доведено через Chainlink VRF, а призи розподіляються автоматично. Ми створюємо прозорі лотереї та web3 лотереї під ключ — від концепції до аудиту та деплою. Наша команда має 5+ років досвіду в блокчейн-розробці та запустила понад 20 лотерейних проєктів, включаючи NFT-рафли та no-loss лотереї з TVL понад $10 млн. Нижче — типові проблеми та їх рішення.

Chainlink VRF — це криптографічний доказ випадковості, який перевіряється на блокчейні, з повним документуванням Chainlink VRF. Оператор не може вплинути на результат: запит і відповідь верифікуються. Chainlink VRF у 1000 разів прозоріше за централізовані рішення. У коді нижче — базова реалізація розіграшу з кількома переможцями.

Код смарт-контракту лотереї
// 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 BlockchainLottery is VRFConsumerBaseV2Plus {
    struct Lottery {
        uint256 ticketPrice;
        uint256 startTime;
        uint256 endTime;
        uint256 maxTickets;
        uint256 ticketsSold;
        address[] participants;
        uint256 prizePool;
        LotteryStatus status;
        uint256 vrfRequestId;
        address winner;
        uint256[] prizeDistribution; // [7000, 2000, 1000] = 70%, 20%, 10%
    }
    
    enum LotteryStatus { OPEN, DRAWING, CLOSED, CANCELLED }
    
    mapping(uint256 => Lottery) public lotteries;
    uint256 public lotteryCount;
    
    // Комісія оператора
    uint256 public operatorFee = 300; // 3% в basis points
    
    function createLottery(
        uint256 ticketPrice,
        uint256 duration,
        uint256 maxTickets,
        uint256[] calldata prizeDistribution
    ) external onlyOwner returns (uint256 lotteryId) {
        require(_validateDistribution(prizeDistribution), "Invalid distribution");
        
        lotteryId = ++lotteryCount;
        lotteries[lotteryId] = Lottery({
            ticketPrice: ticketPrice,
            startTime: block.timestamp,
            endTime: block.timestamp + duration,
            maxTickets: maxTickets,
            participants: new address[](0),
            prizePool: 0,
            status: LotteryStatus.OPEN,
            vrfRequestId: 0,
            winner: address(0),
            prizeDistribution: prizeDistribution
        });
    }
    
    function buyTickets(uint256 lotteryId, uint256 amount) external payable {
        Lottery storage lottery = lotteries[lotteryId];
        require(lottery.status == LotteryStatus.OPEN, "Not open");
        require(block.timestamp < lottery.endTime, "Lottery ended");
        require(lottery.ticketsSold + amount <= lottery.maxTickets, "Not enough tickets");
        require(msg.value == lottery.ticketPrice * amount, "Wrong payment");
        
        for (uint256 i = 0; i < amount; i++) {
            lottery.participants.push(msg.sender);
        }
        
        lottery.ticketsSold += amount;
        
        uint256 fee = (msg.value * operatorFee) / 10000;
        lottery.prizePool += msg.value - fee;
        
        emit TicketsPurchased(lotteryId, msg.sender, amount);
    }
    
    function drawWinners(uint256 lotteryId) external {
        Lottery storage lottery = lotteries[lotteryId];
        require(
            block.timestamp >= lottery.endTime || lottery.ticketsSold == lottery.maxTickets,
            "Lottery not ended"
        );
        require(lottery.status == LotteryStatus.OPEN, "Wrong status");
        require(lottery.ticketsSold > 0, "No participants");
        
        lottery.status = LotteryStatus.DRAWING;
        
        uint256 numWinners = lottery.prizeDistribution.length;
        uint256 requestId = s_vrfCoordinator.requestRandomWords(
            VRFV2PlusClient.RandomWordsRequest({
                keyHash: KEY_HASH,
                subId: SUBSCRIPTION_ID,
                requestConfirmations: 3,
                callbackGasLimit: 500_000,
                numWords: uint32(numWinners),
                extraArgs: VRFV2PlusClient._argsToBytes(
                    VRFV2PlusClient.ExtraArgsV1({nativePayment: false})
                )
            })
        );
        
        lottery.vrfRequestId = requestId;
        vrfToLottery[requestId] = lotteryId;
    }
    
    function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) 
        internal override 
    {
        uint256 lotteryId = vrfToLottery[requestId];
        Lottery storage lottery = lotteries[lotteryId];
        
        uint256 participantCount = lottery.participants.length;
        address[] memory winners = new address[](randomWords.length);
        bool[] memory isSelected = new bool[](participantCount);
        
        for (uint256 i = 0; i < randomWords.length; i++) {
            uint256 idx = randomWords[i] % participantCount;
            
            while (isSelected[idx]) {
                idx = (idx + 1) % participantCount;
            }
            
            isSelected[idx] = true;
            winners[i] = lottery.participants[idx];
            
            uint256 prize = (lottery.prizePool * lottery.prizeDistribution[i]) / 10000;
            payable(winners[i]).transfer(prize);
            
            emit WinnerPaid(lotteryId, winners[i], i + 1, prize);
        }
        
        lottery.status = LotteryStatus.CLOSED;
        emit LotteryDrawn(lotteryId, winners);
    }
}

Чим no-loss лотерея відрізняється від класичної?

Класична лотерея вимагає ризику: ви платите за квиток і можете втратити всю суму. No-loss модель (PoolTogether) змінює правила: ваш депозит ніколи не згорає — він вноситься в yield-протокол на кшталт AAVE, а відсотки розподіляються серед переможців. Для консервативних користувачів це ідеальний варіант: вони зберігають капітал, але отримують шанс на виграш. No-loss лотерея повністю усуває ризик втрати депозиту — це в 10 разів безпечніше за класичну модель. Реалізація складніша: потрібна інтеграція з AAVE або Compound, а також захист від помилок округлення.

contract NoLossLottery {
    IERC20 public depositToken;    // USDC
    IAAVE public aavePool;         // AAVE lending pool
    IERC20 public aToken;          // aUSDC (yield bearing)
    
    mapping(address => uint256) public deposits;
    uint256 public totalDeposited;
    
    function deposit(uint256 amount) external {
        depositToken.transferFrom(msg.sender, address(this), amount);
        
        depositToken.approve(address(aavePool), amount);
        aavePool.deposit(address(depositToken), amount, address(this), 0);
        
        deposits[msg.sender] += amount;
        totalDeposited += amount;
        
        emit Deposited(msg.sender, amount);
    }
    
    function triggerDraw() external {
        uint256 totalWithYield = aToken.balanceOf(address(this));
        uint256 yieldEarned = totalWithYield - totalDeposited;
        
        require(yieldEarned > MIN_PRIZE, "Not enough yield");
        
        _requestRandomWinner(yieldEarned);
    }
    
    function withdraw(uint256 amount) external {
        require(deposits[msg.sender] >= amount, "Insufficient balance");
        
        deposits[msg.sender] -= amount;
        totalDeposited -= amount;
        
        aavePool.withdraw(address(depositToken), amount, msg.sender);
        
        emit Withdrawn(msg.sender, amount);
    }
}
Параметр Класична лотерея No-loss лотерея
Ризик для гравця Високий (втрата депозиту) Нульовий (депозит збережено)
Джерело призів Внески гравців + комісія Відсотки від yield-протоколу
Складність контракту Середня (1-2 контракти) Висока (інтеграція з AAVE)
Газова вартість Низька Середня (депозит і вивід)

Навіщо автоматизувати розіграш?

Ручний виклик drawWinners — вузьке місце: оператор може забути, затримати або просто не захотіти запускати транзакцію. Chainlink Automation вирішує це. Контракт сам перевіряє умови (час вийшов або всі квитки продані) і викликає автоматичний розіграш. У коді нижче — реалізація автоматичного апкіпу.

import {AutomationCompatibleInterface} from "@chainlink/contracts/src/v0.8/automation/AutomationCompatible.sol";

contract AutoLottery is BlockchainLottery, AutomationCompatibleInterface {
    function checkUpkeep(bytes calldata) external view override 
        returns (bool upkeepNeeded, bytes memory performData) 
    {
        for (uint256 i = 1; i <= lotteryCount; i++) {
            Lottery storage lottery = lotteries[i];
            if (
                lottery.status == LotteryStatus.OPEN &&
                block.timestamp >= lottery.endTime &&
                lottery.ticketsSold > 0
            ) {
                return (true, abi.encode(i));
            }
        }
        return (false, "");
    }
    
    function performUpkeep(bytes calldata performData) external override {
        uint256 lotteryId = abi.decode(performData, (uint256));
        drawWinners(lotteryId);
    }
}

Процес розробки лотереї на блокчейні

Розробка децентралізованої лотереї — це комплексний процес, що включає проектування архітектури, написання смарт-контрактів, інтеграцію з Chainlink VRF та Automation, а також ретельне тестування. Ми використовуємо Foundry для юніт-тестів, Echidna для фаззингу та Slither для статичного аналізу. Газ-оптимізація на етапі розробки дозволяє знизити вартість транзакцій на 30–50% без втрати безпеки. Понад 95% наших клієнтів задоволені результатом.

Кроки створення рафл-лотереї

  1. Визначення токеноміки: вирішуємо, які токени приймати, як розподіляються призи, комісія оператора.
  2. Проектування смарт-контракту: на Solidity 0.8.x з урахуванням патернів захисту від реентерабельності та flash loan-атак.
  3. Інтеграція Chainlink VRF: підключення VRFConsumerBaseV2Plus, налаштування підписки.
  4. Розробка фронтенду: web3-інтерфейс з wagmi та RainbowKit для безшовної взаємодії.
  5. Аудит та формальна верифікація: внутрішній аудит (Slither, Mythril) + замовний зовнішній аудит у партнерів.
  6. Деплой та моніторинг: розгортання на цільовій L1/L2 (Ethereum, Polygon, Arbitrum, Base), налаштування моніторингу через Tenderly.

Кожен етап завершується код-рев'ю та тестовими прогонами. На виході ви отримуєте прозору, перевірену систему, готову до експлуатації.

Забезпечення безпеки смарт-контрактів

Безпека — ключовий аспект лотерейного контракту. Ми використовуємо патерн Checks-Effects-Interactions для захисту від reentrancy-атак. Обмежуємо кількість квитків на один гаманець і вводимо часову затримку між депозитом та розіграшем, щоб запобігти flash loan маніпуляціям. Всі контракти проходять fuzzing-тестування з Echidna: 100 000 випадкових сценаріїв на кожен контракт. Аудит обов'язковий: внутрішніми інструментами (Slither, Mythril) та зовнішніми партнерами. Це виключає витік коштів. Ми гарантуємо безпеку коштів завдяки сертифікованому аудиту та багаторічному досвіду.

Етап Зміст Терміни (орієнтовно)
Аналітика Вимоги, вибір мережі, токеноміка 3-5 днів
Проектування Архітектура контрактів, розрахунок газу 5-7 днів
Розробка Solidity 0.8.x, тести (Foundry), fuzzing (Echidna) 10-20 днів
Інтеграція VRF/Automation Chainlink VRF V2, Automation 3-5 днів
Аудит Slither, Mythril, зовнішній аудит 7-14 днів
Деплой Ethereum, Polygon, Arbitrum, BNB Chain 2-3 дні

Вартість проєкту: від $5,000 для базового рафлу до $15,000 для складної no-loss лотереї. Остаточні терміни розраховуються після аналізу вимог. Вартість визначається індивідуально — без фіксованих цін. Отримайте консультацію щодо вашого проєкту: зв'яжіться з нами, щоб обговорити деталі та замовити розробку.

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

У вартість розробки входить: технічна документація, доступ до вихідного коду (GitHub), навчання команди (1 день), підтримка після запуску (3 місяці).

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).