//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

error NotOwner();
error MaxTeamMints();
error RoundSoldOut();
error SaleNotStarted();
error InvalidValue();
error MaxMints();
error ContractMint();
error BurnNotActive();
error BattleNotStarted();
/*

░█████╗░██╗░░░██╗██████╗░███████╗░█████╗░███╗░░██╗███████╗
██╔══██╗██║░░░██║██╔══██╗██╔════╝██╔══██╗████╗░██║╚════██║
██║░░╚═╝██║░░░██║██████╦╝█████╗░░███████║██╔██╗██║░░███╔═╝
██║░░██╗██║░░░██║██╔══██╗██╔══╝░░██╔══██║██║╚████║██╔══╝░░
╚█████╔╝╚██████╔╝██████╦╝███████╗██║░░██║██║░╚███║███████╗
░╚════╝░░╚═════╝░╚═════╝░╚══════╝╚═╝░░╚═╝╚═╝░░╚══╝╚══════╝

@0xShimazu
*/


import "erc721a/contracts/extensions/ERC721AQueryable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";



contract CuBeanz is  ERC721AQueryable , Ownable {

    //@dev mint constraints
    uint  constant roundOneSupply = 2222;
    uint  constant roundTwoSupply = 1778;
    uint  constant maxRoundOneMints = 2;
    uint  constant maxRoundTwoMints = 1;

    uint80  constant public burnRate = 10000 ether;
    uint80  constant public cubePrice = 22000 ether; 
    
    //@dev number of mints reserved for the cubeanz team
    uint maxTeamMints = 444;
    //@dev counter for teamMints
    uint public teamMints;

    //@dev this is maximum number of times that a cube can attack a bean
    //each time a bean attacks a cube, it's attack counter is incremented
    //50% chance to lose traits, 50% chance to upgrade a single trait.abi
    uint public maxAttacks  = 5;
    

    //@dev roundNum==1? round one is live. roundNum == 2? round two is live :)
    uint public roundNum;
    bool public burnMintActive;
    bool public revealed;   

    //@dev get ready....
    bool public battleStarted;
    

    MintTracker public mintTracker = MintTracker(0,0);

    //@dev tracks amount of tokens burned in round one and two.
    //@notice, we could have used built in totalSupply() function for round one
    //but for clean syntax we introduce a new counter
    struct MintTracker {
            uint16 roundOneMints;
            uint16 roundTwoMints;
        }

    //@dev ryoToken obtained by burning cubes
    IERC20 public ryoToken; 

    //@dev tokenUri factory
    string public baseURI;
    string public notRevealedUri;
    string public uriSuffix = ".json";
    
   //@dev maps tokenIds to number of times they've attacked
    mapping(uint => uint) public numAttacks;

    
    // @dev tracks mints per round: Rounds are 1 and 2
    mapping(address => mapping(uint => uint)) public mintsPerRound;




    constructor()
        ERC721A("CuBeanz", "CBZ")

    {
        setBaseURI("");
        setNotRevealedURI("ipfs://QmYLBMRzuHewKXghmbgxfrQvzVDRDXxZVmJVDYp5kF5RgH");
    }

    event ATTACK(uint indexed numAttack,uint indexed tokenId);


     /*//////////////////////////////////////////////////////////
                               TEAM MINT
    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

    function teamMint(address to ,uint amount) external onlyOwner  {
        if(teamMints + amount > maxTeamMints) revert MaxTeamMints();
        teamMints+= amount;
        _mint(to,amount);
    }

    /*//////////////////////////////////////////////////////////
                       ROUND ONE AND TWO MINTS
    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

    function roundOneMint(uint amount) external  {
        if(roundNum!=1) revert SaleNotStarted();
        if(msg.sender != tx.origin) revert ContractMint();
        if(mintTracker.roundOneMints + amount > roundOneSupply) revert RoundSoldOut();
        if(mintsPerRound[msg.sender][1]  + amount > maxRoundOneMints) revert MaxMints();
         mintsPerRound[msg.sender][1] +=amount;
         mintTracker.roundOneMints += uint16(amount);
         _mint(msg.sender,amount);
    }

    function roundTwoMint(uint amount) external  {
        if(roundNum !=2) revert SaleNotStarted();
        if(msg.sender != tx.origin) revert ContractMint();
        if(mintTracker.roundTwoMints + amount > roundTwoSupply) revert RoundSoldOut();
        if(mintsPerRound[msg.sender][2] + amount > maxRoundTwoMints) revert MaxMints();
        mintsPerRound[msg.sender][2]  += amount;
        mintTracker.roundTwoMints += uint16(amount);
        _mint(msg.sender,amount);
    }

    /*//////////////////////////////////////////////////////////
                             RYO MINT
    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/


     function ryoMint(uint amount) external{
         if(!burnMintActive) revert BurnNotActive();
         ryoToken.transferFrom(msg.sender,address(this), cubePrice * amount);
         _mint(msg.sender,amount);
     }

    /*//////////////////////////////////////////////////////////
                              ATTACK
    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
     function attackBean(uint tokenId) public {
        if(msg.sender != ownerOf(tokenId)) revert NotOwner();
        if(!battleStarted) revert BattleNotStarted();
         require(numAttacks[tokenId] < maxAttacks);
         numAttacks[tokenId]++;
         emit ATTACK(numAttacks[tokenId],tokenId);
     }

     function gangUpOnBean(uint[] calldata tokenIds) external {
        for(uint i; i<tokenIds.length;i++){
            attackBean(tokenIds[i]);
        }

     }

    /*//////////////////////////////////////////////////////////
                             BURN MINTS
    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
    function burnCube(uint tokenId) external {
        if(msg.sender != ownerOf(tokenId)) revert NotOwner();
        ryoToken.mint(msg.sender, burnRate);
        _burn(tokenId);
    }

    function burnBatchCubes(uint[] calldata tokenIds) external{
        for(uint i; i<tokenIds.length;i++){
            if(msg.sender != ownerOf(tokenIds[i])) revert NotOwner();
            _burn(tokenIds[i]);
        }
        ryoToken.mint(msg.sender, burnRate * tokenIds.length);
    }


    /*//////////////////////////////////////////////////////////
                                SETTERS
    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
    function setMaxAttacks(uint amountAttacks) external onlyOwner {
        require(amountAttacks > maxAttacks);
        maxAttacks = amountAttacks;
    }

    function setRyoToken(address _address) external onlyOwner{
        ryoToken =  IERC20(_address);
    }


    function setRevealed(bool status) external onlyOwner {
        revealed = status;
    }

    function setNotRevealedURI(string memory _notRevealedURI) public onlyOwner {
        notRevealedUri = _notRevealedURI;
    }

    function setBaseURI(string memory _newBaseURI) public onlyOwner {
        baseURI = _newBaseURI;
    }

   
    function setUriSuffix(string memory _newSuffix) external onlyOwner{
        uriSuffix = _newSuffix;
    }

      function setRoundNum(uint _roundNum) external onlyOwner{
            roundNum = _roundNum;
    }

    function setBattleStarted(bool status) external onlyOwner{
        battleStarted = status;
    }
    function setBurnMintActive(bool status) external onlyOwner{
        burnMintActive = status;
     }

 

    


 
 


    // FACTORY

    function tokenURI(uint256 _tokenId)
        public
        view

        //ignore
        override(ERC721A)
        returns (string memory)
    {
        if (revealed == false) {
            return notRevealedUri;
        }

        string memory currentBaseURI = baseURI;
        return
            bytes(currentBaseURI).length > 0
                ? string(abi.encodePacked(currentBaseURI, _toString(_tokenId),uriSuffix))
                : "";
    }


  
    function withdrawRyoToken() external onlyOwner{
        uint balance = ryoToken.balanceOf(address(this));
        ryoToken.transfer(owner(), balance);
    }

   
    


}

interface IERC20{

    function mint(address holder, uint tokenId) external;
    function balanceOf(address account) external view returns(uint);
    function transferFrom(address from, address to, uint amount) external;
    function transfer(address to, uint amount) external;
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"ApprovalToCurrentOwner","type":"error"},{"inputs":[],"name":"ApproveToCaller","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"BattleNotStarted","type":"error"},{"inputs":[],"name":"BurnNotActive","type":"error"},{"inputs":[],"name":"ContractMint","type":"error"},{"inputs":[],"name":"InvalidQueryRange","type":"error"},{"inputs":[],"name":"MaxMints","type":"error"},{"inputs":[],"name":"MaxTeamMints","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"NotOwner","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"RoundSoldOut","type":"error"},{"inputs":[],"name":"SaleNotStarted","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"numAttack","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ATTACK","type":"event"},{"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":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"attackBean","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":[],"name":"battleStarted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"burnBatchCubes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burnCube","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"burnMintActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burnRate","outputs":[{"internalType":"uint80","name":"","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cubePrice","outputs":[{"internalType":"uint80","name":"","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"explicitOwnershipOf","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"bool","name":"burned","type":"bool"}],"internalType":"struct IERC721A.TokenOwnership","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"explicitOwnershipsOf","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"bool","name":"burned","type":"bool"}],"internalType":"struct IERC721A.TokenOwnership[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"gangUpOnBean","outputs":[],"stateMutability":"nonpayable","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":"maxAttacks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintTracker","outputs":[{"internalType":"uint16","name":"roundOneMints","type":"uint16"},{"internalType":"uint16","name":"roundTwoMints","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"mintsPerRound","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":"notRevealedUri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"numAttacks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":[],"name":"revealed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"roundNum","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"roundOneMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"roundTwoMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ryoMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ryoToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"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":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_newBaseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"status","type":"bool"}],"name":"setBattleStarted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"status","type":"bool"}],"name":"setBurnMintActive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountAttacks","type":"uint256"}],"name":"setMaxAttacks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_notRevealedURI","type":"string"}],"name":"setNotRevealedURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"status","type":"bool"}],"name":"setRevealed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_roundNum","type":"uint256"}],"name":"setRoundNum","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"setRyoToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_newSuffix","type":"string"}],"name":"setUriSuffix","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":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"teamMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"teamMints","outputs":[{"internalType":"uint256","name":"","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":"owner","type":"address"}],"name":"tokensOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"stop","type":"uint256"}],"name":"tokensOfOwnerIn","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"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":"uriSuffix","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawRyoToken","outputs":[],"stateMutability":"nonpayable","type":"function"}]