How It Works
ETH bridged to Blast is held on Ethereum by Blast’s ETHYieldManager, which earns Blast’s native yield on it. Withdrawals use the standard OP Stack bridge together with Blast’s ETH withdrawal queue:- On Blast, your ETH is sent to the L2BlastBridge, which starts the withdrawal.
- When you prove on Ethereum, the withdrawal is added to the ETH withdrawal queue.
- When you finalize, the ETH is claimed from the queue and sent to your recipient.
Before You Start
Your L2 Contract Must Be Able to Call the Bridge
The withdrawal is initiated by the contract that holds the ETH on the L2. It must be able to make the following contract calls:bridgeETHTo on the L2BlastBridge with ETH attached, and withdraw on WETH if it holds WETH. A contract that can only transfer tokens to an address can’t start a withdrawal itself; move the ETH to a contract or account that can.
Verify Your L1 Recipient
Before you start, verify that the intended recipient address meets the following conditions:- It exists on Ethereum. There is contract code at the address, or it’s an EOA you control. If nothing is deployed there yet, the ETH still arrives, but whoever later deploys a contract at that address controls it. A not-yet-deployed multisig must later be deployed with exactly the same configuration to reach the same address.
- It’s the contract you expect. Its source is verified and it’s the contract type you intended. For a multisig, it has the owners and threshold you expect.
- It can receive ETH. It must accept plain ETH transfers with empty calldata (a
receive()or payablefallback()function). If it rejects the ETH, the delivery fails (see Delivered?). - It can transfer the ETH out. It must be able to send ETH it holds to another address. A contract that can receive ETH but not send it leaves the ETH stuck permanently.
Anyone Can Prove and Finalize
proveWithdrawalTransaction and finalizeWithdrawalTransaction on Ethereum don’t check who sends them. Any EOA with ETH for gas can submit them; your L2 contract and its signers don’t need to do anything on Ethereum.
Test With a Small Amount First
Be sure to run the whole flow end-to-end with a small amount before moving large balances.Withdraw Your ETH
1
Unwrap WETH, if needed (Blast)
WETH can’t be bridged directly. If your contract holds WETH, unwrap it to ETH first. Skip this step otherwise.
2
Initiate the withdrawal (Blast)
_minGasLimit is the gas available for delivering the ETH to your recipient on Ethereum. 200000 is enough for a plain receive(); increase it if your recipient’s receive() does more work.Record these values. The later steps and status checks use them:- the L2 transaction hash
- its L2 block number
- the
withdrawalHash: the last field of theMessagePassedevent emitted by the L2ToL1MessagePasser (0x4200000000000000000000000000000000000016) in this transaction
3
Prove the withdrawal (Ethereum)
Wait until the L2 output containing your transaction has been posted to Ethereum, up to ~1 hour (see Ready to Prove?). Then call The proof arguments are built from your L2 transaction hash. See Building the Prove and Finalize Transactions for code that does this.Proving also adds your withdrawal to the ETH withdrawal queue and assigns it a
proveWithdrawalTransaction on the OptimismPortal (0x0Ec68c5B10F21EFFb74f2A5C61DFe6b08C0Db6Cb):requestId, which the finalize step needs.4
Wait for the challenge period
Wait 1 day after proving.
5
Finalize the withdrawal (Ethereum)
Call See Building the Prove and Finalize Transactions for code that builds and sends this call.The ETH is sent to your recipient in the same transaction. Confirm it arrived (see Delivered?).
finalizeWithdrawalTransaction on the OptimismPortal:The amount paid out is calculated from the queue checkpoint’s share price. It can be slightly less than the amount withdrawn only if the underlying yield had an uncovered loss.
Building the Prove and Finalize Transactions
The examples below use TypeScript and viem2.57.3. Each builds the transaction from the L2 transaction hash of your initiate step and sends it.
Setup
Prove
Run this once the withdrawal is ready to prove.Finalize
Run this once the challenge period is over, 1 day after proving. Then confirm delivery.Checking Withdrawal Status
Every stage can be checked with read-only calls on Ethereum, from your own tooling or the Read as Proxy tab on Etherscan, using the L2 block number andwithdrawalHash you recorded when you initiated.
Ready to Prove?
Proven?
timestamp of 0 means the withdrawal hasn’t been proven. requestId is your ETH withdrawal queue request ID.
Challenge Period Over?
timestamp is the proven timestamp from the previous check. The challenge period is currently 86400 seconds (1 day).
ETH Queue Processed?
requestId is from Proven?. Once this is true, ETHYieldManager.findCheckpointHint(requestId, 1, ETHYieldManager.getLastCheckpointId()) returns the hintId to finalize with.
Finalized?
Delivered?
Finalizing hands the withdrawal to the L1CrossDomainMessenger (0x5D4472f31Bd9385709ec61305AFc749F0fA8e9d0), which calls the L1BlastBridge to send the ETH to your recipient. If that call fails (for example, because your recipient rejects the ETH or runs out of gas), the withdrawal is still marked as finalized, but the messenger records the message as failed and keeps the ETH. Anyone can retry it with relayMessage on the L1CrossDomainMessenger, with more gas or after fixing the recipient.
Confirm delivery in any of these ways:
- Your recipient’s ETH balance increased by your amount.
- The finalize transaction emitted
ETHBridgeFinalizedfrom the L1BlastBridge. L1CrossDomainMessenger.successfulMessages(keccak256(data))istrue, wheredatais thedatafield of your withdrawal’sMessagePassedevent.