abstract contract GeneticChain721 is
    ERC721SeqEnumerable,
    Ownable
{
    using ECDSA for bytes32;
    using State for State.Data;

    //-------------------------------------------------------------------------
    // fields
    //-------------------------------------------------------------------------

    // erc721 metadata
    string constant private __name   = "Pixel Personalities";
    string constant private __symbol = "PIXELPERSON";

    // metadata
    uint256 constant public projectId  = 13;
    string constant public artist      = "Silver";
    string constant public description = "1/1 animated pixels characters expressing their personality.";

    // mint price
    uint256 constant public memberPrice = .1 ether;

    // verification address
    address constant private _signer = 0x1A6d7E583aA172C00289DC7857A3D2f79772Da5c;

    // token limits
    uint256 public immutable publicMax;
    uint256 public immutable galleryMax;

    // contract state
    State.Data private _state;

    // track mint count per address
    mapping (address => uint256) private _mints;

    //-------------------------------------------------------------------------
    // modifiers
    //-------------------------------------------------------------------------

    modifier validTokenId(uint256 tokenId) {
        require(_exists(tokenId), "invalid token");
        _;
    }

    //-------------------------------------------------------------------------

    modifier approvedOrOwner(address operator, uint256 tokenId) {
        require(_isApprovedOrOwner(operator, tokenId));
        _;
    }

    //-------------------------------------------------------------------------
    // ctor
    //-------------------------------------------------------------------------

    constructor(
        uint256[2] memory tokenMax_)
        ERC721Sequential(__name, __symbol)
    {
        publicMax  = tokenMax_[0];
        galleryMax = tokenMax_[1];

        // start tokens at 1 index
        _owners.push();
    }

    //-------------------------------------------------------------------------
    // accessors
    //-------------------------------------------------------------------------

    /**
     * Get total gallery has minted.
     */
    function galleryMinted()
        public view returns (uint256)
    {
        return _state._gallery;
    }

    //-------------------------------------------------------------------------

    /**
     * Get total public has minted.
     */
    function publicMinted()
        public view returns (uint256)
    {
        return _state._public;
    }

    //-------------------------------------------------------------------------
    // security
    //-------------------------------------------------------------------------

    /**
     * Validate hash contains input data.
     */
    function validateHash(
            bytes32 msgHash,
            address sender,
            uint256 allocation,
            uint256 count)
        private pure returns(bool)
    {
        return ECDSA.toEthSignedMessageHash(
            keccak256(abi.encodePacked(sender, allocation, count))) == msgHash;
    }

    //-------------------------------------------------------------------------

    /**
     * Validate message was signed by signer.
     */
    function validateSigner(bytes32 msgHash, bytes memory signature)
        private pure returns(bool)
    {
        return msgHash.recover(signature) == _signer;
    }

    //-------------------------------------------------------------------------
    // minting
    //-------------------------------------------------------------------------

    /**
     * Mint count tokens using securely signed message.
     */
    function secureMint(
            bytes32 msgHash,
            bytes calldata signature,
            uint256 allocation,
            uint256 count)
        payable external
    {
        require(memberPrice * count == msg.value, "insufficient funds");
        require(_state._public + count <= publicMax, "exceed public supply");
        require(_mints[msg.sender] + count <= allocation, "exceed allocation");
        require(validateSigner(msgHash, signature), "invalid signer");
        require(validateHash(msgHash, msg.sender, allocation, count), "invalid hash");
        _state.addPublic(count);
        unchecked {
            _mints[msg.sender] += count;
        }
        for (uint256 i = 0; i < count; ++i) {
            _safeMint(msg.sender);
        }
    }

    //-------------------------------------------------------------------------

    /**
     * @dev Mints a token to an address.
     * @param _to address of the future owner of the token
     */
    function galleryMintTo(address _to, uint256 count)
        public onlyOwner
    {
        require(_state._gallery + count <= galleryMax, "exceed gallery supply");
        _state.addGallery(count);
        for (uint256 i = 0; i < count; ++i) {
            _safeMint(_to);
        }
    }

    //-------------------------------------------------------------------------
    // money
    //-------------------------------------------------------------------------

    /**
     * Pull money out of this contract.
     */
    function withdraw(address to, uint256 amount)
        public onlyOwner
    {
        require(amount > 0, "amount empty");
        require(amount <= address(this).balance, "amount exceeds balance");
        require(to != address(0), "address null");
        payable(to).transfer(amount);
    }

}
/**
 * @title GeneticChain721
 *
 * ERC721 contract with various features:
 *  - low-gas implmentation
 *  - off-chain whitelist verify (secure minting)
 *  - dynamic token allocation
 *  - gallery allocation
 *  - protected controlled burns
 *  - simple funds withdrawl
 */
function galleryMintTo(address _to, uint256 count)
    public onlyOwner
{
    require(_state._gallery + count <= galleryMax, "exceed gallery supply");
    _state.addGallery(count);
    for (uint256 i = 0; i < count; ++i) {
        _safeMint(_to);
    }
}

/**
 * @dev Mints a token to an address.
 * @param _to address of the future owner of the token
 */