LayerHopDocsLaunch app (opens in a new tab)

Recover stuck withdrawals

Find Arbitrum and OP Stack withdrawals to Ethereum that were never claimed, and build the prove, finalize or claim transaction for the user's wallet to sign.

Withdrawing from a rollup back to Ethereum through its native bridge takes two transactions: one on the L2, then, after a waiting period, one on Ethereum. If nobody sends the second one, the funds stay in the bridge. They aren't lost, but nobody gets them either. Recover finds those withdrawals and builds the missing transaction. The user's own wallet signs it; LayerHop never signs or sends anything.

The same tool runs at app.layerhop.com/recover.

Which bridges are covered?#

Chain Steps on Ethereum Wait Address scan
Arbitrum One, Arbitrum Nova claim (Outbox.executeTransaction) about 6.4 days full history since Nitro (Aug 2022)
OP Mainnet, Unichain, Ink, Soneium prove, then finalize 7 days after proving full history (OP Mainnet since Bedrock, June 2023)
World Chain, Lisk, Mode prove, then finalize 7 days after proving full history
Base prove, then finalize 1 day after proving, plus the time Base's dispute game takes to settle about the last 2 days; older ones by transaction hash
Zora prove, then finalize 7 days after proving about the last 2 days; older ones by transaction hash

On OP Mainnet, Unichain, Ink and Soneium the dispute games prove super roots, and we don't build that proof yet: those withdrawals come back as prove_elsewhere. Prove them on Superbridge, then come back to finalize.

Arbitrum token withdrawals are found when they go through the standard, custom, WETH or DAI gateway. Not covered: Polygon PoS, zkSync, Linea, Scroll, Arbitrum Classic withdrawals (before Aug 2022) and OP Mainnet withdrawals from before Bedrock. Use those chains' own bridges.

Scan an address#

Terminal
curl "https://api.layerhop.com/v1/recover/withdrawals?address=0xa5BA6e62e9a2CBDeac73F675C26257C75A3FE401"

GET /v1/recover/withdrawals checks every chain above (or one, with chain=) and returns each withdrawal with its state:

state Meaning What to do
ready The final step can be sent now (step: execute or finalize). Build it with GET /v1/recover/claim.
ready_to_prove OP Stack step 1 of 2. Build the prove, send it, then finalize after the proof window.
waiting Inside the challenge or proof window. Come back at readyAt (readyAtEstimated when it's our estimate).
prove_elsewhere Needs a proof we don't build for this chain. Prove on bridgeUrl, finalize here.
claimed Already done. Nothing.
unknown We couldn't read the state right now. Try again later.

coverage tells you, per chain, whether the whole history was scanned (complete) and where it started (fromBlock). A chain that couldn't be scanned is reported with error: true instead of failing the request. Scans look at up to 40 withdrawal transactions per chain, newest first, and are cached for 5 minutes.

Look up one withdrawal#

When you have the L2 transaction (or the scan can't reach back far enough), look it up directly; this works for any age:

Terminal
curl "https://api.layerhop.com/v1/recover/withdrawals/base/0xf71673a8269db98a30c358ece7d4b55dc8786fa0622772731920631806737560"

Build the next transaction#

Terminal
curl "https://api.layerhop.com/v1/recover/claim?chain=arbitrum&txHash=0x397ea01fc089ed8b89bbdf6eb588755996dc6b90ce1eb641369cc05f3659c1c6&index=0&sender=0xYourWallet"

The answer carries the withdrawal's fresh state and, when it is actionable, step, tx (an EvmTx on Ethereum, chain id 1) and simulation, the result of an eth_call of tx from sender. Send tx from the user's wallet only when simulation.ok is true. index is the last part of the withdrawal's id.

  • Arbitrum: Outbox.executeTransaction with a Merkle proof built against the latest send root the rollup confirmed on Ethereum. We check the proof's root against the Outbox before returning it.
  • OP Stack prove: OptimismPortal.proveWithdrawalTransaction against the latest dispute game. We rebuild the game's output root from the L2 block and storage proof and only return the transaction when it matches the game's root claim and the simulation passes.
  • OP Stack finalize: finalizeWithdrawalTransactionExternalProof with whoever proved it. A proof whose game lost, was blacklisted or was retired needs a new proof: the withdrawal comes back as ready_to_prove.

Anyone can send these transactions, and the funds always go to the withdrawal's recipient (to), never to the sender. That makes it safe to help someone else claim.

Limits and keys#

At most 2 scans and 30 lookups / claim builds per IP per minute (cached answers don't count), within per-minute budgets shared by all clients (a busy server answers RATE_LIMITED with retry-after). A claim whose simulation on Ethereum fails comes back without tx (with simulation.error), so nothing that would revert is offered for signing. Token withdrawals show a token and amount only from Arbitrum's known gateway contracts. The operator can make scans faster and fuller with ETHERSCAN_API_KEY (Etherscan v2; Base needs a paid plan) and BLOCKSCOUT_API_KEY (higher limits on the public Blockscout explorers). Without them the scan uses public explorers and RPCs.

Last updated