Transaction Hash:
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 | ||
---|---|---|---|---|---|
0x42F7A61b...01bf4eD27 |
8.552977465629425661 Eth
Nonce: 84
|
8.551974497221488753 Eth
Nonce: 85
| 0.001002968407936908 | ||
0x833c6Afd...5cBbe3C63 | |||||
0xBaF6dC2E...Cd66C5e19
Miner
| (MEV Builder: 0xBaF...e19) | 6.031044888430013038 Eth | 6.031051355030013038 Eth | 0.0000064666 |
Execution Trace
MARBLEXB.evaluateRace( _raceIndex=8, _winnerMarbleIndex=1 )
-
0x42f7a61b1cdfdba57ff06b87d3025fc01bf4ed27.CALL( )
evaluateRace[MARBLEXB (ln:420)]
transfer[MARBLEXB (ln:431)]
transfer[MARBLEXB (ln:441)]
// 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 {} }