// File: contracts/Minter.sol



// SPDX-License-Identifier: AGPL-3.0
// ©2023 Ponderware Ltd

pragma solidity ^0.8.17;

import "./lib/SignaturePass.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

interface IEthyToken {
    function mint (address to, bytes31 parameters, bytes32 extension) external;
}

contract EthyMinter is SignaturePass, Ownable {

    address immutable EthyTokenAddress;

    constructor (address ethyTokenAddress, address passSignerAddress)
        SignaturePass(passSignerAddress, "ETHY")
        {
            EthyTokenAddress = ethyTokenAddress;
        }

    function mint (bytes31 parameters, uint nonce, bytes memory pass) public {
        validatePass(msg.sender, nonce, "", pass);
        IEthyToken(EthyTokenAddress).mint(msg.sender, parameters, "");
    }

    function extendNonces (uint256 count) public onlyOwner {
        _extendNonces(count);
    }

    function setPassSigner (address passSignerAddress) public onlyOwner {
        _setPassSigner(passSignerAddress);
    }

}

// File: contracts/lib/SignaturePass.sol



// SPDX-License-Identifier: AGPL-3.0
// ©2023 Ponderware Ltd

pragma solidity ^0.8.17;

contract SignaturePass {

    address internal passSigner;

    string internal PREFIX;

    bytes32[] internal Nonces;

    uint256 public availableNonces = 0;

    function totalNonces () public view returns (uint256) {
        return Nonces.length * 256;
    }

    function nonceBlock (uint256 index) public view returns (bytes32) {
        return Nonces[index];
    }

    constructor (address passSignerAddress, string memory passPrefix) {
        PREFIX = passPrefix;
        passSigner = passSignerAddress;
    }

    bytes32 constant Mask =  0x0000000000000000000000000000000000000000000000000000000000000001;

    function clearNonce (uint256 nonce) private {
        uint256 wordIndex = nonce / 256;
        uint256 bitIndex = nonce % 256;
        bytes32 mask = ~(Mask << (255 - bitIndex));
        Nonces[wordIndex] &= mask;
    }

    function nonceAvailable (uint256 nonce) public view returns (bool) {
        uint256 wordIndex = nonce / 256;
        require(wordIndex < Nonces.length, "Nonce out of range");
        uint256 bitIndex = nonce % 256;
        bytes32 mask = Mask << (255 - bitIndex);
        return (mask & Nonces[wordIndex]) != 0;
    }

    function _extendNonces (uint256 count) internal {
        for (uint i = 0; i < count; i++) {
            Nonces.push(bytes32(type(uint256).max));
        }
        availableNonces += (count * 256);
    }

    function _setPassSigner (address newPassSigner) internal {
        passSigner = newPassSigner;
    }

    function validPass (address user,
                        uint256 nonce,
                        bytes32 payload,
                        bytes memory pass)
        public view returns (bool)
    {
        bytes32 m = keccak256(
            abi.encodePacked(
                "\x19Ethereum Signed Message:\n32",
                keccak256(
                          abi.encodePacked(PREFIX, user, nonce, payload)
                )
            )
        );

        uint8 v;
        bytes32 r;
        bytes32 s;

        require(pass.length == 65, "Invalid Pass Structure");

        assembly {
            r := mload(add(pass, 32))
            s := mload(add(pass, 64))
            v := byte(0, mload(add(pass, 96)))
        }

        return (ecrecover(m, v, r, s) == passSigner);
    }

    function validatePass (address user, uint256 nonce, bytes32 payload, bytes memory pass) internal {
        require(nonceAvailable(nonce), "Nonce reuse");
        require(validPass(user, nonce, payload, pass), "Invalid Pass");
        clearNonce(nonce);
        availableNonces--;
    }
}

// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
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 Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        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);
    }
}

// File: @openzeppelin/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}