Skip to main content
This guide covers the steps necessary to bridge USDB from Blast (L2) to Ethereum (L1), where it is paid out as DAI. The guide is geared towards exchanges and other custodians that need to bridge USDB from contracts on L2, such as multisigs or custody contracts. The steps on Blast are calls made by the contract that holds your USDB; each lists the contract, function, arguments, and ETH value, so you can execute them with whatever your contract or custody platform supports. The steps on Ethereum can be sent from any account, except the final claim, which must be sent by your recipient. See Building the Prove, Finalize and Claim Transactions for TypeScript code examples.

How It Works

USDB is backed by DAI held on Ethereum by Blast’s USDYieldManager, which earns Blast’s native yield on it. Withdrawing USDB uses the standard OP Stack bridge together with Blast’s USD withdrawal queue, and pays out DAI:
  • On Blast, your USDB is sent to the L2BlastBridge, which starts the withdrawal.
  • When you finalize on Ethereum, a DAI withdrawal request is created for your recipient in the USD withdrawal queue.
  • Once Blast has processed the USD withdrawal queue, up to 1 day after finalizing, your recipient claims the DAI.

Before You Start

Your L2 Contract Must Be Able to Call the Bridge

The withdrawal is initiated by the contract that holds the USDB on the L2. It must be able to make the following contract call: bridgeERC20To on the L2BlastBridge. A contract that can only transfer tokens to an address can’t start a withdrawal itself; move the USDB 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 DAI withdrawal request is still created for it, 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 claim the DAI. The DAI is not sent automatically: the recipient itself must call claimWithdrawal on the USDYieldManager. Multisigs can do this. Many deposit-forwarder and sweeper contracts can’t, and the DAI would be stuck. If your L1 contract can’t make arbitrary contract calls, withdraw to a multisig or EOA you control first.
  • It can transfer the DAI out. It must be able to send ERC20 tokens it holds to another address. A contract that can receive tokens but not send them leaves the DAI 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. Only the claim must be sent by your recipient.

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 USDB

1

Initiate the withdrawal (Blast)

No token approval is needed for USDB.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 the MessagePassed event emitted by the L2ToL1MessagePasser (0x4200000000000000000000000000000000000016) in this transaction
2

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 proveWithdrawalTransaction on the OptimismPortal (0x0Ec68c5B10F21EFFb74f2A5C61DFe6b08C0Db6Cb):
The proof arguments are built from your L2 transaction hash. See Building the Prove, Finalize and Claim Transactions for code that does this.
3

Wait for the challenge period

Wait 1 day after proving.
4

Finalize the withdrawal (Ethereum)

Call finalizeWithdrawalTransaction on the OptimismPortal, with hintId set to 0:
This signature differs from other OP Stack chains: Blast’s portal takes an extra hintId argument before the withdrawal. For USDB, hintId is always 0.
Set the finalize transaction’s gas limit explicitly. The portal reverts with SafeCall: Not enough gas if the limit is too low. A withdrawal initiated with _minGasLimit 800000 needs about 1,300,000 gas, so use 1,600,000.
See Building the Prove, Finalize and Claim Transactions for code that builds and sends this call.Finalizing creates a DAI withdrawal request for your recipient in the USD withdrawal queue. Record the finalize transaction hash: the claim step uses it to find the request’s requestId.
5

Wait for the USD withdrawal queue

Wait up to 1 day after finalizing, until Blast has processed your request in the USD withdrawal queue (see USD Queue Processed?).
6

Claim the DAI (Ethereum, sent by your recipient)

The call reverts with CallerIsNotRecipient() if it’s sent from any other address. On success, the DAI is transferred to your recipient.See Building the Prove, Finalize and Claim Transactions for code that finds the requestId and hintId and builds this call for your recipient to submit.
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, Finalize and Claim Transactions

The examples below use TypeScript and viem 2.57.3. The claim must be sent by your recipient, so the claim example builds the call for your recipient to submit instead of sending it.

Setup

Prove

Run this once the withdrawal is ready to prove.

Finalize

Run this once the challenge period is over. Then confirm delivery.

Claim

Run this with the hash of your finalize transaction once the USD queue has processed your request, up to 1 day after finalizing. It returns the call to submit from your L1 recipient.

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 and withdrawalHash you recorded when you initiated, and the requestId from your finalize transaction.

Ready to Prove?

Proven?

A timestamp of 0 means the withdrawal hasn’t been proven. requestId is always 0 for USDB; the USD queue request is created when you finalize.

Challenge Period Over?

timestamp is the proven timestamp from the previous check. The challenge period is currently 86400 seconds (1 day).

Finalized?

This means the finalize transaction has run. It doesn’t by itself mean the DAI withdrawal request was created: check Delivered?.

Delivered?

Finalizing hands the withdrawal to the L1CrossDomainMessenger (0x5D4472f31Bd9385709ec61305AFc749F0fA8e9d0), which calls the L1BlastBridge to create the DAI withdrawal request. If that call fails, the withdrawal is still marked as finalized, but the messenger records the message as failed and no request is created. Confirm delivery in any of these ways:
  • The finalize transaction emitted WithdrawalRequested from the USDYieldManager, with recipient set to your recipient. Its requestId is the one to claim.
  • L1CrossDomainMessenger.successfulMessages(keccak256(data)) is true, where data is the data field of your withdrawal’s MessagePassed event.

USD Queue Processed?

Once this is true, USDYieldManager.findCheckpointHint(requestId, 1, USDYieldManager.getLastCheckpointId()) returns the hintId to claim with.

Claimed?

Once isClaimed is true, the DAI has been transferred to your recipient.

Contract Addresses

Blast (Chain ID 81457)

Ethereum (Chain ID 1)