Contract Source Code:
File 1 of 1 : Micro
pragma experimental "v0.5.0";
//Micro Etheroll contract
//version 0.8.0
//// minimal Etheroll "interface"
contract Etheroll {
function playerRollDice(uint rollUnder) public payable;
function playerWithdrawPendingTransactions() public returns (bool);
}
//// Proxy contract for Micro Etheroll - providing a cheap and empty payable fallback function
contract Proxy {
address etheroll;
address micro;
address owner;
uint roundID = 0;
event GotFunds(uint indexed roundID, address indexed sender, uint indexed amount);
event SentFunds(uint indexed roundID, uint indexed amount, uint indexed rollUnder);
event WithdrawPendingTransactionsResult(bool indexed result);
constructor(address etherollAddress, address ownerAddress) public {
etheroll = etherollAddress;
owner = ownerAddress;
micro = msg.sender;
roundID = 0;
}
//// Getters
function getBalance() view external returns (uint) {
return address(this).balance;
}
function getRoundID() view external returns (uint) {
return roundID;
}
function getEtherollAddress() view external returns (address) {
return etheroll;
}
//// Secutity modifier
modifier onlyOwner {
require(msg.sender == owner);
_;
}
modifier onlyMicro {
require(msg.sender == micro);
_;
}
//// Insecure public payable fallback function
function () external payable {
emit GotFunds(roundID, msg.sender, msg.value);
}
//// Sending bets and withdrawing winnings
function sendToEtheroll(uint rollUnder, uint newRoundID) external payable
onlyMicro
{
roundID = newRoundID;
Etheroll e = Etheroll(etheroll);
e.playerRollDice.value(msg.value)(rollUnder);
emit SentFunds(roundID, msg.value, rollUnder);
}
function withdrawWinnings() external
onlyMicro
{
Micro m = Micro(micro);
m.withdrawWinnings.value(address(this).balance)();
}
//// Owner security & maintenance functions
function withdrawRefund() external
onlyMicro
{
Micro m = Micro(micro);
m.withdrawRefund.value(address(this).balance)();
}
function withdrawPendingTransactions() external
onlyOwner
{
Etheroll e = Etheroll(etheroll);
emit WithdrawPendingTransactionsResult(e.playerWithdrawPendingTransactions());
}
function ownerWithdraw() external
onlyOwner
{
owner.transfer(address(this).balance);
}
function setEtherollAddress(address etherollAddress) external
onlyOwner
{
etheroll = etherollAddress;
}
}
//// Main Micro Etheroll Contract
contract Micro {
address[110] bets;
address proxy;
address owner;
uint roundID;
bool betsState = true;
bool rolled = false;
bool emergencyBlock = false;
bool betsBlock = false;
uint rollUnder = 90;
uint participants = 10; // Number of effective participants (without extraBets)
uint extraBets = 1;
uint oneBet = 0.01 ether;
uint8 numberOfBets = 0;
uint houseEdgeDivisor = 1000;
uint houseEdge = 990;
uint expectedReturn;
event GotBet(uint indexed roundID, address indexed sender, uint8 indexed numberOfBets);
event BetResult(uint indexed roundID, uint8 indexed result, uint indexed amount);
event ReadyToRoll(uint indexed roundID, uint indexed participants, uint indexed oneBet);
event SendError(uint indexed roundID, address addr, uint amount);
event Emergency(uint indexed roundID);
constructor(address etherollAddress) public {
owner = msg.sender;
proxy = new Proxy(etherollAddress, owner);
setExpectedReturn((((((oneBet*participants) * (100-(rollUnder-1))) / (rollUnder-1)+(oneBet*participants)))*houseEdge/houseEdgeDivisor) / 0.01 ether);
roundID = 0;
}
//// Getters and Setter
function setExpectedReturn(uint rounded) internal {
expectedReturn = rounded * 0.01 ether;
}
function getBetsState() external view returns (bool) {
return betsState;
}
function getRolled() external view returns (bool) {
return rolled;
}
function getExpectedReturn() external view returns (uint) {
return expectedReturn;
}
function getNumberOfBets() external view returns (uint) {
return numberOfBets;
}
function getRollUnder() external view returns (uint) {
return rollUnder;
}
function getOneBet() external view returns (uint) {
return oneBet;
}
function getParticipants() external view returns (uint) {
return participants;
}
function getExtraBets() external view returns (uint) {
return extraBets;
}
function getBetsBlock() external view returns (bool) {
return betsBlock;
}
function getRoundID() view external returns (uint) {
return roundID;
}
function getWaitingState() external view returns (uint) {
if (!betsState && !rolled) return 1; //waiting for roll()
if (!betsState && rolled && (address(proxy).balance > 0)) return 2; //waiting for wakeUpProxy(), but needs a check if funds on proxy are winnings
if (emergencyBlock) return 9; //is in emergency block
if (betsBlock) return 8; //bets block active
if (betsState && !rolled) return 0; //not waiting, accepting bids
return 5; // unknown state, probably waiting for etheroll
}
// Combined "one-request" getState for web-requests optimisation
function getState() external view returns (bool, bool, uint, uint, uint, uint, uint, uint, bool, uint, uint) {
return (this.getBetsState(),
this.getRolled(),
this.getExpectedReturn(),
this.getNumberOfBets(),
this.getRollUnder(),
this.getOneBet(),
this.getParticipants(),
this.getExtraBets(),
this.getBetsBlock(),
this.getRoundID(),
this.getWaitingState());
}
//// Security function modifiers
modifier onlyOwner {
require(msg.sender == owner);
_;
}
modifier onlyProxy {
require(msg.sender == proxy);
_;
}
modifier betsOver {
require (!betsState);
_;
}
modifier betsActive {
require (betsState);
_;
}
modifier noBets {
require (numberOfBets == 0);
_;
}
modifier hasRolled {
require(rolled);
_;
}
modifier hasntRolled {
require(!rolled);
_;
}
modifier hasMoney {
require(address(proxy).balance > 0);
_;
}
modifier noEmergencyBlock {
require(!emergencyBlock);
_;
}
//// Secure payable fallback function - receives bets
function () external payable {
require((msg.value == oneBet) || (msg.sender == owner));
if (msg.sender != owner) {
require(betsState && !emergencyBlock);
require(!betsBlock);
if (numberOfBets < participants+(extraBets-1)) {
bets[numberOfBets] = msg.sender;
numberOfBets++;
emit GotBet(roundID, msg.sender, numberOfBets);
} else {
bets[numberOfBets] = msg.sender;
numberOfBets++;
emit GotBet(roundID, msg.sender, numberOfBets);
betsState = false;
emit ReadyToRoll(roundID, participants+extraBets, oneBet);
}
}
}
//// Main contract callable functions
function roll() external
betsOver
hasntRolled
noEmergencyBlock
{
require(numberOfBets == (participants + extraBets));
rolled = true;
Proxy p = Proxy(proxy);
p.sendToEtheroll.value((participants) * oneBet)(rollUnder, roundID);
}
function wakeUpProxy() external
onlyOwner
betsOver
hasRolled
hasMoney
noEmergencyBlock
{
rolled = false;
Proxy p = Proxy(proxy);
p.withdrawWinnings();
}
//// Withdraw and distribute winnings
function withdrawWinnings() external payable
onlyProxy
{
if ((msg.value > expectedReturn) && !emergencyBlock) {
emit BetResult(roundID, 1, msg.value); // We won! Set 1
distributeWinnings(msg.value);
} else {
emit BetResult(roundID, 0, msg.value); // We lost :( Set 0
}
numberOfBets = 0;
betsState = true;
roundID++;
}
function proxyGetRefund() external
onlyOwner
betsOver
hasRolled
hasMoney
{
rolled = false;
Proxy p = Proxy(proxy);
p.withdrawRefund();
}
function withdrawRefund() external payable
onlyProxy
{
emit BetResult(roundID, 2, msg.value); // Set 2 for Refund
distributeWinnings(msg.value+(oneBet*extraBets)); // Distribute the refund and return extraBets
numberOfBets = 0;
betsState = true;
roundID++;
}
function distributeWinnings(uint value) internal
betsOver
{
require(numberOfBets == (participants + extraBets)); // Check if count of participants+extraBets matches numberOfBets
uint share = value / (numberOfBets); // Calculate the share out of value received div by number of bets
for (uint i = 0; i<(numberOfBets); i++) {
if (!(bets[i].send(share))) emit SendError(roundID, bets[i], share); // Send an SendError event if something goes wrong
}
}
//// Owner security & maintenance functions
function resetState() external
onlyOwner
{
numberOfBets = 0;
betsState = true;
rolled = false;
roundID++;
}
function returnBets() external
onlyOwner
{
require(emergencyBlock || betsBlock);
require(numberOfBets>0);
for (uint i = 0; i<(numberOfBets); i++) {
if (!(bets[i].send(oneBet))) emit SendError(roundID, bets[i], oneBet); // Send an SendError event if something goes wrong
}
numberOfBets = 0;
betsState = true;
rolled = false;
roundID++;
}
function changeParticipants(uint newParticipants) external
onlyOwner
betsActive
{
require((newParticipants <= 100) && (newParticipants > numberOfBets)); //Check that newParticipants don't exceed bets array length and exceed current round existing bets
participants = newParticipants;
setExpectedReturn((((((oneBet*participants) * (100-(rollUnder-1))) / (rollUnder-1)+(oneBet*participants)))*houseEdge/houseEdgeDivisor) / 0.01 ether);
}
function changeExtraBets(uint newExtraBets) external
onlyOwner
betsActive
{
require(participants+newExtraBets < bets.length);
require(participants+newExtraBets > numberOfBets);
extraBets = newExtraBets;
}
function changeOneBet(uint newOneBet) external
onlyOwner
betsActive
noBets
{
require(newOneBet > 0);
oneBet = newOneBet;
setExpectedReturn((((((oneBet*participants) * (100-(rollUnder-1))) / (rollUnder-1)+(oneBet*participants)))*houseEdge/houseEdgeDivisor) / 0.01 ether);
}
function changeRollUnder(uint newRollUnder) external
onlyOwner
betsActive
{
require((newRollUnder > 1) && (newRollUnder < 100));
rollUnder = newRollUnder;
setExpectedReturn((((((oneBet*participants) * (100-(rollUnder-1))) / (rollUnder-1)+(oneBet*participants)))*houseEdge/houseEdgeDivisor) / 0.01 ether);
}
function enableEmergencyBlock() external
onlyOwner
{
emergencyBlock = true;
emit Emergency(roundID);
}
function disableEmergencyBlock() external
onlyOwner
{
emergencyBlock = false;
}
function enableBets() external
onlyOwner
{
betsBlock = false;
}
function disableBets() external
onlyOwner
{
betsBlock = true;
}
function ownerWithdraw() external
onlyOwner
{
owner.transfer(address(this).balance);
}
function ownerkill() external
onlyOwner
{
selfdestruct(owner);
}
}