Matching an Across bridge transfer
How Forensia finds the other half of an Across deposit between Ethereum and Base, and how strict the match is.
#Plain languageIn plain terms
For one bridge on one route, Forensia can look across the gap. Given a deposit into Across on Ethereum or Base, it searches the other network for the event that fills that deposit.
It does not match on "about the same amount, around the same time". All supported original relay-identity fields must match. Changed execution terms, such as an updated recipient or output amount, are shown separately; they must not be mistaken for the original deposit terms. Missing or conflicting correspondence stays unresolved.
#How it worksHow it works
- Read the deposit event from the transaction's receipt.
- Work out roughly which blocks on the other network correspond to that time.
- Search that window for fill events that name this deposit.
- Fetch each candidate's receipt and confirm the event is really in it.
- Compare the deposit and the fill field by field.
#Technical detailThe exact rules
| Network | SpokePool contract |
|---|---|
| Ethereum | 0x5c7bcd6e7de5423a257d81b442095a1a6ced35c5 |
| Base | 0x09aea4b2242abc8bb4bb78d537a67a245a7bec64 |
The deposit event is FundsDeposited and the fill event is FilledRelay. Each is decoded strictly, then re-encoded and compared byte for byte with the original, which rejects any non-standard padding or trailing data.
Choosing where to search
Block numbers on two networks are unrelated, so the window is found by time. The lookup halves the destination chain's block range by timestamp, up to 16 times, until it is within 1,024 blocks of the deposit's time. The search then starts 128 blocks earlier, because a fill can be recorded slightly before its deposit, and covers at most 5,000 blocks.
| Limit | Value |
|---|---|
| Search window | 5,000 blocks |
| Fill events the source may return | 8 |
| Receipts fetched | 4 |
| Requests | 48 |
| Total time | 25 seconds |
The 48 requests and 25 seconds cover both networks together. If the source returns more than 8 fill events, or they sit in more than 4 transactions, the check stops at a limit. No partial list is used. On each network, every request after the first goes to the one endpoint that answered first.
What counts as a match
A fill matches a deposit only when all 13 of these are identical: origin network, destination network, deposit ID, depositor, recipient, exclusive relayer, input token, output token, input amount, output amount, fill deadline, exclusivity deadline, and message hash.
| Result | Meaning |
|---|---|
| Matched events | Exactly one fill with an identical relay. |
| Unresolved | No matching fill in the window searched. |
| Ambiguous | More than one fill, or a duplicate deposit. |
| Unsupported route | The deposit targets a network other than Ethereum or Base. |
A check can also end before any comparison: no supported deposit event in the transaction, a source that could not finish, a size, time or request limit, records that disagree about the network, transaction or block, or records that cannot be safely read. In each case no destination match is accepted.
When a match is found, the fill's execution terms are compared with the deposit's. If the recipient, message or output amount were updated before the fill, each change is shown side by side: what was requested, and what the destination reports. Amounts are compared as exact raw integers in the token's smallest unit, never as rounded decimals.
How the fill is described
| Fill type in the event | What it means here |
|---|---|
| Fast fill | A relayer filled the deposit. This says nothing about how long delivery took, and repayment of the relayer is not checked. |
| Fast fill replacing a slow-fill request | A relayer replaced a slow fill that had been requested. It is not a second delivery, and not evidence of a completed refund. |
| Slow fill | The protocol's slow-fill path. Its proof and settlement are not checked. |
Checking the events against the calls
Where the operator has enabled call tracing for the source, the same check also asks the source for the transaction's call tree and lines the receipt's events up against it, in order. It is accepted only when every event in the receipt matches a call that did not fail, and the block is read again afterwards. That result is labelled as reported by the source, not as independently verified execution.
| Call check result | Meaning |
|---|---|
| Matched to a provider-reported call | Every event in the receipt agrees with the source's call tree, in order. |
| Not enabled | Call tracing is off for this source. No trace was requested. |
| Unavailable, limit, invalid or unsupported | No call match was accepted. The event match above still stands on its own. |
| Conflict | The calls or the later block disagree with the receipt. Do not treat the event match as a verified path. |
Some sources are used for analysis in the moment only. For those, the original response is not kept or exported: the record shows its fingerprint and says "raw response not included".
#LimitsWhat it does not tell you
- No matching event in the window does not mean the bridge failed. The fill may be outside the 5,000 blocks.
- Only Ethereum and Base, in either direction, are covered.
- If a transaction contains several deposits, only one is checked, and the page says so.
- Refunds are not checked. A fill type in the event is not proof that a refund or a relayer repayment happened.
- Matching events is not a proof of finality or of the recipient's final balance.
- The contract code behind the events at that time is not verified, and the transaction is not replayed. A call match is what the source reported, not an independent re-execution.
- The check runs once per open result, with no retry. It is kept apart from the saved result and its report, and has its own "Save bridge check" button.