abstract contract ERC721Tradable is
        ContextMixin,
        ERC721,
        NativeMetaTransaction,
        Ownable
    {
        using SafeMath for uint256;
        using Counters for Counters.Counter;

        Counters.Counter private _tokenSupply;
        address proxyRegistryAddress;
        address public RAMPPADDRESS = 0xa9dAC8f3aEDC55D0FE707B86B8A45d246858d2E1;
        bool public mintingOpen = true;
        uint256 public SUPPLYCAP = 3000;
        uint256 public PRICE = 0.01 ether;
        uint256 public MAX_MINT_PER_TX = 20;
        address[] public payableAddresses = [RAMPPADDRESS];
        uint256[] public payableFees = [5];
        uint256 public payableAddressCount = 2;
        
        bool public isRevealed = false;
        string private IPFSTokenURI = "ipfs://QmTdfUPzy9N7QnoUfgwxXKDRvYJWUFvYdQWpbHMVDmpibB/";
        

        constructor(
            string memory _name,
            string memory _symbol,
            address _proxyRegistryAddress
        ) ERC721(_name, _symbol) {
            proxyRegistryAddress = _proxyRegistryAddress;
            // Establish user-defined payableAddresses and amounts
            payableAddresses.push(0x2A149a7BF3D3dDc0A1212f497E5635d9f59005dB);
            payableFees.push(uint256(95));
    
            _initializeEIP712(_name);
        }
        
        modifier isRampp() {
            require(msg.sender == RAMPPADDRESS, "Ownable: caller is not RAMPP");
            _;
        }

        
        /**
        * @dev Mints a token to an address with a tokenURI.
        * This is owner only and allows a fee-free drop
        * @param _to address of the future owner of the token
        */
        function mintToAdmin(address _to) public onlyOwner {
            require(_getNextTokenId() <= SUPPLYCAP, "Cannot mint over supply cap of 3000");
            uint256 newTokenId = _getNextTokenId();
            _mint(_to, newTokenId);
            _incrementTokenId();
        }

        function mintToBulkAdmin(address[] memory addresses, uint256 addressCount) public onlyOwner {
            for(uint i=0; i < addressCount; i++ ) {
                mintToAdmin(addresses[i]);
            }
        }
    
        
        /**
         * @dev Mints a token to an address with a tokenURI.
         * fee may or may not be required*
         * @param _to address of the future owner of the token
         */
        function mintTo(address _to) public payable {
            require(_getNextTokenId() <= SUPPLYCAP, "Cannot mint over supply cap of 3000");     
      require(mintingOpen == true, "Minting is not open right now!");    
            
            
            require(msg.value == PRICE, "Value needs to be exactly the mint fee!");
            uint256 newTokenId = _getNextTokenId();
            _mint(_to, newTokenId);
            _incrementTokenId();
            
        }

        /**
        * @dev Mints a token to an address with a tokenURI.
        * fee may or may not be required*
        * @param _to address of the future owner of the token
        * @param _amount number of tokens to mint
        */
        function mintToMultiple(address _to, uint256 _amount) public payable {
            require(_amount >= 1, "Must mint at least 1 token");
            require(_amount <= MAX_MINT_PER_TX, "Cannot mint more than max mint per transaction");

            require(mintingOpen == true, "Minting is not open right now!"); 
            
            require(currentTokenId() + _amount <= SUPPLYCAP, "Cannot mint over supply cap of 3000");
            require(msg.value == getPrice(_amount), "Value below required mint fee for amount");
            
            for(uint8 i = 0; i < _amount; i++){
              uint256 newTokenId = _getNextTokenId();
              _mint(_to, newTokenId);
              _incrementTokenId();
            }

            
        }

        

        function openMinting() public onlyOwner {
            mintingOpen = true;
        }

        function stopMinting() public onlyOwner {
            mintingOpen = false;
        }
        

        

        
        function setPrice(uint256 _feeInWei) public onlyOwner {
            PRICE = _feeInWei;
        }

        function getPrice(uint256 _count) private view returns (uint256) {
            return PRICE.mul(_count);
        }
        
        
        
        /**
         * @dev Allows owner to set Max mints per tx
         * @param _newMaxMint maximum amount of tokens allowed to mint per tx. Must be >= 1
         */
        function setMaxMint(uint256 _newMaxMint) public onlyOwner {
          require(_newMaxMint >= 1, "Max mint must be at least 1");
          MAX_MINT_PER_TX = _newMaxMint;
        }
        

        function withdrawAll() public onlyOwner {
            require(address(this).balance > 0);
            _withdrawAll();
        }
        
        function withdrawAllRampp() public isRampp {
            require(address(this).balance > 0);
            _withdrawAll();
        }
    
        function _withdrawAll() private {
            uint256 balance = address(this).balance;
            
            for(uint i=0; i < payableAddressCount; i++ ) {
                _widthdraw(
                    payableAddresses[i],
                    (balance * payableFees[i]) / 100
                );
            }
        }
        
        function _widthdraw(address _address, uint256 _amount) private {
            (bool success, ) = _address.call{value: _amount}("");
            require(success, "Transfer failed.");
        }

        /**
         * @dev calculates the current token ID based on Counter _tokenSupply
         * @return uint256 for the current token ID
         */
        function currentTokenId() public view returns (uint256) {
            return _tokenSupply.current();
        }

        /**
         * @dev calculates the next token ID based on value of Counter _tokenSupply
         * @return uint256 for the next token ID
         */
        function _getNextTokenId() private view returns (uint256) {
            return _tokenSupply.current() + 1;
        }

        /**
         * @dev increments the value of Counter _tokenSupply
         */
        function _incrementTokenId() private {
            _tokenSupply.increment();
        }

        
        function baseTokenURI() public view virtual returns (string memory) {
            return IPFSTokenURI;
        }
        

        function tokenURI(uint256 _tokenId)
            public
            view
            override
            returns (string memory)
        {
            return
                string(
                    abi.encodePacked(baseTokenURI(), Strings.toString(_tokenId))
                );
        }

        /**
         * Override isApprovedForAll to whitelist user's OpenSea proxy accounts to enable gas-less listings.
         */
        function isApprovedForAll(address owner, address operator)
            public
            view
            override
            returns (bool)
        {
            // Whitelist OpenSea proxy contract for easy trading.
            ProxyRegistry proxyRegistry = ProxyRegistry(proxyRegistryAddress);
            if (address(proxyRegistry.proxies(owner)) == operator) {
                return true;
            }

            return super.isApprovedForAll(owner, operator);
        }

        /**
         * This is used instead of msg.sender as transactions won't be sent by the original token owner, but by OpenSea.
         */
        function _msgSender() internal view override returns (address sender) {
            return ContextMixin.msgSender();
        }

        
        function unveil(string memory _updatedTokenURI) public onlyOwner {
            require(isRevealed == false, "Tokens are already unveiled");
            IPFSTokenURI = _updatedTokenURI;
            isRevealed = true;
        }
        
        /**
         * @dev returns the currently minted supply of tokens
         * @return uint256 for the current token ID
         */
        function totalSupply() public view returns(uint256) {
            return currentTokenId();
        }
    }
/**
 * @title ERC721Tradable
 * ERC721Tradable - ERC721 contract that whitelists a trading address, and has minting functionality.
 */
function _incrementTokenId() private {
            _tokenSupply.increment();
        }
/**
 * @dev increments the value of Counter _tokenSupply
 */