Blockchain lottery development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Blockchain lottery development
Medium
~5 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Blockchain Lottery Development

Blockchain lottery — one of the most natural smart contract use cases: transparent prize fund storage, verifiable drawing via Chainlink VRF, automatic payouts without intermediaries. Unlike traditional lotteries — no one can manipulate results, funds cannot be stolen, payout guaranteed by code.

Lottery Types

Fixed prize (Powerball-style): fixed jackpot, player selects numbers, wins on match. Fund taken from ticket sales + operator reserve.

Prize pool lottery (Raffle): entire prize fund = sum of all bets (minus fee). More participants = bigger prize. One winner takes all or multiple prize places.

No-loss lottery (PoolTogether-style): participants deposit into yield-generating protocol (AAVE), accumulated interest goes to winner, deposits returned to all. "Play for free, risk only the yield".

Smart Contract — Raffle Lottery

// 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; // array for weighted selection
        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;
    
    // Operator fee
    uint256 public operatorFee = 300; // 3% in 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,
            ticketsSold: 0,
            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");
        
        // Add participant amount times (weighted: more tickets = more chances)
        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;
        
        // Request N random numbers (by count of prize places)
        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;
            
            // If participant already selected — find next
            while (isSelected[idx]) {
                idx = (idx + 1) % participantCount;
            }
            
            isSelected[idx] = true;
            winners[i] = lottery.participants[idx];
            
            // Pay prize
            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 Lottery (PoolTogether Model)

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);
        
        // Deposit into AAVE for yield
        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 {
        // Total aTokens value > totalDeposited = accumulated yield
        uint256 totalWithYield = aToken.balanceOf(address(this));
        uint256 yieldEarned = totalWithYield - totalDeposited;
        
        require(yieldEarned > MIN_PRIZE, "Not enough yield");
        
        // Request VRF for winner selection
        _requestRandomWinner(yieldEarned);
    }
    
    // Withdraw: user gets full deposit back
    function withdraw(uint256 amount) external {
        require(deposits[msg.sender] >= amount, "Insufficient balance");
        
        deposits[msg.sender] -= amount;
        totalDeposited -= amount;
        
        // Withdraw from AAVE
        aavePool.withdraw(address(depositToken), amount, msg.sender);
        
        emit Withdrawn(msg.sender, amount);
    }
}

Chainlink Automation for Automatic Drawing

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

contract AutoLottery is BlockchainLottery, AutomationCompatibleInterface {
    // Chainlink Automation calls checkUpkeep every block
    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, "");
    }
    
    // Automatically called when upkeepNeeded = true
    function performUpkeep(bytes calldata performData) external override {
        uint256 lotteryId = abi.decode(performData, (uint256));
        drawWinners(lotteryId);
    }
}

Developing basic raffle lottery — 2-3 weeks. No-loss lottery with AAVE integration — 4-5 weeks. Chainlink Automation for automation — add 1 week.