The draw results database for the WeeklyLotteryB lottery, storing each draw's date, numbers and prize tallies.
Context
Deployed on 15 July 2016 as the results store for the WeeklyLotteryB game at wlb.ethereumlottery.net.
Key Facts
Description
A storage contract that records the results of each WeeklyLotteryB draw. Each entry holds the draw date, the three drawn numbers, and the count and total value of three-number and two-number wins. New draws can be appended only by the linked game contract, which the owner sets through setGameAddress, and any past draw can be read back with getDraw. It holds no ether and contains no game logic itself.
Source Verified
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
Homestead Era
The first planned hard fork. Removed the canary contract, adjusted gas costs.
Bytecode Overview
Verified Source Available
Source verified through compiler archaeology and exact bytecode matching.
View Verification ProofShow source code (Solidity)
// Submitted by EthereumHistory (ethereumhistory.com)
/*
WeeklyLotteryB
Coded by: iFA
http://wlb.ethereumlottery.net
ver: 1.3
*/
contract WLBDrawsDB {
address private owner;
address private game;
struct draws_s {
uint date;
uint8[3] numbers;
uint hit3Count;
uint hit3Value;
uint hit2Count;
uint hit2Value;
}
draws_s[] private draws;
function WLBDrawsDB() {
owner = msg.sender;
}
function newDraw(uint date, uint8[3] numbers, uint hit3Count, uint hit3Value, uint hit2Count, uint hit2Value) noEther {
if ( msg.sender != owner && msg.sender != game ) { throw; }
draws.push( draws_s( date, numbers, hit3Count, hit3Value, hit2Count, hit2Value) );
}
function getDraw(uint id) constant returns (uint date, uint8[3] numbers, uint hit3Count, uint hit3Value, uint hit2Count, uint hit2Value) {
return ( draws[id].date, draws[id].numbers, draws[id].hit3Count, draws[id].hit3Value, draws[id].hit2Count, draws[id].hit2Value );
}
function setGameAddress(address _game) noEther OnlyOwner external {
game = _game;
}
modifier noEther() { if (msg.value > 0) { throw; } _ }
modifier OnlyOwner() { if (owner != msg.sender) { throw; } _ }
}
contract WeeklyLotteryB {
/* structures */
struct games_s {
uint ticketsCount;
mapping(bytes32 => uint) hit3Hash;
mapping(bytes32 => uint) hit2Hash;
uint startTimestamp;
uint endTimestamp;
bytes3 winningNumbersBytes;
uint prepareBlock;
bool drawDone;
uint prizePot;
uint hit3Count;
uint hit3Value;
uint hit2Count;
uint hit2Value;
}
struct playerGames_s {
bytes3[] numbersBytes;
mapping(bytes32 => uint) hit3Hash;
mapping(bytes32 => uint) hit2Hash;
bool checked;
}
struct players_s {
mapping(uint => playerGames_s) games;
}
struct investors_s {
address owner;
uint value;
uint balance;
bool live;
bool valid;
uint begins;
}
struct draws_s {
uint date;
uint gameID;
bytes3 numbersBytes;
uint hit3Count;
uint hit3Value;
uint hit2Count;
uint hit2Value;
}
/* config */
uint public constant ticketPrice = 100 finney; // 0.1 ether
uint private constant drawMaxNumber = 50;
uint private constant drawBlockDelay = 5;
uint private constant prizeDismissDelay = 5;
uint private constant drawBlockLimit = 128;
uint private constant contractDismissDelay = 5 weeks;
uint private constant investUnit = 1 ether;
uint private constant investMinimum = 10 ether;
uint private constant investUserLimit = 200;
uint private constant investMinDuration = 5; // 5 draw!
uint private constant investIdleTime = 1 days;
uint private constant forOwner = 2; //%
uint private constant forInvestors = 40; //%
uint private constant forHit2 = 30; //%
/* variables */
address private WLBdrawsDBAddr;
address private owner;
uint private currentJackpot;
uint private investmentsValue;
uint private extraJackpot;
uint private ticketCounter;
uint private currentGame;
uint private ownerBalance;
uint private oldContractLastGame;
bool public contractEnabled = true;
uint private contractDisabledTimeStamp;
mapping(address => players_s) private players;
games_s[] private games;
investors_s[] private investors;
/* events */
event NewTicketEvent(address Player, uint8 Number1, uint8 Number2, uint8 Number3);
event ContractDisabledEvent(uint DeadlineTime);
event DrawPrepareEvent(uint BlockNumber);
event DrawEvent(uint GameID, uint8 Number1, uint8 Number2, uint8 Number3, uint Hit3Count, uint Hit3Value, uint Hit2Count, uint Hit2Value);
event InvestAddEvent(address Investor, uint Value);
event InvestCancelEvent(address Investor, uint Value);
/* constructor */
function WeeklyLotteryB(address _WLBdrawsDBAddr, uint _oldContractLastGame) {
WLBdrawsDBAddr = _WLBdrawsDBAddr;
owner = msg.sender;
currentGame = 0;
oldContractLastGame = _oldContractLastGame;
games.length = 1;
games[0].startTimestamp = now;
uint ret = 1470571200;
while (ret < now) {
ret += 1 weeks;
}
games[0].endTimestamp = ret;
}
/* constant functions */
function Visit() constant returns (string) { return "http://wlb.ethereumlottery.net"; }
function Draws(uint id) constant returns (uint date, uint8[3] Numbers, uint hit3Count, uint hit3Value, uint hit2Count, uint hit2Value) {
return WLBDrawsDB( WLBdrawsDBAddr ).getDraw(id);
}
function CurrentGame() constant returns (uint GameID, uint Jackpot, uint Start, uint End, uint Tickets) {
return (currentGame+oldContractLastGame, currentJackpot, games[currentGame].startTimestamp, games[currentGame].endTimestamp, games[currentGame].ticketsCount);
}
function PlayerTickets(address Player, uint GameID, uint TicketID) constant returns (uint8[3] numbers, bool Checked) {
GameID -= oldContractLastGame;
return ( getNumbersFromBytes( players[Player].games[GameID].numbersBytes[TicketID] ), players[Player].games[GameID].checked);
}
function Investors(address Address) constant returns(uint Investment, uint Balance, bool Live) {
var (found, InvestorID) = getInvestorByAddress(Address);
if (found == false || ! investors[InvestorID].valid) {
return (0, 0, false);
}
return (investors[InvestorID].value, investors[InvestorID].balance, investors[InvestorID].live);
}
function CheckPrize(address Address) constant returns(uint value) {
uint gameID;
uint gameLowID;
uint8[3] memory numbers;
uint hit3Count;
uint hit2Count;
for ( gameID=currentGame ; gameID>=0 ; gameID-- ) {
if ( ! players[Address].games[gameID].checked) {
if (games[gameID].drawDone) {
numbers = getNumbersFromBytes(games[gameID].winningNumbersBytes);
hit3Count = players[Address].games[gameID].hit3Hash[sha3( numbers[0], numbers[1], numbers[2] )];
value += hit3Count * games[gameID].hit3Value;
hit2Count = players[Address].games[gameID].hit2Hash[sha3( numbers[0], numbers[1] )];
hit2Count += players[Address].games[gameID].hit2Hash[sha3( numbers[0], numbers[2] )];
hit2Count += players[Address].games[gameID].hit2Hash[sha3( numbers[1], numbers[2] )];
hit2Count -= hit3Count*3;
value += hit2Count * games[gameID].hit2Value;
} else if ( ! contractEnabled && gameID == currentGame) {
value += players[Address].games[gameID].numbersBytes.length * ticketPrice;
}
}
if (gameID == 0 || gameID-prizeDismissDelay == gameID) { break; }
}
}
/* callback function */
function () {
var Numbers = getNumbersFromHash(sha3(block.coinbase, now, ticketCounter));
CreateTicket(msg.sender, Numbers[0], Numbers[1], Numbers[2]);
}
/* external functions for players */
function BuyTicket(uint8 Number1, uint8 Number2, uint8 Number3) external {
CreateTicket(msg.sender, Number1, Number2, Number3);
}
function CheckTickets() external noEther noContract {
uint _value;
uint _subValue;
uint gameID;
uint gameLowID;
uint8[3] memory numbers;
bool changed;
uint hit3Count;
uint hit2Count;
for ( gameID=currentGame ; gameID>=0 ; gameID-- ) {
if ( ! players[msg.sender].games[gameID].checked) {
if (games[gameID].drawDone) {
numbers = getNumbersFromBytes(games[gameID].winningNumbersBytes);
hit3Count = players[msg.sender].games[gameID].hit3Hash[sha3( numbers[0], numbers[1], numbers[2] )];
_subValue += hit3Count * games[gameID].hit3Value;
hit2Count = players[msg.sender].games[gameID].hit2Hash[sha3( numbers[0], numbers[1] )];
hit2Count += players[msg.sender].games[gameID].hit2Hash[sha3( numbers[0], numbers[2] )];
hit2Count += players[msg.sender].games[gameID].hit2Hash[sha3( numbers[1], numbers[2] )];
hit2Count -= hit3Count*3;
_subValue += hit2Count * games[gameID].hit2Value;
games[gameID].prizePot -= _subValue;
_value += _subValue;
players[msg.sender].games[gameID].checked = true;
changed = true;
} else if ( ! contractEnabled && gameID == currentGame) {
_value += players[msg.sender].games[gameID].numbersBytes.length * ticketPrice;
players[msg.sender].games[gameID].checked = true;
changed = true;
}
}
if (gameID == 0 || gameID-prizeDismissDelay == gameID) { break; }
}
if ( ! changed) { throw; }
if (_value > 0) { if ( ! msg.sender.send(_value)) { throw; } }
}
function BuyTicketForOther(address Address, uint8 Number1, uint8 Number2, uint8 Number3) external {
if (Address == 0x0) { throw; }
CreateTicket(Address, Number1, Number2, Number3);
}
/* external functions for investors */
function InvestAdd() external OnlyEnabled noContract {
uint value_ = msg.value;
if (value_ < investUnit) { throw; }
if (value_ % investUnit > 0) {
if ( ! msg.sender.send( value_ % investUnit )) { throw; }
value_ = value_ - (value_ % investUnit);
}
if (value_ < investMinimum) { throw; }
var (found, InvestorID) = getInvestorByAddress(msg.sender);
if (found == false) {
if (investors.length == investUserLimit) { throw; }
InvestorID = investors.length;
investors.length++;
}
if (investors[InvestorID].valid && investors[InvestorID].live) {
investors[InvestorID].value += value_;
} else {
investors[InvestorID].value = value_;
}
investors[InvestorID].begins = currentGame;
investors[InvestorID].valid = true;
investors[InvestorID].live = true;
investors[InvestorID].owner = msg.sender;
investmentsValue += value_;
setJackpot();
InvestAddEvent(msg.sender, value_);
}
function InvestWithdraw() external noEther {
var (found, InvestorID) = getInvestorByAddress(msg.sender);
if (found == false) { throw; }
if ( ! investors[InvestorID].valid) { throw; }
uint _balance = investors[InvestorID].balance;
if (_balance == 0) { throw; }
investors[InvestorID].balance = 0;
if ( ! msg.sender.send( _balance )) { throw; }
}
function InvestCancel() external noEther {
var (found, InvestorID) = getInvestorByAddress(msg.sender);
if (found == false) { throw; }
if ( ! investors[InvestorID].valid) { throw; }
if (contractEnabled) {
if (investors[InvestorID].begins+investMinDuration < now) { throw; }
if (games[currentGame].startTimestamp+investIdleTime < now) { throw; }
}
uint balance_;
if (investors[InvestorID].live) {
investmentsValue -= investors[InvestorID].value;
balance_ = investors[InvestorID].value;
setJackpot();
InvestCancelEvent(msg.sender, investors[InvestorID].value);
}
if (investors[InvestorID].balance > 0) {
balance_ += investors[InvestorID].balance;
}
delete investors[InvestorID];
if ( ! msg.sender.send( balance_ )) { throw; }
}
/* draw functions for everyone*/
function DrawPrepare() noContract OnlyEnabled noEther {
if (games[currentGame].endTimestamp > now || (games[currentGame].prepareBlock != 0 && games[currentGame].prepareBlock+(drawBlockLimit-drawBlockDelay) > block.number)) { throw; }
games[currentGame].prepareBlock = block.number+drawBlockDelay;
DrawPrepareEvent(games[currentGame].prepareBlock);
}
function Draw() noContract OnlyEnabled noEther {
if (games[currentGame].prepareBlock == 0 || games[currentGame].prepareBlock > block.number || games[currentGame].prepareBlock+(drawBlockLimit-drawBlockDelay) <= block.number) { throw; }
bytes32 _hash;
uint hit3Value;
uint hit3Count;
uint hit2Value;
uint hit2Count;
uint a;
for ( a = 1 ; a <= drawBlockDelay ; a++ ) {
_hash = sha3(_hash, block.blockhash(games[currentGame].prepareBlock - drawBlockDelay+a));
}
var numbers = getNumbersFromHash(_hash);
games[currentGame].winningNumbersBytes = getBytesFromNumbers( numbers );
hit3Count += games[currentGame].hit3Hash[ sha3( numbers[0], numbers[1],numbers[2] ) ];
hit2Count += games[currentGame].hit2Hash[ sha3( numbers[0], numbers[1]) ];
hit2Count += games[currentGame].hit2Hash[ sha3( numbers[0], numbers[2]) ];
hit2Count += games[currentGame].hit2Hash[ sha3( numbers[1], numbers[2]) ];
hit2Count -= hit3Count*3;
uint totalPot = games[currentGame].ticketsCount*ticketPrice;
hit2Value = ( totalPot * forHit2 / 100 );
if (hit2Count > 0) {
games[currentGame].prizePot = hit2Value;
}
hit2Value = hit2Value / hit2Count;
totalPot -= hit2Value;
uint _ownerBalance = totalPot * forOwner / 100;
totalPot -= _ownerBalance;
ownerBalance += _ownerBalance;
uint _addInvestorsValue = totalPot * forInvestors / 100;
addInvestorsValue(_addInvestorsValue);
totalPot -= _addInvestorsValue;
if (hit3Count > 0) {
games[currentGame].prizePot += currentJackpot;
for ( a=0 ; a < investors.length ; a++ ) {
delete investors[a].live;
}
hit3Value = currentJackpot / hit3Count;
extraJackpot = 0;
investmentsValue = 0;
}
extraJackpot += totalPot;
DrawEvent(currentGame+oldContractLastGame, numbers[0], numbers[1], numbers[2], hit3Count, hit3Value, hit2Count, hit2Value);
WLBDrawsDB( WLBdrawsDBAddr ).newDraw( now, numbers, hit3Count, hit3Value, hit2Count, hit2Value);
games[currentGame].hit3Count = hit3Count;
games[currentGame].hit3Value = hit3Value;
games[currentGame].hit2Count = hit2Count;
games[currentGame].hit2Value = hit2Value;
games[currentGame].drawDone = true;
newGame();
setJackpot();
}
/* owner functions */
function OwnerGetFee() external OnlyOwner {
if (ownerBalance == 0) { throw; }
if (owner.send(ownerBalance) == false) { throw; }
ownerBalance = 0;
}
function OwnerCloseContract() external OnlyOwner noEther {
if ( ! contractEnabled) {
if (contractDisabledTimeStamp+contractDismissDelay < now) {
suicide(owner);
}
} else {
contractEnabled = false;
contractDisabledTimeStamp = now;
ContractDisabledEvent(contractDisabledTimeStamp+contractDismissDelay);
ownerBalance += extraJackpot;
extraJackpot = 0;
games[currentGame].prizePot = games[currentGame].ticketsCount*ticketPrice;
}
}
/* private functions */
function CreateTicket(address Addr, uint8 Number1, uint8 Number2, uint8 Number3) private noContract OnlyEnabled {
var Numbers = [Number1 , Number2 , Number3];
if ( ! checkNumbers( Numbers )) { throw; }
Numbers = sortNumbers(Numbers);
if (msg.value < ticketPrice) { throw; }
if (msg.value-ticketPrice > 0) { if ( ! Addr.send( msg.value-ticketPrice )) { throw; } }
if (currentJackpot == 0) { throw; }
if (games[currentGame].endTimestamp < now) { throw; }
ticketCounter++;
games[currentGame].ticketsCount++;
bytes32 hash0 = sha3( Numbers[0], Numbers[1], Numbers[2] );
bytes32 hash1 = sha3( Numbers[0], Numbers[1]);
bytes32 hash2 = sha3( Numbers[0], Numbers[2]);
bytes32 hash3 = sha3( Numbers[1], Numbers[2]);
games[currentGame].hit3Hash[hash0]++;
games[currentGame].hit2Hash[hash1]++;
games[currentGame].hit2Hash[hash2]++;
games[currentGame].hit2Hash[hash3]++;
players[Addr].games[currentGame].numbersBytes.push ( getBytesFromNumbers(Numbers) );
players[Addr].games[currentGame].hit3Hash[hash0]++;
players[Addr].games[currentGame].hit2Hash[hash1]++;
players[Addr].games[currentGame].hit2Hash[hash2]++;
players[Addr].games[currentGame].hit2Hash[hash3]++;
NewTicketEvent( Addr, Numbers[0], Numbers[1], Numbers[2] );
}
function addInvestorsValue(uint value) private {
bool done;
uint a;
for ( a=0 ; a < investors.length ; a++ ) {
if (investors[a].live && investors[a].valid) {
investors[a].balance += value * investors[a].value / investmentsValue;
done = true;
}
}
if ( ! done) {
ownerBalance += value;
}
}
function newGame() private {
var nextDraw = games[currentGame].endTimestamp + 1 weeks;
currentGame++;
uint gamesID = games.length;
games.length++;
games[gamesID].startTimestamp = now;
games[gamesID].endTimestamp = nextDraw;
if (games.length > prizeDismissDelay) {
extraJackpot += games[currentGame-prizeDismissDelay].prizePot;
delete games[currentGame-prizeDismissDelay];
}
}
function getNumbersFromHash(bytes32 hash) private returns (uint8[3] numbers) {
bool ok = true;
uint8 num = 0;
uint hashpos = 0;
uint8 a;
uint8 b;
for (a = 0 ; a < numbers.length ; a++) {
while (true) {
ok = true;
if (hashpos == 32) {
hashpos = 0;
hash = sha3(hash);
}
num = getPart( hash, hashpos );
num = num % uint8(drawMaxNumber) + 1;
hashpos += 1;
for (b = 0 ; b < numbers.length ; b++) {
if (numbers[b] == num) {
ok = false;
break;
}
}
if (ok == true) {
numbers[a] = num;
break;
}
}
}
numbers = sortNumbers( numbers );
}
function getPart(bytes32 a, uint i) private returns (uint8) { return uint8(byte(bytes32(uint(a) * 2 ** (8 * i)))); }
function setJackpot() private {
currentJackpot = investmentsValue + extraJackpot;
}
function getInvestorByAddress(address Address) private returns (bool found, uint id) {
for ( id=0 ; id < investors.length ; id++ ) {
if (investors[id].owner == Address) {
return (true, id);
}
}
return (false, 0);
}
function checkNumbers(uint8[3] Numbers) private returns (bool) {
for ( uint a = 0 ; a < Numbers.length ; a++ ) {
if (Numbers[a] > drawMaxNumber || Numbers[a] == 0) { return; }
for ( uint b = 0 ; a < Numbers.length ; a++ ) {
if (a != b && Numbers[a] == Numbers[b]) { return; }
}
}
return true;
}
function sortNumbers(uint8[3] numbers) private returns(uint8[3] sNumbers) {
sNumbers = numbers;
for (uint8 i=0; i<numbers.length; i++) {
for (uint8 j=i+1; j<numbers.length; j++) {
if (sNumbers[i] > sNumbers[j]) {
uint8 t = sNumbers[i];
sNumbers[i] = sNumbers[j];
sNumbers[j] = t;
}
}
}
}
function getNumbersFromBytes(bytes3 Bytes) private returns (uint8[3] Numbers){
Numbers[0] = uint8(Bytes);
Numbers[1] = uint8(uint24(Bytes) /256);
Numbers[2] = uint8(uint24(Bytes) /256/256);
}
function getBytesFromNumbers(uint8[3] Numbers) private returns (bytes3 Bytes) {
return bytes3(uint(Numbers[0])+uint(Numbers[1])*256+uint(Numbers[2])*256*256);
}
/* modifiers */
modifier noContract() {if (tx.origin != msg.sender) { throw; } _ }
modifier noEther() { if (msg.value > 0) { throw; } _ }
modifier OnlyOwner() { if (owner != msg.sender) { throw; } _ }
modifier OnlyEnabled() { if ( ! contractEnabled) { throw; } _ }
}External Links
Related contracts
swap
Same eraA swap contract, deployed May 2016.
0x5bc45b...dab809May 7, 2016DAO_Creator
Same eraA DAO_Creator factory, the contract that stamps out new DAO instances, deployed 24 May 2016.
0x7d7336...9b663cMay 24, 2016Contract 0xe5411a...0df31b
Same eraA liveness canary for the Ethereum Alarm Clock, deployed 1 July 2016.
0xe5411a...0df31bJuly 1, 2016EscrowFoundry
Same eraA factory contract for generating custom escrow agreements between buyers, sellers, and arbiters. Compiled with solc v0.3.5, optimizer on.
0xbe7193...38f389July 6, 2016