账户
0xef...a5ca
0xeF...A5CA

0xeF...A5CA

US$0.00
此合同的源代码已经过验证!
合同元数据
编译器
0.5.16+commit.9c3226ce
语言
Solidity
合同源代码
文件 1 的 1:Game.sol
pragma solidity 0.5.16;

// @title SelfDestructingSender
// @notice Sends funds to any address using selfdestruct to bypass fallbacks.
// @dev Is invoked by the _forceTransfer() function in the Game contract.
contract SelfDestructingSender {
    constructor(address payable payee) public payable {
        selfdestruct(payee);
    }
}


// @title Game
// @notice A money game in which only one player can lose. Everyone else leaves with more money than they started with.
// Dozens may play. Only one will lose.
// @dev The UI will initially launch at playescalation.com. If that site ever goes down, look for an `Announce` event at
// https://etherscan.io/address/0xbea62796855548154464f6c8e7bc92672c9f87b8#events for where to find the latest UI.
// IMPORTANT: This is not an investment scheme. This is a game. There is up to one loser per game and that could be you.
// IMPORTANT: Do not play with more money than you are comfortable losing.
// IMPORTANT: It is *impossible* to get back any money you lose, because that money is used to pay out all the other players.
// IMPORTANT: The creators of this game have no control over this game or the payouts. This code is autonomous.
// IMPORTANT: By interacting with this contract you agree to not hold the creators responsible for any loses you may incur. Use it at your own risk.
// IMPORTANT: Have fun and play responsibly!
// @dev This game works as follows:
// - To ready the game, at least 0.0435 ETH is donated to the contract. Anyone can do this. It will probably be done by the game creators.
// - Then anyone can start the game by calling the `firstPlay` function, which requires staking exactly 0.05 ETH with this contract.
// - This starts a countdown clock that counts down from 30 minutes.
// - During the 30 minutes, anyone can play by calling the `play` function, which requires staking funds with this contract.
// - Each play of the game causes the countdown timer to be reset back to 30 minutes.
// - Each successive play of the game requires a stake that is exactly 15% larger than the previous stake.
// - The game ends when the countdown clock gets to 0.
// - If two or more people play after you then you are a "small winner" and you are instantly given your stake back plus an additional 10% profit.
// -    This "small winner" payout happens automatically, as soon as the second player after you plays.
// -    You do not need to wait until the game ends to get paid. You are paid out instantly, with no need to send a withdrawal transaction.
// - If nobody plays after you then you are a "big winner" and, when the clock gets to 0, you are given your stake back plus an additional 20% profit.
// -    This "big winner" payout happens automatically when the game is reset.
// -    Anyone can reset the game by calling the `reset` function. It costs only gas.
// - If *exactly one* other person has played after you when the countdown clock gets to 0 then you are the game's only loser. You lose your stake.
// -    That is, if you are the one sorry bastard who ends up in second place when the countdown clock gets to 0, you lose.
// -    The only way to lose money in this game is to be the second to last person who played.
// - TL;DR: So long as you are not in second place when the countdown clock gets to 0, you will leave with more money that you started with.
//
// NOTES:
// - It is okay for a person to play more than once during a game. Each deposit is treated as a distinct play. The identity of the depositor is not important.
//      For example, if you want to, you can put the profit won from an earlier play towards a later play. Or even play several times in a row.
// - If the countdown clock is getting close to 0 and you are in second place, one possible way to avoid losing your stake
//      is to play again (but only if you are comfortable putting more money at risk). This would knock your old play out of second place
//      so you would get it back immediately, along with 10% profit, and your new deposit would have a chance at being the "big winner".
// - This contract itself can accumulate funds over time. These funds are used to seed future rounds of the game.
//      This seeding of the next round happens automatically when the `reset` function is called.
// - If you ever find that nobody is paying attention to this contract and it has money in it, you can profit from that by being
//      the first player. If nobody else is paying attention then you will also be the last player because nobody will play after you.
//      This means you'll get your deposit back plus 20% profit. You can repeat this over and over until this contract is drained of all its funds.
// - To any copycats thinking of offering similar games and just tweaking the constants: please be very careful.
//      The maths behind the choice of constants is extremely sensitive, and it is easy to introduce critical insolvency vulnerabilities if you
//      don't know what you are doing. This contract was created and rigorously tested by experienced contract developers. If you want to make
//      changes to it please write full integration tests to be sure your choice of constants will not cause security issues.
contract Game {
    using SafeMath for uint256;

    // ======
    // EVENTS
    // ======

    event Action(
        uint256 indexed gameNumber,
        uint256 indexed depositNumber,
        address indexed depositorAddress,
        uint256 block,
        uint256 time,
        address payoutProof, // address of the SelfDestructingSender contract that paid out the `(depositNumber - 2)th` deposit
        bool gameOver
    );

    // ======================================================================
    // CONSTANTS
    // SECURITY: DO NOT CHANGE THESE CONSTANTS! THEY ARE NOT ARBITRARY!
    // CHANGING THESE CONSTANTS MAY RESULT IN AN INSOLVENCY VULNERABILITY!
    // ======================================================================

    uint256 constant internal DEPOSIT_INCREMENT_PERCENT = 15;
    uint256 constant internal BIG_REWARD_PERCENT = 20;
    uint256 constant internal SMALL_REWARD_PERCENT = 10;
    uint256 constant internal MAX_TIME = 30 minutes;
    uint256 constant internal NEVER = uint256(-1);
    uint256 constant internal INITIAL_INCENTIVE = 0.0435 ether;
    address payable constant internal _designers = 0xBea62796855548154464F6C8E7BC92672C9F87b8; // address has no power

    // =========
    // VARIABLES
    // =========

    uint256 public endTime; // UNIX timestamp of when the current game ends
    uint256 public escrow; // the amount of ETH (in wei) currently held in the game's escrow
    uint256 public currentGameNumber;
    uint256 public currentDepositNumber;
    address payable[2] public topDepositors; // stores the addresses of the 2 most recent depositors
    // NOTE: topDepositors[0] is the most recent depositor
    mapping (uint256 => uint256) public requiredDeposit; // maps `n` to the required deposit size (in wei) required for the `n`th play
    // NOTE: in practice, the parameter `n` should never exceed 200, since by then the required deposit would be more ETH than exists.
    uint256[] internal _startBlocks; // for front-end use only: game number i starts at _startBlocks[i]
    bool internal _gameStarted;

    // ============
    // CUSTOM TYPES
    // ============

    // the states of the state machine
    enum GameState {NEEDS_DONATION, READY_FOR_FIRST_PLAY, IN_PROGRESS, GAME_OVER}

    // =========
    // MODIFIERS
    // =========

    // prevents transactions meant for one game from being used in a subsequent game
    modifier currentGame(uint256 gameNumber) {
        require(gameNumber == currentGameNumber, "Wrong game number.");
        _;
    }

    // ensures that the deposit provided is exactly the amount of the current required deposit
    modifier exactDeposit() {
        require(
            msg.value == requiredDeposit[currentDepositNumber],
            "Incorrect deposit amount. Perhaps another player got their txn mined before you. Try again."
        );
        _;
    }

    // ========================
    // FALLBACK AND CONSTRUCTOR
    // ========================

    // SECURITY: Fallback must NOT be payable
    // This prevents players from losing money if they attempt to play by sending money directly to
    // this contract instead of calling one of the play functions
    // Any funds sent to this contract via self-destruct will be applied to the INITIAL_INCENTIVE of subsequent games
    function() external { }

    constructor() public {
        endTime = NEVER;
        currentGameNumber = 1;
        currentDepositNumber = 1;
        _startBlocks.push(0);

        // Here we are precomputing and storing values that we will use later
        // These values will be needed during every play of the game and they are expensive to compute
        // So we compute them up front and store them to save the players gas later
        // These are the required sizes (in wei) of the `depositNumber`th deposit
        // This computes and stores `INITIAL_INCENTIVE * (100 + DEPOSIT_INCREMENT_PERCENT / 100) ^ n`,
        // rounded to 4 (ETH) decimal places, for n=0 to 200.  It must be done using a loop because solidity 0.5 does not
        // support raising fractions to integer powers.
        // SECURITY: The argument `depositNumber` will never be larger than 200 (since then the
        // required deposit would be far more ETH than exists).
        // SECURITY: SafeMath not used here for gas efficiency reasons.
        // Since `depositNumber` will never be > 200 and since `INITIAL_INCENTIVE` and
        // `DEPOSIT_INCREMENT_PERCENT` are small and constant, there is no risk of overflow here.
        uint256 value = INITIAL_INCENTIVE;
        uint256 r = DEPOSIT_INCREMENT_PERCENT;
        requiredDeposit[0] = INITIAL_INCENTIVE;
        for (uint256 i = 1; i <= 200; i++) { // `depositNumber` will never exceed 200
            value += value * r / 100;
            requiredDeposit[i] = value / 1e14 * 1e14; // round output to 4 (ETH) decimal places
        }
        // SECURITY: No entries in the requiredDeposit mapping should ever change again
        // SECURITY: After the constructor runs, requiredDeposit should be read-only
    }

    // ============================
    // PRIVATE / INTERNAL FUNCTIONS
    // ============================

    // @notice Transfers ETH to an address without any possibility of failing
    // @param payee The address to which the ETH will be sent
    // @param amount The amount of ETH (in wei) to be sent
    // @return address The address of the SelfDestructingSender contract that delivered the funds
    // @dev This allows us to use a push-payments pattern with no security risk
    // For most applications the gas cost is too high to do this, but for this game
    // the winnings on every deposit (other than the one losing deposit) far exceed the
    // gas costs of this transfer method when players use reasonable gas prices -- for example, under 40 gwei for `firstPlay`
    // @dev NOTE the following security concerns:
    // SECURITY: MUST BE PRIVATE OR INTERNAL!
    // SECURITY: THE PLAYERS MUST BE ABLE TO VERIFY SelfDestructingSender CONTRACT CODE!
    function _forceTransfer(address payable payee, uint256 amount) internal returns (address) {
        return address((new SelfDestructingSender).value(amount)(payee));
    }

    // @notice Computes the current game state
    // @return The current game state
    function _gameState() private view returns (GameState) {
        if (!_gameStarted) {
            // then the game state is either NEEDS_DONATION or READY_FOR_FIRST_PLAY
            if (escrow < INITIAL_INCENTIVE) {
                return GameState.NEEDS_DONATION;
            } else {
                return GameState.READY_FOR_FIRST_PLAY;
            }
        } else {
            // then the game state is either IN_PROGRESS or GAME_OVER
            if (now >= endTime) {
                return GameState.GAME_OVER;
            } else {
                return GameState.IN_PROGRESS;
            }
        }
    }

    // =============================================
    // EXTERNAL FUNCTIONS THAT MODIFY CONTRACT STATE
    // =============================================

    // @notice This is a function used to donate money that will be used to incentivize the first player to play
    // Anyone can donate money, though in practice only the `_designers` likely will since nobody directly benefits from it
    // Donations can be accepted only when the game is in the NEEDS_DONATION state
    // Donations are added to escrow until escrow == INITIAL_INCENTIVE
    // Any remaining donations are kept in address(this).balance and are used to seed future games
    // SECURITY: Can be called only when the game state is NEEDS_DONATION
    function donate() external payable {
        require(_gameState() == GameState.NEEDS_DONATION, "No donations needed.");
        // NOTE: if the game is in the NEEDS_DONATION state then escrow < INITIAL_INCENTIVE
        uint256 maxAmountToPutInEscrow = INITIAL_INCENTIVE.sub(escrow);
        if (msg.value > maxAmountToPutInEscrow) {
            escrow = escrow.add(maxAmountToPutInEscrow);
        } else {
            escrow = escrow.add(msg.value);
        }
    }

    // @notice Used to make the first play of a game
    // @param gameNumber The current gameNumber
    // SECURITY: Can be called only when the game state is READY_FOR_FIRST_PLAY
    // SECURITY: Function call can be front-run. That is acceptable and may be part of game dynamics during competitive play.
    function firstPlay(uint256 gameNumber) external payable currentGame(gameNumber) exactDeposit {
        require(_gameState() == GameState.READY_FOR_FIRST_PLAY, "Game not ready for first play.");

        emit Action(currentGameNumber, currentDepositNumber, msg.sender, block.number, now, address(0), false);

        topDepositors[0] = msg.sender;
        endTime = now.add(MAX_TIME);
        escrow = escrow.add(msg.value);
        currentDepositNumber++;
        _gameStarted = true;
        _startBlocks.push(block.number);
    }

    // @notice Used to make any subsequent play of the game
    // @param gameNumber The current gameNumber
    // SECURITY: Can be called only when the game state is IN_PROGRESS
    // SECURITY: Function call can be front-run. That is acceptable and may be part of game dynamics during competitive play.
    function play(uint256 gameNumber) external payable currentGame(gameNumber) exactDeposit {
        require(_gameState() == GameState.IN_PROGRESS, "Game is not in progress.");

        // We pay out the person who will no longer be the second-largest depositor
        address payable addressToPay = topDepositors[1];
        // They will receive their original deposit back plus SMALL_REWARD_PERCENT percent more
        // NOTE: The first time the `play` function is called `currentDepositNumber` is at least 2, so
        // the subtraction here will never cause a revert
        uint256 amountToPay = requiredDeposit[currentDepositNumber.sub(2)].mul(SMALL_REWARD_PERCENT.add(100)).div(100);

        address payoutProof = address(0);
        if (addressToPay != address(0)) { // we never send money to the zero address
            payoutProof = _forceTransfer(addressToPay, amountToPay);
        }

        // tell the front end what happened
        emit Action(currentGameNumber, currentDepositNumber, msg.sender, block.number, now, payoutProof, false);

        // store the new top depositors
        topDepositors[1] = topDepositors[0];
        topDepositors[0] = msg.sender;
        // reset the clock
        endTime = now.add(MAX_TIME);
        // track the changes to escrow
        // NOTE: even if addressToPay is address(0) we still reduce escrow by amountToPay
        // Any money that would have gone to address(0) is is later put towards the INITIAL_INCENTIVE
        // for the next game (see the end of the `reset` function)
        escrow = escrow.sub(amountToPay).add(msg.value);
        currentDepositNumber++;
    }

    // @notice Used to pay out the final depositor of a game and reset variables for the next game
    // SECURITY: Can be called only when the game state is GAME_OVER
    function reset() external {
        require(_gameState() == GameState.GAME_OVER, "Game is not over.");
        // We pay out the largest depositor
        address payable addressToPay = topDepositors[0];

        // They will receive their original deposit back plus BIG_REWARD_PERCENT percent more
        uint256 amountToPay = requiredDeposit[currentDepositNumber.sub(1)].mul(BIG_REWARD_PERCENT.add(100)).div(100);
        address payoutProof = _forceTransfer(addressToPay, amountToPay);

        // track the payout in escrow
        escrow = escrow.sub(amountToPay);

        // tell the front end what happened
        emit Action(currentGameNumber, currentDepositNumber, address(0), block.number, now, payoutProof, true);

        // if there is anything left in escrow, give it to the _designers as a reward for maintaining the game
        if (escrow > 0) {
            _forceTransfer(_designers, escrow);
        }

        // reset the game vars for the next game
        endTime = NEVER;
        escrow = 0;
        currentGameNumber++;
        currentDepositNumber = 1;
        _gameStarted = false;
        topDepositors[0] = address(0);
        topDepositors[1] = address(0);

        // apply anything left over in address(this).balance to the next game's escrow
        // being sure not to exceed INITIAL_INCENTIVE
        if (address(this).balance > INITIAL_INCENTIVE) {
            escrow = INITIAL_INCENTIVE;
        } else {
            escrow = address(this).balance;
        }
    }

    // =======================
    // EXTERNAL VIEW FUNCTIONS
    // =======================

    // @notice Returns the required deposit size (in wei) required for the next deposit of the game
    function currentRequiredDeposit() external view returns (uint256) {
        return requiredDeposit[currentDepositNumber];
    }

    // @notice Returns the current state of the game
    function gameState() external view returns (GameState) {
        return _gameState();
    }

    // @notice returns the block at which game number `index` began, or 0 if referring to
    // a game that has not yet started
    function startBlocks(uint256 index) external view returns (uint256) {
        if (index >= _startBlocks.length) {
            return 0; // the front-end will handle this properly
        } else {
            return _startBlocks[index];
        }
    }
}




/**
 * @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;
    }
}
设置
{
  "compilationTarget": {
    "Game.sol": "Game"
  },
  "evmVersion": "istanbul",
  "libraries": {},
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"gameNumber","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"depositNumber","type":"uint256"},{"indexed":true,"internalType":"address","name":"depositorAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"block","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"},{"indexed":false,"internalType":"address","name":"payoutProof","type":"address"},{"indexed":false,"internalType":"bool","name":"gameOver","type":"bool"}],"name":"Action","type":"event"},{"payable":false,"stateMutability":"nonpayable","type":"fallback"},{"constant":true,"inputs":[],"name":"currentDepositNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"currentGameNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"currentRequiredDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"donate","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"endTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"escrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"gameNumber","type":"uint256"}],"name":"firstPlay","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"gameState","outputs":[{"internalType":"enum Game.GameState","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"gameNumber","type":"uint256"}],"name":"play","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"requiredDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"reset","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"startBlocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"topDepositors","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}]