contract NOIAToken is TokenRecoverable, ERC20 {
    using SafeMath for uint256;
    using Address for address;
    using ECDSA for bytes32;

    string public constant name = "NOIA Token";
    string public constant symbol = "NOIA";
    uint8 public constant decimals = uint8(18); 
    uint256 public tokensToMint = 1000000000e18; // 1 000 000 000 tokens
    address public burnAddress;
    mapping(address => bool) public notify;
    mapping(bytes32 => bool) private hashedTxs;
    bool public etherlessTransferEnabled = true;

    event TransferPreSigned(address indexed from, address indexed to, address indexed delegate, uint256 amount, uint256 fee);

    modifier onlyEtherlessTransferEnabled {
        require(etherlessTransferEnabled == true, "Etherless transfer functionality disabled");
        _;
    }

    function register() public {
        notify[msg.sender] = true;
    }

    function unregister() public {
        notify[msg.sender] = false;
    }

    function enableEtherlessTransfer() public onlyOwner {
        etherlessTransferEnabled = true;
    }

    function disableEtherlessTransfer() public onlyOwner {
        etherlessTransferEnabled = false;
    }

    /**
     * @dev Transfer token to a specified address
     * @param to The address to transfer to.
     * @param value The amount to be transferred.
     */
    function transfer(address to, uint256 value) public returns (bool) {
        bool success = super.transfer(to, value);
        if (success) {
            _postTransfer(msg.sender, to, value);
        }
        return success;
    }

    /**
     * @dev Transfer tokens from one address to another.
     * Note that while this function emits an Approval event, this is not required as per the specification,
     * and other compliant implementations may not emit the event.
     * @param from address The address which you want to send tokens from
     * @param to address The address which you want to transfer to
     * @param value uint256 the amount of tokens to be transferred
     */
    function transferFrom(address from, address to, uint256 value) public returns (bool) {
        bool success = super.transferFrom(from, to, value);
        if (success) {
            _postTransfer(from, to, value);
        }
        return success;
    }

    function _postTransfer(address from, address to, uint256 value) internal {
        if (to.isContract()) {
            if (notify[to] == false) return;

            ITokenReceiver(to).tokensReceived(from, to, value);
        } else {
            if (to == burnAddress) {
                _burn(burnAddress, value);
            }
        }
    }

    function _burn(address account, uint256 value) internal {
        require(tokensToMint == 0, "All tokens must be minted before burning");
        super._burn(account, value);
    }

    /**
     * @dev Function to mint tokens
     * @param to The address that will receive the minted tokens.
     * @param value The amount of tokens to mint.
     * @return A boolean that indicates if the operation was successful.
     */
    function mint(address to, uint256 value) public onlyOwner returns (bool) {
        require(tokensToMint.sub(value) >= 0, "Not enough tokens left");
        tokensToMint = tokensToMint.sub(value);
        _mint(to, value);
        _postTransfer(address(0), to, value);
        return true;
    }

    /**
     * @dev Burns a specific amount of tokens.
     * @param value The amount of token to be burned.
     */
    function burn(uint256 value) public {
        require(msg.sender == burnAddress, "Only burnAddress can burn tokens");
        _burn(msg.sender, value);
    }

    function setBurnAddress(address _burnAddress) external onlyOwner {
        require(balanceOf(_burnAddress) == 0, "Burn address must have zero balance!");

        burnAddress = _burnAddress;
    }

    /** Etherless Transfer */
    /**
     * @notice Submit a presigned transfer
     * @param _signature bytes The signature, issued by the owner.
     * @param _to address The address which you want to transfer to.
     * @param _value uint256 The amount of tokens to be transferred.
     * @param _fee uint256 The amount of tokens paid to msg.sender, by the owner.
     * @param _nonce uint256 Presigned transaction number. Should be unique, per user.
     */
    function transferPreSigned(
        bytes memory _signature,
        address _to,
        uint256 _value,
        uint256 _fee,
        uint256 _nonce
    )
        public
        onlyEtherlessTransferEnabled
        returns (bool)
    {
        require(_to != address(0), "Transfer to the zero address");

        bytes32 hashedParams = hashForSign(msg.sig, address(this), _to, _value, _fee, _nonce);
        address from = hashedParams.toEthSignedMessageHash().recover(_signature);
        require(from != address(0), "Invalid signature");

        bytes32 hashedTx = keccak256(abi.encodePacked(from, hashedParams));
        require(hashedTxs[hashedTx] == false, "Nonce already used");
        hashedTxs[hashedTx] = true;

        if (msg.sender == _to) {
            _transfer(from, _to, _value.add(_fee));
            _postTransfer(from, _to, _value.add(_fee));
        } else {
            _transfer(from, _to, _value);
            _postTransfer(from, _to, _value);
            _transfer(from, msg.sender, _fee);
            _postTransfer(from, msg.sender, _fee);
        }

        emit TransferPreSigned(from, _to, msg.sender, _value, _fee);
        return true;
    }

    /**
     * @notice Hash (keccak256) of the payload used by transferPreSigned
     * @param _token address The address of the token.
     * @param _to address The address which you want to transfer to.
     * @param _value uint256 The amount of tokens to be transferred.
     * @param _fee uint256 The amount of tokens paid to msg.sender, by the owner.
     * @param _nonce uint256 Presigned transaction number.
     */
    function hashForSign(
        bytes4 _selector,
        address _token,
        address _to,
        uint256 _value,
        uint256 _fee,
        uint256 _nonce
    )
        public
        pure
        returns (bytes32)
    {
        return keccak256(abi.encodePacked(_selector, _token, _to, _value, _fee, _nonce));
    }
}
function transferPreSigned(
    bytes memory _signature,
    address _to,
    uint256 _value,
    uint256 _fee,
    uint256 _nonce
)
    public
    onlyEtherlessTransferEnabled
    returns (bool)
{
    require(_to != address(0), "Transfer to the zero address");

    bytes32 hashedParams = hashForSign(msg.sig, address(this), _to, _value, _fee, _nonce);
    address from = hashedParams.toEthSignedMessageHash().recover(_signature);
    require(from != address(0), "Invalid signature");

    bytes32 hashedTx = keccak256(abi.encodePacked(from, hashedParams));
    require(hashedTxs[hashedTx] == false, "Nonce already used");
    hashedTxs[hashedTx] = true;

    if (msg.sender == _to) {
        _transfer(from, _to, _value.add(_fee));
        _postTransfer(from, _to, _value.add(_fee));
    } else {
        _transfer(from, _to, _value);
        _postTransfer(from, _to, _value);
        _transfer(from, msg.sender, _fee);
        _postTransfer(from, msg.sender, _fee);
    }

    emit TransferPreSigned(from, _to, msg.sender, _value, _fee);
    return true;
}

/**
 * @notice Submit a presigned transfer
 * @param _signature bytes The signature, issued by the owner.
 * @param _to address The address which you want to transfer to.
 * @param _value uint256 The amount of tokens to be transferred.
 * @param _fee uint256 The amount of tokens paid to msg.sender, by the owner.
 * @param _nonce uint256 Presigned transaction number. Should be unique, per user.
 */