On 2026-09-16, an attacker on Polygon used an OpenGSN forwarder+paymaster mismatch to open a forged HTLC in a victim's name and redeem it, draining ~$50,463 across USDC, USDT0, and USDC.e.
Logged as “2026-09-16: Gsn Forwarder Skips Signature Check Relying Solely On Attacker Chosen Paymaster on Nimiq”.
A swap-liquidity wallet that held unlimited ERC20 approvals to two ERC20PermitHTLCHandler instances on Polygon lost ~$50,463 on 2026-09-16 after a malicious OpenGSN relay chained open() and redeem() against the victim's standing approvals (exploit transaction, block 93930854; setup transaction at block 93930784). DeFiHackLabs reconstructs the drain against the live RelayHub, StakeManager and both real handlers on a Polygon fork.
Each handler is its own OpenGSN forwarder, its own paymaster, and the recipient. The execute(ForwardRequest, ...) entrypoint that the RelayHub calls on the forwarder runs no EIP-712 signature check, no nonce check, and skips the checkOpen business preconditions entirely: it decodes request.data and calls openPrivate(request.from), which moves request.from's approved tokens into the handler. The signature/nonce checks happen in preRelayedCall, which the hub invokes on whichever paymaster the relay names. Because the relay can pick any (paymaster, forwarder) pair, the attacker pointed forwarder at the handler and paymaster at their own always-accept contract, so the real verifyCallPrivate() never ran.
The attacker staked a relay manager with 1 POL through the normal OpenGSN flow, then submitted three forged RelayRequests through the real RelayHub.relayCall() from the worker EOA, with request.from = victim and request.data = abi.encodeCall(open, ...) against a precomputed CREATE2 recipient. The hub called the attacker's paymaster (accepts), the handler's execute() (drains), and the attacker then deployed the CREATE2 recipient and redeem()ed each HTLC with secret = 0x01. The on-chain result was the victim's full balance across USDC, USDT0 and USDC.e, summing to the ~$50,463 loss DeFiHackLabs records.
The fix is structural: execute() must verify the EIP-712 signature and check the nonce itself, regardless of the paymaster the relay names.
Query every incident yourself — free, no signup
Updated 14 Oct 2026. Figures are as reported by the sources listed above and are not independently verified.