ETH Price: $2,440.84 (+0.27%)

Transaction Decoder

Block:
6575864 at Oct-24-2018 04:54:52 PM +UTC
Transaction Fee:
0.00064728 ETH $1.58
Gas Used:
80,910 Gas / 8 Gwei

Emitted Events:

43 GameChannel.LogGameCreated( user=[Sender] 0x89a548195582228eb1267908d7758b507b1ab847, gameId=1743, stake=10000000000000000, serverEndHash=6E62867B40952DB6CE95F8FE5118C2683D5A2BE281A6CFEF75DC18F853F8F6D7, userEndHash=AF6894EE48D86460B06DA82F55C4D7ED2E331BABF0EDE266E1867874EA1FF52E )

Account State Difference:

  Address   Before After State Difference Code
(Nanopool)
8,374.393331579298685347 Eth8,374.393978859298685347 Eth0.00064728
0x89a54819...07b1ab847
0.012996908318332754 Eth
Nonce: 57
0.002349628318332754 Eth
Nonce: 58
0.01064728
0xEB6F4eC3...3e271dF3b 17.734997815 Eth17.744997815 Eth0.01

Execution Trace

ETH 0.01 GameChannel.createGame( _userEndHash=AF6894EE48D86460B06DA82F55C4D7ED2E331BABF0EDE266E1867874EA1FF52E, _previousGameId=1687, _createBefore=1540421687, _serverEndHash=6E62867B40952DB6CE95F8FE5118C2683D5A2BE281A6CFEF75DC18F853F8F6D7, _serverSig=0xB2CD2DD7D69187F132D40C9D77DBADE8CDF9B441A3999BC4409049F979EAE70E0F28B6683C7EFA19018AD7B562985C1477EEB7A9184C7C15288BCE7F635011E51B )
  • ConflictResolution.minHouseStake( activeGames=2 ) => ( 15000000000000000000 )
  • Null: 0x000...001.2ad38e3f( )
    File 1 of 2: GameChannel
    pragma solidity ^0.4.24;
    
    interface ConflictResolutionInterface {
        function minHouseStake(uint activeGames) external pure returns(uint);
    
        function maxBalance() external pure returns(int);
    
        function conflictEndFine() external pure returns(int);
    
        function isValidBet(uint8 _gameType, uint _betNum, uint _betValue) external pure returns(bool);
    
        function endGameConflict(
            uint8 _gameType,
            uint _betNum,
            uint _betValue,
            int _balance,
            uint _stake,
            bytes32 _serverSeed,
            bytes32 _userSeed
        )
            external
            view
            returns(int);
    
        function serverForceGameEnd(
            uint8 gameType,
            uint _betNum,
            uint _betValue,
            int _balance,
            uint _stake,
            uint _endInitiatedTime
        )
            external
            view
            returns(int);
    
        function userForceGameEnd(
            uint8 _gameType,
            uint _betNum,
            uint _betValue,
            int _balance,
            uint _stake,
            uint _endInitiatedTime
        )
            external
            view
            returns(int);
    }
    
    library MathUtil {
        /**
         * @dev Returns the absolute value of _val.
         * @param _val value
         * @return The absolute value of _val.
         */
        function abs(int _val) internal pure returns(uint) {
            if (_val < 0) {
                return uint(-_val);
            } else {
                return uint(_val);
            }
        }
    
        /**
         * @dev Calculate maximum.
         */
        function max(uint _val1, uint _val2) internal pure returns(uint) {
            return _val1 >= _val2 ? _val1 : _val2;
        }
    
        /**
         * @dev Calculate minimum.
         */
        function min(uint _val1, uint _val2) internal pure returns(uint) {
            return _val1 <= _val2 ? _val1 : _val2;
        }
    }
    
    contract Ownable {
        address public owner;
        address public pendingOwner;
    
        event LogOwnerShipTransferred(address indexed previousOwner, address indexed newOwner);
        event LogOwnerShipTransferInitiated(address indexed previousOwner, address indexed newOwner);
    
        /**
         * @dev Modifier, which throws if called by other account than owner.
         */
        modifier onlyOwner {
            require(msg.sender == owner);
            _;
        }
    
        /**
         * @dev Modifier throws if called by any account other than the pendingOwner.
         */
        modifier onlyPendingOwner() {
            require(msg.sender == pendingOwner);
            _;
        }
    
        /**
         * @dev Set contract creator as initial owner
         */
        constructor() public {
            owner = msg.sender;
            pendingOwner = address(0);
        }
    
        /**
         * @dev Allows the current owner to set the pendingOwner address.
         * @param _newOwner The address to transfer ownership to.
         */
        function transferOwnership(address _newOwner) public onlyOwner {
            pendingOwner = _newOwner;
            emit LogOwnerShipTransferInitiated(owner, _newOwner);
        }
    
        /**
         * @dev PendingOwner can accept ownership.
         */
        function claimOwnership() public onlyPendingOwner {
            owner = pendingOwner;
            pendingOwner = address(0);
            emit LogOwnerShipTransferred(owner, pendingOwner);
        }
    }
    
    contract Activatable is Ownable {
        bool public activated = false;
    
        /// @dev Event is fired if activated.
        event LogActive();
    
        /// @dev Modifier, which only allows function execution if activated.
        modifier onlyActivated() {
            require(activated);
            _;
        }
    
        /// @dev Modifier, which only allows function execution if not activated.
        modifier onlyNotActivated() {
            require(!activated);
            _;
        }
    
        /// @dev activate contract, can be only called once by the contract owner.
        function activate() public onlyOwner onlyNotActivated {
            activated = true;
            emit LogActive();
        }
    }
    
    contract ConflictResolutionManager is Ownable {
        /// @dev Conflict resolution contract.
        ConflictResolutionInterface public conflictRes;
    
        /// @dev New Conflict resolution contract.
        address public newConflictRes = 0;
    
        /// @dev Time update of new conflict resolution contract was initiated.
        uint public updateTime = 0;
    
        /// @dev Min time before new conflict res contract can be activated after initiating update.
        uint public constant MIN_TIMEOUT = 3 days;
    
        /// @dev Min time before new conflict res contract can be activated after initiating update.
        uint public constant MAX_TIMEOUT = 6 days;
    
        /// @dev Update of conflict resolution contract was initiated.
        event LogUpdatingConflictResolution(address newConflictResolutionAddress);
    
        /// @dev New conflict resolution contract is active.
        event LogUpdatedConflictResolution(address newConflictResolutionAddress);
    
        /**
         * @dev Constructor
         * @param _conflictResAddress conflict resolution contract address.
         */
        constructor(address _conflictResAddress) public {
            conflictRes = ConflictResolutionInterface(_conflictResAddress);
        }
    
        /**
         * @dev Initiate conflict resolution contract update.
         * @param _newConflictResAddress New conflict resolution contract address.
         */
        function updateConflictResolution(address _newConflictResAddress) public onlyOwner {
            newConflictRes = _newConflictResAddress;
            updateTime = block.timestamp;
    
            emit LogUpdatingConflictResolution(_newConflictResAddress);
        }
    
        /**
         * @dev Active new conflict resolution contract.
         */
        function activateConflictResolution() public onlyOwner {
            require(newConflictRes != 0);
            require(updateTime != 0);
            require(updateTime + MIN_TIMEOUT <= block.timestamp && block.timestamp <= updateTime + MAX_TIMEOUT);
    
            conflictRes = ConflictResolutionInterface(newConflictRes);
            newConflictRes = 0;
            updateTime = 0;
    
            emit LogUpdatedConflictResolution(newConflictRes);
        }
    }
    
    contract Pausable is Activatable {
        using SafeMath for uint;
    
        /// @dev Is contract paused. Initial it is paused.
        bool public paused = true;
    
        /// @dev Time pause was called
        uint public timePaused = block.timestamp;
    
        /// @dev Modifier, which only allows function execution if not paused.
        modifier onlyNotPaused() {
            require(!paused, "paused");
            _;
        }
    
        /// @dev Modifier, which only allows function execution if paused.
        modifier onlyPaused() {
            require(paused);
            _;
        }
    
        /// @dev Modifier, which only allows function execution if paused longer than timeSpan.
        modifier onlyPausedSince(uint timeSpan) {
            require(paused && (timePaused.add(timeSpan) <= block.timestamp));
            _;
        }
    
        /// @dev Event is fired if paused.
        event LogPause();
    
        /// @dev Event is fired if pause is ended.
        event LogUnpause();
    
        /**
         * @dev Pause contract. No new game sessions can be created.
         */
        function pause() public onlyOwner onlyNotPaused {
            paused = true;
            timePaused = block.timestamp;
            emit LogPause();
        }
    
        /**
         * @dev Unpause contract. Initial contract is paused and can only be unpaused after activating it.
         */
        function unpause() public onlyOwner onlyPaused onlyActivated {
            paused = false;
            timePaused = 0;
            emit LogUnpause();
        }
    }
    
    contract Destroyable is Pausable {
        /// @dev After pausing the contract for 20 days owner can selfdestruct it.
        uint public constant TIMEOUT_DESTROY = 20 days;
    
        /**
         * @dev Destroy contract and transfer ether to owner.
         */
        function destroy() public onlyOwner onlyPausedSince(TIMEOUT_DESTROY) {
            selfdestruct(owner);
        }
    }
    
    contract GameChannelBase is Destroyable, ConflictResolutionManager {
        using SafeCast for int;
        using SafeCast for uint;
        using SafeMath for int;
        using SafeMath for uint;
    
    
        /// @dev Different game session states.
        enum GameStatus {
            ENDED, ///< @dev Game session is ended.
            ACTIVE, ///< @dev Game session is active.
            USER_INITIATED_END, ///< @dev User initiated non regular end.
            SERVER_INITIATED_END ///< @dev Server initiated non regular end.
        }
    
        /// @dev Reason game session ended.
        enum ReasonEnded {
            REGULAR_ENDED, ///< @dev Game session is regularly ended.
            SERVER_FORCED_END, ///< @dev User did not respond. Server forced end.
            USER_FORCED_END, ///< @dev Server did not respond. User forced end.
            CONFLICT_ENDED ///< @dev Server or user raised conflict ans pushed game state, opponent pushed same game state.
        }
    
        struct Game {
            /// @dev Game session status.
            GameStatus status;
    
            /// @dev User's stake.
            uint128 stake;
    
            /// @dev Last game round info if not regularly ended.
            /// If game session is ended normally this data is not used.
            uint8 gameType;
            uint32 roundId;
            uint betNum;
            uint betValue;
            int balance;
            bytes32 userSeed;
            bytes32 serverSeed;
            uint endInitiatedTime;
        }
    
        /// @dev Minimal time span between profit transfer.
        uint public constant MIN_TRANSFER_TIMESPAN = 1 days;
    
        /// @dev Maximal time span between profit transfer.
        uint public constant MAX_TRANSFER_TIMSPAN = 6 * 30 days;
    
        bytes32 public constant TYPE_HASH = keccak256(abi.encodePacked(
            "uint32 Round Id",
            "uint8 Game Type",
            "uint16 Number",
            "uint Value (Wei)",
            "int Current Balance (Wei)",
            "bytes32 Server Hash",
            "bytes32 Player Hash",
            "uint Game Id",
            "address Contract Address"
         ));
    
        /// @dev Current active game sessions.
        uint public activeGames = 0;
    
        /// @dev Game session id counter. Points to next free game session slot. So gameIdCntr -1 is the
        // number of game sessions created.
        uint public gameIdCntr = 1;
    
        /// @dev Only this address can accept and end games.
        address public serverAddress;
    
        /// @dev Address to transfer profit to.
        address public houseAddress;
    
        /// @dev Current house stake.
        uint public houseStake = 0;
    
        /// @dev House profit since last profit transfer.
        int public houseProfit = 0;
    
        /// @dev Min value user needs to deposit for creating game session.
        uint128 public minStake;
    
        /// @dev Max value user can deposit for creating game session.
        uint128 public maxStake;
    
        /// @dev Timeout until next profit transfer is allowed.
        uint public profitTransferTimeSpan = 14 days;
    
        /// @dev Last time profit transferred to house.
        uint public lastProfitTransferTimestamp;
    
        /// @dev Maps gameId to game struct.
        mapping (uint => Game) public gameIdGame;
    
        /// @dev Maps user address to current user game id.
        mapping (address => uint) public userGameId;
    
        /// @dev Maps user address to pending returns.
        mapping (address => uint) public pendingReturns;
    
        /// @dev Modifier, which only allows to execute if house stake is high enough.
        modifier onlyValidHouseStake(uint _activeGames) {
            uint minHouseStake = conflictRes.minHouseStake(_activeGames);
            require(houseStake >= minHouseStake, "inv houseStake");
            _;
        }
    
        /// @dev Modifier to check if value send fulfills user stake requirements.
        modifier onlyValidValue() {
            require(minStake <= msg.value && msg.value <= maxStake, "inv stake");
            _;
        }
    
        /// @dev Modifier, which only allows server to call function.
        modifier onlyServer() {
            require(msg.sender == serverAddress);
            _;
        }
    
        /// @dev Modifier, which only allows to set valid transfer timeouts.
        modifier onlyValidTransferTimeSpan(uint transferTimeout) {
            require(transferTimeout >= MIN_TRANSFER_TIMESPAN
                    && transferTimeout <= MAX_TRANSFER_TIMSPAN);
            _;
        }
    
        /// @dev This event is fired when user creates game session.
        event LogGameCreated(address indexed user, uint indexed gameId, uint128 stake, bytes32 indexed serverEndHash, bytes32 userEndHash);
    
        /// @dev This event is fired when user requests conflict end.
        event LogUserRequestedEnd(address indexed user, uint indexed gameId);
    
        /// @dev This event is fired when server requests conflict end.
        event LogServerRequestedEnd(address indexed user, uint indexed gameId);
    
        /// @dev This event is fired when game session is ended.
        event LogGameEnded(address indexed user, uint indexed gameId, uint32 roundId, int balance, ReasonEnded reason);
    
        /// @dev this event is fired when owner modifies user's stake limits.
        event LogStakeLimitsModified(uint minStake, uint maxStake);
    
        /**
         * @dev Contract constructor.
         * @param _serverAddress Server address.
         * @param _minStake Min value user needs to deposit to create game session.
         * @param _maxStake Max value user can deposit to create game session.
         * @param _conflictResAddress Conflict resolution contract address.
         * @param _houseAddress House address to move profit to.
         * @param _chainId Chain id for signature domain.
         */
        constructor(
            address _serverAddress,
            uint128 _minStake,
            uint128 _maxStake,
            address _conflictResAddress,
            address _houseAddress,
            uint _chainId
        )
            public
            ConflictResolutionManager(_conflictResAddress)
        {
            require(_minStake > 0 && _minStake <= _maxStake);
    
            serverAddress = _serverAddress;
            houseAddress = _houseAddress;
            lastProfitTransferTimestamp = block.timestamp;
            minStake = _minStake;
            maxStake = _maxStake;
        }
    
        /**
         * @dev Set gameIdCntr. Can be only set before activating contract.
         */
        function setGameIdCntr(uint _gameIdCntr) public onlyOwner onlyNotActivated {
            require(gameIdCntr > 0);
            gameIdCntr = _gameIdCntr;
        }
    
        /**
         * @notice Withdraw pending returns.
         */
        function withdraw() public {
            uint toTransfer = pendingReturns[msg.sender];
            require(toTransfer > 0);
    
            pendingReturns[msg.sender] = 0;
            msg.sender.transfer(toTransfer);
        }
    
        /**
         * @notice Transfer house profit to houseAddress.
         */
        function transferProfitToHouse() public {
            require(lastProfitTransferTimestamp.add(profitTransferTimeSpan) <= block.timestamp);
    
            // update last transfer timestamp
            lastProfitTransferTimestamp = block.timestamp;
    
            if (houseProfit <= 0) {
                // no profit to transfer
                return;
            }
    
            uint toTransfer = houseProfit.castToUint();
    
            houseProfit = 0;
            houseStake = houseStake.sub(toTransfer);
    
            houseAddress.transfer(toTransfer);
        }
    
        /**
         * @dev Set profit transfer time span.
         */
        function setProfitTransferTimeSpan(uint _profitTransferTimeSpan)
            public
            onlyOwner
            onlyValidTransferTimeSpan(_profitTransferTimeSpan)
        {
            profitTransferTimeSpan = _profitTransferTimeSpan;
        }
    
        /**
         * @dev Increase house stake by msg.value
         */
        function addHouseStake() public payable onlyOwner {
            houseStake = houseStake.add(msg.value);
        }
    
        /**
         * @dev Withdraw house stake.
         */
        function withdrawHouseStake(uint value) public onlyOwner {
            uint minHouseStake = conflictRes.minHouseStake(activeGames);
    
            require(value <= houseStake && houseStake.sub(value) >= minHouseStake);
            require(houseProfit <= 0 || houseProfit.castToUint() <= houseStake.sub(value));
    
            houseStake = houseStake.sub(value);
            owner.transfer(value);
        }
    
        /**
         * @dev Withdraw house stake and profit.
         */
        function withdrawAll() public onlyOwner onlyPausedSince(3 days) {
            houseProfit = 0;
            uint toTransfer = houseStake;
            houseStake = 0;
            owner.transfer(toTransfer);
        }
    
        /**
         * @dev Set new house address.
         * @param _houseAddress New house address.
         */
        function setHouseAddress(address _houseAddress) public onlyOwner {
            houseAddress = _houseAddress;
        }
    
        /**
         * @dev Set stake min and max value.
         * @param _minStake Min stake.
         * @param _maxStake Max stake.
         */
        function setStakeRequirements(uint128 _minStake, uint128 _maxStake) public onlyOwner {
            require(_minStake > 0 && _minStake <= _maxStake);
            minStake = _minStake;
            maxStake = _maxStake;
            emit LogStakeLimitsModified(minStake, maxStake);
        }
    
        /**
         * @dev Close game session.
         * @param _game Game session data.
         * @param _gameId Id of game session.
         * @param _userAddress User's address of game session.
         * @param _reason Reason for closing game session.
         * @param _balance Game session balance.
         */
        function closeGame(
            Game storage _game,
            uint _gameId,
            uint32 _roundId,
            address _userAddress,
            ReasonEnded _reason,
            int _balance
        )
            internal
        {
            _game.status = GameStatus.ENDED;
    
            activeGames = activeGames.sub(1);
    
            payOut(_userAddress, _game.stake, _balance);
    
            emit LogGameEnded(_userAddress, _gameId, _roundId, _balance, _reason);
        }
    
        /**
         * @dev End game by paying out user and server.
         * @param _userAddress User's address.
         * @param _stake User's stake.
         * @param _balance User's balance.
         */
        function payOut(address _userAddress, uint128 _stake, int _balance) internal {
            int stakeInt = _stake;
            int houseStakeInt = houseStake.castToInt();
    
            assert(_balance <= conflictRes.maxBalance());
            assert((stakeInt.add(_balance)) >= 0);
    
            if (_balance > 0 && houseStakeInt < _balance) {
                // Should never happen!
                // House is bankrupt.
                // Payout left money.
                _balance = houseStakeInt;
            }
    
            houseProfit = houseProfit.sub(_balance);
    
            int newHouseStake = houseStakeInt.sub(_balance);
            houseStake = newHouseStake.castToUint();
    
            uint valueUser = stakeInt.add(_balance).castToUint();
            pendingReturns[_userAddress] += valueUser;
            if (pendingReturns[_userAddress] > 0) {
                safeSend(_userAddress);
            }
        }
    
        /**
         * @dev Send value of pendingReturns[_address] to _address.
         * @param _address Address to send value to.
         */
        function safeSend(address _address) internal {
            uint valueToSend = pendingReturns[_address];
            assert(valueToSend > 0);
    
            pendingReturns[_address] = 0;
            if (_address.send(valueToSend) == false) {
                pendingReturns[_address] = valueToSend;
            }
        }
    
        /**
         * @dev Verify signature of given data. Throws on verification failure.
         * @param _sig Signature of given data in the form of rsv.
         * @param _address Address of signature signer.
         */
        function verifySig(
            uint32 _roundId,
            uint8 _gameType,
            uint _num,
            uint _value,
            int _balance,
            bytes32 _serverHash,
            bytes32 _userHash,
            uint _gameId,
            address _contractAddress,
            bytes _sig,
            address _address
        )
            internal
            view
        {
            // check if this is the correct contract
            address contractAddress = this;
            require(_contractAddress == contractAddress, "inv contractAddress");
    
            bytes32 roundHash = calcHash(
                    _roundId,
                    _gameType,
                    _num,
                    _value,
                    _balance,
                    _serverHash,
                    _userHash,
                    _gameId
            );
    
            verify(
                    roundHash,
                    _sig,
                    _address
            );
        }
    
         /**
         * @dev Check if _sig is valid signature of _hash. Throws if invalid signature.
         * @param _hash Hash to check signature of.
         * @param _sig Signature of _hash.
         * @param _address Address of signer.
         */
        function verify(
            bytes32 _hash,
            bytes _sig,
            address _address
        )
            internal
            pure
        {
            (bytes32 r, bytes32 s, uint8 v) = signatureSplit(_sig);
            address addressRecover = ecrecover(_hash, v, r, s);
            require(addressRecover == _address, "inv sig");
        }
    
        /**
         * @dev Calculate typed hash of given data (compare eth_signTypedData).
         * @return Hash of given data.
         */
        function calcHash(
            uint32 _roundId,
            uint8 _gameType,
            uint _num,
            uint _value,
            int _balance,
            bytes32 _serverHash,
            bytes32 _userHash,
            uint _gameId
        )
            private
            view
            returns(bytes32)
        {
            bytes32 betHash = keccak256(abi.encodePacked(
                _roundId,
                _gameType,
                uint16(_num),
                _value,
                _balance,
                _serverHash,
                _userHash,
                _gameId,
                address(this)
            ));
    
            return keccak256(abi.encodePacked(
                TYPE_HASH,
                betHash
            ));
        }
    
        /**
         * @dev Split the given signature of the form rsv in r s v. v is incremented with 27 if
         * it is below 2.
         * @param _signature Signature to split.
         * @return r s v
         */
        function signatureSplit(bytes _signature)
            private
            pure
            returns (bytes32 r, bytes32 s, uint8 v)
        {
            require(_signature.length == 65, "inv sig");
    
            assembly {
                r := mload(add(_signature, 32))
                s := mload(add(_signature, 64))
                v := and(mload(add(_signature, 65)), 0xff)
            }
            if (v < 2) {
                v = v + 27;
            }
        }
    }
    
    contract GameChannelConflict is GameChannelBase {
        using SafeCast for int;
        using SafeCast for uint;
        using SafeMath for int;
        using SafeMath for uint;
    
        /**
         * @dev Contract constructor.
         * @param _serverAddress Server address.
         * @param _minStake Min value user needs to deposit to create game session.
         * @param _maxStake Max value user can deposit to create game session.
         * @param _conflictResAddress Conflict resolution contract address
         * @param _houseAddress House address to move profit to
         * @param _chainId Chain id for signature domain.
         */
        constructor(
            address _serverAddress,
            uint128 _minStake,
            uint128 _maxStake,
            address _conflictResAddress,
            address _houseAddress,
            uint _chainId
        )
            public
            GameChannelBase(_serverAddress, _minStake, _maxStake, _conflictResAddress, _houseAddress, _chainId)
        {
            // nothing to do
        }
    
        /**
         * @dev Used by server if user does not end game session.
         * @param _roundId Round id of bet.
         * @param _gameType Game type of bet.
         * @param _num Number of bet.
         * @param _value Value of bet.
         * @param _balance Balance before this bet.
         * @param _serverHash Hash of server seed for this bet.
         * @param _userHash Hash of user seed for this bet.
         * @param _gameId Game session id.
         * @param _contractAddress Address of this contract.
         * @param _userSig User signature of this bet.
         * @param _userAddress Address of user.
         * @param _serverSeed Server seed for this bet.
         * @param _userSeed User seed for this bet.
         */
        function serverEndGameConflict(
            uint32 _roundId,
            uint8 _gameType,
            uint _num,
            uint _value,
            int _balance,
            bytes32 _serverHash,
            bytes32 _userHash,
            uint _gameId,
            address _contractAddress,
            bytes _userSig,
            address _userAddress,
            bytes32 _serverSeed,
            bytes32 _userSeed
        )
            public
            onlyServer
        {
            verifySig(
                    _roundId,
                    _gameType,
                    _num,
                    _value,
                    _balance,
                    _serverHash,
                    _userHash,
                    _gameId,
                    _contractAddress,
                    _userSig,
                    _userAddress
            );
    
            serverEndGameConflictImpl(
                    _roundId,
                    _gameType,
                    _num,
                    _value,
                    _balance,
                    _serverHash,
                    _userHash,
                    _serverSeed,
                    _userSeed,
                    _gameId,
                    _userAddress
            );
        }
    
        /**
         * @notice Can be used by user if server does not answer to the end game session request.
         * @param _roundId Round id of bet.
         * @param _gameType Game type of bet.
         * @param _num Number of bet.
         * @param _value Value of bet.
         * @param _balance Balance before this bet.
         * @param _serverHash Hash of server seed for this bet.
         * @param _userHash Hash of user seed for this bet.
         * @param _gameId Game session id.
         * @param _contractAddress Address of this contract.
         * @param _serverSig Server signature of this bet.
         * @param _userSeed User seed for this bet.
         */
        function userEndGameConflict(
            uint32 _roundId,
            uint8 _gameType,
            uint _num,
            uint _value,
            int _balance,
            bytes32 _serverHash,
            bytes32 _userHash,
            uint _gameId,
            address _contractAddress,
            bytes _serverSig,
            bytes32 _userSeed
        )
            public
        {
            verifySig(
                _roundId,
                _gameType,
                _num,
                _value,
                _balance,
                _serverHash,
                _userHash,
                _gameId,
                _contractAddress,
                _serverSig,
                serverAddress
            );
    
            userEndGameConflictImpl(
                _roundId,
                _gameType,
                _num,
                _value,
                _balance,
                _userHash,
                _userSeed,
                _gameId,
                msg.sender
            );
        }
    
        /**
         * @notice Cancel active game without playing. Useful if server stops responding before
         * one game is played.
         * @param _gameId Game session id.
         */
        function userCancelActiveGame(uint _gameId) public {
            address userAddress = msg.sender;
            uint gameId = userGameId[userAddress];
            Game storage game = gameIdGame[gameId];
    
            require(gameId == _gameId, "inv gameId");
    
            if (game.status == GameStatus.ACTIVE) {
                game.endInitiatedTime = block.timestamp;
                game.status = GameStatus.USER_INITIATED_END;
    
                emit LogUserRequestedEnd(msg.sender, gameId);
            } else if (game.status == GameStatus.SERVER_INITIATED_END && game.roundId == 0) {
                cancelActiveGame(game, gameId, userAddress);
            } else {
                revert();
            }
        }
    
        /**
         * @dev Cancel active game without playing. Useful if user starts game session and
         * does not play.
         * @param _userAddress Users' address.
         * @param _gameId Game session id.
         */
        function serverCancelActiveGame(address _userAddress, uint _gameId) public onlyServer {
            uint gameId = userGameId[_userAddress];
            Game storage game = gameIdGame[gameId];
    
            require(gameId == _gameId, "inv gameId");
    
            if (game.status == GameStatus.ACTIVE) {
                game.endInitiatedTime = block.timestamp;
                game.status = GameStatus.SERVER_INITIATED_END;
    
                emit LogServerRequestedEnd(msg.sender, gameId);
            } else if (game.status == GameStatus.USER_INITIATED_END && game.roundId == 0) {
                cancelActiveGame(game, gameId, _userAddress);
            } else {
                revert();
            }
        }
    
        /**
        * @dev Force end of game if user does not respond. Only possible after a certain period of time
        * to give the user a chance to respond.
        * @param _userAddress User's address.
        */
        function serverForceGameEnd(address _userAddress, uint _gameId) public onlyServer {
            uint gameId = userGameId[_userAddress];
            Game storage game = gameIdGame[gameId];
    
            require(gameId == _gameId, "inv gameId");
            require(game.status == GameStatus.SERVER_INITIATED_END, "inv status");
    
            // theoretically we have enough data to calculate winner
            // but as user did not respond assume he has lost.
            int newBalance = conflictRes.serverForceGameEnd(
                game.gameType,
                game.betNum,
                game.betValue,
                game.balance,
                game.stake,
                game.endInitiatedTime
            );
    
            closeGame(game, gameId, game.roundId, _userAddress, ReasonEnded.SERVER_FORCED_END, newBalance);
        }
    
        /**
        * @notice Force end of game if server does not respond. Only possible after a certain period of time
        * to give the server a chance to respond.
        */
        function userForceGameEnd(uint _gameId) public {
            address userAddress = msg.sender;
            uint gameId = userGameId[userAddress];
            Game storage game = gameIdGame[gameId];
    
            require(gameId == _gameId, "inv gameId");
            require(game.status == GameStatus.USER_INITIATED_END, "inv status");
    
            int newBalance = conflictRes.userForceGameEnd(
                game.gameType,
                game.betNum,
                game.betValue,
                game.balance,
                game.stake,
                game.endInitiatedTime
            );
    
            closeGame(game, gameId, game.roundId, userAddress, ReasonEnded.USER_FORCED_END, newBalance);
        }
    
        /**
         * @dev Conflict handling implementation. Stores game data and timestamp if game
         * is active. If server has already marked conflict for game session the conflict
         * resolution contract is used (compare conflictRes).
         * @param _roundId Round id of bet.
         * @param _gameType Game type of bet.
         * @param _num Number of bet.
         * @param _value Value of bet.
         * @param _balance Balance before this bet.
         * @param _userHash Hash of user's seed for this bet.
         * @param _userSeed User's seed for this bet.
         * @param _gameId game Game session id.
         * @param _userAddress User's address.
         */
        function userEndGameConflictImpl(
            uint32 _roundId,
            uint8 _gameType,
            uint _num,
            uint _value,
            int _balance,
            bytes32 _userHash,
            bytes32 _userSeed,
            uint _gameId,
            address _userAddress
        )
            private
        {
            uint gameId = userGameId[_userAddress];
            Game storage game = gameIdGame[gameId];
            int maxBalance = conflictRes.maxBalance();
            int gameStake = game.stake;
    
            require(gameId == _gameId, "inv gameId");
            require(_roundId > 0, "inv roundId");
            require(keccak256(abi.encodePacked(_userSeed)) == _userHash, "inv userSeed");
            require(-gameStake <= _balance && _balance <= maxBalance, "inv balance"); // game.stake save to cast as uint128
            require(conflictRes.isValidBet(_gameType, _num, _value), "inv bet");
            require(gameStake.add(_balance).sub(_value.castToInt()) >= 0, "value too high"); // game.stake save to cast as uint128
    
            if (game.status == GameStatus.SERVER_INITIATED_END && game.roundId == _roundId) {
                game.userSeed = _userSeed;
                endGameConflict(game, gameId, _userAddress);
            } else if (game.status == GameStatus.ACTIVE
                    || (game.status == GameStatus.SERVER_INITIATED_END && game.roundId < _roundId)) {
                game.status = GameStatus.USER_INITIATED_END;
                game.endInitiatedTime = block.timestamp;
                game.roundId = _roundId;
                game.gameType = _gameType;
                game.betNum = _num;
                game.betValue = _value;
                game.balance = _balance;
                game.userSeed = _userSeed;
                game.serverSeed = bytes32(0);
    
                emit LogUserRequestedEnd(msg.sender, gameId);
            } else {
                revert("inv state");
            }
        }
    
        /**
         * @dev Conflict handling implementation. Stores game data and timestamp if game
         * is active. If user has already marked conflict for game session the conflict
         * resolution contract is used (compare conflictRes).
         * @param _roundId Round id of bet.
         * @param _gameType Game type of bet.
         * @param _num Number of bet.
         * @param _value Value of bet.
         * @param _balance Balance before this bet.
         * @param _serverHash Hash of server's seed for this bet.
         * @param _userHash Hash of user's seed for this bet.
         * @param _serverSeed Server's seed for this bet.
         * @param _userSeed User's seed for this bet.
         * @param _userAddress User's address.
         */
        function serverEndGameConflictImpl(
            uint32 _roundId,
            uint8 _gameType,
            uint _num,
            uint _value,
            int _balance,
            bytes32 _serverHash,
            bytes32 _userHash,
            bytes32 _serverSeed,
            bytes32 _userSeed,
            uint _gameId,
            address _userAddress
        )
            private
        {
            uint gameId = userGameId[_userAddress];
            Game storage game = gameIdGame[gameId];
            int maxBalance = conflictRes.maxBalance();
            int gameStake = game.stake;
    
            require(gameId == _gameId, "inv gameId");
            require(_roundId > 0, "inv roundId");
            require(keccak256(abi.encodePacked(_serverSeed)) == _serverHash, "inv serverSeed");
            require(keccak256(abi.encodePacked(_userSeed)) == _userHash, "inv userSeed");
            require(-gameStake <= _balance && _balance <= maxBalance, "inv balance"); // game.stake save to cast as uint128
            require(conflictRes.isValidBet(_gameType, _num, _value), "inv bet");
            require(gameStake.add(_balance).sub(_value.castToInt()) >= 0, "too high value"); // game.stake save to cast as uin128
    
            if (game.status == GameStatus.USER_INITIATED_END && game.roundId == _roundId) {
                game.serverSeed = _serverSeed;
                endGameConflict(game, gameId, _userAddress);
            } else if (game.status == GameStatus.ACTIVE
                    || (game.status == GameStatus.USER_INITIATED_END && game.roundId < _roundId)) {
                game.status = GameStatus.SERVER_INITIATED_END;
                game.endInitiatedTime = block.timestamp;
                game.roundId = _roundId;
                game.gameType = _gameType;
                game.betNum = _num;
                game.betValue = _value;
                game.balance = _balance;
                game.serverSeed = _serverSeed;
                game.userSeed = _userSeed;
    
                emit LogServerRequestedEnd(_userAddress, gameId);
            } else {
                revert("inv state");
            }
        }
    
        /**
         * @dev End conflicting game without placed bets.
         * @param _game Game session data.
         * @param _gameId Game session id.
         * @param _userAddress User's address.
         */
        function cancelActiveGame(Game storage _game, uint _gameId, address _userAddress) private {
            // user need to pay a fee when conflict ended.
            // this ensures a malicious, rich user can not just generate game sessions and then wait
            // for us to end the game session and then confirm the session status, so
            // we would have to pay a high gas fee without profit.
            int newBalance = -conflictRes.conflictEndFine();
    
            // do not allow balance below user stake
            int stake = _game.stake;
            if (newBalance < -stake) {
                newBalance = -stake;
            }
            closeGame(_game, _gameId, 0, _userAddress, ReasonEnded.CONFLICT_ENDED, newBalance);
        }
    
        /**
         * @dev End conflicting game.
         * @param _game Game session data.
         * @param _gameId Game session id.
         * @param _userAddress User's address.
         */
        function endGameConflict(Game storage _game, uint _gameId, address _userAddress) private {
            int newBalance = conflictRes.endGameConflict(
                _game.gameType,
                _game.betNum,
                _game.betValue,
                _game.balance,
                _game.stake,
                _game.serverSeed,
                _game.userSeed
            );
    
            closeGame(_game, _gameId, _game.roundId, _userAddress, ReasonEnded.CONFLICT_ENDED, newBalance);
        }
    }
    
    contract GameChannel is GameChannelConflict {
        /**
         * @dev contract constructor
         * @param _serverAddress Server address.
         * @param _minStake Min value user needs to deposit to create game session.
         * @param _maxStake Max value user can deposit to create game session.
         * @param _conflictResAddress Conflict resolution contract address.
         * @param _houseAddress House address to move profit to.
         * @param _chainId Chain id for signature domain.
         */
        constructor(
            address _serverAddress,
            uint128 _minStake,
            uint128 _maxStake,
            address _conflictResAddress,
            address _houseAddress,
            uint _chainId
        )
            public
            GameChannelConflict(_serverAddress, _minStake, _maxStake, _conflictResAddress, _houseAddress, _chainId)
        {
            // nothing to do
        }
    
        /**
         * @notice Create games session request. msg.value needs to be valid stake value.
         * @param _userEndHash Last entry of users' hash chain.
         * @param _previousGameId User's previous game id, initial 0.
         * @param _createBefore Game can be only created before this timestamp.
         * @param _serverEndHash Last entry of server's hash chain.
         * @param _serverSig Server signature. See verifyCreateSig
         */
        function createGame(
            bytes32 _userEndHash,
            uint _previousGameId,
            uint _createBefore,
            bytes32 _serverEndHash,
            bytes _serverSig
        )
            public
            payable
            onlyValidValue
            onlyValidHouseStake(activeGames + 1)
            onlyNotPaused
        {
            uint previousGameId = userGameId[msg.sender];
            Game storage game = gameIdGame[previousGameId];
    
            require(game.status == GameStatus.ENDED, "prev game not ended");
            require(previousGameId == _previousGameId, "inv gamePrevGameId");
            require(block.timestamp < _createBefore, "expired");
    
            verifyCreateSig(msg.sender, _previousGameId, _createBefore, _serverEndHash, _serverSig);
    
            uint gameId = gameIdCntr++;
            userGameId[msg.sender] = gameId;
            Game storage newGame = gameIdGame[gameId];
    
            newGame.stake = uint128(msg.value); // It's safe to cast msg.value as it is limited, see onlyValidValue
            newGame.status = GameStatus.ACTIVE;
    
            activeGames = activeGames.add(1);
    
            // It's safe to cast msg.value as it is limited, see onlyValidValue
            emit LogGameCreated(msg.sender, gameId, uint128(msg.value), _serverEndHash,  _userEndHash);
        }
    
    
        /**
         * @dev Regular end game session. Used if user and house have both
         * accepted current game session state.
         * The game session with gameId _gameId is closed
         * and the user paid out. This functions is called by the server after
         * the user requested the termination of the current game session.
         * @param _roundId Round id of bet.
         * @param _balance Current balance.
         * @param _serverHash Hash of server's seed for this bet.
         * @param _userHash Hash of user's seed for this bet.
         * @param _gameId Game session id.
         * @param _contractAddress Address of this contract.
         * @param _userAddress Address of user.
         * @param _userSig User's signature of this bet.
         */
        function serverEndGame(
            uint32 _roundId,
            int _balance,
            bytes32 _serverHash,
            bytes32 _userHash,
            uint _gameId,
            address _contractAddress,
            address _userAddress,
            bytes _userSig
        )
            public
            onlyServer
        {
            verifySig(
                    _roundId,
                    0,
                    0,
                    0,
                    _balance,
                    _serverHash,
                    _userHash,
                    _gameId,
                    _contractAddress,
                    _userSig,
                    _userAddress
            );
    
            regularEndGame(_userAddress, _roundId, _balance, _gameId, _contractAddress);
        }
    
        /**
         * @notice Regular end game session. Normally not needed as server ends game (@see serverEndGame).
         * Can be used by user if server does not end game session.
         * @param _roundId Round id of bet.
         * @param _balance Current balance.
         * @param _serverHash Hash of server's seed for this bet.
         * @param _userHash Hash of user's seed for this bet.
         * @param _gameId Game session id.
         * @param _contractAddress Address of this contract.
         * @param _serverSig Server's signature of this bet.
         */
        function userEndGame(
            uint32 _roundId,
            int _balance,
            bytes32 _serverHash,
            bytes32 _userHash,
            uint _gameId,
            address _contractAddress,
            bytes _serverSig
        )
            public
        {
            verifySig(
                    _roundId,
                    0,
                    0,
                    0,
                    _balance,
                    _serverHash,
                    _userHash,
                    _gameId,
                    _contractAddress,
                    _serverSig,
                    serverAddress
            );
    
            regularEndGame(msg.sender, _roundId, _balance, _gameId, _contractAddress);
        }
    
        /**
         * @dev Verify server signature.
         * @param _userAddress User's address.
         * @param _previousGameId User's previous game id, initial 0.
         * @param _createBefore Game can be only created before this timestamp.
         * @param _serverEndHash Last entry of server's hash chain.
         * @param _serverSig Server signature.
         */
        function verifyCreateSig(
            address _userAddress,
            uint _previousGameId,
            uint _createBefore,
            bytes32 _serverEndHash,
            bytes _serverSig
        )
            private view
        {
            address contractAddress = this;
            bytes32 hash = keccak256(abi.encodePacked(
                contractAddress, _userAddress, _previousGameId, _createBefore, _serverEndHash
            ));
    
            verify(hash, _serverSig, serverAddress);
        }
    
        /**
         * @dev Regular end game session implementation. Used if user and house have both
         * accepted current game session state. The game session with gameId _gameId is closed
         * and the user paid out.
         * @param _userAddress Address of user.
         * @param _balance Current balance.
         * @param _gameId Game session id.
         * @param _contractAddress Address of this contract.
         */
        function regularEndGame(
            address _userAddress,
            uint32 _roundId,
            int _balance,
            uint _gameId,
            address _contractAddress
        )
            private
        {
            uint gameId = userGameId[_userAddress];
            Game storage game = gameIdGame[gameId];
            int maxBalance = conflictRes.maxBalance();
            int gameStake = game.stake;
    
            require(_gameId == gameId, "inv gameId");
            require(_roundId > 0, "inv roundId");
            // save to cast as game.stake hash fixed range
            require(-gameStake <= _balance && _balance <= maxBalance, "inv balance");
            require(game.status == GameStatus.ACTIVE, "inv status");
    
            assert(_contractAddress == address(this));
    
            closeGame(game, gameId, _roundId, _userAddress, ReasonEnded.REGULAR_ENDED, _balance);
        }
    }
    
    library SafeCast {
        /**
         * Cast unsigned a to signed a.
         */
        function castToInt(uint a) internal pure returns(int) {
            assert(a < (1 << 255));
            return int(a);
        }
    
        /**
         * Cast signed a to unsigned a.
         */
        function castToUint(int a) internal pure returns(uint) {
            assert(a >= 0);
            return uint(a);
        }
    }
    
    library SafeMath {
    
        /**
        * @dev Multiplies two unsigned integers, throws on overflow.
        */
        function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
            // Gas optimization: this is cheaper than asserting '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;
            }
    
            c = a * b;
            assert(c / a == b);
            return c;
        }
    
        /**
        * @dev Multiplies two signed integers, throws on overflow.
        */
        function mul(int256 a, int256 b) internal pure returns (int256) {
            // Gas optimization: this is cheaper than asserting '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;
            }
            int256 c = a * b;
            assert(c / a == b);
            return c;
        }
    
        /**
        * @dev Integer division of two unsigned integers, 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 a / b;
        }
    
        /**
        * @dev Integer division of two signed integers, truncating the quotient.
        */
        function div(int256 a, int256 b) internal pure returns (int256) {
            // assert(b > 0); // Solidity automatically throws when dividing by 0
            // Overflow only happens when the smallest negative int is multiplied by -1.
            int256 INT256_MIN = int256((uint256(1) << 255));
            assert(a != INT256_MIN || b != - 1);
            return a / b;
        }
    
        /**
        * @dev Subtracts two unsigned integers, 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 Subtracts two signed integers, throws on overflow.
        */
        function sub(int256 a, int256 b) internal pure returns (int256) {
            int256 c = a - b;
            assert((b >= 0 && c <= a) || (b < 0 && c > a));
            return c;
        }
    
        /**
        * @dev Adds two unsigned integers, throws on overflow.
        */
        function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
            c = a + b;
            assert(c >= a);
            return c;
        }
    
        /**
        * @dev Adds two signed integers, throws on overflow.
        */
        function add(int256 a, int256 b) internal pure returns (int256) {
            int256 c = a + b;
            assert((b >= 0 && c >= a) || (b < 0 && c < a));
            return c;
        }
    }

    File 2 of 2: ConflictResolution
    pragma solidity ^0.4.24;
    
    interface ConflictResolutionInterface {
        function minHouseStake(uint activeGames) external pure returns(uint);
    
        function maxBalance() external pure returns(int);
    
        function conflictEndFine() external pure returns(int);
    
        function isValidBet(uint8 _gameType, uint _betNum, uint _betValue) external pure returns(bool);
    
        function endGameConflict(
            uint8 _gameType,
            uint _betNum,
            uint _betValue,
            int _balance,
            uint _stake,
            bytes32 _serverSeed,
            bytes32 _userSeed
        )
            external
            view
            returns(int);
    
        function serverForceGameEnd(
            uint8 gameType,
            uint _betNum,
            uint _betValue,
            int _balance,
            uint _stake,
            uint _endInitiatedTime
        )
            external
            view
            returns(int);
    
        function userForceGameEnd(
            uint8 _gameType,
            uint _betNum,
            uint _betValue,
            int _balance,
            uint _stake,
            uint _endInitiatedTime
        )
            external
            view
            returns(int);
    }
    
    contract ConflictResolution is ConflictResolutionInterface {
        using SafeCast for int;
        using SafeCast for uint;
        using SafeMath for int;
        using SafeMath for uint;
    
        uint public constant DICE_RANGE = 100;
        uint public constant HOUSE_EDGE = 150;
        uint public constant HOUSE_EDGE_DIVISOR = 10000;
    
        uint public constant SERVER_TIMEOUT = 6 hours;
        uint public constant USER_TIMEOUT = 6 hours;
    
        uint8 public constant DICE_LOWER = 1; ///< @dev dice game lower number wins
        uint8 public constant DICE_HIGHER = 2; ///< @dev dice game higher number wins
    
        uint public constant MIN_BET_VALUE = 1e13; /// min 0.00001 ether bet
        uint public constant MIN_BANKROLL = 15e18;
    
        int public constant NOT_ENDED_FINE = 1e15; /// 0.001 ether
    
        int public constant CONFLICT_END_FINE = 1e15; /// 0.001 ether
    
        uint public constant PROBABILITY_DIVISOR = 10000;
    
        int public constant MAX_BALANCE = int(MIN_BANKROLL / 2);
    
        modifier onlyValidBet(uint8 _gameType, uint _betNum, uint _betValue) {
            require(isValidBet(_gameType, _betNum, _betValue), "inv bet");
            _;
        }
    
        modifier onlyValidBalance(int _balance, uint _gameStake) {
            require(-_gameStake.castToInt() <= _balance && _balance <= MAX_BALANCE, "inv balance");
            _;
        }
    
        /**
         * @dev Calc max bet we allow
         * We definitely do not allow bets greater than kelly criterion would allow.
         * => The max bet is limited to the max profit of houseEdge * bankroll.
         * => maxBet = houseEdge / (1/p * (1 - houseEdge) - 1) * bankroll, with p is win probability.
         * The max bet can be further restricted on backend.
         * @param _winProbability winProbability.
         * @return max allowed bet.
         */
        function maxBet(uint _winProbability) public pure returns(uint) {
            assert(0 < _winProbability && _winProbability < PROBABILITY_DIVISOR);
    
            uint tmp1 = PROBABILITY_DIVISOR.mul(HOUSE_EDGE_DIVISOR).div(_winProbability);
            uint tmp2 = PROBABILITY_DIVISOR.mul(HOUSE_EDGE).div(_winProbability);
    
            uint enumerator = HOUSE_EDGE.mul(MIN_BANKROLL);
            uint denominator = tmp1.sub(tmp2).sub(HOUSE_EDGE_DIVISOR);
            uint maxBetVal = enumerator.div(denominator);
    
            return maxBetVal.add(5e14).div(1e15).mul(1e15); // round to multiple of 0.001 Ether
        }
    
        /**
         * @dev Check if bet is valid.
         * @param _gameType Game type.
         * @param _betNum Number of bet.
         * @param _betValue Value of bet.
         * @return True if bet is valid false otherwise.
         */
        function isValidBet(uint8 _gameType, uint _betNum, uint _betValue) public pure returns(bool) {
            bool validMinBetValue = MIN_BET_VALUE <= _betValue;
            bool validGame = false;
    
            if (_gameType == DICE_LOWER) {
                validGame = _betNum > 0 && _betNum < DICE_RANGE - 1;
                validGame = validGame && _betValue <= maxBet(_betNum * PROBABILITY_DIVISOR / DICE_RANGE);
            } else if (_gameType == DICE_HIGHER) {
                validGame = _betNum > 0 && _betNum < DICE_RANGE - 1;
                validGame = validGame && _betValue <= maxBet((DICE_RANGE - _betNum - 1) * PROBABILITY_DIVISOR / DICE_RANGE);
            } else {
                validGame = false;
            }
    
            return validMinBetValue && validGame;
        }
    
        /**
         * @return Conflict end fine.
         */
        function conflictEndFine() public pure returns(int) {
            return CONFLICT_END_FINE;
        }
    
        /**
         * @return Max balance.
         */
        function maxBalance() public pure returns(int) {
            return MAX_BALANCE;
        }
    
        /**
         * Calculate minimum needed house stake.
         */
        function minHouseStake(uint activeGames) public pure returns(uint) {
            return  MathUtil.min(activeGames, 1) * MIN_BANKROLL;
        }
    
        /**
         * @dev Calculates game result and returns new balance.
         * @param _gameType Type of game.
         * @param _betNum Bet number.
         * @param _betValue Value of bet.
         * @param _balance Current balance.
         * @param _serverSeed Server's seed of current round.
         * @param _userSeed User's seed of current round.
         * @return New game session balance.
         */
        function endGameConflict(
            uint8 _gameType,
            uint _betNum,
            uint _betValue,
            int _balance,
            uint _stake,
            bytes32 _serverSeed,
            bytes32 _userSeed
        )
            public
            view
            onlyValidBet(_gameType, _betNum, _betValue)
            onlyValidBalance(_balance, _stake)
            returns(int)
        {
            require(_serverSeed != 0 && _userSeed != 0, "inv seeds");
    
            int newBalance =  processBet(_gameType, _betNum, _betValue, _balance, _serverSeed, _userSeed);
    
            // user need to pay a fee when conflict ended.
            // this ensures a malicious, rich user can not just generate game sessions and then wait
            // for us to end the game session and then confirm the session status, so
            // we would have to pay a high gas fee without profit.
            newBalance = newBalance.sub(CONFLICT_END_FINE);
    
            // do not allow balance below user stake
            int stake = _stake.castToInt();
            if (newBalance < -stake) {
                newBalance = -stake;
            }
    
            return newBalance;
        }
    
        /**
         * @dev Force end of game if user does not respond. Only possible after a time period.
         * to give the user a chance to respond.
         * @param _gameType Game type.
         * @param _betNum Bet number.
         * @param _betValue Bet value.
         * @param _balance Current balance.
         * @param _stake User stake.
         * @param _endInitiatedTime Time server initiated end.
         * @return New game session balance.
         */
        function serverForceGameEnd(
            uint8 _gameType,
            uint _betNum,
            uint _betValue,
            int _balance,
            uint _stake,
            uint _endInitiatedTime
        )
            public
            view
            onlyValidBalance(_balance, _stake)
            returns(int)
        {
            require(_endInitiatedTime + SERVER_TIMEOUT <= block.timestamp, "too low timeout");
            require(isValidBet(_gameType, _betNum, _betValue)
                    || (_gameType == 0 && _betNum == 0 && _betValue == 0 && _balance == 0), "inv bet");
    
    
            // assume user has lost
            int newBalance = _balance.sub(_betValue.castToInt());
    
            // penalize user as he didn't end game
            newBalance = newBalance.sub(NOT_ENDED_FINE);
    
            // do not allow balance below user stake
            int stake = _stake.castToInt();
            if (newBalance < -stake) {
                newBalance = -stake;
            }
    
            return newBalance;
        }
    
        /**
         * @dev Force end of game if server does not respond. Only possible after a time period
         * to give the server a chance to respond.
         * @param _gameType Game type.
         * @param _betNum Bet number.
         * @param _betValue Value of bet.
         * @param _balance Current balance.
         * @param _endInitiatedTime Time server initiated end.
         * @return New game session balance.
         */
        function userForceGameEnd(
            uint8 _gameType,
            uint _betNum,
            uint _betValue,
            int _balance,
            uint  _stake,
            uint _endInitiatedTime
        )
            public
            view
            onlyValidBalance(_balance, _stake)
            returns(int)
        {
            require(_endInitiatedTime + USER_TIMEOUT <= block.timestamp, "too low timeout");
            require(isValidBet(_gameType, _betNum, _betValue)
                || (_gameType == 0 && _betNum == 0 && _betValue == 0 && _balance == 0), "inv bet");
    
            int profit = 0;
            if (_gameType == 0 && _betNum == 0 && _betValue == 0 && _balance == 0) {
                // user cancelled game without playing
                profit = 0;
            } else {
                profit = calculateProfit(_gameType, _betNum, _betValue); // safe to cast as ranges are limited
            }
    
            // penalize server as it didn't end game
            profit = profit.add(NOT_ENDED_FINE);
    
            return _balance.add(profit);
        }
    
        /**
         * @dev Calculate new balance after executing bet.
         * @param _gameType game type.
         * @param _betNum Bet Number.
         * @param _betValue Value of bet.
         * @param _balance Current balance.
         * @param _serverSeed Server's seed
         * @param _userSeed User's seed
         * return new balance.
         */
        function processBet(
            uint8 _gameType,
            uint _betNum,
            uint _betValue,
            int _balance,
            bytes32 _serverSeed,
            bytes32 _userSeed
        )
            public
            pure
            returns (int)
        {
            bool won = hasUserWon(_gameType, _betNum, _serverSeed, _userSeed);
            if (!won) {
                return _balance.sub(_betValue.castToInt());
            } else {
                int profit = calculateProfit(_gameType, _betNum, _betValue);
                return _balance.add(profit);
            }
        }
    
        /**
         * @dev Calculate user profit.
         * @param _gameType type of game.
         * @param _betNum bet numbe.
         * @param _betValue bet value.
         * return profit of user
         */
        function calculateProfit(uint8 _gameType, uint _betNum, uint _betValue) private pure returns(int) {
            uint betValueInGwei = _betValue / 1e9; // convert to gwei
            int res = 0;
    
            if (_gameType == DICE_LOWER) {
                res = calculateProfitGameType1(_betNum, betValueInGwei);
            } else if (_gameType == DICE_HIGHER) {
                res = calculateProfitGameType2(_betNum, betValueInGwei);
            } else {
                assert(false);
            }
            return res.mul(1e9); // convert to wei
        }
    
        /**
         * Calculate user profit from total won.
         * @param _totalWon user winning in gwei.
         * @return user profit in gwei.
         */
        function calcProfitFromTotalWon(uint _totalWon, uint _betValue) private pure returns(int) {
            uint houseEdgeValue = _totalWon.mul(HOUSE_EDGE).div(HOUSE_EDGE_DIVISOR);
    
            return _totalWon.castToInt().sub(houseEdgeValue.castToInt()).sub(_betValue.castToInt());
        }
    
        /**
         * @dev Calculate user profit if user has won for game type 1 (dice lower wins).
         * @param _betNum Bet number of user.
         * @param _betValue Value of bet in gwei.
         * @return Users' profit.
         */
        function calculateProfitGameType1(uint _betNum, uint _betValue) private pure returns(int) {
            assert(_betNum > 0 && _betNum < DICE_RANGE);
    
            uint totalWon = _betValue.mul(DICE_RANGE).div(_betNum);
            return calcProfitFromTotalWon(totalWon, _betValue);
        }
    
        /**
         * @dev Calculate user profit if user has won for game type 2 (dice lower wins).
         * @param _betNum Bet number of user.
         * @param _betValue Value of bet in gwei.
         * @return Users' profit.
         */
        function calculateProfitGameType2(uint _betNum, uint _betValue) private pure returns(int) {
            assert(_betNum >= 0 && _betNum < DICE_RANGE - 1);
    
            // safe as ranges are fixed
            uint totalWon = _betValue.mul(DICE_RANGE).div(DICE_RANGE.sub(_betNum).sub(1));
            return calcProfitFromTotalWon(totalWon, _betValue);
        }
    
        /**
         * @dev Check if user hash won or lost.
         * @return true if user has won.
         */
        function hasUserWon(
            uint8 _gameType,
            uint _betNum,
            bytes32 _serverSeed,
            bytes32 _userSeed
        )
            public
            pure
            returns(bool)
        {
            bytes32 combinedHash = keccak256(abi.encodePacked(_serverSeed, _userSeed));
            uint randNum = uint(combinedHash);
    
            if (_gameType == 1) {
                return calculateWinnerGameType1(randNum, _betNum);
            } else if (_gameType == 2) {
                return calculateWinnerGameType2(randNum, _betNum);
            } else {
                assert(false);
            }
        }
    
        /**
         * @dev Calculate winner of game type 1 (roll lower).
         * @param _randomNum 256 bit random number.
         * @param _betNum Bet number.
         * @return True if user has won false if he lost.
         */
        function calculateWinnerGameType1(uint _randomNum, uint _betNum) private pure returns(bool) {
            assert(_betNum > 0 && _betNum < DICE_RANGE);
    
            uint resultNum = _randomNum % DICE_RANGE; // bias is negligible
            return resultNum < _betNum;
        }
    
        /**
         * @dev Calculate winner of game type 2 (roll higher).
         * @param _randomNum 256 bit random number.
         * @param _betNum Bet number.
         * @return True if user has won false if he lost.
         */
        function calculateWinnerGameType2(uint _randomNum, uint _betNum) private pure returns(bool) {
            assert(_betNum >= 0 && _betNum < DICE_RANGE - 1);
    
            uint resultNum = _randomNum % DICE_RANGE; // bias is negligible
            return resultNum > _betNum;
        }
    }
    
    library MathUtil {
        /**
         * @dev Returns the absolute value of _val.
         * @param _val value
         * @return The absolute value of _val.
         */
        function abs(int _val) internal pure returns(uint) {
            if (_val < 0) {
                return uint(-_val);
            } else {
                return uint(_val);
            }
        }
    
        /**
         * @dev Calculate maximum.
         */
        function max(uint _val1, uint _val2) internal pure returns(uint) {
            return _val1 >= _val2 ? _val1 : _val2;
        }
    
        /**
         * @dev Calculate minimum.
         */
        function min(uint _val1, uint _val2) internal pure returns(uint) {
            return _val1 <= _val2 ? _val1 : _val2;
        }
    }
    
    library SafeCast {
        /**
         * Cast unsigned a to signed a.
         */
        function castToInt(uint a) internal pure returns(int) {
            assert(a < (1 << 255));
            return int(a);
        }
    
        /**
         * Cast signed a to unsigned a.
         */
        function castToUint(int a) internal pure returns(uint) {
            assert(a >= 0);
            return uint(a);
        }
    }
    
    library SafeMath {
    
        /**
        * @dev Multiplies two unsigned integers, throws on overflow.
        */
        function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
            // Gas optimization: this is cheaper than asserting '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;
            }
    
            c = a * b;
            assert(c / a == b);
            return c;
        }
    
        /**
        * @dev Multiplies two signed integers, throws on overflow.
        */
        function mul(int256 a, int256 b) internal pure returns (int256) {
            // Gas optimization: this is cheaper than asserting '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;
            }
            int256 c = a * b;
            assert(c / a == b);
            return c;
        }
    
        /**
        * @dev Integer division of two unsigned integers, 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 a / b;
        }
    
        /**
        * @dev Integer division of two signed integers, truncating the quotient.
        */
        function div(int256 a, int256 b) internal pure returns (int256) {
            // assert(b > 0); // Solidity automatically throws when dividing by 0
            // Overflow only happens when the smallest negative int is multiplied by -1.
            int256 INT256_MIN = int256((uint256(1) << 255));
            assert(a != INT256_MIN || b != - 1);
            return a / b;
        }
    
        /**
        * @dev Subtracts two unsigned integers, 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 Subtracts two signed integers, throws on overflow.
        */
        function sub(int256 a, int256 b) internal pure returns (int256) {
            int256 c = a - b;
            assert((b >= 0 && c <= a) || (b < 0 && c > a));
            return c;
        }
    
        /**
        * @dev Adds two unsigned integers, throws on overflow.
        */
        function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
            c = a + b;
            assert(c >= a);
            return c;
        }
    
        /**
        * @dev Adds two signed integers, throws on overflow.
        */
        function add(int256 a, int256 b) internal pure returns (int256) {
            int256 c = a + b;
            assert((b >= 0 && c >= a) || (b < 0 && c < a));
            return c;
        }
    }