pragma solidity ^0.4.25; //@dev update web3 to deal with mappings contract Boomerang { /////////// //STORAGE// /////////// address public admin; uint256 public contestCount; mapping(uint256 => bool) public prizeAwarded; mapping(uint256 => bool) public contestStarted; mapping(uint256 => address) public winner; mapping(uint256 => uint256) public prize; mapping(uint256 => uint256) public highest; mapping(uint256 => uint256) public contestClose; mapping(uint256 => uint256) public totalBounceVolume; mapping(uint256 => mapping(address => uint256)) public playerTotal; mapping(uint256 => mapping(address => bool)) public playerExists; /////////////// //CONSTRUCTOR// /////////////// constructor() public { admin = msg.sender; } ///////////// //MODIFIERS// ///////////// modifier notContract() { require(msg.sender == tx.origin, "no contracts allowed"); _; } modifier onlyAdmin() { require(msg.sender == admin, "you must be the admin"); _; } ////////// //EVENTS// ////////// event newContest(uint256 _prizeTotal, uint256 _contestClose, string _message); event prizeAdded(address _caller, uint256 _amountAdded, uint256 _newPrizeTotal, string _message); event playerBounce(address _player, uint256 _bounceAmount, uint256 _playerTotal, uint256 _volume, string _message); event winnerPayed(address _caller, address _winner, uint256 _prize, string _message); /////////////////// //ADMIN FUNCTIONS// /////////////////// function openContest(uint256 _durationHours) external payable onlyAdmin() { require (block.timestamp > contestClose[contestCount], "there is an active contest"); require (msg.value >= 1 ether, "you must provide a prize seed"); require (_durationHours >= 1, "the contest must be at least 1 hour"); //payout winner from last round if (prizeAwarded[contestCount] == false && contestCount > 0) { payoutWinner(); } contestCount++; contestStarted[contestCount] = true; contestClose[contestCount] = block.timestamp + _durationHours * 3600; //multiply _duration (hours) by 3600 seconds/hour prize[contestCount] += msg.value; emit prizeAdded(msg.sender, msg.value, prize[contestCount], "ETH was just added to the prize"); emit newContest(prize[contestCount], contestClose[contestCount], "a new contest just started"); } // emergency withdraw after contest closes // can only be called if ether is left over in contract after game ends and prize is awarded function withdrawPostPrize() external onlyAdmin() { require (block.timestamp >= contestClose[contestCount] && prizeAwarded[contestCount] == true, "the contest is not active"); admin.transfer(address(this).balance); } ////////////////// //USER FUCNTIONS// ////////////////// //generous donations accepted function addPrize() external payable notContract() { require (block.timestamp < contestClose[contestCount] && contestCount > 0); prize[contestCount] += msg.value; emit prizeAdded(msg.sender, msg.value, prize[contestCount], "ETH was just added to the prize"); } function() external payable notContract() { bounce(); } //user sends funds to contract and they are immediately returned function bounce() public payable notContract() { require (contestStarted[contestCount] == true && block.timestamp < contestClose[contestCount], "the contest is not active"); //update player accounting if (msg.value >= 0 && playerExists[contestCount][msg.sender] == false) { playerExists[contestCount][msg.sender] = true; } playerTotal[contestCount][msg.sender] += msg.value; //update contest accounting totalBounceVolume[contestCount] += msg.value; //check if there is a new winner and update if necessary //if there is a tie, the first person to reach the highest balance wins if (playerTotal[contestCount][msg.sender] > highest[contestCount]){ highest[contestCount] = playerTotal[contestCount][msg.sender]; winner[contestCount] = msg.sender; } //return user funds msg.sender.transfer(msg.value); emit playerBounce(msg.sender, msg.value, playerTotal[contestCount][msg.sender], totalBounceVolume[contestCount], "a player just bounced some ETH"); } //anyone can call this when a contest ends to distribute the prize function payoutWinner() public notContract() { require(block.timestamp >= contestClose[contestCount], "the contest is still active"); require(prizeAwarded[contestCount] == false, "the prize was awarded already"); //confirm there is actually a winner and send them the prize if(playerExists[contestCount][winner[contestCount]] == true){ prizeAwarded[contestCount] = true; winner[contestCount].transfer(prize[contestCount]); } else { //return balance to admin if there is no valid player and reset //will only hold true if there are zero players //prevents locked ether if no valid players join during contract duration prizeAwarded[contestCount] = true; admin.transfer(prize[contestCount]); } emit winnerPayed(msg.sender, winner[contestCount], prize[contestCount], "the winner was just payed"); } ////////////////// //VIEW FUNCTIONS// ////////////////// function isContestOpen() external view returns(bool _isOpen) { if(contestStarted[contestCount] && block.timestamp < contestClose[contestCount]){ return true; } else { return false; } } function timeUntilClose() external view returns(uint256 _timeLeft) { if(contestStarted[contestCount] && block.timestamp < contestClose[contestCount]){ uint256 timeLeft = contestClose[contestCount] - block.timestamp; if (timeLeft < 0) { return 0; } else { return timeLeft; } } else { return 0; } } function contractBalance() external view returns(uint256 _contractBalance) { return address(this).balance; } }
0.4.25