You're doing $100M a day across five chains. Ethereum. Polygon. Arbitrum. Optimism. Base. Every withdrawal creates two records. One in your database. One on-chain. These need to match or you're bleeding money.
Reconciliation is the unglamorous part. Nobody cares about it when it works. Everyone screams when it breaks.
One unreconciled transaction is a problem. A thousand means you're losing revenue or refunding money you don't have. Scale doesn't forgive this. Most teams still use spreadsheets and daily batch jobs. That falls apart fast.
What actually goes wrong
Simple example. Alice withdraws. Your database logs it. Transaction ID, timestamp, chain, amount, address. The contract emits an event with the same data. Somewhere between your record and the chain, they should match.
They don't always.
Transaction gets initiated. Stuck in mempool for hours. Might get dropped from low gas. Initiated and confirmed, event logged, but your database missed it because a service crashed. Initiated and confirmed, address got the money, but Alice swears she never saw it. Address typo on her end.
At scale these aren't rare. Thousands per day.
The lazy approach is daily batch reconciliation. Pull 24 hours of data from your database. Pull 24 hours from each chain. Compare. Flag weird ones for someone to investigate manually. Works at $10M daily. Fails catastrophically at $100M.
Bridges make it worse. Customer deposits on Ethereum. You want to settle on Polygon. Transaction crosses a bridge. Takes 30 minutes to 24 hours to finalize depending on which bridge. Your database thinks it's done. The destination chain says it's still in flight. You're tracking state across two networks now.
Streaming makes reconciliation real
Stop batching. Stream it.
Continuously grab events from each chain. Match them to your database immediately. Flag mismatches the second they appear.
Use The Graph to index events from your contracts. Run a service that hits The Graph every 10 seconds. Look at each event. Find the matching transaction in your database. Mismatches have patterns. Database has it, chain doesn't? Transaction initiated but never confirmed. Retry with more gas. Chain has it, database doesn't? Your service crashed or the event vanished. Credit the customer anyway. Customer says they didn't get paid but the address received funds? Probably a typo on their end. Make a call about refunding.
Bridges are messier because finality varies. Track state carefully. Initiated on source. Relayed to destination. Confirmed on destination. Stuck means it's been pending longer than the bridge's normal window. When that happens, trigger recovery. Different bridge, different recovery. Uniswap Across, query the relayer API. Connext, check the destination chain directly. Some bridges need manual finalization.
Write a runbook for every bridge you use. Document what queries work. Document the failure modes you've seen. Document how to fix it. When your ops team hits a stuck transaction they shouldn't be Googling "how does Connext work." They should open the runbook and follow steps.
The tooling part
Reconciliation is just code. Service that runs, queries chains and your database, compares, records the result.
The Graph indexes your contracts on each chain. Subgraph watches the addresses. For each chain, query your database in the same window. Match on transaction ID, amount, destination, chain. Anything that doesn't match, categorize it. The Graph scales fine at heavy volume.
Store reconciliation results in a separate table. Transaction ID, on-chain status, database status, match result, timestamp. Audit trail for when customers dispute stuff. Build a dashboard showing unreconciled count per chain, average time to reconciliation, stuck ones, alerts when things get bad.
For common failures build procedures. Stuck in mempool? Estimate current gas. Bump it. Resubmit. Bridge transaction stuck? Follow the bridge runbook. Address received funds but customer claims no? Verify the address was right. Document their claim. Decide if you refund.
Why you have to obsess about this
Reconciliation isn't a checkbox. It's the warning light. When reconciliation breaks, your payment system is broken and you don't know it yet. Every hour you're blind is worse.
A few minutes delay is normal. An hour delay means something in your chain interaction is slow. Usually The Graph lag. More than six hours means transactions are failing and you haven't noticed.
The teams that run reliable multi-chain stuff never stop watching reconciliation. Dashboard open. Alerts set. Respond to mismatches in minutes. They know reconciliation is the pulse. No pulse, the patient's dead.
At $100M daily, six hour reconciliation latency means undetected failures are happening. Watch it constantly. The operators winning at multi-chain know in real time if anything's out of sync and exactly why.