pragma solidity ^0.4.18;
// zeppelin-solidity: 1.8.0
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
contract ChampionSimple is Ownable {
using SafeMath for uint;
event LogDistributeReward(address addr, uint reward);
event LogParticipant(address addr, uint choice, uint betAmount);
event LogModifyChoice(address addr, uint oldChoice, uint newChoice);
event LogRefund(address addr, uint betAmount);
event LogWithdraw(address addr, uint amount);
event LogWinChoice(uint choice, uint reward);
uint public minimumBet = 5 * 10 ** 16;
uint public deposit = 0;
uint public totalBetAmount = 0;
uint public startTime;
uint public winChoice;
uint public winReward;
uint public numberOfBet;
bool public betClosed = false;
struct Player {
uint betAmount;
uint choice;
}
address [] public players;
mapping(address => Player) public playerInfo;
mapping(uint => uint) public numberOfChoice;
mapping(uint => mapping(address => bool)) public addressOfChoice;
mapping(address => bool) public withdrawRecord;
modifier beforeTimestamp(uint timestamp) {
require(now < timestamp);
_;
}
modifier afterTimestamp(uint timestamp) {
require(now >= timestamp);
_;
}
/**
* @dev the construct function
* @param _startTime the deadline of betting
* @param _minimumBet the minimum bet amount
*/
function ChampionSimple(uint _startTime, uint _minimumBet) payable public {
require(_startTime > now);
deposit = msg.value;
startTime = _startTime;
minimumBet = _minimumBet;
}
/**
* @dev find a player has participanted or not
* @param player the address of the participant
*/
function checkPlayerExists(address player) public view returns (bool) {
if (playerInfo[player].choice == 0) {
return false;
}
return true;
}
/**
* @dev to bet which team will be the champion
* @param choice the choice of the participant(actually team id)
*/
function placeBet(uint choice) payable beforeTimestamp(startTime) public {
require(choice > 0);
require(!checkPlayerExists(msg.sender));
require(msg.value >= minimumBet);
playerInfo[msg.sender].betAmount = msg.value;
playerInfo[msg.sender].choice = choice;
totalBetAmount = totalBetAmount.add(msg.value);
numberOfBet = numberOfBet.add(1);
players.push(msg.sender);
numberOfChoice[choice] = numberOfChoice[choice].add(1);
addressOfChoice[choice][msg.sender] = true;
LogParticipant(msg.sender, choice, msg.value);
}
/**
* @dev allow user to change their choice before a timestamp
* @param choice the choice of the participant(actually team id)
*/
function modifyChoice(uint choice) beforeTimestamp(startTime) public {
require(choice > 0);
require(checkPlayerExists(msg.sender));
uint oldChoice = playerInfo[msg.sender].choice;
numberOfChoice[oldChoice] = numberOfChoice[oldChoice].sub(1);
numberOfChoice[choice] = numberOfChoice[choice].add(1);
playerInfo[msg.sender].choice = choice;
addressOfChoice[oldChoice][msg.sender] = false;
addressOfChoice[choice][msg.sender] = true;
LogModifyChoice(msg.sender, oldChoice, choice);
}
/**
* @dev close who is champion bet with the champion id
*/
function saveResult(uint teamId) onlyOwner public {
winChoice = teamId;
betClosed = true;
winReward = deposit.add(totalBetAmount).div(numberOfChoice[winChoice]);
LogWinChoice(winChoice, winReward);
}
/**
* @dev every user can withdraw his reward
*/
function withdrawReward() public {
require(betClosed);
require(!withdrawRecord[msg.sender]);
require(winChoice > 0);
require(winReward > 0);
require(addressOfChoice[winChoice][msg.sender]);
msg.sender.transfer(winReward);
withdrawRecord[msg.sender] = true;
LogDistributeReward(msg.sender, winReward);
}
/**
* @dev anyone could recharge deposit
*/
function rechargeDeposit() payable public {
deposit = deposit.add(msg.value);
}
/**
* @dev get player bet information
* @param addr indicate the bet address
*/
function getPlayerBetInfo(address addr) view public returns (uint, uint) {
return (playerInfo[addr].choice, playerInfo[addr].betAmount);
}
/**
* @dev get the bet numbers of a specific choice
* @param choice indicate the choice
*/
function getNumberByChoice(uint choice) view public returns (uint) {
return numberOfChoice[choice];
}
/**
* @dev if there are some reasons lead game postpone or cancel
* the bet will also cancel and refund every bet
*/
function refund() onlyOwner public {
for (uint i = 0; i < players.length; i++) {
players[i].transfer(playerInfo[players[i]].betAmount);
LogRefund(players[i], playerInfo[players[i]].betAmount);
}
}
/**
* @dev get the players
*/
function getPlayers() view public returns (address[]) {
return players;
}
/**
* @dev dealer can withdraw the remain ether if distribute exceeds max length
*/
function withdraw() onlyOwner public {
uint _balance = address(this).balance;
owner.transfer(_balance);
LogWithdraw(owner, _balance);
}
}
{
"compilationTarget": {
"ChampionSimple.sol": "ChampionSimple"
},
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[{"name":"choice","type":"uint256"}],"name":"placeBet","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"teamId","type":"uint256"}],"name":"saveResult","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"choice","type":"uint256"}],"name":"getNumberByChoice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"player","type":"address"}],"name":"checkPlayerExists","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"playerInfo","outputs":[{"name":"betAmount","type":"uint256"},{"name":"choice","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"withdrawRecord","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"refund","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalBetAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"startTime","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"winChoice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getPlayers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"address"}],"name":"addressOfChoice","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"betClosed","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"choice","type":"uint256"}],"name":"modifyChoice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"minimumBet","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"rechargeDeposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"withdrawReward","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"deposit","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"numberOfBet","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"winReward","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"getPlayerBetInfo","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"numberOfChoice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"players","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_startTime","type":"uint256"},{"name":"_minimumBet","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"reward","type":"uint256"}],"name":"LogDistributeReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"choice","type":"uint256"},{"indexed":false,"name":"betAmount","type":"uint256"}],"name":"LogParticipant","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"oldChoice","type":"uint256"},{"indexed":false,"name":"newChoice","type":"uint256"}],"name":"LogModifyChoice","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"betAmount","type":"uint256"}],"name":"LogRefund","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"LogWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"choice","type":"uint256"},{"indexed":false,"name":"reward","type":"uint256"}],"name":"LogWinChoice","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]