pragma solidity 0.8.4;

/// ============ Imports ============




///@title TheMetalorianDAOOnix
///@notice Token ERC721 for the Metalorian DAO
contract TheMetalorianDAOOnix is ERC721, Ownable {   

    ///============================================
    ///============= Errors =======================
    
    ///@notice NFT per address limit exceeded
    ///@param user Caller address 
    ///@param balanceOf User NFT balance
    error NftLimitPerDirection(address user, uint256 balanceOf); 

    ///@notice The sent value of ETH is not correct to make the purchase
    ///@param user Caller address 
    ///@param amountSent Amount sent by the user 
    error IncorrectPayment(address user, uint256 amountSent);
   
    ///@notice No funds to transfer
    ///@param owner Caller address
    error NotFondsToTranfer(address owner);

    ///@notice Error transferring funds to account
    ///@param owner Caller address
    error UnsuccessfulPayout(address owner);

    ///@notice Invoked token number does not exist    
    error TokenDoesNotExist();
    
    ///@notice The sale not started
    ///@param user Caller address
    error SaleNotStarted(address user);

    ///@notice No token available for sale
    ///@param user Caller address
    error NftSoldOut(address user);
   

    /// ===========================================
    /// ============ Immutable storage ============
    /// @notice Available NFT supply
    uint256 public constant AVAILABLE_SUPPLY = 300;
   
    ///@notice Maximum number of nft to buy per address
    uint256 public constant LIMIT_NFT_BY_ADDRES = 300;

    /// ===========================================
    /// ============ Mutable storage ==============  
    ///@notice Type of variable used for handling numeric sequences
    using Counters for Counters.Counter;

    ///@notice String with the base for the tokenURI
    string public baseURI;   

    ///@notice used to know if the sale of NFT has started
    bool public startSale;   

     /// @notice Cost to mint each NFT 
    uint256 public mintCost;
 
    /// @notice Number of NFTs minted
    Counters.Counter public tokenIdCounter;    

    
    /// ======================================================
    /// ============ Constructor =============================
    constructor() ERC721("The Metalorian DAO Onix", "TMDO") {

        tokenIdCounter.increment();
        baseURI = "ipfs://bafybeicxcqzcjbheng5mqstj43cichhwhvyrwy7zqzb345ctduj3kxq3ne/OnixMetadata/";   
        mintCost = 11 ether;
    }

    /// ========================================================
    /// ============= Event ====================================
    /// @notice Emitted after a successful Withdraw Proceeds
    /// @param owner Address of owner 
    /// @param amount Amount of proceeds claimed by owner
    event WithdrawProceeds(address indexed owner, uint256 amount );

    /// @notice Emitted after a successful Mint Nft
    /// @param user Address of the user Mint
    /// @param tokenId Number token Mint
    event MintTheMetalorianDAOOnix(address indexed user, uint256 tokenId); 

    /// @notice Emitted after a successful change starSale variable
    /// @param owner Address of owner
    /// @param date Date when change 
    event SetStartSale(address indexed owner, uint256 date);

    ///@notice Emitted after a successful change mintCost variable
    ///@param owner Address of owner
    ///@param amount new value per NFT 
    event SetMintCost(address indexed owner, uint256 amount);

    
    /// =========================================================
    /// ============ Functions ==================================  

    //****************************************************** */
    // ************* functions set parameter *************** */
    ///@notice start public sale
    ///@param value value in bowling for sale
    function setStartSale(bool value) external onlyOwner {
        startSale = value;
        emit SetStartSale(msg.sender, block.timestamp);
    }
  
    ///@notice Sets baseURI of NFT
    ///@param setBaseUri string whit baseURI
    function setBaseURI(string memory setBaseUri) external onlyOwner {
        baseURI = setBaseUri;
    }

    ///@notice Allows set price mint by owner
    ///@param newMintCost value price mint
    function setMintCost(uint256 newMintCost) external onlyOwner {
        mintCost = newMintCost;
        emit SetMintCost(msg.sender, newMintCost);
    }

    ///@notice return tokenURI for each token
    ///@param tokenId the id of the token you want the tokenURI
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {   
        if (!_exists(tokenId)){
            revert TokenDoesNotExist();
        }     
        return string(abi.encodePacked(baseURI, Strings.toString(tokenId), ".json"));
    }
      
  
    //************************************************* */
    //************** mint function********************* */  
    ///@notice buy from NFT
    ///@param amountNft number of NFTs you want to buy
    function mintTheMetalorianDAOOnix(uint256 amountNft) external payable {

        if(!startSale){
            revert SaleNotStarted(msg.sender);
        }

        if (msg.value != mintCost * amountNft) {
            revert IncorrectPayment(msg.sender, msg.value);
        }

        if(amountNft > (LIMIT_NFT_BY_ADDRES - balanceOf(msg.sender))){
            revert NftLimitPerDirection(
                msg.sender,
                balanceOf(msg.sender));                
        }                

        for(uint i; i < amountNft ; i++){
            
            if(tokenIdCounter.current() > AVAILABLE_SUPPLY){
                revert NftSoldOut(msg.sender);
            }   

            emit MintTheMetalorianDAOOnix(msg.sender, tokenIdCounter.current());            
            _safeMint(msg.sender, tokenIdCounter.current());        
            tokenIdCounter.increment();
        }          
   
    }

    //****************************************************** */
    //***************** withdraw function******************* */
    ///@notice withdraw funds by owner
    function withdrawProceeds() external onlyOwner {
        uint256 balance = address(this).balance;        
        if (balance == 0){ revert NotFondsToTranfer(msg.sender);}        
        (bool sent, ) = payable(msg.sender).call{value: balance}("");
        if (!sent){revert UnsuccessfulPayout(msg.sender);}        
        emit WithdrawProceeds(msg.sender, balance);
    }    
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"amountSent","type":"uint256"}],"name":"IncorrectPayment","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"balanceOf","type":"uint256"}],"name":"NftLimitPerDirection","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"NftSoldOut","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"NotFondsToTranfer","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"SaleNotStarted","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"UnsuccessfulPayout","type":"error"},{"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":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"MintTheMetalorianDAOOnix","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":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetMintCost","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"date","type":"uint256"}],"name":"SetStartSale","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawProceeds","type":"event"},{"inputs":[],"name":"AVAILABLE_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIMIT_NFT_BY_ADDRES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","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":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintCost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountNft","type":"uint256"}],"name":"mintTheMetalorianDAOOnix","outputs":[],"stateMutability":"payable","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":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","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":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"setBaseUri","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMintCost","type":"uint256"}],"name":"setMintCost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"value","type":"bool"}],"name":"setStartSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startSale","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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":[],"name":"tokenIdCounter","outputs":[{"internalType":"uint256","name":"_value","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"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":"withdrawProceeds","outputs":[],"stateMutability":"nonpayable","type":"function"}]