/** * Source Code first verified at https://etherscan.io on Tuesday, April 30, 2019 (UTC) */ pragma solidity >=0.5.0 <0.6.0; /* HEY WELCOME TO MyEther.DATE's OFFICIAL SMART CONTRACT! My name is Tay Tay and I will help you digest the material of this contract, all in layman's terms. */ contract myEtherDate { struct Player { uint commitBlock; uint stake; } mapping(address => Player) public player; uint public maxStake; address public owner; modifier onlyOwner() { require(msg.sender == owner); _; } constructor() public { owner = msg.sender; } /* Here is where the fun begins. When you materialize your 4 dates, all * the contract really cares is WHEN you did it and how big was your stake. * so yeah, sorry to burst the bubble but the matchmaking has nothing to do * with the outcome of your bet. */ function set() public payable returns (bool success) { // this is to make sure our bankroll can cover the maximum payout // for your stake and also to make sure your stake is greater than zero. require(msg.value > 0 && msg.value <= maxStake); // Since future hashes are quite hard to predict, // your random seed will be the hash of the next block player[msg.sender].commitBlock = block.number + 1; player[msg.sender].stake = msg.value; return true; } /* RANDOM NUMBER GENERATION function * * This is pretty much a copy-paste of cryptokitties gene science algorithm but * tailored to our specific purposes. * * This function is public (gas-free) so it is called by MyEther.DATE's interface on behalf * of the user, automatically, as soon as random numbers are available. * * The interface will then interpret these random numbers and tell the player if he won or not. * If he won, it is up to him to call the "claim" function. * */ function getRand() view public returns (uint[4] memory) { // convert our "pseudo-random" hash to human-redeable integers uint256 randomN = uint256(blockhash(player[msg.sender].commitBlock)); // this function will not work if it is called to soon // (like right after the bet was placed, because the hash for the next block is not yet available), // or too late (256+ blocks after the bet was placed, because the etheruem blockchain // only stores the most recent 256 block hashes) require(randomN != 0); uint256 offset; uint[4] memory randNums; // this loop will slice our random number into 4 smaller numbers, // each one from 0 to 65535 for(uint i = 0; i < 4; i++){ randNums[i] = _sliceNumber(randomN, 16, offset); offset += 32; } // return our 4 random numbers return randNums; } /* CLAIM function * * This function can be evoked by anybody, but it will only payout ether to actual * winners. * */ function claim() public payable returns (bool success) { uint[4] memory rand = getRand(); player[msg.sender].commitBlock = 0; uint256 stake = player[msg.sender].stake; player[msg.sender].stake = 0; uint256 successfulDate; // you get 4 random numbers for 4 date outcomes.. // To get a successful date, any of your random numbers must be less than 8110 // and since they range from 0 to 65536, you have a winning probability // of 0.12375 on each date for (uint i = 0; i < 4; i++) { if (rand[i] < 8110) successfulDate++; } if (successfulDate != 0) { // for each successful date, we double your stake, // this equals a 1% edge... uint256 payout = SafeMath.mul(stake, 2); payout = SafeMath.mul(payout, successfulDate); msg.sender.transfer(payout); updateMaxStake(); } return true; } /// @dev given a number get a slice of any bits, at certain offset /// @param _n a number to be sliced /// @param _nbits how many bits long is the new number /// @param _offset how many bits to skip function _sliceNumber(uint256 _n, uint256 _nbits, uint256 _offset) private pure returns (uint256) { // mask is made by shifting left an offset number of times uint256 mask = uint256((2**_nbits) - 1) << _offset; // AND n with mask, and trim to max of _nbits bits return uint256((_n & mask) >> _offset); } function fundBankroll() public payable returns(bool success) { updateMaxStake(); return true; } function updateMaxStake() public returns (bool success) { uint256 newMax = SafeMath.div(address(this).balance, 8); maxStake = newMax; return true; } function collect(uint256 ammount) public onlyOwner returns (bool success) { msg.sender.transfer(ammount); updateMaxStake(); return true; } function transferOwnership(address newOwner) public onlyOwner { if (newOwner != address(0)) { owner = newOwner; } } } /** * @title SafeMath * @dev Unsigned math operations with safety checks that revert on error */ library SafeMath { /** * @dev Multiplies two unsigned integers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b); return c; } /** * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 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 unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; return c; } /** * @dev Adds two unsigned integers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } /** * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } }