- Initiate on L2 — call
withdraw()(via the SDK’screate) to start the withdrawal.
This burns/locks funds on L2 and emits logs; funds are still unavailable on L1. - Finalize on L1 — call
finalize(l2TxHash)to release funds on L1.
This submits an L1 tx; only then does your ETH or token balance increase on Ethereum.
Why finalization matters
- Funds remain locked until you (or anyone) finalizes.
- Anyone can finalize — typically the withdrawer does.
- Finalization costs L1 gas — budget for it.
Finalization methods
All methods accept either a handle (from
create) or a raw L2 tx hash. If you only have
the hash, you can still finalize.Phases
Examples
try-finalize.ts
Operational tips
- Gate UX with phases: Display a Finalize button only when
status.phase === 'READY_TO_FINALIZE'. - Polling cadence:
wait(..., { for: 'ready' })defaults to ~5500 ms. Adjust withpollMsif needed. - Timeouts: Use
timeoutMsfor long windows and fall back tostatus(...)to keep the UI responsive. - Receipts can be
null:wait(..., { for: 'finalized' })can resolvenullif finalized but receipt isn’t retrievable; consider showing a link to the L1 explorer based on the tx hash you submitted.
Common errors
- RPC/network hiccups: thrown
ZKsyncErrorwith kindRPC. Retry with backoff. - Internal decode issues: thrown
ZKsyncErrorwith kindINTERNAL. Capture logs and report.