ETH Price: $1,903.97 (-0.99%)

Transaction Decoder

Block:
17862692 at Aug-07-2023 11:14:35 AM +UTC
Transaction Fee:
0.001002968407936908 ETH $1.91
Gas Used:
64,666 Gas / 15.509980638 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x42F7A61b...01bf4eD27
8.552977465629425661 Eth
Nonce: 84
8.551974497221488753 Eth
Nonce: 85
0.001002968407936908
0x833c6Afd...5cBbe3C63
(MEV Builder: 0xBaF...e19)
6.031044888430013038 Eth6.031051355030013038 Eth0.0000064666

Execution Trace

MARBLEXB.evaluateRace( _raceIndex=8, _winnerMarbleIndex=1 )
  • 0x42f7a61b1cdfdba57ff06b87d3025fc01bf4ed27.CALL( )
    // SPDX-License-Identifier: MIT
    
    //
    //
    //
    // ███    ███  █████  ██████  ██████  ██      ███████ ██   ██       ██████  
    // ████  ████ ██   ██ ██   ██ ██   ██ ██      ██       ██ ██        ██   ██ 
    // ██ ████ ██ ███████ ██████  ██████  ██      █████     ███   █████ ██████  
    // ██  ██  ██ ██   ██ ██   ██ ██   ██ ██      ██       ██ ██        ██   ██ 
    // ██      ██ ██   ██ ██   ██ ██████  ███████ ███████ ██   ██       ██████  
    //                                                                                                                                                                                                       
    // Socials : https://www.Marblex.live
    // 
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    
    interface IERC20 {
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
    
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
    
        /**
         * @dev Moves `amount` tokens from the caller's account to `recipient`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address recipient, uint256 amount) external returns (bool);
    
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
    
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
    
        /**
         * @dev Moves `amount` tokens from `sender` to `recipient` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external returns (bool);
    
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
    
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    
    pragma solidity ^0.8.7;
    
    contract MARBLEXB{
    
        address public adminAddress; // address of the admin
        address public operatorAddress; // address of the operator
        address public tokenAddress; // address of the betb token
        bool public pause;
        uint256 public intervalSeconds; // interval in seconds between two prediction rounds
        uint256 public bufferSeconds; // number of seconds for valid execution of a prediction round
        uint256 public currentRace; 
        uint256 public minTokenAmount;
        uint256 public minRacePrice;
    
        struct Marble{
            uint256 marbleId;
            string color; // color name or RGB notation for dynamic color in frontend
            uint256 totalBet; //Bet on individual Marble by all players 
            //bool isActive;
        }
    
        struct Race {
            uint256[] marbles; 
            bool paidOut; // after a race is paidOut it can be considered done
            uint256 startTimestamp; //unix timestamp
            uint256 lockTimestamp; //unix timestamp
            uint256 closeTimestamp; //unix timestamp
            uint256[] bets;
            int winnerIdInMarbleIdToRace; // -1 until a winner is determined
            uint256 totalBet; //Bet on all Marbles by all players
            uint256 price; //price of the race
            bool voidRace;
        }
    
        struct Bet {
            address payable bettorAddr;//bettor address
            bool rewarded; // if true, person already has been rewarded
            uint256 idInMarbleIdToRace; //marble on which better is betting
            uint256 betAmount; //amount they bet
            uint256 raceId;
            uint256 placeTimestamp;
        }
    
        struct BetInfo {
            uint256 marbleId;
            uint256 amount;
            bool withdraw; // default false
            uint256 count;
        }
    
        mapping(address => bool) authorized;
    
        // lookup betIds from the uint256[] of bets in Race structs
        mapping(uint256 => Bet) public betIdToBet;
        mapping(uint256 => Marble) public superSetmMarbles;
        mapping(uint256 => Race) public races;
        mapping(uint256 => mapping(address => BetInfo)) public betledger;
        mapping(address => uint256[]) public userRounds;
        mapping(uint256 => mapping(uint256 => Marble)) public marbleIdToRace;
    
        uint256 betsInSystem = 0;
        uint256 marblesRaceInSystem = 0;
        uint256 totalMarbles;
    
        address payable public ecoSystemWallet ;
        uint256 public ecoSystemFeePercentage;
        
    
        constructor(address payable _ecoSystemAddress, uint256 _ecoSystemFeePercentage, address _tokenAddress){
            superSetmMarbles[1] = Marble(1,"Satoshi Nakamoto",0); // RGB notation to pick the color from frontend
            superSetmMarbles[2] = Marble(2,"Vitalik Buterin",0);
            superSetmMarbles[3] = Marble(3,"Changpeng Zhao",0);
            superSetmMarbles[4] = Marble(4,"Elon Musk",0);
            superSetmMarbles[5] = Marble(5,"SBF",0);
            superSetmMarbles[6] = Marble(6,"Brian Armstrong",0);
            superSetmMarbles[7] = Marble(7,"Gary Gensler",0);
    
            totalMarbles = 7;
            ecoSystemWallet = _ecoSystemAddress;
            ecoSystemFeePercentage= _ecoSystemFeePercentage;
            authorized[msg.sender] = true;
            authorized[_ecoSystemAddress] = true;
    
            adminAddress = msg.sender;
            operatorAddress = msg.sender;
            tokenAddress = _tokenAddress;
            minTokenAmount = 200000000000000000000000;
            pause = false;
            intervalSeconds = 15*60; //racetime+15mins
            bufferSeconds = 30*60; //racetime+30mins
            currentRace = 0;
            minRacePrice = 9000000000000000;
        }
    
        event StartRound(uint256 indexed race);
        event VoidRound(uint256 indexed race, bool status);
    
        modifier onlyAuthorized
        {
            require( authorized[msg.sender] == true, "Not Authorized to call...!" );
            _;
        }
    
        modifier whenNotPaused() {
            require(pause == false, "Contract is pause");
            _;
        }
    
        modifier onlyAdmin() {
            require(msg.sender == adminAddress, "Not admin");
            _;
        }
    
        modifier onlyAdminOrOperator() {
            require(msg.sender == adminAddress || msg.sender == operatorAddress, "Not operator/admin");
            _;
        }
    
        modifier onlyOperator() {
            require(msg.sender == operatorAddress, "Not operator");
            _;
        }
    
        function isAuthorized(address _address) public view returns(bool) {
            return authorized[_address];
        }
         function getNumberOfBetsOnRace(uint256 _raceIndex) public view returns(uint256) {
            return races[_raceIndex].bets.length;
        }
    
        function getNumberOfMarblesInRace(uint256 _raceIndex) public view returns(uint256) {
            return races[_raceIndex].marbles.length;
        }
    
        function getAvailableMarbleIdsInRace(uint256 _raceIndex) public view returns(uint256[] memory) {
            return races[_raceIndex].marbles;
        }
    
        function getNumberOfBetsInRace(uint256 _raceIndex) public view returns(uint256[] memory) {
            return races[_raceIndex].bets;
        }
       
        function getTotalBetInRace(uint256 _raceIndex) public view returns(uint256) {
            return races[_raceIndex].totalBet;
        }
        
        function authorize(address _address) public onlyOperator {
            authorized[_address] = true;
        }
    
        function unAuthorize(address _address) public onlyOperator {
            authorized[_address] = false;
        }
    
        function setEcoSystemWallet(address payable _ecoSystemAddress, uint256 _ecoSystemFeePercentage) public onlyAdmin {
            ecoSystemWallet = _ecoSystemAddress;
            ecoSystemFeePercentage= _ecoSystemFeePercentage;
        }
    
        function setBetClamCond(address _tokenAddress, uint256 _minTokenAmount) public onlyAdmin {
            tokenAddress = _tokenAddress;
            minTokenAmount = _minTokenAmount;
        }
    
        function pauseContract(bool _status) public onlyAdmin {
            pause = _status;
        }
    
        function setAdmin(address _adminAddress, address _operatorAddress) public onlyAdmin {
            adminAddress = _adminAddress;
            operatorAddress = _operatorAddress;
        }
    
        function setIntialValues(uint256 _intervalSeconds, uint256 _bufferSeconds, uint256 _minRacePrice) public onlyOperator {
            intervalSeconds = _intervalSeconds;
            bufferSeconds = _bufferSeconds;
            minRacePrice = _minRacePrice;
        }
    
        function AddMarble(string memory color) public onlyOperator{
            totalMarbles++;
            superSetmMarbles[totalMarbles] = Marble(totalMarbles, color, 0);
        }
    
        function GetUnStuckBalance(address receiver, uint256 amountToWithdraw) public onlyAdmin{
            uint256 amount = (amountToWithdraw <= address(this).balance) ? amountToWithdraw : address(this).balance;
            payable(receiver).transfer(amount);
        }
    
        function sort_array(uint256[] memory arr) private pure returns (uint256[] memory _sortArray) {
            uint256 l = arr.length;
            for(uint i = 0; i < l; i++) {
                for(uint j = i+1; j < l ;j++) {
                    if(arr[i] > arr[j]) {
                        uint256 temp = arr[i];
                        arr[i] = arr[j];
                        arr[j] = temp;
                    }
                }
            }
            return arr;
        }
    
        function removeDuplicateRaces(uint256[] memory _marbleIds) public view returns(uint256[] memory _distinctMarbleIds)
        {
            uint256[] memory _sortArray = sort_array(_marbleIds);
            uint256[] memory distinctMarbleIds = new uint256[](_sortArray.length) ;
            uint256 k = 0;
                 
            for(uint256 i=0; i< (_sortArray.length);i++)
            {
                bool isExistsOrInvalid = false;
                uint256 id = _sortArray[i];
                if( 0 < id && id <= totalMarbles)
                {
                    for(int256 j= int256(i)-1; j >= 0 ;j--)
                    {
                        if(( id ==_sortArray[uint256(j)]) && (i!= uint256(j) ) )
                        {
                            isExistsOrInvalid = true;                        
                        }
                    }
                }
                else{
                    isExistsOrInvalid = true;
                }
                if(!isExistsOrInvalid){ 
                    distinctMarbleIds[k] = id;
                    k++;
                }
                
            }
            return removeZeroEntries(distinctMarbleIds, k);
        }
        
        function removeZeroEntries(uint256[] memory _marbleIds, uint256 nonZerolength) public pure returns(uint256[] memory _nonZeroMarblesInRace)
        {
            require(nonZerolength <= _marbleIds.length, "non ZeroValues length is greater than actual array size..!");
            uint256[] memory nonZeroMarblesInRace = new uint256[](nonZerolength);
    
            uint256 j = 0;
            for(uint256 i=0; i< _marbleIds.length && j< nonZerolength; i++)
            {
                uint256 id = _marbleIds[i];
                if( id != 0)
                {
                    nonZeroMarblesInRace[j] = id;
                    j++;
                }
            }
    
            return nonZeroMarblesInRace;
        }
    
        function newRace(uint256[] memory _marbleIds, uint256 _raceTime, uint256 _racePrice) public whenNotPaused onlyAdminOrOperator {
            require( _marbleIds.length >= 7 , "Atleast 7 marbles!");
            require(_raceTime > block.timestamp, "Race must take place for future");
            require(_racePrice > minRacePrice, "price must be greater than min limit");
    
            uint256[] memory bets;
            uint256[] memory _distinctMarbleIds = removeDuplicateRaces(_marbleIds);
    
            currentRace = currentRace + 1;
    
            races[currentRace] = Race(_distinctMarbleIds, false, _raceTime, _raceTime + intervalSeconds, _raceTime + bufferSeconds, bets, -1, 0, _racePrice, false);
    
            emit StartRound(currentRace);
        }
    
        function setVoidRace(uint256 _raceIndex, bool _status) public whenNotPaused onlyAdminOrOperator {
            require(races[_raceIndex].closeTimestamp > block.timestamp, "Race already finished");
            Race storage races = races[_raceIndex];
            races.voidRace = _status;
    
            emit VoidRound(_raceIndex, _status);
        }
    
        function claimBetAmt(uint256 _raceIndex) public {
            require(races[_raceIndex].voidRace, "race is not yet void");
            require(betledger[_raceIndex][msg.sender].amount > 0, " no bet is placed in this round");
            require(!betledger[_raceIndex][msg.sender].withdraw, "amount already withdrawn for this round");
    
             
            payable(msg.sender).transfer(betledger[_raceIndex][msg.sender].amount);
            betledger[_raceIndex][msg.sender].withdraw = true;
        }
    
        function createBet(uint256 _raceIndex, uint256 _marbleIndex) public payable{
            require(races[_raceIndex].price == msg.value, "price is wrong");
            require(races[_raceIndex].startTimestamp < block.timestamp, "race is not yet started");
            require(races[_raceIndex].lockTimestamp > block.timestamp, "race is already locked");
            require(!races[_raceIndex].voidRace, "race is already void");
            bool exists = false;
            for (uint i = 0; i < races[_raceIndex].marbles.length; i++) {
                if (races[_raceIndex].marbles[i] == _marbleIndex) {
                    exists = true;
                }
            }
            require(exists, "_marbleIndex not exists!");
            if(minTokenAmount > 0) {
                require(IERC20(tokenAddress).balanceOf(msg.sender) >= minTokenAmount, "token Balance limit fail");
            }
    
            betsInSystem++;
            uint256 newBetId = (betsInSystem);
    
            // Update user data
            BetInfo storage betInfo = betledger[_raceIndex][msg.sender];
            // betledger[_raceIndex][msg.sender].marbleId[betledger[_raceIndex][msg.sender].count+1] = _marbleIndex;
            betInfo.marbleId = _marbleIndex;
            betInfo.amount += msg.value;
            betInfo.withdraw = false;
            betInfo.count += 1;
    
            userRounds[msg.sender].push(_raceIndex);
    
            races[_raceIndex].totalBet += msg.value; //adding total amount for all marbles in race
            marbleIdToRace[_raceIndex][_marbleIndex].totalBet += msg.value; //adding participants amount for indvidual marbles in race
            betIdToBet[newBetId] = Bet(payable(msg.sender), false, _marbleIndex, msg.value, _raceIndex, block.timestamp);
            races[_raceIndex].bets.push(newBetId);
        }
    
        function getUserRounds(address _add) public view returns(uint256[] memory) {
            return userRounds[_add];
        }
    
        // do we need ths as a payable? as we are not specifying how much to send and sending from self wallet
        function evaluateRace(uint256 _raceIndex, uint256 _winnerMarbleIndex ) public onlyAdminOrOperator {
            // require(races[_raceIndex].closeTimestamp < block.timestamp, "Race not yet finished");
            require(races[_raceIndex].paidOut == false, "Race already evaluated");
            require(!races[_raceIndex].voidRace, "race is already void");
    
            uint256 _totalRaceBet = races[_raceIndex].totalBet;
            uint256 _totalWinnerMarbleBet = marbleIdToRace[_raceIndex][_winnerMarbleIndex].totalBet;
    
            uint256 _ecoSystemBalance = (_totalRaceBet * ecoSystemFeePercentage) > 100 ? (_totalRaceBet * ecoSystemFeePercentage) / 100 : 0;
            uint256 _remainingBalance = _totalRaceBet - _ecoSystemBalance;
    
            ecoSystemWallet.transfer(_ecoSystemBalance);
            
            if( (races[_raceIndex].bets.length > 0) && (_totalWinnerMarbleBet > 0) ){
                uint256 _multiplierPercentage = (_remainingBalance * 100) /  _totalWinnerMarbleBet;
                for(uint256 i = 0; i < races[_raceIndex].bets.length; i++){
                    Bet memory tempBet = betIdToBet[races[_raceIndex].bets[i]];
                    if(tempBet.idInMarbleIdToRace == _winnerMarbleIndex) {
                        uint256 _betAmount = tempBet.betAmount;
                        uint256 winAmount = (_betAmount * _multiplierPercentage) > 100 ? (_betAmount * _multiplierPercentage) / 100 : 0;
                        require(address(this).balance >= winAmount, "Not enough funds to reward bettor");
                        tempBet.bettorAddr.transfer(winAmount);
                    }
                }
            }
    
            races[_raceIndex].paidOut = true;
            races[_raceIndex].winnerIdInMarbleIdToRace =  int(_winnerMarbleIndex);
        }
        receive() payable external {}
    }