Transaction Hash:
Block:
13725145 at Dec-02-2021 03:59:17 AM +UTC
Transaction Fee:
0.002896475350671135 ETH
$6.98
Gas Used:
24,771 Gas / 116.930093685 Gwei
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x829BD824...93333A830
Miner
| (F2Pool Old) | 2,359.378790430457525423 Eth | 2,359.378827586957525423 Eth | 0.0000371565 | |
0xC0A8b415...7803e0F65 |
0.084758852839441362 Eth
Nonce: 41
|
0.081862377488770227 Eth
Nonce: 42
| 0.002896475350671135 |
Execution Trace
ETH 0.069
BasementDwellersStore.takingTickets( _amount=1 )
takingTickets[BasementDwellersStore (ln:193)]
TakingTickets[BasementDwellersStore (ln:204)]
transfer[BasementDwellersStore (ln:206)]
payable[BasementDwellersStore (ln:206)]
// 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 BasementDwellersStore is Ownable { Pass public constant pass = Pass(0x3c17017F5a584628cf8cb579A38455DBBF3f93e7); Factory public constant bdFactory = Factory(0x9A95eCEe5161b888fFE9Abd3D920c5D38e8539dA); address private constant core1Address = 0x105195bE68a677d80303B9962b309A30DEf24783; uint256 private constant core1Shares = 62625; address private constant core2Address = 0x073DfB7bf2A23f13a547FB4898a46d072f6792f8; uint256 private constant core2Shares = 26375; address private constant core3Address = 0x32b9d0167BD2ffaf4Fb6Be5B1CA5cc3FB29b7c40; address private constant core4Address = 0x704c7dA8D117Ff5cf3C3268EeCaB6A80188B2AAc; uint256 private constant core4Shares = 7000; address private constant advisor1Address = 0xD09659effC0DE1855e9D33A4F80Eb4348CA50aF2; uint256 private constant advisor1Shares = 4000; uint256 private constant baseMod = 100000; /** Numbers for Basement Dwellers Factory */ uint256 public constant maxDwellers = 10000; /** Team allocated Dwellers */ // Dwellers which is minted by the owner uint256 public preMintedDwellers = 0; // MAX Dwellers which owner can mint uint256 public constant maxPreMintDwellers = 150; /** Mint Pass */ uint256 public newlyMintedDwellersWithPass = 0; uint256 public constant maxDwellersPerPass = 5; mapping(address => uint256) public mintedDwellersOf; /** Tracking Dweller Sales After Presale */ uint256 public mintedDwellersAfterPresale = 0; /** Scheduling */ uint256 public openingHours = 1633996800; // Mon Oct 11 2021 17:00:00 PDT uint256 public constant operationSecondsForVIP = 3600 * 24; // 24 hours uint256 public constant operationSeconds = 3600 * 24; // 24 hours /** Ticket */ uint256 public constant ticketPrice = 0.069 ether; uint256 public totalTickets = 0; mapping(address => ticket) public ticketsOf; struct ticket { uint256 index; // Incl uint256 amount; } /** Withdrawal totals */ uint256 public withdrawTotal = 0; /** 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; } bool public isSoldOut = false; uint256 public remainingDwellers = 0; // event SetPass(address pass); // event SetDwellersFactory(address bdFactory); event SetOpeningHours(uint256 openingHours); event MintWithPass(address account, uint256 amount, uint256 changes); event TakingTickets(address account, uint256 amount, uint256 changes); event SetRemainingDwellers(uint256 remainingDwellers); event RunRaffle(uint256 raffleNumber); event SetResult( address account, uint256 validTicketAmount, uint256 changes ); event PurchaseRemainingDwellers(address account, uint256 amount); event MintDwellers(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" ); _; } modifier onlyOwnerOrTeam() { require( core1Address == msg.sender || core2Address == msg.sender || core3Address == msg.sender || core4Address == msg.sender || owner() == msg.sender, "caller is neither Team Wallet nor Owner" ); _; } // function setPass(Pass _pass) external onlyOwner { // pass = _pass; // emit SetPass(address(_pass)); // } // function setDwellersFactory(Factory _bdFactory) external onlyOwner { // bdFactory = _bdFactory; // emit SetDwellersFactory(address(_bdFactory)); // } function setOpeningHours(uint256 _openingHours) external onlyOwner { openingHours = _openingHours; emit SetOpeningHours(_openingHours); } // Do not update newlyMintedDwellers to prevent withdrawal function preMintDwellers(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( preMintedDwellers + totalRecipients <= maxPreMintDwellers, "Exceeds max pre-mint Dwellers" ); for (uint256 i = 0; i < totalRecipients; i++) { address to = recipients[i]; require(to != address(0), "receiver can not be empty address"); bdFactory.mint(to); } preMintedDwellers += 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 mintedDwellers = mintedDwellersOf[msg.sender]; uint256 passAmount = pass.balanceOf(msg.sender); // if (passAmount < bonusPassAmount) { // passAmount = bonusPassAmount; // } require( passAmount * maxDwellersPerPass - mintedDwellers >= _amount, "Not enough Pass" ); uint256 totalPrice = ticketPrice * _amount; require(totalPrice <= msg.value, "Not enough money"); for (uint256 i = 0; i < _amount; i += 1) { bdFactory.mint(msg.sender); } mintedDwellersOf[msg.sender] = mintedDwellers + _amount; newlyMintedDwellersWithPass += _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( block.timestamp > openingHours + operationSecondsForVIP + operationSeconds, "Store is not closed yet" ); require(raffleNumber == 0, "raffle number is already set"); raffleNumber = _raffleNumber; uint256 _remainingDwellers = maxDwellers - preMintedDwellers - newlyMintedDwellersWithPass; if (totalTickets <= _remainingDwellers) { isSoldOut = false; remainingDwellers = _remainingDwellers - totalTickets; mintedDwellersAfterPresale = totalTickets; } else { isSoldOut = true; slotSize = totalTickets / _remainingDwellers; offsetInSlot = _raffleNumber % slotSize; lastTargetIndex = slotSize * _remainingDwellers - 1; mintedDwellersAfterPresale = _remainingDwellers; } 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; if (!isSoldOut) { validTicketAmount = myTicket.amount; } else { 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 purchaseRemainingDwellers(uint256 _amount) external payable { require(_amount <= remainingDwellers, "Exceeds dwellers max supply"); uint256 totalPrice = ticketPrice * _amount; require(totalPrice <= msg.value, "Not enough money"); for (uint256 i = 0; i < _amount; i += 1) { bdFactory.mint(msg.sender); } mintedDwellersAfterPresale += _amount; remainingDwellers -= _amount; uint256 changes = msg.value - totalPrice; if (changes > 0) { payable(msg.sender).transfer(changes); } emit PurchaseRemainingDwellers(msg.sender, _amount); } function mintDwellers() 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; // test if over max mint per tx if (myResult.validTicketAmount > maxMintPerTx) { mintRequestAmount = maxMintPerTx; myResult.validTicketAmount -= maxMintPerTx; } else { mintRequestAmount = myResult.validTicketAmount; myResult.validTicketAmount = 0; } for (uint256 i = 0; i < mintRequestAmount; i += 1) { bdFactory.mint(msg.sender); } emit MintDwellers(msg.sender, mintRequestAmount); } // withdraw eth for sold Dwellers function withdrawAll() external onlyOwnerOrTeam { uint256 maxWithdrawalAmount = ticketPrice * (newlyMintedDwellersWithPass + mintedDwellersAfterPresale); require( maxWithdrawalAmount > withdrawTotal, "No Funds Currently To Withdraw" ); uint256 withdrawalAmount = maxWithdrawalAmount - withdrawTotal; _splitAll(withdrawalAmount); withdrawTotal += withdrawalAmount; } function _splitAll(uint256 _amount) private { uint256 singleShare = _amount / baseMod; _withdraw(core1Address, singleShare * core1Shares); _withdraw(core2Address, singleShare * core2Shares); _withdraw(core4Address, singleShare * core4Shares); _withdraw(advisor1Address, singleShare * advisor1Shares); } function _withdraw(address _address, uint256 _amount) private { payable(_address).transfer(_amount); } } // 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; } }