UMAprotocol / UMAprotocol/protocol
fx-tunnel-relayer: only the first MessageSent event per Polygon transaction is relayed
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 485
- Forks
- 215
- Avg merge
- 18m
- Merged PRs (30d)
- 1
Description
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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at Relayer._relayMessage and inspect the maticPosClient.exitUtil.buildPayloadForExit call, then trace the fetched MessageSent events to their transaction receipts. Use the documented Polygon transactions to verify that each matching log is handled independently and that EXIT_ALREADY_PROCESSED failures are debug-logged rather than silently swallowed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- backend, blockchain
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 70/100