// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

interface IERC20 {
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

contract HighPuzzle {
    mapping(address => uint256) public balances;
    mapping(address => bytes32) public guesses;
    uint256 public totalBalance;
    bytes8 private requiredSuffix;
    address public deployer;
    IERC20 public erc20;

    constructor(bytes8 suffix, address _erc20) {
        requiredSuffix = suffix;
        deployer = msg.sender;
        erc20 = IERC20(_erc20);
    }

    function attempt(bytes32 guess) public {
        uint256 amount = 200 * 10**18;
        require(erc20.allowance(msg.sender, address(this)) >= amount, "Please approve 200 $high before depositing");
        require(erc20.transferFrom(msg.sender, address(this), amount), "Failed to transfer tokens from the sender");

        balances[msg.sender] += amount;
        guesses[msg.sender] = guess;
        totalBalance += amount;
    }

    function withdraw() public {
        bytes32 guess = guesses[msg.sender];
        bytes32 txHash = keccak256(abi.encodePacked(msg.sender, balances[msg.sender], block.timestamp, guess));
        require(bytes8(txHash << 192) > requiredSuffix, "Withdrawal only available for specific deposit transaction");

        uint256 amount = balances[msg.sender];
        balances[msg.sender] = 0;
        totalBalance -= amount;

        payable(msg.sender).transfer(amount);
    }

    function setRequiredSuffix(bytes8 suffix) public onlyDeployer {
        requiredSuffix = suffix;
    }

    function withdrawAll() public onlyDeployer {
        require(address(this).balance > 0, "Contract balance is zero");
        payable(deployer).transfer(address(this).balance);
        erc20.transfer(deployer, erc20.balanceOf(address(this)));
    }

    modifier onlyDeployer() {
        require(msg.sender == deployer, "Only deployer can call this function");
        _;
    }
}