//SPDX-License-Identifier: MIT
pragma solidity 0.8.13;


interface IERC20 {


    /**
    * @dev Returns the decimals.
    */
    function decimals() external view returns (uint256);

    /**
    * @dev Returns the totalSupply
    */
    function totalSupply() external view returns (uint256);

    /**
    * @dev Returns the amount of tokens owned by `account`.
    */
    function balanceOf(address account) external view returns (uint256);

    /**
        * @dev Moves `amount` tokens from the caller's account to `recipient`.
        *
        * Returns a boolean value indicating whether the operation succeeded.
        *
        * Emits a {Transfer} event.
        */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
        * @dev Returns the remaining number of tokens that `spender` will be
        * allowed to spend on behalf of `owner` through {transferFrom}. This is
        * zero by default.
        *
        * This value changes when {approve} or {transferFrom} are called.
        */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
        * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
        *
        * Returns a boolean value indicating whether the operation succeeded.
        *
        * IMPORTANT: Beware that changing an allowance with this method brings the risk
        * that someone may use both the old and the new allowance by unfortunate
        * transaction ordering. One possible solution to mitigate this race
        * condition is to first reduce the spender's allowance to 0 and set the
        * desired value afterwards:
        * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
        *
        * Emits an {Approval} event.
        */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
        * @dev Moves `amount` tokens from `sender` to `recipient` using the
        * allowance mechanism. `amount` is then deducted from the caller's
        * allowance.
        *
        * Returns a boolean value indicating whether the operation succeeded.
        *
        * Emits a {Transfer} event.
        */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
        * @dev Emitted when `value` tokens are moved from one account (`from`) to
        * another (`to`).
        *
        * Note that `value` may be zero.
        */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
        * @dev Emitted when the allowance of a `spender` for an `owner` is set by
        * a call to {approve}. `value` is the new allowance.
        */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

/*
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }*/

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

interface IUniswapV2Pair {
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
}

contract KPTNFTB is ERC721Enumerable, Ownable{
    using SafeMath for uint256;
    using Counters for Counters.Counter;
    using SafeERC20 for IERC20;

    modifier onlyClevel() {
        require(msg.sender == walletA || msg.sender == owner());
    _;
    }

    Counters.Counter private _tokenIds;

    address walletA;
    address uniswapExchangeAddress;
    uint256 public phaseEndsAt = 10000;
    uint256 private mintPriceEth;
    uint256 public mintNormalPriceEth = 0.015 ether;
    uint256 public mintSuperPriceEth = 0.025 ether;
    uint256 public mintMysteryPriceEth = 0.045 ether;
    uint256 public mintSpecialPriceEth = 0.065 ether;
    uint256 public mintEmptyPriceEth = 0.005 ether;
    IERC20 public kryptoPetTokenContract;
    string private baseTokenURI;

    event minting(uint256 id, address indexed customer);

    constructor(address _walletA, address _kryptoPetTokenContract, string memory _baseTokenURI, address _uniswapExchangeAddress) ERC721("KPTNFTB", "KPTNFTB") {
        baseTokenURI = _baseTokenURI;
        walletA = _walletA;
        kryptoPetTokenContract = IERC20(_kryptoPetTokenContract);
        uniswapExchangeAddress = _uniswapExchangeAddress;
    }

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

    function tokensOfOwner(address _owner) external view returns(uint256[] memory ) {
        uint256 tokenCount = balanceOf(_owner);
        if (tokenCount == 0) {
            // Return an empty array
            return new uint256[](0);
        } else {
            uint256[] memory result = new uint256[](tokenCount);
            uint256 index;
            for (index = 0; index < tokenCount; index++) {
                result[index] = tokenOfOwnerByIndex(_owner, index);
            }
            return result;
        }
    }

    function airdrop_nft(uint256 _amount, address _destination) public payable returns (uint256[] memory minted)  {
        require(_tokenIds.current() < phaseEndsAt, "Minting Phase Ended");
        require(_amount>0, "NFT: Invalid amount to mint.");
        require(msg.sender == owner(), "Who are you?");
        // require(msg.value==_amount*mintPriceEth , "NFT: Invalid value.");

        uint256[] memory tokensMinted;
        for (uint256 i = 0; i < _amount; i++) {
            _tokenIds.increment();
            uint256 newPack = _tokenIds.current();
            _mint(_destination, newPack);

            emit minting(newPack, _destination);
        }
        return tokensMinted;
    }

    function mint_box_for_eth(uint256 _amount, uint256 _boxType) public payable returns (uint256[] memory minted)  {

        if (_boxType == 1) {
          mintPriceEth = mintNormalPriceEth;
        } else if (_boxType == 2) {
          mintPriceEth = mintSuperPriceEth;
        } else if (_boxType == 3) {
          mintPriceEth = mintMysteryPriceEth;
        } else if (_boxType == 4) {
          mintPriceEth = mintEmptyPriceEth;
        } else if (_boxType == 5) {
          mintPriceEth = mintSpecialPriceEth;
        }

        require(_tokenIds.current() < phaseEndsAt, "Minting Phase Ended");
        require(_amount>0, "NFT: Invalid amount to mint.");
        require(msg.value==_amount*mintPriceEth , "NFT: Invalid value.");

        uint256[] memory tokensMinted;
        for (uint256 i = 0; i < _amount; i++) {
            _tokenIds.increment();
            uint256 newPack = _tokenIds.current();
            _mint(msg.sender, newPack);

            emit minting(newPack, msg.sender);
        }
        return tokensMinted;
    }

    function getTokensForEth(uint _amountETH) public view returns(uint256){

        IUniswapV2Pair pair = IUniswapV2Pair(uniswapExchangeAddress);
        IERC20 token0 = IERC20(pair.token0());
        (uint Res0, uint Res1,) = pair.getReserves();

        // decimals
        uint res1 = Res1*(10**token0.decimals());
        uint ethPrice = ((10e18*res1)/Res0); // return amount of ETH needed to buy KPET
        uint amountTokens = (_amountETH*10e18)/ethPrice;
        return amountTokens;
    }

    function mint_box_for_kpet(uint256 _amount, uint256 _boxType) public payable returns (uint256[] memory minted)  {

      if (_boxType == 1) {
        mintPriceEth = mintNormalPriceEth;
      } else if (_boxType == 2) {
        mintPriceEth = mintSuperPriceEth;
      } else if (_boxType == 3) {
        mintPriceEth = mintMysteryPriceEth;
      } else if (_boxType == 4) {
        mintPriceEth = mintEmptyPriceEth;
      } else if (_boxType == 5) {
        mintPriceEth = mintSpecialPriceEth;
      }

        require(_tokenIds.current() < phaseEndsAt, "Minting Phase Ended");
        require(_amount>0, "NFT: Invalid amount to mint.");

        uint256 numTokensNeeded= getTokensForEth(_amount*mintPriceEth);
        // uint256 numTokensNeeded= 10;
        require(kryptoPetTokenContract.balanceOf(msg.sender)>=numTokensNeeded, "Insufficient token balance.");
        kryptoPetTokenContract.transferFrom(msg.sender, address(this) , numTokensNeeded);

        uint256[] memory tokensMinted;
        for (uint256 i = 0; i < _amount; i++) {
            _tokenIds.increment();
            uint256 newPack = _tokenIds.current();
            _mint(msg.sender, newPack);

            emit minting(newPack, msg.sender);
        }
        return tokensMinted;
    }

    function setEthNormalMintPrice(uint256 _priceEth) public onlyOwner {
         mintNormalPriceEth =_priceEth;
    }

    function setEthSuperMintPrice(uint256 _priceEth) public onlyOwner {
         mintSuperPriceEth =_priceEth;
    }

    function setEthMysteryMintPrice(uint256 _priceEth) public onlyOwner {
         mintMysteryPriceEth =_priceEth;
    }

    function setEthEmptyMintPrice(uint256 _priceEth) public onlyOwner {
         mintEmptyPriceEth =_priceEth;
    }

    function setEthSpecialMintPrice(uint256 _priceEth) public onlyOwner {
         mintSpecialPriceEth =_priceEth;
    }

    function withdraw_all() public onlyClevel {

        if(address(this).balance>0){
            uint256 amountEthA = address(this).balance;
            payable(walletA).transfer(amountEthA);
        }

        uint256 tokenBalance = kryptoPetTokenContract.balanceOf(address(this));
        if (tokenBalance>0){
            kryptoPetTokenContract.safeTransfer(walletA, tokenBalance);
        }

    }

    function setBaseTokenURI(string memory _baseTokenURI) public onlyOwner {
        baseTokenURI = _baseTokenURI;
    }

    function setKryptoPetContractAddress(address _uniswapExchangeAddress) public onlyOwner {
        uniswapExchangeAddress = _uniswapExchangeAddress;
    }

    function setUniswapAddress(address _kryptoPetTokenContract) public onlyOwner {
        kryptoPetTokenContract = IERC20(_kryptoPetTokenContract);
    }

    function setWalletA(address _walletA) public {
        require (msg.sender == walletA, "Who are you?");
        require (_walletA != address(0x0), "Invalid wallet");
        walletA = _walletA;
    }

    function setPhaseEndsAt(uint256 _phaseEnds) public onlyOwner{
        phaseEndsAt = _phaseEnds;
    }
}
[{"inputs":[{"internalType":"address","name":"_walletA","type":"address"},{"internalType":"address","name":"_kryptoPetTokenContract","type":"address"},{"internalType":"string","name":"_baseTokenURI","type":"string"},{"internalType":"address","name":"_uniswapExchangeAddress","type":"address"}],"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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"customer","type":"address"}],"name":"minting","type":"event"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_destination","type":"address"}],"name":"airdrop_nft","outputs":[{"internalType":"uint256[]","name":"minted","type":"uint256[]"}],"stateMutability":"payable","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":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountETH","type":"uint256"}],"name":"getTokensForEth","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":"kryptoPetTokenContract","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintEmptyPriceEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintMysteryPriceEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintNormalPriceEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintSpecialPriceEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintSuperPriceEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_boxType","type":"uint256"}],"name":"mint_box_for_eth","outputs":[{"internalType":"uint256[]","name":"minted","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_boxType","type":"uint256"}],"name":"mint_box_for_kpet","outputs":[{"internalType":"uint256[]","name":"minted","type":"uint256[]"}],"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":"phaseEndsAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"_baseTokenURI","type":"string"}],"name":"setBaseTokenURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_priceEth","type":"uint256"}],"name":"setEthEmptyMintPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_priceEth","type":"uint256"}],"name":"setEthMysteryMintPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_priceEth","type":"uint256"}],"name":"setEthNormalMintPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_priceEth","type":"uint256"}],"name":"setEthSpecialMintPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_priceEth","type":"uint256"}],"name":"setEthSuperMintPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_uniswapExchangeAddress","type":"address"}],"name":"setKryptoPetContractAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_phaseEnds","type":"uint256"}],"name":"setPhaseEndsAt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_kryptoPetTokenContract","type":"address"}],"name":"setUniswapAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_walletA","type":"address"}],"name":"setWalletA","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":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","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":[],"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_all","outputs":[],"stateMutability":"nonpayable","type":"function"}]