/* Crafted with love by Fueled on Bacon https://fueledonbacon.com */ //SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/access/AccessControl.sol'; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '../ERC721A.sol'; import '../interfaces/IERC721E.sol'; import '../ERC721AMarketplace.sol'; import "../VenueRegistar.sol"; contract ERC721E is IERC721E, ERC721AMarketplace, Ownable, AccessControl { bytes32 public constant MINTER_ROLE = keccak256('MINTER_ROLE'); bytes32 public constant CREATOR_ROLE = keccak256('CREATOR_ROLE'); address public venue; string internal _baseUri; uint256 public entriesCount; //starts from 1 uint256 public eventDate; //epoch mapping(uint256 => EntryType) private _entryByPos; //in the form of userId => (entryPos => amountOfTickets) mapping(uint256 => mapping(uint256 => uint256)) private _userTicketsBought; VenueRegistar private _venueRegistar; constructor( string memory name, string memory symbol, address venue_, bytes memory data ) ERC721A(name, symbol) { venue = venue_; ( string memory baseUri, address venueRegistar, address paymentToken_, address accessControl, address royaltyReceiver, address secondarySalesRoyaltyReceiver, uint96 royaltyFeeNumerator, uint96 secondarySalesRoyaltyFeeDenominator, bytes memory extraData ) = abi.decode(data, (string, address, address, address, address, address, uint96, uint96, bytes)); _baseUri = baseUri; paymentToken = paymentToken_; _setupRole(DEFAULT_ADMIN_ROLE, accessControl); _setupRole(MINTER_ROLE, accessControl); _setupRole(CREATOR_ROLE, accessControl); _venueRegistar = VenueRegistar(payable(venueRegistar)); transferOwnership(_venueRegistar.getOwner()); _secondarySalesRoyaltyReceiver = secondarySalesRoyaltyReceiver; _secondarySalesRoyaltyFeeDenominator = secondarySalesRoyaltyFeeDenominator; _setDefaultRoyalty(royaltyReceiver, royaltyFeeNumerator); eventDate = abi.decode(extraData, (uint256)); if (eventDate <= block.timestamp) revert ERC721WrongEventDate(); } /// @inheritdoc IERC721AMarketplace function versionERC721() external pure virtual override returns (string memory) { return '1.0.0-beta.0+fob.rsv.iERC721E'; } /// @inheritdoc IERC721Wide function setBaseURI(string memory baseUri) external virtual override onlyRole(DEFAULT_ADMIN_ROLE) { _baseUri = baseUri; } /// @dev override base uri. It will be combined with token ID /// @inheritdoc ERC721A function _baseURI() internal view override returns (string memory) { return _baseUri; } /// @inheritdoc IERC721E function createEntry( string memory name, uint256 price, uint256 maxSupply, uint256 maxBuy, uint256 saleEnd ) external virtual override onlyRole(CREATOR_ROLE) { // TODO: not for production // if (price == 0) revert ERC721EWrongPrice(); if (saleEnd < block.timestamp) revert ERC721WrongSaleEnd(); if (maxSupply == 0) revert ERC721WrongMaxSupply(); entriesCount += 1; uint256 pos = entriesCount; _entryByPos[pos] = EntryType(name, price, maxSupply, maxBuy, 0, saleEnd); emit NewEntry(pos, name, price, maxSupply, saleEnd); } /// @inheritdoc IERC721Wide function setEventDate(uint256 _eventDate) external virtual override onlyRole(DEFAULT_ADMIN_ROLE) { if (_eventDate <= block.timestamp) revert ERC721WrongEventDate(); eventDate = _eventDate; } /// @inheritdoc IERC721Wide function editEntryName(uint256 entryPos, string memory name) external virtual override onlyRole(CREATOR_ROLE) { if (entryPos > entriesCount) revert ERC721WrongEntryPos(); EntryType storage entry = _entryByPos[entryPos]; entry.name = name; } /// @inheritdoc IERC721Wide function editEntryPrice(uint256 entryPos, uint256 price) external virtual override onlyRole(CREATOR_ROLE) { if (entryPos > entriesCount) revert ERC721WrongEntryPos(); // TODO: not for production // if (price == 0) revert ERC721EWrongPrice(); EntryType storage entry = _entryByPos[entryPos]; entry.price = price; } /// @inheritdoc IERC721Wide function editEntryMaxSupply(uint256 entryPos, uint256 maxSupply) external virtual override onlyRole(CREATOR_ROLE) { if (entryPos > entriesCount) revert ERC721WrongEntryPos(); EntryType storage entry = _entryByPos[entryPos]; if (maxSupply < entry.sold) revert ERC721WrongMaxSupply(); entry.maxSupply = maxSupply; } /// @inheritdoc IERC721Wide function editEntryMaxBuy(uint256 entryPos, uint256 maxBuy) external virtual override onlyRole(CREATOR_ROLE) { if (entryPos > entriesCount) revert ERC721WrongEntryPos(); EntryType storage entry = _entryByPos[entryPos]; entry.maxBuy = maxBuy; } /// @inheritdoc IERC721Wide function editEntrySaleEnd(uint256 entryPos, uint256 saleEnd) external virtual override onlyRole(CREATOR_ROLE) { if (entryPos > entriesCount) revert ERC721WrongEntryPos(); if (saleEnd < block.timestamp) revert ERC721WrongSaleEnd(); EntryType storage entry = _entryByPos[entryPos]; entry.saleEnd = saleEnd; } /// @inheritdoc IERC721E function buyTickets( address receiver, uint256 userId, uint256 entryPos, uint256 amount, uint256[] memory sellingPrices ) external virtual override { if (amount != sellingPrices.length) revert ERC721WrongBatchLengths(); EntryType memory entryType = _entryByPos[entryPos]; if (entryType.saleEnd < block.timestamp) revert ERC721SaleEnded(); uint256 soldTickets = entryType.sold; uint256 maxSupply = entryType.maxSupply; if (soldTickets + amount > maxSupply) revert ERC721ReachedMaxSupply(); uint256 maxBuy = entryType.maxBuy; if (_userTicketsBought[userId][entryPos] + amount > maxBuy) revert ERC721TooManyTickets(); uint256 price = entryType.price; entryType.sold += amount; _entryByPos[entryPos] = entryType; _saveTickets(_currentIndex, amount, entryPos, sellingPrices); _userTicketsBought[userId][entryPos] += amount; //Implement royalties if (price > 0 && _msgSender() != _venueRegistar.tokenCreator()) { uint256 totalPrice = amount * price; (address receiverRoyalty, uint256 amountRoyalty) = _getDefaultRoyaltyBatch(totalPrice); IERC20(paymentToken).transferFrom(_msgSender(), venue, totalPrice - amountRoyalty); IERC20(paymentToken).transferFrom(_msgSender(), receiverRoyalty, amountRoyalty); } _safeMint(receiver, amount); emit MintTickets(receiver, userId, entryPos, amount); } /// @inheritdoc IERC721E function getTicketInfo(uint256 tokenId) external view virtual override returns (EntryType memory) { if (!_exists(tokenId)) revert ERC721NonExistentToken(); uint256 entryPos = _ticketByEntryPos[tokenId]; return _entryByPos[entryPos]; } /// @inheritdoc IERC721E function getEntry(uint256 pos) external view virtual override returns (EntryType memory) { return _entryByPos[pos]; } function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721AMarketplace, AccessControl) returns (bool) { return interfaceId == type(IERC721E).interfaceId || interfaceId == type(IERC721Wide).interfaceId || interfaceId == type(IERC721Receiver).interfaceId || super.supportsInterface(interfaceId); } }