contract CryptoMfers is ERC721, Ownable {
    using Strings for uint256;
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdTracker;

    string public baseURI;
    string public baseExtension = ".json";
    uint256 public price = 0.01 ether;
    uint256 public maxSupply = 10020;
    uint256 public maxMintAmount = 100;
    uint256 public communityPercentage = 10;
    address public communityAddress = 0x21130E908bba2d41B63fbca7caA131285b8724F8;

    constructor(string memory initBaseURI) ERC721("CryptoMfers", "CM") {
        setBaseURI(initBaseURI);
    }

    // ********** internal

    function _baseURI() internal view virtual override returns (string memory) {
        return baseURI;
    }

    // ********** public

    function mint(address to, uint256 amount) public payable {
        uint256 supply = _tokenIdTracker.current();

        require(amount > 0, "Too small amount");
        require(amount <= maxMintAmount, "Too big amount");
        require(supply + amount <= maxSupply, "Not enough NFTs to mint");
        require(msg.value >= amount * price, "Not enough ETH");

        for (uint256 i = 0; i < amount; i++) {
            _safeMint(to, _tokenIdTracker.current());
            _tokenIdTracker.increment();
        }
    }

    function minted() public view returns (uint256) {
        return _tokenIdTracker.current();
    }

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId));

        string memory currentBaseURI = _baseURI();

        return string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension));
    }

    // ********** only owner

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

    function withdraw() public payable onlyOwner {
        require(address(this).balance > 0);

        uint256 communityAmount = address(this).balance / (100 / communityPercentage);

        (bool os1, ) = payable(communityAddress).call{value: communityAmount}("");
        require(os1);

        (bool os2, ) = payable(owner()).call{value: address(this).balance}("");
        require(os2);
    }
}
function _baseURI() internal view virtual override returns (string memory) {
    return baseURI;
}

// ********** internal