Skip to main content
polynode monitors every Polymarket settlement in real time, including ones that fail. This page documents our ongoing research into settlement failures caused by the incrementNonce() exploit on Polymarket’s V1 CTF Exchange. All data on this page is from Polygon mainnet. Every transaction hash is real and verifiable.
Update — 2026-04-17: Fixed in V2. The V2 CTF Exchange Order struct removes the nonce field entirely (along with taker, expiration, and feeRateBps). With no on-chain nonce to manipulate, incrementNonce() no longer exists as an attack surface. V2 cutover is scheduled for April 29, 2026 — at that point, Variant 1 of this exploit becomes structurally impossible. See the V2 Migration Guide for the full Order struct changes.

Background

Polymarket uses a hybrid architecture: orders are matched off-chain by the CLOB (Central Limit Order Book), then settled on-chain by an operator calling matchOrders on the CTF Exchange contract. There is a 2-3 second window between when the operator builds the settlement transaction and when it lands in a block. This window is the attack surface.

The exploit

The CTF Exchange contract has a public function called incrementNonce() (selector 0x627cdcb9). Anyone can call it on their own address for less than $0.10 in gas. When called, it increments the caller’s on-chain nonce, which instantly invalidates every outstanding order they have on the exchange. The attack:
  1. The attacker places orders on both sides of a market (YES and NO)
  2. One side gets matched by the CLOB
  3. Before the operator’s matchOrders transaction lands on-chain, the attacker calls incrementNonce() on the CTF Exchange
  4. The matchOrders transaction reverts because the attacker’s order nonce no longer matches their on-chain nonce
  5. Since matchOrders is atomic (no try/catch per order), one invalid order kills the entire batch — every other order in that transaction also fails
  6. The counterparties who were matched against the attacker get nothing. Their trades evaporate.
  7. Polymarket’s off-chain system removes all affected orders from the book
The attacker keeps their winning side and loses nothing on their losing side. The cost is ~$0.10 per cycle.

Two attack variants

Our research identified two distinct methods that cause settlement failures:

Variant 1: Nonce flip

The attacker calls incrementNonce() directly on the CTF Exchange contract. This is the canonical exploit described in public disclosures. The on-chain call is permanent and auditable — every instance can be traced to a specific wallet and block.

Variant 2: Balance drain

The attacker transfers their USDC out of their wallet between order matching and on-chain settlement. When the operator’s matchOrders transaction executes, the token transfer fails with "ERC20: transfer amount exceeds balance". The effect is identical — the entire atomic batch reverts, killing all orders in the transaction. This variant does not leave an incrementNonce() trace. It manifests as a standard ERC-20 balance error. Polymarket’s CLOB validates balances at order placement time, so a balance error at settlement time indicates the funds were moved deliberately in the ~2 second window between matching and settlement. Both variants produce the same outcome: ghost fills. A trade appears to match off-chain but never settles on-chain.

Live audit: April 9, 2026

Settlement failure rate

We ran 10 consecutive 30-second monitoring sessions collecting every matchOrders transaction from polynode’s settlement stream and checking each receipt for revert status. A separate scan of 1,000 consecutive blocks (~33 minutes, blocks 85296314–85297314) found 66,638 total matchOrders transactions with 100 reverts (0.150% failure rate). The rate fluctuates between 0% and 1% depending on whether short-term crypto markets are actively resolving.

Per-round breakdown

Market category breakdown

93.5% of all settlement failures occur in short-term crypto markets. These are the 5-minute and 15-minute BTC/ETH “up or down” markets where the outcome becomes clear in the final seconds, giving the attacker a window to invalidate their losing orders before settlement.

Revert cause analysis

We traced every reverted transaction using trace_transaction to decode the exact revert reason. All 31 reverts in this sample produced balance-related errors at the ERC-20 transfer level, consistent with both the nonce flip (which cascades into balance errors when the atomic batch unwinds) and the balance drain variant.

Attacker identification

We scanned 1,800 consecutive blocks (~1 hour) for all calls to incrementNonce() (selector 0x627cdcb9) on the CTF Exchange contract (0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E). 21 incrementNonce() calls found in 1 hour. Two wallets responsible for all of them.

Attacker 1: 0xe5f5f9bf130f4e0947ab9e79b2ee0ab3f33885f5

  • 12 calls in 1 hour
  • Calls incrementNonce() directly on the CTF Exchange (no proxy)
  • Regular ~150 block cadence (~5 minutes), matching BTC/ETH short-term market resolution cycles
  • No Polymarket profile found for this address
  • Directly caused 7 of 31 reverted settlements in our audit (confirmed via same-block correlation)
Nonce flip blocks: 85296283, 85296433, 85296583, 85296733, 85296883, 85297033, 85297183, 85297333, 85297483, 85297633, 85297783, 85297933 At block 85297633, this wallet called incrementNonce() in the same block as 3 reverted BTC Up/Down settlements, and 4 more reverted in the next 1–3 blocks as the operator’s queued transactions hit the invalidated nonce.

Attacker 2: 0xfd29d745edaf724bae8ade4f7b3a3465eed3b905

  • 7 calls in 1 hour
  • Calls incrementNonce() through a Safe proxy wallet at 0xfc64b6660ed1...
  • Similar ~5 minute cadence
  • Has a Polymarket account and profile page
  • Attribution to specific reverts pending (proxy routing makes block-level correlation less precise)
Nonce flip blocks (via proxy): 85296604, 85296825, 85297016, 85297484, 85297770, 85297921, 85297985

Additional callers

Two wallets (0x2df3626c..., 0xa2b2a759...) made single incrementNonce() calls during the scan period. These may be legitimate order cancellations rather than exploit activity.

Attribution: nonce flip to settlement failure

For the 31 reverted settlements in our audit, we cross-referenced each revert block against the incrementNonce() call log: The 7 confirmed nonce-flip reverts all occurred in BTC Up/Down 2:10–2:15AM ET markets within blocks 85297633–85297636. The 24 unattributed reverts are concentrated in ETH Up/Down markets and revert with balance errors, consistent with the balance drain variant.

Sample reverted transactions

How polynode detects ghost fills

polynode detects settlements from the mempool 3–5 seconds before on-chain confirmation. When a matchOrders transaction is detected, a pending settlement event is emitted. When the block confirms:
  • If the receipt shows status: 0x1 (success): a status_update event is emitted with confirmed_fills containing exact on-chain execution data
  • If the receipt shows status: 0x0 (reverted): no status_update is emitted. The pending settlement was a ghost fill.
To detect ghost fills, track pending settlements that never receive a status_update:
For verified trade data, use the confirmed_fills field on status_update events. See the Trade Tracking Guide for details on using pending settlements vs confirmed fills.

Methodology

All data was collected using polynode’s live settlement stream and Polygon RPC. Settlement failure rate: Subscribe to settlements via WebSocket, collect unique tx_hash values for a fixed period, wait for block confirmations, batch-query receipts, check receipt.status. Revert cause analysis: Use trace_transaction on reverted TXs to decode the revert reason string from the deepest failing call. Attacker identification: Scan all transactions in the block range for calls to incrementNonce() (selector 0x627cdcb9) targeting the CTF Exchange contract (0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E) or the NegRisk CTF Exchange. Both direct calls and proxy calls (where the selector appears inside execTransaction calldata) are detected. Attribution: Cross-reference incrementNonce() call blocks against reverted settlement blocks. A revert is “confirmed” if a nonce flip occurred in the same block or 1–3 blocks prior. No mock data is used. All transaction hashes, block numbers, and wallet addresses are from Polygon mainnet.