// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.6.0/contracts/access/Ownable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.6.0/contracts/utils/structs/EnumerableSet.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an minter) that can be granted exclusive access to
* specific functions.
*
* By default, the minter account will be the one that deploys the contract. This
* can later be changed with {setMinter}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyMinter`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Mintable is Ownable {
using EnumerableSet for EnumerableSet.AddressSet;
// Track registered minters
EnumerableSet.AddressSet private _minters;
/**
* @dev Initializes the contract setting the deployer as the initial minter.
*/
constructor() {
}
/**
* @dev Returns the address of the current minters.
*/
function getMinters() external view returns (address[] memory minters) {
minters = new address[](_minters.length());
for (uint i = 0; i < _minters.length(); i++) {
minters[i] = _minters.at(i);
}
return minters;
}
/**
* @dev Throws if called by any account other than the Minter.
*/
modifier onlyMinter() {
require(owner() == _msgSender() || _minters.contains(msg.sender), "Mintable: caller is not the owner or minter");
_;
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function addMinter(address newMinter) external virtual onlyOwner {
require(newMinter != address(0), "Mintable: new minter is the zero address.");
require(!_minters.contains(newMinter),"Mintable: Minter already exists.");
_addMinter(newMinter);
}
/**
* @dev Revoke a minter
*/
function revokeMinter(address minter) external onlyOwner {
if (_minters.contains(minter)) {
_minters.remove(minter);
}
}
function _addMinter(address newMinter) private {
_minters.add(newMinter);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.6.0/contracts/access/Ownable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.6.0/contracts/token/ERC1155/extensions/ERC1155Supply.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.6.0/contracts/utils/Strings.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.6.0/contracts/token/ERC721/IERC721.sol";
import "./library/Mintable.sol";
/**
* @title CalladitaTickets contract
* @author @FrankNFT.eth
*/
contract CalladitaTickets is ERC1155Supply, Mintable {
using Strings for uint256;
uint256 public constant POPCORN_TOKEN_MAX_SUPPLY = 100000;
uint256 public constant MOVIE_TOKEN_MAX_SUPPLY = 20000;
uint256 public constant MOVIE_TOKEN_ID = 0;
uint256 public constant PORNCORN_TOKEN_ID = 1;
uint256 public tokenPrice = 0.02 ether;
uint256 public teamWithdraw;
uint256 public cyberWithdraw;
uint256 public totalWithdraw;
uint public constant MAX_PURCHASE = 26; // set 1 to high to avoid some gas
address private constant TEAM = 0xd105eA47f73A120Fd2EfE1151E73231A0f9445FD;
address private constant CYBER = 0xA422bfFF5dABa6eeeFAFf84Debf609Edf0868C5f;
address private constant MOVIE = 0x1Bb96B19858b12d91B8512580147A03cCa62C29e;
bool public saleIsActive;
IERC721 calladita;
mapping(uint256 => bool) private tokenUsed;
event priceChange(address _by, uint256 price);
constructor() ERC1155("ipfs://QmSyt9T8Gsxt4so2zcB5sCMjzQhfhs9vmo3whLhNawKfWX/") {
// ERC721's we interact with (mainnet)
calladita = IERC721(0xdCb68d47423d244319a5101eAe78716AffBa8655);
}
/**
* Pause sale if active, make active if paused
*/
function flipSaleState() external onlyMinter {
saleIsActive = !saleIsActive;
}
/**
* @dev set contract
*/
function setContract(address token) external onlyMinter {
calladita = IERC721(token);
}
/**
* @dev Set new baseURI
*/
function setURI(string memory newuri) external onlyMinter {
_setURI(newuri);
}
/**
* Set price
*/
function setPrice(uint256 price) external onlyMinter {
tokenPrice = price;
emit priceChange(msg.sender, tokenPrice);
}
/**
* @dev Removing the token substituion and replacing it with the implementation of the ERC721
*/
function uri(uint256 token) public view virtual override returns (string memory) {
string memory baseURI = super.uri(token);
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, token.toString())) : "";
}
/**
* @dev airdrop a specific token to a list of addresses
*/
function airdrop(address[] calldata addresses, uint256 token, uint numberOfTokens) external onlyMinter {
if (token==0){
require(totalSupply(MOVIE_TOKEN_ID) + numberOfTokens <= MOVIE_TOKEN_MAX_SUPPLY, "Reserve would exceed max supply of Tokens");
}else{
require(totalSupply(POPCORN_TOKEN_MAX_SUPPLY) + numberOfTokens <= POPCORN_TOKEN_MAX_SUPPLY, "Reserve would exceed max supply of Tokens");
}
uint length = addresses.length;
for (uint i=0; i < length;) {
_mint(addresses[i], token, numberOfTokens, "");
unchecked{ i++;}
}
}
modifier mintConditions (uint numberOfTokens) {
require(saleIsActive,"Sale NOT active yet");
require(numberOfTokens != 0, "numberOfNfts cannot be 0");
require(numberOfTokens < MAX_PURCHASE, "Can only mint 25 tokens at a time");
require(totalSupply(MOVIE_TOKEN_ID) + numberOfTokens <= MOVIE_TOKEN_MAX_SUPPLY, "Purchase would exceed max supply of Tokens");
_;
}
function mintWithCaladita(uint[] calldata tokenIds) external mintConditions(tokenIds.length){
uint amount;
uint length = tokenIds.length;
for (uint i=0; i < length;) {
if (!tokenUsed[tokenIds[i]] && calladita.ownerOf(tokenIds[i])==msg.sender){
tokenUsed[tokenIds[i]]=true;
unchecked{ amount++;}
}
unchecked{ i++;}
}
_mint(msg.sender, MOVIE_TOKEN_ID, amount, "");
_mint(msg.sender, PORNCORN_TOKEN_ID, 10*tokenIds.length, "");
}
function mintWithPopcorn(uint numberOfTokens) external payable mintConditions(numberOfTokens){
require(balanceOf(msg.sender,PORNCORN_TOKEN_ID)>=numberOfTokens,"not enough popcorn");
require(tokenPrice * numberOfTokens <= msg.value, "Ether value sent is not correct");
_mint(msg.sender, MOVIE_TOKEN_ID, numberOfTokens, "");
_burn(msg.sender, PORNCORN_TOKEN_ID, numberOfTokens);
_mintEligiblePOPCORN(msg.sender,numberOfTokens);
totalWithdraw += msg.value;
if(totalWithdraw>240 ether){
teamWithdraw += msg.value*15/100;
cyberWithdraw += msg.value*15/100;
}else{
teamWithdraw+= msg.value*5/100;
}
}
function calladitaUsed(uint id) external view returns (bool){
return tokenUsed[id];
}
function _mintEligiblePOPCORN(address mintTo, uint multiplier) internal {
if (totalSupply(PORNCORN_TOKEN_ID) > POPCORN_TOKEN_MAX_SUPPLY-1){
return;
}
if (totalSupply(MOVIE_TOKEN_ID) < 3000) {
_mint(mintTo, PORNCORN_TOKEN_ID, 5*multiplier, "");
}
else if (totalSupply(MOVIE_TOKEN_ID) < 5000) {
_mint(mintTo, PORNCORN_TOKEN_ID, 3*multiplier, "");
}
else if (totalSupply(MOVIE_TOKEN_ID) < 10000) {
_mint(mintTo, PORNCORN_TOKEN_ID, 2*multiplier, "");
}
else if (totalSupply(MOVIE_TOKEN_ID) < 15000){
_mint(mintTo, PORNCORN_TOKEN_ID, 1, "");
}
}
function withdraw() external onlyOwner {
uint256 balance = address(this).balance;
require(balance > 0, "Insufficent balance");
_withdraw(TEAM, teamWithdraw);
_withdraw(CYBER, cyberWithdraw);
_withdraw(MOVIE, address(this).balance);
teamWithdraw=0;
cyberWithdraw=0;
}
/**
* @dev calladitaInWallet
* @return tokens id owned by the given address
* This read function is O(totalSupply). If calling from a separate contract, be sure to test gas first.
* It may also degrade with extremely large collection sizes (e.g >> 10000), test for your use case.
*/
function calladitaInWallet(address _owner) external view returns (uint256[] memory){
uint256 ownerTokenCount = calladita.balanceOf(_owner);
uint256[] memory ownedTokenIds = new uint256[](ownerTokenCount);
uint256 currentTokenId = 0;
uint256 ownedTokenIndex = 0;
while ( ownedTokenIndex < ownerTokenCount && currentTokenId < 2412 ) { /// MAGIC number is the calladita max token count
if (_caladitaSafeOwnerOf(currentTokenId) == _owner) {
ownedTokenIds[ownedTokenIndex] = currentTokenId;
unchecked{ ownedTokenIndex++;}
}
unchecked{ currentTokenId++;}
}
return ownedTokenIds;
}
function _caladitaSafeOwnerOf(uint256 tokenId) private view returns (address owner){
try calladita.ownerOf(tokenId) returns (address v) {
return v;
} catch (bytes memory /*lowLevelData*/) {
return address(0);
}
}
///////////// Add name and symbol for etherscan /////////////////
function name() public pure returns (string memory) {
return "Calladita Movie Tickets";
}
function symbol() public pure returns (string memory) {
return "POP";
}
/**
* Helper method to allow ETH withdraws.
*/
function _withdraw(address _address, uint256 _amount) internal {
(bool success, ) = _address.call{ value: _amount }("");
require(success, "Failed to widthdraw Ether");
}
// contract can recieve Ether
receive() external payable {
totalWithdraw += msg.value;
if(totalWithdraw>240 ether){
teamWithdraw += msg.value*15/100;
cyberWithdraw += msg.value*15/100;
}else{
teamWithdraw+= msg.value*5/100;
}
}
}
{
"compilationTarget": {
"contracts/MovieTicket.sol": "CalladitaTickets"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_by","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"priceChange","type":"event"},{"inputs":[],"name":"MAX_PURCHASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MOVIE_TOKEN_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MOVIE_TOKEN_MAX_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POPCORN_TOKEN_MAX_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PORNCORN_TOKEN_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newMinter","type":"address"}],"name":"addMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"},{"internalType":"uint256","name":"token","type":"uint256"},{"internalType":"uint256","name":"numberOfTokens","type":"uint256"}],"name":"airdrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"calladitaInWallet","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"calladitaUsed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cyberWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"exists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flipSaleState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getMinters","outputs":[{"internalType":"address[]","name":"minters","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"mintWithCaladita","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numberOfTokens","type":"uint256"}],"name":"mintWithPopcorn","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"}],"name":"revokeMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"saleIsActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"setContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"name":"setPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newuri","type":"string"}],"name":"setURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"teamWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"token","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]