Back to Home

HonestDice

Wallet
0xc4c51de1abf5...fd8f021ae9fc
FrontierExact Bytecode Match
Deployed August 12, 2015 (10 years ago)Block 74,817

One of the earliest provably-fair dice gambling contracts on Ethereum, deployed August 12, 2015 (day 13 of Frontier). Commit-reveal dice game with 2% house edge...

Key Facts

Deployment Block
74,817
Deployment Date
Aug 12, 2015, 03:21 PM
Code Size
1.9 KB
Transactions by Year
2015195
20164
20178
20181
20191
20223
20231
20241
20251
202630

Description

HonestDice was a commit-reveal dice game deployed just 13 days after Ethereum's Frontier launch. Players submitted a bet with a secret hash; the server provided a random seed; the player then revealed their secret to determine the outcome. The game offered configurable odds (1-255) with a 2% house edge and a maximum payout of 5% of the contract's bankroll per roll.

The contract enforced a 10 ETH minimum bet (the published GitHub source shows 1 ETH, but the as-deployed version used 10 ETH). A server-provided seed within 20 blocks prevented front-running.

Notably, the contract contains a shadowing bug: both lockBetsForWithdraw() and unlockBets() declare a local uint betsLocked variable instead of modifying the state variable. Both functions are no-ops on-chain, meaning the owner could never actually lock or unlock bets.

The contract was mislabeled as 'EtherDice' in several academic smart contract analysis datasets. The actual contract name in the source code is HonestDice.

122 ETH remains locked in the contract. The deployer's key appears to be lost.

Source Verified

SolidityExact bytecode match(1,992 bytes)
Compiler: soljson

Exact creation (1,992 bytes) + runtime (1,927 bytes) match. soljson v0.1.1, optimizer enabled.

Heuristic Analysis

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

Detected Type: Wallet

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

Opcodes1,992
Unique Opcodes177
Jump Instructions132
Storage Operations71

Verified Source Available

Source verified through compiler archaeology and exact bytecode matching.

View Verification Proof
Show source code (Solidity)
contract HonestDice {
	
	event Bet(address indexed user, uint blocknum, uint256 amount, uint chance);
	event Won(address indexed user, uint256 amount, uint chance);
	
	struct Roll {
		uint256 value;
		uint chance;
		uint blocknum;
		bytes32 secretHash;
		bytes32 serverSeed;
	}
	
	uint betsLocked;
	address owner;
	address feed;				   
	uint256 minimumBet = 10 * 1000000000000000000; // 10 Ether
	uint256 constant maxPayout = 5; // 5% of bankroll
	uint constant seedCost = 100000000000000000; // This is the cost of supplyin the server seed, deduct it;
	mapping (address => Roll) rolls;
	uint constant timeout = 20; // 5 Minutes
	
	function HonestDice() {
		owner = msg.sender;
		feed = msg.sender;
	}
	
	function roll(uint chance, bytes32 secretHash) {
		if (chance < 1 || chance > 255 || msg.value < minimumBet || calcWinnings(msg.value, chance) > getMaxPayout() || betsLocked != 0) { 
			msg.sender.send(msg.value); // Refund
			return;
		}
		rolls[msg.sender] = Roll(msg.value, chance, block.number, secretHash, 0);
		Bet(msg.sender, block.number, msg.value, chance);
	}
	
	function serverSeed(address user, bytes32 seed) {
		// The server calls this with a random seed
		if (msg.sender != feed) return;
		if (rolls[user].serverSeed != 0) return;
		rolls[user].serverSeed = seed;
	}
	
	function hashTo256(bytes32 hash) constant returns (uint _r) {
		// Returns a number between 0 - 255 from a hash
		return uint(hash) & 0xff;
	}
	
	function hash(bytes32 input) constant returns (uint _r) {
		// Simple sha3 hash. Not to be called via the blockchain
		return uint(sha3(input));
	}
	
	function isReady() constant returns (bool _r) {
		return isReadyFor(msg.sender);
	}
	
	function isReadyFor(address _user) constant returns (bool _r) {
		Roll r = rolls[_user];
		if (r.serverSeed == 0) return false;
		return true;
	}
	
	function getResult(bytes32 secret) constant returns (uint _r) {
		// Get the result number of the roll
		Roll r = rolls[msg.sender];
		if (r.serverSeed == 0) return;
		if (sha3(secret) != r.secretHash) return;
		return hashTo256(sha3(secret, r.serverSeed));
	}
	
	function didWin(bytes32 secret) constant returns (bool _r) {
		// Returns if the player won or not
		Roll r = rolls[msg.sender];
		if (r.serverSeed == 0) return;
		if (sha3(secret) != r.secretHash) return;
		if (hashTo256(sha3(secret, r.serverSeed)) < r.chance) { // Winner
			return true;
		}
		return false;
	}
	
	function calcWinnings(uint256 value, uint chance) constant returns (uint256 _r) {
		// 1% house edge
		return (value * 98 / 100) * 256 / chance;
	}
	
	function getMaxPayout() constant returns (uint256 _r) {
		return this.balance * maxPayout / 100;
	}
	
	function claim(bytes32 secret) {
		Roll r = rolls[msg.sender];
		if (r.serverSeed == 0) return;
		if (sha3(secret) != r.secretHash) return;
		if (hashTo256(sha3(secret, r.serverSeed)) < r.chance) { // Winner
			msg.sender.send(calcWinnings(r.value, r.chance));
			Won(msg.sender, r.value, r.chance);
		}
		
		delete rolls[msg.sender];
	}
	
	function canClaimTimeout() constant returns (bool _r) {
		Roll r = rolls[msg.sender];
		if (r.serverSeed != 0) return false;
		if (r.value <= 0) return false;
		if (block.number < r.blocknum + timeout) return false;
		return true;
	}
	
	function claimTimeout() {
		// Get your monies back if the server isn't responding with a seed
		if (!canClaimTimeout()) return;
		Roll r = rolls[msg.sender];
		msg.sender.send(r.value);
		delete rolls[msg.sender];
	}
	
	function getMinimumBet() constant returns (uint _r) {
		return minimumBet;
	}
	
	function getBankroll() constant returns (uint256 _r) {
		return this.balance;
	}
	
	function getBetsLocked() constant returns (uint _r) {
		return betsLocked;
	}
	
	function setMinimumBet(uint256 _minimumBet) {
		if (msg.sender != owner) return;
		minimumBet = _minimumBet;
	}

	function setFeed(address newFeed) {
		if (msg.sender != owner) return;
		feed = newFeed;
	}
	
	function lockBetsForWithdraw() {
		if (msg.sender != owner) return;
		uint betsLocked = block.number;
	}
	
	function unlockBets() {
		if (msg.sender != owner) return;
		uint betsLocked = 0;
	}
	
	function withdraw(uint amount) {
		if (msg.sender != owner) return;
		if (betsLocked == 0 || block.number < betsLocked + 5760) return;
		owner.send(amount);
	}
}

External Links