Keno Game Development on Blockchain: VRF, Smart Contracts, Gas Optimization

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 1All 1305 services
Keno Game Development on Blockchain: VRF, Smart Contracts, Gas Optimization
Medium
~3-5 days
Frequently Asked Questions

Blockchain Development Services

Blockchain Development Stages

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1349
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1247
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    949
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1183
  • image_logo-advance_0.webp
    B2B Advance company logo design
    642
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    921

Keno Game Development on Blockchain

Keno is a lottery game: the player picks numbers from a 1–80 range, the system randomly selects 20 numbers, and winnings depend on the number of matches. The mechanics are simple, but implementing them on a blockchain requires solving several non-trivial problems: verifiable randomness for selecting 20 numbers, gas-efficient match checking, and a mathematically correct payout table. We have been developing such games turnkey for over 5 years and have successfully launched 20+ projects on Ethereum, Polygon, and Arbitrum.

Unlike Crash, Keno is a game with a fixed outcome before cashout: numbers are drawn, and the result is immediately known. This simplifies the architecture but requires special attention to the quality of RNG and bankroll management. Our team helps clients avoid typical mistakes—such as incorrect house edge calculation or gas overruns during drawing. Get in touch for a free consultation on contract architecture.

How to Ensure Randomness in Keno?

The main technical challenge: from a single VRF random seed, obtain 20 unique numbers in the 1–80 range. A naive approach (rand % 80 repeated 20 times) creates collisions—a number can appear twice. We use Fisher-Yates shuffle in a smart contract:

function drawNumbers(uint256 seed) public pure returns (uint8[20] memory drawn) {
    // Initialize array 1..80
    uint8[80] memory pool;
    for (uint8 i = 0; i < 80; i++) {
        pool[i] = i + 1;
    }
    
    // Fisher-Yates: shuffle first 20 positions
    for (uint8 i = 0; i < 20; i++) {
        // Get pseudo-random index from seed
        uint256 j = uint256(keccak256(abi.encodePacked(seed, i))) % (80 - i);
        
        // Swap pool[i] and pool[i + j]
        uint8 temp = pool[i];
        pool[i] = pool[i + j];
        pool[i + j] = temp;
        
        drawn[i] = pool[i];
    }
}

Fisher-Yates guarantees unique numbers without reject sampling. On-chain execution: 20 iterations × keccak256 ≈ 80,000–100,000 gas. On Arbitrum this is ~$0.01—acceptable for most scenarios.

Why Fisher-Yates over Bitmap?

A bitmap approach (marking used numbers in a bitmask) is simpler but can require more keccak256 calls when selecting the last numbers due to collisions. Fisher-Yates guarantees a fixed number of iterations—20. For predictable gas, we recommend it, especially for mass draws.

Match Checking: Gas-Efficient Implementation

The player selects M numbers (1–10), and we need to count matches with the 20 drawn numbers. Nested loops O(M×20) are acceptable for small M, but we use a bitmap to save gas:

function countMatches(
    uint8[] memory playerPicks,
    uint8[20] memory drawnNumbers
) public pure returns (uint8 matches) {
    // Build bitmap of drawn numbers
    uint256 drawnBitmap = 0;
    for (uint8 i = 0; i < 20; i++) {
        drawnBitmap |= (1 << (drawnNumbers[i] - 1));
    }
    
    // Check player picks against bitmap
    for (uint8 i = 0; i < playerPicks.length; i++) {
        if (drawnBitmap & (1 << (playerPicks[i] - 1)) != 0) {
            matches++;
        }
    }
}

Bitwise operations are faster than nested loops. For typical 1–10 picks: ~3,000–5,000 additional gas.

Payout Table and House Edge

Keno payouts are the most important economic part. You need to balance the house edge (typically 20–35% in Keno) for different numbers of picks. Here is a snippet of multiplier table for popular variants:

Number of Picks Matches Multiplier (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

The house edge is verified mathematically: for each pick group, Expected Value is calculated:

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

EV should be ~0.70–0.80 (70–80% RTP, 20–30% house edge)

For example, for 1 pick: P(1 match) = 20/80 = 0.25, EV = 0.25 × 3.6 = 0.9, i.e., RTP 90%, house edge 10%.

Complete On-Chain Game Cycle

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");
        
        // Validate picks (1-80, unique)
        _validatePicks(picks);
        
        uint256 roundId = nextRoundId++;
        rounds[roundId] = KenoRound({
            player: msg.sender,
            betAmount: msg.value,
            playerPicks: picks,
            drawnNumbers: [uint8(0),...], // filled in callback
            matchCount: 0,
            payout: 0,
            status: RoundStatus.PENDING,
            vrfRequestId: 0
        });
        
        // Request VRF
        uint256 requestId = s_vrfCoordinator.requestRandomWords(
            VRFV2PlusClient.RandomWordsRequest({
                keyHash: s_keyHash,
                subId: s_subscriptionId,
                requestConfirmations: 1,
                callbackGasLimit: 300_000, // buffer for 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];
        
        // Draw 20 numbers
        round.drawnNumbers = drawNumbers(randomWords[0]);
        
        // Count matches
        round.matchCount = countMatches(round.playerPicks, round.drawnNumbers);
        
        // Calculate payout
        uint256 multiplier = payoutTable[round.playerPicks.length][round.matchCount];
        round.payout = round.betAmount * multiplier / 100;
        round.status = RoundStatus.COMPLETED;
        
        // Pay winner
        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
        );
    }
}

What Is a Shared Draw and How Does It Save Gas?

For a casino-style where multiple players participate in one draw round, we implement a shared draw. One VRF request for the entire round is divided among all participants, reducing the gas per player from ~100,000 to ~15,000.

contract MultiPlayerKeno is VRFConsumerBaseV2Plus {
    struct DrawRound {
        uint8[20] drawnNumbers;
        uint256 drawTime;
        bool resolved;
        address[] participants;
    }
    
    // Rounds every N minutes
    uint256 public roundInterval = 3 minutes;
    
    // Bets are tied to a future draw round
    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];
    }
    
    // One VRF request for the entire draw — shared among all participants
    // Gas cost per player: ~15,000 gas (vs ~100,000 for 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;
    }
}

How to Verify the Game Result?

  1. Get the VRF request and response from on-chain events.
  2. Apply drawNumbers(vrfResult) — get the same 20 numbers.
  3. Make sure the house didn't manipulate.

Chainlink publicly publishes the cryptographic proof of each VRF response — verification is possible independently of the casino. This mechanism is based on Chainlink VRF.

What's Included in the Work

  • Development of the game smart contract (single-player or multi-player)
  • Integration of Chainlink VRF V2 Plus
  • Configuration of the payout table with mathematical verification of house edge
  • Creation of a frontend interface (React + wagmi + RainbowKit)
  • Deployment on testnet and mainnet (Polygon, Arbitrum, or another network)
  • Smart contract audit (internal or third-party)
  • Documentation for integration and support

Indicative Timelines

Phase Duration
Contracts (single player, VRF, payout table) 3–4 weeks
Multi-player shared draw 2 weeks
Frontend + draw animation 2–3 weeks
Bankroll + admin panel 1–2 weeks
Audit + testnet 3–4 weeks

Total MVP (single player Keno): 5–7 weeks. Full platform with multi-player draw: 9–12 weeks.

Our team has 5+ years of blockchain development experience and has successfully delivered 20+ gaming smart contracts. If you are planning to launch your own Keno platform with guaranteed transparency and low gas, get a consultation—we will evaluate your project in one day.

Game Economy, Contracts, and On-Chain Mechanics

We’ve seen this scenario multiple times. Axie Infinity generated substantial revenue monthly at its peak, but within 18 months the token crashed by 98% and the audience by 95%. The cause—lack of sinks: players earned SLP and cashed out, while burn mechanisms were insufficient. An analysis of Axie’s economy (Collins Dictionary) confirmed the model turned into a Ponzi scheme. We provide end-to-end GameFi development: from tokenomics to smart contracts, so your economy doesn’t repeat this mistake. Let’s evaluate your project at a meetup or online.

Play-to-Earn Economy Break Points

Inflationary tokenomics without sinks. Players earn tokens through gameplay. If sinks (burn or consumption mechanisms) are insufficient, supply outpaces demand. Price drops. Player fiat income declines. Players leave. A death spiral.

The right structure is a dual-token model with clear separation: a governance/value token with limited supply and a utility/reward token for in-game economy. The utility token must be actively consumed: item crafting, upgrades, entry fees, breeding. Examples: GODS/FLUX in Gods Unchained, AXS/SLP in Axie (though sinks were insufficient there). Historical data shows that without sinks, token supply inflates by 5–10% monthly, leading to price collapse within 6–9 months.

Effective Sink Mechanisms

  • Breeding/crafting — burning utility token to create a new NFT (e.g., Axie). Typical burn costs range from $5–$15 per action, removing 0.5–2% of total supply annually.
  • Character upgrades — each evolution requires token burning, consuming 0.1–0.3% of circulating supply per upgrade cycle.
  • PvP entry fee — token burn for tournament entry, part goes to prize pool. This can burn up to 0.5% of supply per week in active games.
  • Item durability — item breaks after N battles, token spent on repair. Cost per repair ~$0.50–$2.
  • Financial mechanics — staking with lock-up, removing tokens from circulation for a period. Typical lock-up periods of 30–90 days reduce circulating supply by 15–25%.

On-Chain vs Off-Chain: Boundary and Trade-offs

It’s not necessary to put all game logic on-chain—each transaction costs gas and takes 12 seconds. A game cycle is milliseconds. Balance:

Component On-chain Off-chain Examples
Asset Ownership + NFT items, land
Transfer/Trading + Marketplaces
Finance (staking, rewards) + Staking vaults, DAO
Random generation + (via VRF) Chainlink VRF
Gameplay + Battle system, movement
Game world state + Coordinates, health points
Matchmaking + Server-side logic

Gameplay results are transferred to blockchain via signed messages from server or ZK-proof. Verifiable off-chain with ZK: game server generates ZK-proof of session correctness, contract verifies proof and issues rewards. Implementations: Cartridge (Starknet), zkSync game rollups. Gas savings from batching proofs can reach 90% compared to per-action on-chain validation.

How Does Dual-Token Model Prevent Economic Collapse?

Governance token (limited supply) acts as value store and is used for major decisions. Utility token (minted via gameplay) is consumed by sink mechanisms, ensuring deflationary pressure. The ratio of governance to utility tokens in the initial pool should be 1:10 to 1:20. Simulation shows that a 30% burn rate on utility token keeps supply growth below 3% per year, preserving player income and token price.

Implementation of NFT Game Items

Standard: ERC-1155 for fungible items (resources, consumables) + ERC-721 for unique (characters, land). ERC-1155 provides up to 60% gas savings on batch transfers.

How to Implement Dynamic NFTs Without Overloading the Blockchain?

Item attributes change during gameplay (experience, durability, upgrades). Two approaches:

  • Fully on-chain: attributes stored in contract mapping, tokenURI generated from attributes via SVG/JSON encoding. Expensive in gas with frequent updates (e.g., $0.50 per update). Used for land and key assets.
  • Hybrid: attributes stored off-chain, tokenURI contains state hash. Updates signed by server, verified on-chain during transfer or sale. Cheaper ($0.02 per update) but requires server trust or ZK.

Breeding and crafting. Contract: two parent NFTs → pay utility token (burn) → mint new NFT with attributes dependent on parents + Chainlink VRF for randomness. Without VRF, miners can manipulate randomness via block selection.

// 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);
}

Marketplace and Royalties

An integrated marketplace gives control over fee structure and custom logic (e.g., banning item trading below a certain level). Royalties per EIP-2981 are standard but not enforceable: Blur and other marketplaces ignore on-chain royalties. For enforcement—whitelist-only transfer (only through contracts that pay royalties). Sacrifice composability for rights protection. Typical marketplace fee is 2.5–5% per transaction, generating recurring revenue.

Staking and Rewards Distribution

Staking NFTs is a mechanic for player retention. Problem: distributing rewards with thousands of stakers requires constant transactions (expensive). Solution—reward-per-share pattern (as in MasterChef from SushiSwap): global accRewardPerShare, upon claim or state change, debt is recalculated by formula pendingReward = stakedAmount * (accRewardPerShare - userRewardDebt). O(1) complexity regardless of staker count. Gas savings up to 70% compared to per-element distribution. Over a year with 10,000 stakers, this translates to roughly $40,000 saved in gas.

Why Is Reward-Per-Share Pattern Critical for Scalability?

Direct per-user reward updates cost O(n) per block, consuming more than 200,000 gas for 1,000 stakers. Reward-per-share reduces this to 30,000–50,000 gas per user claim, enabling thousands of stakers. Many early P2E games collapsed under gas costs that exceeded reward value. This pattern scales to tens of thousands without infrastructure overhead.

Process and Timelines

We start with a game economics document: token flows, mint/burn mechanics, projected supply schedule, sink analysis. Before writing code, the economy is modeled (Cadence, Python simulation).

GameFi Building Process: 5 Stages

  1. Economic modeling — 1–2 weeks. Develop dual-token model, calculate sinks, outline incentives for long-term holding.
  2. Token contract development — 2–3 weeks. ERC-20 for governance, ERC-20 for utility, with configurable mint/burn policy.
  3. NFT smart contracts — 3–5 weeks. ERC-721 / ERC-1155 with dynamic metadata, breeding/crafting, Chainlink VRF.
  4. Staking + rewards — 2–3 weeks. Contract based on reward-per-share, interfaces for frontend.
  5. Marketplace (optional) — 2–4 weeks. Custom marketplace with enforced royalty.

Work Deliverables

  • Source code for all smart contracts with tests (Foundry/Hardhat)
  • Architecture and economics documentation
  • Integration with Chainlink, Tenderly for monitoring
  • Code audit and formal verification (Slither, Mythril, Echidna)
  • Team training on contract interaction
  • Post-deployment support (3 months)

Basic GameFi stack (tokens + NFTs + staking + marketplace) — 8 to 16 weeks. Full game with on-chain randomness, breeding, dynamic NFTs — 4–8 months. ZK-based verifiable gameplay — a separate project from 6 months.

Contact us for an audit of your tokenomics—we’ll assess risks and refine sink mechanisms. Order GameFi project development—receive a ready product with proven economy. We guarantee contract stability and code transparency. Our experience includes dozens of implemented Web3 projects, including audits of 15+ P2E games. Get a consultation to start your project.