
Date: September 16, 2026
Chain: Polygon
Loss: 26,130.64 USDC, 24,332.49 USDT0 and 0.66 USDC.e, about $50,463
Class: signature checks placed in a gas-payment hook the caller gets to replace
To become a relay operator on OpenGSN's Polygon deployment, you stake 1 POL, which is refundable, and register a worker. On September 16, 2026, an attacker did exactly that, deployed a paymaster that said yes to everything, and used the two together to empty a Nimiq partner's swap-liquidity wallet of 26,130.64 USDC, 24,332.49 USDT0 and 0.66 USDC.e.
Nimiq's handler contracts did check signatures and nonces before moving tokens on someone's behalf. They just did it inside the paymaster hook, and in OpenGSN the paymaster is named by whoever submits the request. The attacker named their own, and the checks that protected the partner's wallet never ran.
Nimiq is a browser-based crypto payments ecosystem whose wallet lets people hold, send and swap NIM, BTC and stablecoins. On Polygon, its contracts power gas-free stablecoin transfers and in-wallet atomic swaps built on HTLCs, hashed timelock contracts that lock funds until someone reveals a secret matching a stored hash.
The gas-free part runs on OpenGSN, a meta-transaction framework. A user signs a request off-chain, a relay worker submits it on-chain through a contract called RelayHub, and a paymaster contract decides whether to cover the gas. A forwarder sits in front of the target contract to vouch for who really sent the request. According to Verichains' analysis, Nimiq's handlers played all three roles at once, forwarder, paymaster and recipient, and put the signature check, the nonce check and the business preconditions in the paymaster hook, preRelayedCall().
The swap-liquidity wallet at 0x24Cb...6773, run by a Nimiq partner, held standing, effectively unlimited token approvals to both handlers, ERC20PermitHTLCHandler for USDC and ERC20MetaHTLCHandler for USDT0 and USDC.e. That let the handlers move the wallet's tokens into HTLCs whenever a properly authorized request came in.
The handler's execute() function is the forwarder entry point that actually moves funds. Verichains found that it discarded the signature it was handed, never checked the nonce, and was guarded only by onlyRelayHub, a modifier confirming that the call came through the official RelayHub. Every relay request carries its own paymaster field, and RelayHub calls preRelayedCall() on whichever paymaster the request names. Anyone running a relay can name any paymaster, so a request that came through RelayHub said nothing about who had authorized it.
Think of a building where the security guard who checks ID is hired by each visitor. Visitors who bring a guard of their own walk through the turnstile without anyone looking at their ID, and the turnstile only checks that they came in through the front door. The idea to take from this case is that authentication belongs inside the function that moves the money, anchored to something the caller can't swap out, and never in a component the caller selects.
The same contract shows the right version of this. Verichains notes that its relayWithoutGsn() path performs the correct checks, which means the logic was split across two callbacks and only one of them guarded execute().
In a setup transaction, the attacker's EIP-7702-delegated wallet, 0x2258...C45D, staked the minimum and registered as a relay worker with OpenGSN's StakeManager and RelayHub. The attacker also deployed a paymaster whose preRelayedCall() accepted every request.
The exploit transaction then pushed forged open() requests through RelayHub with request.from set to the partner's liquidity wallet. RelayHub checked the worker registration, the stake and the gas limits, then asked the attacker's paymaster, which approved. execute() decoded the request and opened HTLCs for the wallet's full USDC, USDT0 and USDC.e balances, pulling the tokens in with transferFrom against the wallet's existing approvals.
Each HTLC named a precomputed CREATE2 address controlled by the attacker as recipient, with a hashlock whose secret was 0x01. The attacker redeemed them with that secret through a non-relayed call. The gas price on the relayed calls was set to zero, so the RelayHub fee came to nothing and the attacker's paymaster deposit was never touched. Verichains adds that a forged relayed redeem could have paid out any open HTLC to an arbitrary address, and that the liquidity wallet was simply the largest pot.
On September 17, Nimiq said on X that it was investigating an OpenGSN-related security issue and had temporarily disabled all stablecoin transactions using Gas Abstraction across Nimiq Pay and Nimiq Wallet. Two days later it confirmed that about $50.4K in stablecoins had been stolen from the liquidity wallet of a Nimiq partner on Polygon, that it had filed reports with the relevant security and compliance channels and was working to trace and, where possible, recover the funds, and that its assessment so far showed no other funds were affected. Citing the remaining risk, including frontrunning, Nimiq kept OpenGSN-based Gas Abstraction for stablecoins switched off while it builds and reviews a safer alternative.
Olympix reads a contract the way an attacker does, as states, transitions and entry points, and takes in the context around it, here the OpenGSN roles Nimiq combined into one contract and the rules RelayHub follows when it picks a paymaster. From that it infers the invariants the system depends on. For this handler the invariant is plain: tokens can only move out of request.from if request.from actually signed that request, as checked by code the requester can't replace.
Relay requests have a huge input space, and signals tell the engine where to look. Olympix's static analysis surfaces the signal in this case, a function that passes a caller-supplied from address into transferFrom while the signature it receives goes unused and the only verification sits in a hook chosen by a field of the request itself. That narrows the search from every path, to the feasible ones, to the paths where request.from and the real signer can differ.
BugPoCer picks up from that signal and asks the question the partner's wallet depended on, whether any sequence of calls can move its tokens without its signature. It answers by Counterexample-Guided Invariant Refutation, executing paths from the system outward until the authorization rule fails, and the failing path here is short: register a relay, name a paymaster that accepts everything, set request.from to a wallet with approvals, and call execute(). BugPoCer returns that sequence as a counterexample with the minimal conditions that trigger it and a generated Forge test that replays it, which is the September 16 drain produced in development.
Olympix has flagged this class before in other codebases, where a caller-controlled address ended up serving as the anchor for authorization. Integrations with outside infrastructure like OpenGSN are where it tends to hide, because each piece reads correctly on its own and the gap only appears in how they're wired together. The engine runs in development, at every merge and before release, so that wiring gets tested whenever either side changes.
Nimiq's handlers verified every request they were supposed to verify, in a place an attacker could route around for the cost of a refundable 1 POL stake. Any contract that accepts relayed or meta-transactions should check the signer inside the function that moves funds and treat everything the relay request names as attacker-chosen. To see Olympix trace the authorization paths in your own contracts and return a runnable counterexample for each one that lets the wrong address through, book a technical walkthrough.
Protocol: Nimiq
Chain: Polygon
Date: September 16, 2026
Loss: 26,130.64 USDC, 24,332.49 USDT0 and 0.66 USDC.e (about $50,463)
Attacker EOA (EIP-7702 delegated): 0x2258...C45D
Victim (Nimiq partner's swap-liquidity wallet): 0x24Cb...6773
ERC20PermitHTLCHandler (USDC): 0x0cFD...959b
ERC20MetaHTLCHandler (USDT0 / USDC.e): 0xF615...359f
OpenGSN RelayHub v2.2: 0x6C28...750d
OpenGSN StakeManager: 0x15C7...67C7
Setup tx: 0xb067...58c0
Exploit tx: 0xb2ca...d169
Verichains: Nimiq Hack Analysis
SlowMist on X: Nimiq Exploit Alert