// ERC721A Contracts v3.3.0
// Creator: Chiru Labs

pragma solidity ^0.8.4;



/**
 * @title ERC721A Burnable Token
 * @dev ERC721A Token that can be irreversibly burned (destroyed).
 */
abstract contract ERC721ABurnable is ERC721A, IERC721ABurnable {
    /**
     * @dev Burns `tokenId`. See {ERC721A-_burn}.
     *
     * Requirements:
     *
     * - The caller must own `tokenId` or be an approved operator.
     */
    function burn(uint256 tokenId) public virtual override {
        _burn(tokenId, true);
    }
}

pragma solidity ^0.8.0;

abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}





pragma solidity ^0.8.0;

// NFT Collection created on Uminty.com

interface IFalcon {
    function getNumberOfTokensByFunder(uint256 _projectID, address _funder) external view returns(uint256);
}


contract contract721 is ERC721ABurnable, Ownable {

    using Strings for uint256;

    uint256 public maxSupply;
    uint256 public _reserved;
    uint256 public _projectID;
    uint256 public maxBatchSize = 8;
    address public falconAddress;

    string public provenanceHash = "";
    string public baseURI;
    string public baseExtension = "";

    bool public revealed;
    bool public burnEnabled;

    constructor(
    address _creator,
    string memory _projectName,
    string memory _projectSymbol,
    string memory _initNotRevealedUri,
    uint256 _maxSupply,
    uint256 _reservedTokens,
    uint256 projectID,
    address walletUminty,
    uint256 tokenToUminty,
    address _falconAddress
    ) ERC721A(_projectName, _projectSymbol) {
    transferOwnership(_creator);
    maxSupply = _maxSupply;
    _reserved = _reservedTokens - tokenToUminty;
    revealed = false;
    baseURI = _initNotRevealedUri;
    _projectID = projectID;
    falconAddress =_falconAddress;
    _safeMint(walletUminty, tokenToUminty);
    }

    function _startTokenId() internal view virtual override returns (uint256) {
        return 1;
    }

    function checkNbOfTokens(address _funder) public view returns(uint256){
        IFalcon IF = IFalcon(falconAddress);
        return IF.getNumberOfTokensByFunder(_projectID, _funder);
    }

    function mint(uint256 _number) public {

        uint256 _quantity = checkNbOfTokens(msg.sender);

        require (_numberMinted(msg.sender) +_number <= _quantity, "Wrong number of NFTs");
        require(_number <= maxBatchSize, "quantity to mint too high");

        _safeMint(msg.sender, _number);
    }

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

    function claimReserved(uint256 _numberToMint, address _receiver) public onlyOwner {

        require(_numberToMint <= _reserved, "That would exceed the max reserved.");

            _safeMint(_receiver, _numberToMint);

        _reserved = _reserved - _numberToMint;
    }

    function getReservedLeft() public view returns (uint256) {
        return _reserved;
    }

    function burn(uint256 _tokenID) public override {
       require(burnEnabled == true, "Burn not enabled"); 
        _burn(_tokenID, true);
    }

    function flipBurnEnabled() public onlyOwner {
        burnEnabled = !burnEnabled;
    }

    function getNumberBurned(address _address) public view returns(uint256) {
        return _numberBurned(_address);
    }

    function setMaxBatchSize(uint256 _value) public onlyOwner {
        maxBatchSize = _value;
    }

    function flipRevealed(string memory _newURI) public onlyOwner {
        revealed = !revealed;
        setBaseURI(_newURI);
    }

    function setBaseExtension(string memory _newBaseExtension) public onlyOwner {
    baseExtension = _newBaseExtension;
    }

    // This should be set before mint open.
    function setProvenanceHash(string memory _provenanceHash) public onlyOwner {
        provenanceHash = _provenanceHash;
    }

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

    function tokenURI(uint256 tokenId)
    public
    view
    virtual
    override(ERC721A)
    returns (string memory)
  {
    if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
    
    if(revealed == false) {
    return baseURI;
    }

    string memory currentBaseURI = _baseURI();
    return bytes(currentBaseURI).length != 0
        ? string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension))
        : "";
  }


}

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

// Create your NFT Collection on Uminty.com

interface IFalconF {
        function setAddrContractViaFactory(uint256 _projectID, address _addrContract) external;
        function setContractMintedViaFactory(uint256 _projectID, bool _value) external;
}


contract FactoryF is Ownable {

    address public Falcon;
    address public walletUminty;
    string public notRevealedUri;
    uint256 public tokenToUminty;



    function createContractViaFalcon(uint256 _projectID, address _creator, string memory _name, string memory _symbol, uint256 _maxTokens, uint256 _reservedTokens) external {
        require(msg.sender == Falcon, "You are not allowed");

        contract721 newContract = new contract721 (_creator, _name, _symbol, notRevealedUri, _maxTokens, _reservedTokens, _projectID, walletUminty, tokenToUminty, Falcon);
        
        IFalconF(Falcon).setAddrContractViaFactory(_projectID, address(newContract));
        IFalconF(Falcon).setContractMintedViaFactory(_projectID, true);

    }


    //ADMIN ONLY

    function setFalcon(address _Falcon) public onlyOwner {
        Falcon = _Falcon;
    }

    function setWalletUminty(address _wallet) public onlyOwner {
        walletUminty = _wallet;
    }

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

    function setTokenToUminty(uint256 _value) public onlyOwner {
        tokenToUminty = _value;
    }



}
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"Falcon","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectID","type":"uint256"},{"internalType":"address","name":"_creator","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint256","name":"_maxTokens","type":"uint256"},{"internalType":"uint256","name":"_reservedTokens","type":"uint256"}],"name":"createContractViaFalcon","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"notRevealedUri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","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":"_Falcon","type":"address"}],"name":"setFalcon","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_URI","type":"string"}],"name":"setNotRevealedURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"setTokenToUminty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"setWalletUminty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tokenToUminty","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":[],"name":"walletUminty","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]