On-Chain Blackjack: Architecture, VRF, and Provably Fair

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
On-Chain Blackjack: Architecture, VRF, and Provably Fair
Medium
~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

On-Chain Blackjack: Architecture, VRF, and Provably Fair

Imagine you want to launch an online Blackjack casino on the blockchain. The problem is how to ensure randomness of cards without trusting a centralized server? In an off-chain casino, the server deals cards but the player doesn't see the deck. On-chain, everything is transparent — anyone can read the contract storage. If we pre-generate a deck and store it, the player can see all future cards. The solution is commit-reveal with VRF or Mental Poker. We use Chainlink VRF, but with optimization: generate the entire deck in one request, then cards are revealed gradually without additional VRF calls. Our team has 7+ years of experience in Web3 and has implemented 20+ blockchain games and DeFi projects. Contact us for architecture consultation and audit.

How Chainlink VRF ensures randomness?

Chainlink VRF generates a verifiable random number off-chain with a cryptographic proof that is verified on-chain. No one — neither the player nor the operator — can predict the result. This is 10 times more reliable than storing a seed in the contract.

Flow for Blackjack:

  1. Player places a bet, contract requests VRF
  2. VRF fulfillment (via callback) — contract receives random, generates deck or first cards
  3. Player decides: hit or stand
  4. If hit — new VRF request for the next card

Problem with "if hit": each VRF request introduces a delay (1-3 blocks) and additional LINK cost. Not comfortable for real-time gaming.

Optimization: request the entire deck at once — blockchain casino blackjack

contract Blackjack is VRFConsumerBaseV2Plus {
    struct Game {
        address player;
        uint256 bet;
        uint8[] deck;     // all 52 cards in encrypted order
        uint8 playerIdx;  // current index in deck
        uint8 dealerIdx;
        bool active;
    }
    
    mapping(uint256 => Game) public games;         // requestId -> game
    mapping(address => uint256) public playerGame; // player -> gameId
    
    function startGame() external payable {
        require(msg.value >= MIN_BET, "Below minimum bet");
        
        uint256 requestId = s_vrfCoordinator.requestRandomWords(
            VRFV2PlusClient.RandomWordsRequest({
                keyHash: KEY_HASH,
                subId: subscriptionId,
                requestConfirmations: 3,
                callbackGasLimit: 300000,
                numWords: 1, // one large number for shuffle
                extraArgs: VRFV2PlusClient._argsToBytes(
                    VRFV2PlusClient.ExtraArgsV1({nativePayment: false})
                )
            })
        );
        
        games[requestId] = Game({
            player: msg.sender,
            bet: msg.value,
            deck: new uint8[](0),
            playerIdx: 0,
            dealerIdx: 4, // dealer draws cards from position 4
            active: false // becomes true after fulfillment
        });
        playerGame[msg.sender] = requestId;
    }
    
    function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal override {
        Game storage game = games[requestId];
        
        // Fisher-Yates shuffle deterministic from one seed
        uint8[52] memory deck;
        for (uint8 i = 0; i < 52; i++) deck[i] = i;
        
        uint256 seed = randomWords[0];
        for (uint8 i = 51; i > 0; i--) {
            seed = uint256(keccak256(abi.encodePacked(seed)));
            uint8 j = uint8(seed % (i + 1));
            (deck[i], deck[j]) = (deck[j], deck[i]);
        }
        
        // First 4 cards are dealt immediately: player, dealer, player, dealer
        game.deck = new uint8[](52);
        for (uint8 i = 0; i < 52; i++) game.deck[i] = deck[i];
        game.active = true;
        
        emit GameStarted(requestId, game.player, deck[0], deck[2]); // player's visible cards
        // deck[1] and deck[3] - dealer cards, deck[1] hidden until end
    }
}

After fulfillment the deck is shuffled and fixed. Subsequent moves (hit) draw cards from the already generated deck — no new VRF requests. Fast and cheap.

Why can't the deck be stored openly?

But the entire deck is stored in game.deck — publicly! Technically, the player can read all future cards from storage.

Solution: store only the seed, compute cards deterministically only when they are "revealed":

// Don't store deck, only seed
mapping(uint256 => uint256) private gameSeeds;

function getCard(uint256 gameId, uint8 position) private view returns (uint8) {
    // Deterministically compute card from seed and position
    // Card not in storage — cannot be read in advance
    return uint8(uint256(keccak256(abi.encodePacked(gameSeeds[gameId], position))) % 52);
}

This doesn't fully solve the problem: technically, one can simulate getCard for all positions in the same block. Full solution is the Mental Poker protocol with encryption of each card, but that is significantly more complex.

How to implement Blackjack logic in a smart contract?

Card values: Ace = 1 or 11, face cards = 10, others by rank:

function cardValue(uint8 card) internal pure returns (uint8) {
    uint8 rank = card % 13; // 0-12: Ace, 2-10, Jack, Queen, King
    if (rank == 0) return 11; // Ace (soft value)
    if (rank >= 10) return 10; // face cards
    return rank + 1;
}

function handScore(uint8[] memory cards) internal pure returns (uint8) {
    uint8 score = 0;
    uint8 aces = 0;
    
    for (uint i = 0; i < cards.length; i++) {
        uint8 val = cardValue(cards[i]);
        if (val == 11) aces++;
        score += val;
    }
    
    // Soft Ace becomes hard (1) if bust
    while (score > 21 && aces > 0) {
        score -= 10;
        aces--;
    }
    
    return score;
}

Dealer logic on-chain: dealer draws cards while score < 17, stops at 17+ (including soft 17 depending on rules).

How to manage bankroll using Kelly Criterion?

The contract must have ETH for payouts. House edge in Blackjack is ~0.5% with optimal strategy — that's the real margin. But variance is high, so sufficient bankroll is needed.

Kelly Criterion for maximum bet: with edge e and bankroll B, maximum bet ≈ B * e / variance. For Blackjack with edge 0.5% and variance ~1.3 — max bet ≈ 0.38% of bankroll. In practice: limit max bet to 1-2% of bankroll.

uint256 public constant MAX_BET_PERCENT = 200; // 2% = 200/10000

function maxBet() public view returns (uint256) {
    return address(this).balance * MAX_BET_PERCENT / 10000;
}

modifier validBet() {
    require(msg.value >= MIN_BET && msg.value <= maxBet(), "Invalid bet");
    _;
}

Why UX is critical for blockchain blackjack?

Blockchain Blackjack requires careful UX for asynchrony. VRF fulfillment is not instantaneous. Player presses "Deal" — waits 1-3 blocks for cards to appear.

Flow in UI:

  1. Stake → startGame() → status "Dealing..." (wait for GameStarted event)
  2. Cards appear → player sees their 2 cards, one dealer card
  3. Hit/Stand → instantaneous, from shuffled deck
  4. Stand → dealer reveals cards → result → payout

For event polling: wagmi with useWatchContractEvent or WebSocket connection to node.

Tech stack

Component Technology
Smart contract Solidity 0.8.x + VRF v2.5
Testing Foundry + VRF mock
Frontend React + wagmi + viem
Network Polygon / Arbitrum (low gas)
Audit Mandatory (gambling + custody of funds)

Contract audit: critical security

The contract holds ETH and pays out winnings — errors in payout logic or randomness generation lead to direct losses. We use Slither and Mythril for static analysis, and also recommend an external audit by partners. Your security is our guarantee. Learn more about smart contract audit in Chainlink documentation.

What is included in on-chain Blackjack development?

  • Architectural documentation and protocol selection (VRF vs Mental Poker)
  • Blackjack smart contract with game logic and bankroll management
  • Tests with Foundry (unit, fuzz, integration)
  • Chainlink VRF (v2.5) integration with subscription
  • Frontend on React + wagmi + viem with wallet support (MetaMask, WalletConnect)
  • Deployment to testnet and mainnet (Polygon/Arbitrum on your choice)
  • Code review and bug fixes
  • Training your team on contract operation
  • Technical support during launch

Comparison: VRF vs Mental Poker

Criteria Chainlink VRF Mental Poker
Implementation complexity Low High (cryptography)
Speed 1-3 blocks 1 round (instant)
Gas cost ~300k gas + LINK ~500k gas (encryption)
Trust Requires trust in oracle Fully decentralized
Recommendation For most projects For projects demanding maximum decentralization
Mental Poker: how it works Mental Poker is a cryptographic protocol that allows players to deal cards without a trusted third party. Each card is encrypted with a shared key, then shuffled and decrypted. This provides maximum decentralization but requires more gas and is more complex to implement.

Estimated timelines

Working on-chain Blackjack (smart contract, tests, basic frontend): from 4 to 5 weeks. With full UI, animations, statistics, and mobile adaptation: from 8 to 10 weeks. Cost is calculated individually after requirements analysis. Order development — we will prepare a detailed plan. Contact us to discuss your project.

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.