Status vs Wait

Snapshot progress with status(...) or block until a checkpoint with wait(..., { for }) for deposits and withdrawals.

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 apply to deposits and withdrawals. Use status(...) for UI refreshes; use wait(...) when you need to gate logic on inclusion/finality.

[!NOTE] You can pass either a handle returned from create(...) or a raw transaction hash.

Withdrawals

withdrawals.status(h | l2TxHash): Promise<WithdrawalStatus>

Input

  • h: a WithdrawalWaitable (e.g., from create) or the L2 transaction hash Hex.

Phases

PhaseMeaning
UNKNOWNHandle doesn’t contain an L2 hash yet.
L2_PENDINGL2 transaction not yet included.
PENDINGL2 included, not yet ready to finalize on L1.
READY_TO_FINALIZEFinalization on L1 would succeed now.
FINALIZEDFinalized on L1; funds released.

Notes

  • No L2 receipt ⇒ L2_PENDING
  • Finalization key derivable but not ready ⇒ PENDING
  • Already finalized ⇒ FINALIZED
withdrawals-status.ts
const s = await sdk.withdrawals.status(handleOrHash);
// s.phase ∈ 'UNKNOWN' | 'L2_PENDING' | 'PENDING' | 'READY_TO_FINALIZE' | 'FINALIZED'

withdrawals.wait(h | l2TxHash, { for, pollMs?, timeoutMs? })

Targets

TargetResolves with
{ for: 'l2' }L2 receipt (`TransactionReceiptnull`)
{ for: 'ready' }null when finalization becomes possible
{ for: 'finalized' }L1 receipt when finalized, or null if finalized but receipt not found

Behavior

  • If the handle has no L2 hash, returns null immediately.
  • Default polling interval: 5500 ms (override with pollMs).
  • timeoutMs → returns null on deadline.
withdrawals-wait.ts
// Wait for L2 inclusion → get L2 receipt (augmented with l2ToL1Logs if available)
const l2Rcpt = await sdk.withdrawals.wait(handle, { for: 'l2', pollMs: 5000 });

// Wait until it becomes finalizable (no side effects)
await sdk.withdrawals.wait(handle, { for: 'ready' });

// Wait for L1 finalization → L1 receipt (or null if not retrievable)
const l1Rcpt = await sdk.withdrawals.wait(handle, { for: 'finalized', timeoutMs: 15 * 60_000 });

[!TIP] Building a UI? Use status(...) to paint the current phase and enable/disable the Finalize button when the phase is READY_TO_FINALIZE.

Deposits

deposits.status(h | l1TxHash): Promise<DepositStatus>

Input

  • h: a DepositWaitable (from create) or L1 transaction hash Hex.

Phases

PhaseMeaning
UNKNOWNNo L1 hash present on the handle.
L1_PENDINGL1 receipt missing.
L1_INCLUDEDL1 included; L2 hash not yet derivable from logs.
L2_PENDINGL2 hash known but L2 receipt missing.
L2_EXECUTEDL2 receipt present with status === 1.
L2_FAILEDL2 receipt present with status !== 1.
deposits-status.ts
const s = await sdk.deposits.status(handleOrL1Hash);
// s.phase ∈ 'UNKNOWN' | 'L1_PENDING' | 'L1_INCLUDED' | 'L2_PENDING' | 'L2_EXECUTED' | 'L2_FAILED'

deposits.wait(h | l1TxHash, { for: 'l1' | 'l2' })

Targets

TargetResolves with
{ for: 'l1' }L1 receipt or null
{ for: 'l2' }L2 receipt or null (waits L1 inclusion then L2 execution)
deposits-wait.ts
const l1Rcpt = await sdk.deposits.wait(handle, { for: 'l1' });
const l2Rcpt = await sdk.deposits.wait(handle, { for: 'l2' });

[!NOTE] wait(..., { for: 'l2' }) waits for both L1 inclusion and canonical L2 execution.

Practical Patterns

Pick the Right Tool

  • Use status(...) for poll-less UI refreshes (e.g., on page focus or controlled intervals).
  • Use wait(...) for workflow gating (scripts, jobs, or “continue when X happens”).

Timeouts & Polling

polling.ts
const ready = await sdk.withdrawals.wait(handle, {
  for: 'ready',
  pollMs: 5500, // minimum enforced internally
  timeoutMs: 30 * 60_000, // 30 minutes → returns null on deadline
});
if (ready === null) {
  // timeout or not yet finalizable — decide whether to retry or show a hint
}

Error Handling

  • Network hiccup while fetching receipts ⇒ throws ZKsyncError of kind RPC.
  • Internal decode issue ⇒ throws ZKsyncError of kind INTERNAL.

Prefer no-throw variants if you want explicit flow control:

no-throw.ts
const r = await sdk.withdrawals.tryWait(handle, { for: 'finalized' });
if (!r.ok) {
  console.error('Finalize wait failed:', r.error);
} else {
  console.log('Finalized L1 receipt:', r.value);
}

Tips & Edge Cases

  • Handles vs hashes: Passing a handle without the relevant hash yields UNKNOWN / null. If you already have a hash, pass it directly.
  • Finalization windows: For withdrawals, READY_TO_FINALIZE may take a while. Use status(...) for responsive UI and reserve wait(..., { for: 'finalized' }) for blocking logic.
  • Retries: If a wait returns null because of timeoutMs, safely call status(...) to decide whether to retry or surface user guidance.