Back to Home

Roulette

Game
0x5fe5b7546d16...de1fc825a4fd
FrontierContract #149Source VerifiedEdit this contract
Deployed August 10, 2015 (10 years ago)Block 66,126

An early Ethereum roulette contract from August 2015, notable for a critical randomness vulnerability: its 'private' seed is hardcoded to 1 in the constructor.

Key Facts

Deployment Block
66,126
Deployment Date
Aug 10, 2015, 11:26 PM
Code Size
2.3 KB
Gas at Deploy
761,357
Transactions by Year
2015446
20174
20182
20191
20201
20212
20222
20232
202514
202610

Description

Roulette is one of the earliest gambling contracts on Ethereum mainnet, deployed at block 66,126 on August 10, 2015 — 11 days after genesis. Created by address 0xa14cf6cec1c6aae4b608458f6e14692863a937aa, it implements a roulette-style betting game where players wager between 1 and 10 ETH on a number.

The contract maintains a Casino struct holding the house address, balance, and betting limits. Players call betOnNumber(uint number) to place a bet; a random number is generated using an internal seed and compared to determine the outcome. Winnings are sent directly to the player's address.

The contract's source code includes a comment claiming that the privSeed used for random number generation is difficult to guess because it is "nowhere visible." However, privSeed is initialized to 1 in the constructor — a hardcoded, publicly known value. Anyone reading the contract source can predict every outcome. This is an early example of the "bad randomness" vulnerability class that would later be systematically exploited across many Ethereum gambling contracts.

The minimum bet is 1 ETH and the maximum is 10 ETH.

Source Verified

Etherscan verified

Heuristic Analysis

The following characteristics were detected through bytecode analysis and may not be accurate.

Detected Type: Game
Contains SELFDESTRUCT opcode

Frontier Era

The initial release of Ethereum. A bare-bones implementation for technical users.

Block span: 01,149,999
July 30, 2015March 14, 2016

Bytecode Overview

Opcodes2,375
Unique Opcodes159
Jump Instructions138
Storage Operations59

Verified Source Available

Source verified on Etherscan.

Show source code (Solidity)
contract Roulette {
    
    // Global variables
    string sWelcome;
    /* Remark: 
     *  Private Seed for generateRand(), 
     *  since this is nowhere visibile, 
     *  it's very hard to guess.
     */
    uint privSeed; 
    struct Casino {
        address addr;
        uint balance;
        uint bettingLimitMin;
        uint bettingLimitMax;
    }
    Casino casino;

    // Init Constructor
    function Roulette() {
        sWelcome = "\n-----------------------------\n     Welcome to Roulette \n Got coins? Then come on in! \n-----------------------------\n";
        privSeed = 1;
        casino.addr = msg.sender;
        casino.balance = 0;
        casino.bettingLimitMin = 1*10**18;
        casino.bettingLimitMax = 10*10**18;
    }
    
    function welcome() constant returns (string) {
        return sWelcome;
    }
    function casinoBalance() constant returns (uint) {
        return casino.balance;
    }
    function casinoDeposit() {
        if (msg.sender == casino.addr)
            casino.balance += msg.value;
        else 
            msg.sender.send(msg.value);
    }
    function casinoWithdraw(uint amount) {
        if (msg.sender == casino.addr && amount <= casino.balance) {
            casino.balance -= amount;
            casino.addr.send(amount);
        }
    }
    
    // Bet on Number
    function betOnNumber(uint number) public returns (string) {
        // Input Handling
        address addr = msg.sender;
        uint betSize = msg.value;
        if (betSize < casino.bettingLimitMin || betSize > casino.bettingLimitMax) {
            // Return Funds
            if (betSize >= 1*10**18)
                addr.send(betSize);
            return "Please choose an amount within between 1 and 10 ETH";
        }
        if (betSize * 36 > casino.balance) {
            // Return Funds
            addr.send(betSize);
            return "Casino has insufficient funds for this bet amount";
        }
        if (number < 0 || number > 36) {
            // Return Funds
            addr.send(betSize);
            return "Please choose a number between 0 and 36";
        }
        // Roll the wheel
        privSeed += 1;
        uint rand = generateRand();
        if (number == rand) {
            // Winner winner chicken dinner!
            uint winAmount = betSize * 36;
            casino.balance -= (winAmount - betSize);
            addr.send(winAmount);
            return "Winner winner chicken dinner!";
        }
        else {
            casino.balance += betSize;
            return "Wrong number.";
        }
    }
    
    // Bet on Color
    function betOnColor(uint color) public returns (string) {
        // Input Handling
        address addr = msg.sender;
        uint betSize = msg.value;
        if (betSize < casino.bettingLimitMin || betSize > casino.bettingLimitMax) {
            // Return Funds
            if (betSize >= 1*10**18)
                addr.send(betSize);
            return "Please choose an amount within between 1 and 10 ETH";
        }
        if (betSize * 2 > casino.balance) {
            // Return Funds
            addr.send(betSize);
            return "Casino has insufficient funds for this bet amount";
        }
        if (color != 0 && color != 1) {
            // Return Funds
            addr.send(betSize);
            return "Please choose either '0' = red or '1' = black as a color";
        }
        // Roll the wheel
        privSeed += 1;
        uint rand = generateRand();
        uint randC = (rand + 1) % 2;
        // Win
        if (rand != 0 && (randC == color)) {
            uint winAmount = betSize * 2;
            casino.balance -= (winAmount - betSize);
            addr.send(winAmount);
            return "Win! Good job.";
        }
        else {
            casino.balance += betSize;
            return "Wrong color.";           
        }
    }
    
    // Returns a pseudo Random number.
    function generateRand() private returns (uint) { 
        // Seeds
        privSeed = (privSeed*3 + 1) / 2;
        privSeed = privSeed % 10**9;
        uint number = block.number; // ~ 10**5 ; 60000
        uint diff = block.difficulty; // ~ 2 Tera = 2*10**12; 1731430114620
        uint time = block.timestamp; // ~ 2 Giga = 2*10**9; 1439147273
        uint gas = block.gaslimit; // ~ 3 Mega = 3*10**6
        // Rand Number in Percent
        uint total = privSeed + number + diff + time + gas;
        uint rand = total % 37;
        return rand;
    }

    // Function to recover the funds on the contract
    function kill() {
        if (msg.sender == casino.addr) 
            suicide(casino.addr);
    }
}

External Links