Transaction Hash:
Block:
13211227 at Sep-12-2021 01:29:45 PM +UTC
Transaction Fee:
0.003619161267163791 ETH
$6.56
Gas Used:
68,363 Gas / 52.940351757 Gwei
Emitted Events:
152 |
URSStore.SetResult( account=[Sender] 0xb0d27d60e77abb8284c8f8543812bd165f4e6d24, validTicketAmount=0, changes=160000000000000000 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0xb0d27D60...65f4e6D24 |
0.115092832408839579 Eth
Nonce: 14
|
0.271473671141675788 Eth
Nonce: 15
| 0.156380838732836209 | ||
0xD19fa156...868555539 | (URS Store) | 1,040.96 Eth | 1,040.8 Eth | 0.16 | |
0xe206e3DC...E7BB88Fd0
Miner
| (Miner: 0xe20...Fd0) | 3,246.566000273501157539 Eth | 3,246.566068636501157539 Eth | 0.000068363 |
Execution Trace
URSStore.CALL( )
- ETH 0.16
0xb0d27d60e77abb8284c8f8543812bd165f4e6d24.CALL( )
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/access/Ownable.sol"; interface Factory { function mint(address) external; } interface Pass { function balanceOf(address) external view returns (uint256); } contract URSStore is Ownable { Pass public constant pass = Pass(0xe0e19Aa07cb819664001adFFeA6a176A75897899); Factory public constant ursFactory = Factory(0x08f0B2A4351514E63E9E03A661aDFe58D463CfBc); /** Numbers for URS Factory */ uint256 public constant maxURS = 10000; /** Team withdraw fund */ // claimed bool internal claimed = false; /** Team allocated URS */ // URS which is minted by the owner uint256 public preMintedURS = 0; // MAX URS which owner can mint uint256 public constant maxPreMintURS = 50; /** Mint Pass */ uint256 public newlyMintedURSWithPass = 0; uint256 public constant maxURSPerPass = 5; mapping(address => uint256) public mintedURSOf; /** Scheduling */ uint256 public openingHours = 1631318400; // Sat Sep 11 2021 00:00:00 GMT+0000 uint256 public constant operationSecondsForVIP = 3600 * 9; // 9 hours uint256 public constant operationSeconds = 3600 * 24; // 24 hours /** Ticket */ uint256 public constant ticketPrice = 0.08 ether; uint256 public totalTickets = 0; mapping(address => ticket) public ticketsOf; struct ticket { uint256 index; // Incl uint256 amount; } /** Security */ uint256 public constant maxMintPerTx = 30; /** Raffle */ uint256 public raffleNumber; uint256 public offsetInSlot; uint256 public slotSize; uint256 public lastTargetIndex; // index greater than this is dis-regarded mapping(address => result) public resultOf; struct result { bool executed; uint256 validTicketAmount; } // event SetPass(address pass); // event SetURSFactory(address ursFactory); event SetOpeningHours(uint256 openingHours); event MintWithPass(address account, uint256 amount, uint256 changes); event TakingTickets(address account, uint256 amount, uint256 changes); event RunRaffle(uint256 raffleNumber); event SetResult( address account, uint256 validTicketAmount, uint256 changes ); event MintURS(address account, uint256 mintRequestAmount); event Withdraw(address to); constructor() {} modifier whenOpened() { require( block.timestamp >= openingHours + operationSecondsForVIP, "Store is not opened" ); require( block.timestamp < openingHours + operationSecondsForVIP + operationSeconds, "Store is closed" ); _; } modifier whenVIPOpened() { require(block.timestamp >= openingHours, "Store is not opened for VIP"); require( block.timestamp < openingHours + operationSecondsForVIP, "Store is closed for VIP" ); _; } // function setPass(Pass _pass) external onlyOwner { // pass = _pass; // emit SetPass(address(_pass)); // } // function setURSFactory(Factory _ursFactory) external onlyOwner { // ursFactory = _ursFactory; // emit SetURSFactory(address(_ursFactory)); // } function setOpeningHours(uint256 _openingHours) external onlyOwner { openingHours = _openingHours; emit SetOpeningHours(_openingHours); } // Do not update newlyMintedURS to prevent withdrawal function preMintURS(address[] memory recipients) external onlyOwner { require( block.timestamp < openingHours + operationSecondsForVIP + operationSeconds, "Not available after ticketing period" ); uint256 totalRecipients = recipients.length; require( totalRecipients > 0, "Number of recipients must be greater than 0" ); require( preMintedURS + totalRecipients <= maxPreMintURS, "Exceeds max pre-mint URS" ); for (uint256 i = 0; i < totalRecipients; i++) { address to = recipients[i]; require(to != address(0), "receiver can not be empty address"); ursFactory.mint(to); } preMintedURS += totalRecipients; } function mintWithPass(uint256 _amount) external payable whenVIPOpened { require(_amount <= maxMintPerTx, "mint amount exceeds maximum"); require(_amount > 0, "Need to mint more than 0"); uint256 mintedURS = mintedURSOf[msg.sender]; uint256 passAmount = pass.balanceOf(msg.sender); require( passAmount * maxURSPerPass - mintedURS >= _amount, "Not enough Pass" ); uint256 totalPrice = ticketPrice * _amount; require(totalPrice <= msg.value, "Not enough money"); for (uint256 i = 0; i < _amount; i += 1) { ursFactory.mint(msg.sender); } mintedURSOf[msg.sender] = mintedURS + _amount; newlyMintedURSWithPass += _amount; // Refund changes uint256 changes = msg.value - totalPrice; emit MintWithPass(msg.sender, _amount, changes); if (changes > 0) { payable(msg.sender).transfer(changes); } } function takingTickets(uint256 _amount) external payable whenOpened { require(_amount > 0, "Need to take ticket more than 0"); ticket storage myTicket = ticketsOf[msg.sender]; require(myTicket.amount == 0, "Already registered"); uint256 totalPrice = ticketPrice * _amount; require(totalPrice <= msg.value, "Not enough money"); myTicket.index = totalTickets; myTicket.amount = _amount; totalTickets = totalTickets + _amount; // Refund changes uint256 changes = msg.value - totalPrice; emit TakingTickets(msg.sender, _amount, changes); if (changes > 0) { payable(msg.sender).transfer(changes); } } function runRaffle(uint256 _raffleNumber) external onlyOwner { require(raffleNumber == 0, "raffle number is already set"); raffleNumber = _raffleNumber; uint256 remainingURS = maxURS - preMintedURS - newlyMintedURSWithPass; // Hopefully consider that totalTickets number is more than remainingURS // Actually this number can be controlled from team by taking tickets slotSize = totalTickets / remainingURS; offsetInSlot = _raffleNumber % slotSize; lastTargetIndex = slotSize * remainingURS - 1; emit RunRaffle(_raffleNumber); } function calculateValidTicketAmount( uint256 index, uint256 amount, uint256 _slotSize, uint256 _offsetInSlot, uint256 _lastTargetIndex ) internal pure returns (uint256 validTicketAmount) { /** /_____fio___\\___________________________________/lio\\___________ v f | v | v | l v | ______slot #n__|___slot #n+1___|____slot #n+2__|____slot #n+3__| f : first index (incl.) l : last index (incl.) v : win ticket fio : first index offset lio : last index offset n, n+1,... : slot index v in (slot #n+1) is ths firstWinIndex v in (slot #n+2) is ths lastWinIndex */ uint256 lastIndex = index + amount - 1; // incl. if (lastIndex > _lastTargetIndex) { lastIndex = _lastTargetIndex; } uint256 firstIndexOffset = index % _slotSize; uint256 lastIndexOffset = lastIndex % _slotSize; uint256 firstWinIndex; if (firstIndexOffset <= _offsetInSlot) { firstWinIndex = index + _offsetInSlot - firstIndexOffset; } else { firstWinIndex = index + _slotSize + _offsetInSlot - firstIndexOffset; } // Nothing is selected if (firstWinIndex > _lastTargetIndex) { validTicketAmount = 0; } else { uint256 lastWinIndex; if (lastIndexOffset >= _offsetInSlot) { lastWinIndex = lastIndex + _offsetInSlot - lastIndexOffset; } else if (lastIndex < _slotSize) { lastWinIndex = 0; } else { lastWinIndex = lastIndex + _offsetInSlot - lastIndexOffset - _slotSize; } if (firstWinIndex > lastWinIndex) { validTicketAmount = 0; } else { validTicketAmount = (lastWinIndex - firstWinIndex) / _slotSize + 1; } } } function calculateMyResult() external { require(raffleNumber > 0, "raffle number is not set yet"); ticket storage myTicket = ticketsOf[msg.sender]; require(myTicket.amount > 0, "No available ticket"); result storage myResult = resultOf[msg.sender]; require(!myResult.executed, "Already checked"); uint256 validTicketAmount = calculateValidTicketAmount( myTicket.index, myTicket.amount, slotSize, offsetInSlot, lastTargetIndex ); myResult.validTicketAmount = validTicketAmount; myResult.executed = true; uint256 remainingTickets = myTicket.amount - validTicketAmount; uint256 changes = remainingTickets * ticketPrice; emit SetResult(msg.sender, validTicketAmount, changes); if (changes > 0) { payable(msg.sender).transfer(changes); } } function mintURS() external { result storage myResult = resultOf[msg.sender]; require(myResult.executed, "result is not calculated yet"); require(myResult.validTicketAmount > 0, "No valid tickets"); uint256 mintRequestAmount = 0; if (myResult.validTicketAmount > maxMintPerTx) { mintRequestAmount = maxMintPerTx; myResult.validTicketAmount -= maxMintPerTx; } else { mintRequestAmount = myResult.validTicketAmount; myResult.validTicketAmount = 0; } for (uint256 i = 0; i < mintRequestAmount; i += 1) { ursFactory.mint(msg.sender); } emit MintURS(msg.sender, mintRequestAmount); } // withdraw eth for sold URS function withdraw(address payable _to) external onlyOwner { require(_to != address(0), "receiver can not be empty address"); require(!claimed, "Already claimed"); require( maxURS - maxPreMintURS <= totalTickets + newlyMintedURSWithPass, "Not enough ethers are collected" ); uint256 withdrawalAmount = ticketPrice * (maxURS - maxPreMintURS); // Send eth to designated receiver emit Withdraw(_to); claimed = true; _to.transfer(withdrawalAmount); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _setOwner(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _setOwner(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _setOwner(newOwner); } function _setOwner(address newOwner) private { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }