Commit-reveal dice game deployed day 13 of Ethereum mainnet with 1,000 ETH bankroll. Still active in 2026 with 84 ETH locked. 1% house edge, 1 ETH minimum bet.
Key Facts
Description
HonestDice is a commit-reveal dice gambling contract deployed on August 12, 2015 (block 75,008), 13 days after Ethereum mainnet launch. It was bankrolled with 1,000 ETH just 2 minutes after deployment (block 75,017), making it one of the most heavily capitalized contracts in Ethereum first two weeks.
The contract implements a sophisticated 20-function gambling system with proper anti-front-running protections. Players use roll(uint256, bytes32) to place bets with a client-side seed hash. The operator then responds with serverSeed(address, bytes32) to provide the server random component. Players verify and collect via claim(bytes32). Supporting functions include getBankroll(), getBetsLocked(), getMinimumBet(), getMaxPayout(), isReady(), isReadyFor(address), calcWinnings(uint256, uint256), didWin(bytes32), and getResult(bytes32). The operator can manage the bankroll via lockBetsForWithdraw(), unlockBets(), and withdraw(uint256).
The same deployer (0x87c5b5874a18b4306df8a752a6c8cc3e82dafc19) deployed at least one other HonestDice variant on the same day. This contract saw 30+ transactions from 4 unique addresses. As of 2026, approximately 83.9 ETH remains permanently locked in the contract.
This contract gained secondary attention in March 2016 when a Reddit user noticed that etherchain.org and etherscan.io showed different bytecode for the same address. The community explained this was a display discrepancy: etherchain was showing contract creation bytecode while etherscan showed the correct runtime bytecode.
Source Verified
1889-byte runtime matches byte-for-byte. soljson v0.1.1+commit.6ff4cd6, optimizer ON. Same deployer and source as the 10 ETH variant (0xc4c51de1). Key differences: 1 ETH min bet (vs 10), 99% payout (vs 98%), no setMinimumBet(). Broken withdrawal lock due to local variable shadowing.
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
Frontier Era
The initial release of Ethereum. A bare-bones implementation for technical users.
Bytecode Overview
Verified Source Available
Source verified through compiler archaeology and exact bytecode matching.
View Verification ProofShow 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 = 1 * 1000000000000000000; // 1 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 * 99 / 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) - seedCost);
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 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);
}
}