Status vs Wait
The SDK exposes two complementary ways to track progress:
status(...)— returns a non-blocking snapshot of where an operation is.wait(..., { for })— blocks/polls until a specified checkpoint is reached.
Both methods work for Deposits and Withdrawals, but Withdrawals add finalization-specific states and targets.
Withdrawals
withdrawals.status(h | l2TxHash) → Promise<WithdrawalStatus>
Input
h: aWithdrawalWaitable(e.g., fromcreate) or the L2 tx hashHex.
Phases returned
UNKNOWN— no L2 hash available on the handle.L2_PENDING— L2 tx not yet included.PENDING— L2 included, but not yet ready to finalize.READY_TO_FINALIZE— finalization would succeed now.FINALIZED— finalized on L1 (funds released).
Notes
- When L2 receipt is missing →
L2_PENDING. - When finalization key can be derived but not ready →
PENDING. - When already finalized →
FINALIZED.
Example
const s = await sdk.withdrawals.status(handleOrHash);
// s.phase in: 'UNKNOWN' | 'L2_PENDING' | 'PENDING' | 'READY_TO_FINALIZE' | 'FINALIZED'
withdrawals.wait(h | l2TxHash, { for, pollMs?, timeoutMs? })
Targets
{ for: 'l2' }→ resolves with L2 receipt (TransactionReceiptZKsyncOS | null){ for: 'ready' }→ resolvesnullwhen finalization becomes possible{ for: 'finalized' }→ resolves L1 receipt when finalized, ornullif finalized but receipt not found
Behavior
- If the handle has no L2 tx hash, returns
nullimmediately. - Default polling interval: 5500ms default or set explicitly if you want.
- Optional
timeoutMsreturnsnullon deadline.
Example
// wait for inclusion on L2, get L2 receipt (augmented with l2ToL1Logs if available)
const l2Rcpt = await sdk.withdrawals.wait(handle, { for: 'l2', pollMs: 5000 });
// wait until it's available to finalize (no side-effects)
await sdk.withdrawals.wait(handle, { for: 'ready' });
// wait until finalized; returns L1 receipt (or null if finalized but receipt not retrievable)
const l1Rcpt = await sdk.withdrawals.wait(handle, { for: 'finalized' });
Common Troubleshooting
- Network hiccup while fetching receipts → thrown
ZKsyncError(RPCkind). - Internal decode issue → thrown
ZKsyncError(INTERNALkind).
Deposits
deposits.status(h | l1TxHash) → Promise<DepositStatus>
Input
h:DepositWaitable(fromcreate) or L1 tx hashHex.
Phases returned
UNKNOWN— no L1 hash.L1_PENDING— L1 receipt missing.L1_INCLUDED— L1 included, but L2 hash not yet derivable from logs.L2_PENDING— L2 hash known but receipt missing.L2_EXECUTED— L2 receipt present withstatus === 1.L2_FAILED— L2 receipt present withstatus !== 1.
Example
const s = await sdk.deposits.status(handleOrL1Hash);
// s.phase in: 'UNKNOWN' | 'L1_PENDING' | 'L1_INCLUDED' | 'L2_PENDING' | 'L2_EXECUTED' | 'L2_FAILED'
deposits.wait(h | l1TxHash, { for: 'l1' | 'l2' })
Targets
{ for: 'l1' }→ waits for L1 inclusion → L1 receipt ornull{ for: 'l2' }→ waits L1 inclusion and canonical L2 execution → L2 receipt ornull
Example
const l1Rcpt = await sdk.deposits.wait(handle, { for: 'l1' });
const l2Rcpt = await sdk.deposits.wait(handle, { for: 'l2' });
Tips & edge cases
- Handles vs hashes: Both methods accept either a handle (from
create) or a raw tx hash (Hex). If you pass a handle without the relevant hash, you’ll getUNKNOWN/null. - Polling: For withdrawals, set
pollMsexplicitly if you want tighter/looser polling; minimum enforced is 5500ms. - Timeouts: Use
timeoutMsfor long waits (e.g., finalization windows) to avoid hanging scripts.