ETH Price: $1,994.26 (-0.06%)

Transaction Decoder

Block:
7231268 at Feb-17-2019 09:01:22 AM +UTC
Transaction Fee:
0.000237944 ETH $0.47
Gas Used:
59,486 Gas / 4 Gwei

Emitted Events:

43 0x3029b0d5e022d19f7c869e9d55449b43b8f8754d.0x014952e2ce3b8fa83537611793c762bcbc39f3ab3d68b71721aa4ffdada2fb3e( 0x014952e2ce3b8fa83537611793c762bcbc39f3ab3d68b71721aa4ffdada2fb3e, 0x0000000000000000000000001e7c3581d7dab8025659cf0c46bbe88ff4f94c19, 0x000000000000000000000000829bd824b016326a401d083b33d092293333a830, 00000000000000000000000000000000000000000000000000038d7ea4c68000 )
44 ClashHash.RoundBetAdded( blockNumber=7233000, user=[Sender] 0x1e7c3581d7dab8025659cf0c46bbe88ff4f94c19, option=0x829BD824...93333A830, value=1000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x1e7C3581...Ff4F94c19
0.011640800421171934 Eth
Nonce: 45
0.010402856421171934 Eth
Nonce: 46
0.001237944
0x3029b0D5...3b8F8754d 0.04 Eth0.041 Eth0.001
(Nanopool)
9,060.820077104532567254 Eth9,060.820315048532567254 Eth0.000237944
0xaE331855...02caEE424

Execution Trace

ETH 0.001 ClashHash.addBet( blockNumber=7233000, option=0x829BD824B016326A401d083B33D092293333A830 )
  • ETH 0.001 0x3029b0d5e022d19f7c869e9d55449b43b8f8754d.12dd9b94( )
    pragma solidity ^0.4.24;
    
    // File: openzeppelin-solidity/contracts/math/SafeMath.sol
    
    /**
     * @title SafeMath
     * @dev Math operations with safety checks that revert on error
     */
    library SafeMath {
    
      /**
      * @dev Multiplies two numbers, 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 numbers truncating the quotient, reverts on division by zero.
      */
      function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0); // Solidity only automatically asserts when dividing by 0
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    
        return c;
      }
    
      /**
      * @dev Subtracts two numbers, 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 numbers, 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 numbers 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;
      }
    }
    
    // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
    
    /**
     * @title Ownable
     * @dev The Ownable contract has an owner address, and provides basic authorization control
     * functions, this simplifies the implementation of "user permissions".
     */
    contract Ownable {
      address private _owner;
    
      event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
      );
    
      /**
       * @dev The Ownable constructor sets the original `owner` of the contract to the sender
       * account.
       */
      constructor() internal {
        _owner = msg.sender;
        emit OwnershipTransferred(address(0), _owner);
      }
    
      /**
       * @return the address of the owner.
       */
      function owner() public view returns(address) {
        return _owner;
      }
    
      /**
       * @dev Throws if called by any account other than the owner.
       */
      modifier onlyOwner() {
        require(isOwner());
        _;
      }
    
      /**
       * @return true if `msg.sender` is the owner of the contract.
       */
      function isOwner() public view returns(bool) {
        return msg.sender == _owner;
      }
    
      /**
       * @dev Allows the current owner to relinquish control of the contract.
       * @notice Renouncing to ownership will leave the contract without an owner.
       * It will not be possible to call the functions with the `onlyOwner`
       * modifier anymore.
       */
      function renounceOwnership() public onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
      }
    
      /**
       * @dev Allows the current owner to transfer control of the contract to a newOwner.
       * @param newOwner The address to transfer ownership to.
       */
      function transferOwnership(address newOwner) public onlyOwner {
        _transferOwnership(newOwner);
      }
    
      /**
       * @dev Transfers control of the contract to a newOwner.
       * @param newOwner The address to transfer ownership to.
       */
      function _transferOwnership(address newOwner) internal {
        require(newOwner != address(0));
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
      }
    }
    
    // File: solidity-rlp/contracts/RLPReader.sol
    
    /*
    * @author Hamdi Allam hamdi.allam97@gmail.com
    * Please reach out with any questions or concerns
    */
    pragma solidity ^0.4.24;
    
    library RLPReader {
        uint8 constant STRING_SHORT_START = 0x80;
        uint8 constant STRING_LONG_START  = 0xb8;
        uint8 constant LIST_SHORT_START   = 0xc0;
        uint8 constant LIST_LONG_START    = 0xf8;
    
        uint8 constant WORD_SIZE = 32;
    
        struct RLPItem {
            uint len;
            uint memPtr;
        }
    
        /*
        * @param item RLP encoded bytes
        */
        function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) {
            if (item.length == 0) 
                return RLPItem(0, 0);
    
            uint memPtr;
            assembly {
                memPtr := add(item, 0x20)
            }
    
            return RLPItem(item.length, memPtr);
        }
    
        /*
        * @param item RLP encoded list in bytes
        */
        function toList(RLPItem memory item) internal pure returns (RLPItem[] memory result) {
            require(isList(item));
    
            uint items = numItems(item);
            result = new RLPItem[](items);
    
            uint memPtr = item.memPtr + _payloadOffset(item.memPtr);
            uint dataLen;
            for (uint i = 0; i < items; i++) {
                dataLen = _itemLength(memPtr);
                result[i] = RLPItem(dataLen, memPtr); 
                memPtr = memPtr + dataLen;
            }
        }
    
        /*
        * Helpers
        */
    
        // @return indicator whether encoded payload is a list. negate this function call for isData.
        function isList(RLPItem memory item) internal pure returns (bool) {
            uint8 byte0;
            uint memPtr = item.memPtr;
            assembly {
                byte0 := byte(0, mload(memPtr))
            }
    
            if (byte0 < LIST_SHORT_START)
                return false;
            return true;
        }
    
        // @return number of payload items inside an encoded list.
        function numItems(RLPItem memory item) internal pure returns (uint) {
            uint count = 0;
            uint currPtr = item.memPtr + _payloadOffset(item.memPtr);
            uint endPtr = item.memPtr + item.len;
            while (currPtr < endPtr) {
               currPtr = currPtr + _itemLength(currPtr); // skip over an item
               count++;
            }
    
            return count;
        }
    
        // @return entire rlp item byte length
        function _itemLength(uint memPtr) internal pure returns (uint len) {
            uint byte0;
            assembly {
                byte0 := byte(0, mload(memPtr))
            }
    
            if (byte0 < STRING_SHORT_START)
                return 1;
            
            else if (byte0 < STRING_LONG_START)
                return byte0 - STRING_SHORT_START + 1;
    
            else if (byte0 < LIST_SHORT_START) {
                assembly {
                    let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is
                    memPtr := add(memPtr, 1) // skip over the first byte
                    
                    /* 32 byte word size */
                    let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len
                    len := add(dataLen, add(byteLen, 1))
                }
            }
    
            else if (byte0 < LIST_LONG_START) {
                return byte0 - LIST_SHORT_START + 1;
            } 
    
            else {
                assembly {
                    let byteLen := sub(byte0, 0xf7)
                    memPtr := add(memPtr, 1)
    
                    let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length
                    len := add(dataLen, add(byteLen, 1))
                }
            }
        }
    
        // @return number of bytes until the data
        function _payloadOffset(uint memPtr) internal pure returns (uint) {
            uint byte0;
            assembly {
                byte0 := byte(0, mload(memPtr))
            }
    
            if (byte0 < STRING_SHORT_START) 
                return 0;
            else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START))
                return 1;
            else if (byte0 < LIST_SHORT_START)  // being explicit
                return byte0 - (STRING_LONG_START - 1) + 1;
            else
                return byte0 - (LIST_LONG_START - 1) + 1;
        }
    
        /** RLPItem conversions into data types **/
    
        // @returns raw rlp encoding in bytes
        function toRlpBytes(RLPItem memory item) internal pure returns (bytes) {
            bytes memory result = new bytes(item.len);
            
            uint ptr;
            assembly {
                ptr := add(0x20, result)
            }
    
            copy(item.memPtr, ptr, item.len);
            return result;
        }
    
        function toBoolean(RLPItem memory item) internal pure returns (bool) {
            require(item.len == 1, "Invalid RLPItem. Booleans are encoded in 1 byte");
            uint result;
            uint memPtr = item.memPtr;
            assembly {
                result := byte(0, mload(memPtr))
            }
    
            return result == 0 ? false : true;
        }
    
        function toAddress(RLPItem memory item) internal pure returns (address) {
            // 1 byte for the length prefix according to RLP spec
            require(item.len <= 21, "Invalid RLPItem. Addresses are encoded in 20 bytes or less");
    
            return address(toUint(item));
        }
    
        function toUint(RLPItem memory item) internal pure returns (uint) {
            uint offset = _payloadOffset(item.memPtr);
            uint len = item.len - offset;
            uint memPtr = item.memPtr + offset;
    
            uint result;
            assembly {
                result := div(mload(memPtr), exp(256, sub(32, len))) // shift to the correct location
            }
    
            return result;
        }
    
        function toBytes(RLPItem memory item) internal pure returns (bytes) {
            uint offset = _payloadOffset(item.memPtr);
            uint len = item.len - offset; // data length
            bytes memory result = new bytes(len);
    
            uint destPtr;
            assembly {
                destPtr := add(0x20, result)
            }
    
            copy(item.memPtr + offset, destPtr, len);
            return result;
        }
    
    
        /*
        * @param src Pointer to source
        * @param dest Pointer to destination
        * @param len Amount of memory to copy from the source
        */
        function copy(uint src, uint dest, uint len) internal pure {
            // copy as many word sizes as possible
            for (; len >= WORD_SIZE; len -= WORD_SIZE) {
                assembly {
                    mstore(dest, mload(src))
                }
    
                src += WORD_SIZE;
                dest += WORD_SIZE;
            }
    
            // left over bytes. Mask is used to remove unwanted bytes from the word
            uint mask = 256 ** (WORD_SIZE - len) - 1;
            assembly {
                let srcpart := and(mload(src), not(mask)) // zero out src
                let destpart := and(mload(dest), mask) // retrieve the bytes
                mstore(dest, or(destpart, srcpart))
            }
        }
    }
    
    // File: contracts/BetStorage.sol
    
    /**
     * @title ClashHash
     * This product is protected under license.  Any unauthorized copy, modification, or use without
     * express written consent from the creators is prohibited.
     */
    
    
    
    
    contract BetStorage is Ownable {
        using SafeMath for uint256;
    
        mapping(address => mapping(address => uint256)) public bets;
        mapping(address => uint256) public betsSumByOption;
        address public wonOption;
    
        event BetAdded(address indexed user, address indexed option, uint256 value);
        event Finalized(address indexed option);
        event RewardClaimed(address indexed user, uint256 reward);
    
        function addBet(address user, address option) public payable onlyOwner {
            require(msg.value > 0, "Empty bet is not allowed");
            require(option != address(0), "Option should not be zero");
    
            bets[user][option] = bets[user][option].add(msg.value);
            betsSumByOption[option] = betsSumByOption[option].add(msg.value);
            emit BetAdded(user, option, msg.value);
        }
    
        function finalize(address option, address admin) public onlyOwner {
            require(wonOption == address(0), "Finalization could be called only once");
            require(option != address(0), "Won option should not be zero");
    
            wonOption = option;
            emit Finalized(option);
    
            if (betsSumByOption[option] == 0) {		
                selfdestruct(admin);		
            }		
        }
    
        function rewardFor(address user) public view returns(uint256 reward) {
            if (bets[user][wonOption] > 0) {
                reward = address(this).balance
                    .mul(bets[user][wonOption])
                    .div(betsSumByOption[wonOption]);
            }
        }
    
        function rewards(
            address user,
            address referrer,
            uint256 referrerFee,
            uint256 adminFee
        )
            public
            view
            returns(uint256 userReward, uint256 referrerReward, uint256 adminReward)
        {
            userReward = rewardFor(user);
            adminReward = userReward.sub(bets[user][wonOption]).mul(adminFee).div(100);
    
            if (referrer != address(0)) {
                referrerReward = adminReward.mul(referrerFee).div(100);
                adminReward = adminReward.sub(referrerReward);
            }
    
            userReward = userReward.sub(adminReward).sub(referrerReward);
        }
    
        function claimReward(
            address user,
            address admin,
            uint256 adminFee,
            address referrer,
            uint256 referrerFee
        )
            public
            onlyOwner
        {
            require(wonOption != address(0), "Round not yet finalized");
    
            (uint256 userReward, uint256 referrerReward, uint256 adminReward) = rewards(
                user,
                referrer,
                referrerFee,
                adminFee
            );
            require(userReward > 0, "Reward was claimed previously or never existed");
            
            betsSumByOption[wonOption] = betsSumByOption[wonOption].sub(bets[user][wonOption]);
            bets[user][wonOption] = 0;
    
            if (referrerReward > 0) {
                referrer.send(referrerReward);
            }
    
            if (adminReward > 0) {
                admin.send(adminReward);
            }
    
            user.transfer(userReward);
            emit RewardClaimed(user, userReward);
    
            if (betsSumByOption[wonOption] == 0) {
                selfdestruct(admin);
            }
        }
    }
    
    // File: contracts/BlockHash.sol
    
    contract BlockHash {
        using SafeMath for uint256;
        using RLPReader for RLPReader.RLPItem;
    
        mapping (uint256 => bytes32) private _hashes;
    
        function blockhashes(
            uint256 blockNumber
        )
            public
            view
            returns(bytes32)
        {
            if (blockNumber >= block.number.sub(256)) {
                return blockhash(blockNumber);
            }
    
            return _hashes[blockNumber];
        }
    
        function addBlocks(
            uint256 blockNumber,
            bytes blocksData,
            uint256[] starts
        )
            public
        {
            require(starts.length > 0 && starts[starts.length - 1] == blocksData.length, "Wrong starts argument");
    
            bytes32 expectedHash = blockhashes(blockNumber);
            for (uint i = 0; i < starts.length - 1; i++) {
                uint256 offset = starts[i];
                uint256 length = starts[i + 1].sub(starts[i]);
                bytes32 result;
                uint256 ptr;
                assembly {
                    ptr := add(add(blocksData, 0x20), offset)
                    result := keccak256(ptr, length)
                }
    
                require(result == expectedHash, "Blockhash didn't match");
                expectedHash = bytes32(RLPReader.RLPItem({len: length, memPtr: ptr}).toList()[0].toUint());
            }
            
            uint256 index = blockNumber.add(1).sub(starts.length);
            if (_hashes[index] == 0) {
                _hashes[index] = expectedHash;
            }
        }
    }
    
    // File: contracts/ClashHash.sol
    
    /**
     * @title ClashHash
     * This product is protected under license.  Any unauthorized copy, modification, or use without
     * express written consent from the creators is prohibited.
     */
    
    contract ClashHash is Ownable {
        using SafeMath for uint256;
        using RLPReader for bytes;
        using RLPReader for RLPReader.RLPItem;
    
        struct Round {
            BetStorage records;
            uint256 betsCount;
            uint256 totalReward;
            address winner;
        }
    
        uint256 public minBet = 0.001 ether;
        uint256 constant public MIN_BLOCKS_BEFORE_ROUND = 10;
        uint256 constant public MIN_BLOCKS_AFTER_ROUND = 10;
        uint256 constant public MAX_BLOCKS_AFTER_ROUND = 256;
    
        uint256 public adminFee = 5;
        uint256 public referrerFee = 50;
    
        mapping(address => address) public referrers;
        mapping(uint256 => Round) public rounds;
        BlockHash public _blockStorage;
    
        //
    
        event RoundCreated(uint256 indexed blockNumber, address contractAddress);
        event RoundBetAdded(uint256 indexed blockNumber, address indexed user, address indexed option, uint256 value);
        event RoundFinalized(uint256 indexed blockNumber, address indexed option);
        event RewardClaimed(uint256 indexed blockNumber, address indexed user, address indexed winner, uint256 reward);
    
        event NewReferral(address indexed user, address indexed referrer);
        event ReferralReward(address indexed user, address indexed referrer, uint256 value);
    
        event AdminFeeUpdate(uint256 oldFee, uint256 newFee);
        event ReferrerFeeUpdate(uint256 oldFee, uint256 newFee);
        event MinBetUpdate(uint256 oldMinBet, uint256 newMinBet);
    
        //
    
        constructor (BlockHash blockStorage) public {
            _blockStorage = blockStorage;
        }
    
        //
    
        function setReferrerFee(uint256 newFee) public onlyOwner {
            emit ReferrerFeeUpdate(referrerFee, newFee);
            referrerFee = newFee;
        }
    
        function setAdminFee(uint256 newFee) public onlyOwner {
            emit AdminFeeUpdate(adminFee, newFee);
            adminFee = newFee;
        }
    
        function setMinBet(uint256 newMinBet) public onlyOwner {
            emit MinBetUpdate(minBet, newMinBet);
            minBet = newMinBet;
        }
    
        /**
         * @param referrer Who has invited the user.
         */
        function addReferral(address referrer) public {
            require(referrer != address(0), "Invalid referrer address");
            require(referrer != msg.sender, "Different addresses required");
            require(referrers[msg.sender] == address(0), "User has referrer already");
    
            referrers[msg.sender] = referrer;
            emit NewReferral(msg.sender, referrer);
        }
    
        function addBet(uint256 blockNumber, address option) public payable {
            require(msg.value >= minBet, "Bet amount is too low");
            require(block.number <= blockNumber.sub(MIN_BLOCKS_BEFORE_ROUND), "It's too late");
    
            Round storage round = rounds[blockNumber];
            if (round.records == address(0)) {
                round.records = new BetStorage();
                emit RoundCreated(blockNumber, round.records);
            }
    
            round.betsCount += 1;
            round.totalReward = round.totalReward.add(msg.value);
            round.records.addBet.value(msg.value)(msg.sender, option);
    
            emit RoundBetAdded(
                blockNumber,
                msg.sender,
                option,
                msg.value
            );
        }
    
        function addBetWithReferrer(
            uint256 blockNumber,
            address option,
            address referrer
        )
            public
            payable
        {
            addReferral(referrer);
            addBet(blockNumber, option);
        }
    
        function claimRewardWithBlockData(uint256 blockNumber, bytes blockData) public {
            if (blockData.length > 0 && rounds[blockNumber].winner == address(0)) {
                addBlockData(blockNumber, blockData);
            }
    
            claimRewardForUser(blockNumber, msg.sender);
        }
    
        function claimRewardForUser(uint256 blockNumber, address user) public {
            Round storage round = rounds[blockNumber];
            require(round.winner != address(0), "Round not yet finished");
            require(address(round.records).balance > 0, "Round prizes are already distributed");
    
            (uint256 userReward, uint256 referrerReward,) = round.records.rewards(
                user,
                referrers[user],
                referrerFee,
                adminFee
            );
            round.records.claimReward(user, owner(), adminFee, referrers[user], referrerFee);
    
            emit RewardClaimed(blockNumber, user, round.winner, userReward);
    
            if (referrerReward > 0) {
                emit ReferralReward(user, referrers[user], referrerReward);
            }
        }
    
        function addBlockData(uint256 blockNumber, bytes blockData) public {
            Round storage round = rounds[blockNumber];
    
            require(round.winner == address(0), "Winner was already submitted");
            require(block.number <= blockNumber.add(MAX_BLOCKS_AFTER_ROUND), "It's too late, 256 blocks gone");
            require(block.number >= blockNumber.add(MIN_BLOCKS_AFTER_ROUND), "Wait at least 10 blocks");
    
            address blockBeneficiary = _readBlockBeneficiary(blockNumber, blockData);
    
            round.winner = blockBeneficiary;
            round.records.finalize(blockBeneficiary, owner());
            emit RoundFinalized(blockNumber, blockBeneficiary);
        }
    
        function _readBlockBeneficiary(
            uint256 blockNumber,
            bytes blockData
        )
            internal
            view
            returns(address)
        {
            require(keccak256(blockData) == _blockStorage.blockhashes(blockNumber), "Block data isn't valid");
            RLPReader.RLPItem[] memory items = blockData.toRlpItem().toList();
            return items[2].toAddress();
        }
    }