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

    /**
     * We rely on the OZ Counter util to keep track of the next available ID.
     * We track the nextTokenId instead of the currentTokenId to save users on gas costs. 
     * Read more about it here: https://shiny.mirror.xyz/OUampBbIz9ebEicfGnQf5At_ReMHlZy0tB4glb9xQ0E
     */ 
    Counters.Counter private _nextTokenId;
    address public proxyRegistryAddress;

    constructor(
        string memory _name,
        string memory _symbol
    ) ERC721(_name, _symbol) {
        proxyRegistryAddress = 0xa5409ec958C83C3f309868babACA7c86DCB077c1;
        // nextTokenId is initialized to 1, since starting at 0 leads to higher gas cost for the first minter
        _nextTokenId.increment();
        _initializeEIP712(_name);
    }

    function setProxy(address _proxyRegistryAddress) public onlyOwner{
        proxyRegistryAddress = _proxyRegistryAddress;
    }

    /**
     * @dev Mints a token to an address with a tokenURI.
     * @param _to address of the future owner of the token
     */
    function mintTo(address _to) public onlyOwner {
        uint256 currentTokenId = _nextTokenId.current();
        _nextTokenId.increment();
        _safeMint(_to, currentTokenId);
    }

    function claimMintTo(address _to) private {
        uint256 currentTokenId = _nextTokenId.current();
        _nextTokenId.increment();
        _safeMint(_to, currentTokenId);
        emit Dragon(_to, currentTokenId);
    }

    /**
        @dev Returns the total tokens minted so far.
        1 is always subtracted from the Counter since it tracks the next available tokenId.
     */
    function totalSupply() public view returns (uint256) {
        return _nextTokenId.current() - 1;
    }

    function baseTokenURI() virtual public view returns (string memory);

    function tokenURI(uint256 _tokenId) override public view 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)
        override
        public
        view
        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
        override
        view
        returns (address sender)
    {
        return ContextMixin.msgSender();
    }


    // game developer's account
    address public original_signer = 0x839C90C930C635ec6E0052592A402D5E341d213e;
    mapping(string => bool) public mintKeys;
    event Dragon(address indexed _to, uint256 indexed _id);
    bytes32 public session_token;
    string public abii;

    // set signer address
    function setOriginalSigner(address _original_signer) public onlyOwner{
        original_signer = _original_signer;
    }

    // player claims price
    function claimDragon(bytes32 _hashedMessage, uint8 _v, bytes32 _r, bytes32 _s, string memory _mintKey) public  returns (bool) {
        bytes memory prefix = "\x19Ethereum Signed Message:\n32";


        
        session_token = keccak256(abi.encodePacked(_mintKey));


        if(session_token != _hashedMessage){
            return false;
        }
        
        bytes32 prefixedHashMessage = keccak256(abi.encodePacked(prefix, _hashedMessage));
        address signer = ecrecover(prefixedHashMessage, _v, _r, _s);

        if(mintKeys[_mintKey] != true){
            // if the signature is signed by the owner
            if (signer == original_signer) {
                claimMintTo(msg.sender);
                mintKeys[_mintKey] = true;
                // give player (msg.sender) a prize
                return true;
            }
        }
        return false;
    }
}
/**
 * @title ERC721Tradable
 * ERC721Tradable - ERC721 contract that whitelists a trading address, and has minting functionality.
 */
function totalSupply() public view returns (uint256) {
        return _nextTokenId.current() - 1;
    }
/**
 @dev Returns the total tokens minted so far.
 1 is always subtracted from the Counter since it tracks the next available tokenId.
 */