Skip to content

fx-tunnel-relayer: only the first MessageSent event per Polygon transaction is relayed #4962

Description

@droplet-rl

Bug

When a single Polygon transaction emits multiple MessageSent events from the OracleChildTunnel (e.g. two OO disputes batched in one transaction), only the first message is ever relayed to the OracleRootTunnel on mainnet. The remaining messages are never received, so those price requests never reach the DVM and the corresponding OO requests stay stuck in Disputed with bonds locked.

Root cause

Relayer._relayMessage builds the exit proof with:

proof = await this.maticPosClient.exitUtil.buildPayloadForExit(
  transactionHash,
  POLYGON_MESSAGE_SENT_EVENT_SIG,
  false
);

matic.js buildPayloadForExit(burnTxHash, logEventSig, isFast, index = 0) proves the index-th log matching the event signature in the transaction, defaulting to the first. The relayer loops over all fetched MessageSent events but never passes an index, so every event in the same transaction produces an identical proof for message #1. The first submission succeeds; subsequent ones revert with FxRootTunnel: EXIT_ALREADY_PROCESSED, which the catch block swallows silently — so the failure is invisible in logs.

Real-world impact (2026-07)

Two Polymarket dispute transactions on Polygon each bridged two disputes atomically; in both cases the second dispute never reached the DVM:

  • 0x0874d7b0… — relayed: Will "Blue" be said…; stuck: Will "Honestly" be said…
  • 0xc6d74727… — relayed: Map Handicap: paiN (-1.5)…; stuck: Map 3 Rounds Handicap: paiN (-3.5)…

Both stuck messages have since been relayed manually with proofs built at tokenIndex=1 (0x37ba42b6…, 0xeaf7b45c…), confirming both the diagnosis and that recovery is permissionless.

Fix

For each MessageSent event, compute its position among all logs matching the MessageSent signature in its transaction receipt and pass that index to buildPayloadForExit. Also log (debug) instead of silently returning when a relay reverts with EXIT_ALREADY_PROCESSED.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions