pragma solidity 0.8.19;






 
contract IOIA is ERC721, Ownable, ReentrancyGuard {
   using Strings for uint;
   using Counters for Counters.Counter;
   enum SaleStatus{ PAUSED,  PRIVATE }
   Counters.Counter private _tokenIds;
   uint public maxSupply = 30;
   uint8 public TOKENS_PER_PERSON_PUB_LIMIT = 5;
   uint public MINT_PRICE = 0 ether;
   SaleStatus public saleStatus = SaleStatus.PAUSED;
   string private _baseURL = "https://black-traditional-tiger-884.mypinata.cloud/ipfs/QmcbayZTxX6YH8WAcVLvhcuXpjFnm248vdJZS3chvHNhG8/";
   bytes32 public root;
   mapping(address => uint) public mintedCount;
   mapping(uint256 => uint256) public allowList;
   bool public allowTransfer;
   uint256 public constant GOLDEN_Card_SUPPLY = 3;
   uint256 public goldenCardCount;
   string private websiteDomain = "ioinvestmentacademy.com";
   
   constructor() ReentrancyGuard() ERC721("IOIA", "IO") {}


    /// @notice Set base URL
   function setRoot(bytes32  rootVal) external onlyOwner {
       root = rootVal;
   }


   function setWebdomain(string calldata _uri) external onlyOwner {
       websiteDomain = _uri;
   }


     /// @notice allow to tranfer list
   function setAllowList(uint256[] memory tokens) external onlyOwner {
       for (uint256 i = 0; i < tokens.length; i++) {
           allowList[tokens[i]]++;
       }
   }


   function removeFromAllowList(uint256 tokenId) external onlyOwner {
        allowList[tokenId] = 0;
    }


    /// @notice Set base URL
   function setbaseUrl(string calldata url) external onlyOwner {
       _baseURL = url;
   }
 
   function totalSupply() external view returns (uint) {
       return _tokenIds.current();
   }
 
   /// @notice Update current sale stage
   function setSaleStatus(SaleStatus status) external onlyOwner {
       saleStatus = status;
   }


   function changeMintPrice(uint price) external onlyOwner {
       MINT_PRICE = price;
   }


 
   function changeTPP(uint8 val) external onlyOwner {
       TOKENS_PER_PERSON_PUB_LIMIT = val;
   }
   
   /// @notice Get token URI. In case of delayed reveal we give user the json of the placeholer metadata.
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
     
       require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
       string memory currentBaseURI = _baseURL;
       return string(abi.encodePacked(currentBaseURI, Strings.toString(tokenId), ".json"));
     
   }
 
   function calcTotal(uint count) public view returns(uint) {
       require(saleStatus != SaleStatus.PAUSED, "IOIA: Sales are off");
       uint price = MINT_PRICE;
       return count * price;
   }


   function getTokensOwnedByAddress(address wallet) external view returns (uint256[] memory) {
    uint256 numTokensOwned = balanceOf(wallet);
    uint256[] memory tokensOwned = new uint256[](numTokensOwned);
    uint256 index = 0;
    uint256 totalAll = _tokenIds.current();
    for (uint256 i = 1; i <= totalAll; i++) {
        if (_exists(i) && ownerOf(i) == wallet) {
            tokensOwned[index] = i;
            index++;
        }
    }


    return tokensOwned;
   }




    function mintGoldenCard(address receiver) external onlyOwner {
        require(goldenCardCount < GOLDEN_Card_SUPPLY, "All golden Cards have been minted");


        if (goldenCardCount == 0) {
            _safeMint(receiver, 899);
        } else if (goldenCardCount == 1) {
            _safeMint(receiver, 900);
        } else if (goldenCardCount == 2) {
            _safeMint(receiver, 901);
        }
        goldenCardCount++;
    }


    function setMaxSupply(uint supply) external onlyOwner {
        maxSupply = supply;
    }




    function privateMint(uint8 numberOfTokens, string calldata domain) nonReentrant external payable {
        require(keccak256(abi.encodePacked(domain)) == keccak256(abi.encodePacked(websiteDomain)), "Unauthorized website domain");
        require(saleStatus != SaleStatus.PAUSED, "IOIA: Sales are Paused");
        require(_tokenIds.current() + numberOfTokens <= maxSupply, "Purchase would exceed max tokens");
        require(mintedCount[msg.sender] + numberOfTokens <= TOKENS_PER_PERSON_PUB_LIMIT, "Exceeded max available to purchase per user");
        require(MINT_PRICE * numberOfTokens <= msg.value, "Ether value sent is not correct");


       
        uint256 totalPrice = MINT_PRICE * numberOfTokens;


         for (uint8 i = 0; i < numberOfTokens; i++) {
        _tokenIds.increment();
        mintedCount[msg.sender]++;
        _safeMint(msg.sender, _tokenIds.current());
        }
        payable(owner()).transfer(totalPrice);
    }




    // @notice Allows owner to mint tokens to a specified address
   function airdrop(address[] calldata addresses) external onlyOwner {
    require(_tokenIds.current() + addresses.length < maxSupply, "Request exceeds collection size");
    for (uint256 i = 0; i < addresses.length; i++) {
        uint256 newItemId = _tokenIds.current() + 1;
        _safeMint(addresses[i], newItemId);
        mintedCount[addresses[i]]++;
        _tokenIds.increment();
    }
   }




    function changeAllowTransfer(bool allow) external onlyOwner  {
       allowTransfer = allow;
    }


    /// @notice Withdraw contract balance
   function withdraw() external onlyOwner {
       uint balance = address(this).balance;
       require(balance == 0, "No balance");
       payable(owner()).transfer(balance);
   }




   function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize) internal virtual override {
        if(from != address(0) && to != address(0)){
            if(allowTransfer){
                return super._beforeTokenTransfer(from, to, tokenId, batchSize);
            }else{
                require(allowList[tokenId]  != 0, "can not transfer if bid is below 0.3 ETH");
                super._beforeTokenTransfer(from, to, tokenId, batchSize);
                allowList[tokenId]--;
            }    
        }
    }  




    function contractURI() public pure returns (string memory) {
       return "https://ioinvestmentacademy.github.io/jsondata/contractUri.json";
   }


}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","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":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"GOLDEN_Card_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINT_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOKENS_PER_PERSON_PUB_LIMIT","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"}],"name":"airdrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allowList","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowTransfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"count","type":"uint256"}],"name":"calcTotal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"allow","type":"bool"}],"name":"changeAllowTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"name":"changeMintPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"val","type":"uint8"}],"name":"changeTPP","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"getTokensOwnedByAddress","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"goldenCardCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"mintGoldenCard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"mintedCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"numberOfTokens","type":"uint8"},{"internalType":"string","name":"domain","type":"string"}],"name":"privateMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"removeFromAllowList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"root","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"saleStatus","outputs":[{"internalType":"enum IOIA.SaleStatus","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokens","type":"uint256[]"}],"name":"setAllowList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"supply","type":"uint256"}],"name":"setMaxSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"rootVal","type":"bytes32"}],"name":"setRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum IOIA.SaleStatus","name":"status","type":"uint8"}],"name":"setSaleStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_uri","type":"string"}],"name":"setWebdomain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"url","type":"string"}],"name":"setbaseUrl","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":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]