Skip to main content
The modal emits every state transition through a single onLifecycle callback. You switch on event.type to update your UI, trigger backend processes, or log analytics. New event variants can be added without changing the prop surface. These callbacks are browser UI signals, not proof of server-side settlement: they stop when the modal closes. Use webhooks on your server for work that must happen regardless, such as crediting a balance or sending a receipt.

Deposit lifecycle

  1. The modal initializes and fires onReady
  2. If the user funds from a wallet, "connected" fires with the EOA address and the smartAccount the deposit lands on
  3. The user selects a source chain, token, and amount, then confirms
  4. The modal submits the transaction on the source chain and emits "submitted"
  5. The bridge routes funds to the target chain. Once they arrive, the modal emits "complete"
If the bridge fails after submission, "failed" is emitted instead of "complete".
"connected" fires only for wallet funding. QR transfer, fiat on-ramp and exchange connect involve no wallet, so it never fires — use onReady if you need a “flow started” signal.

Arrival estimates

From @rhinestone/deposit-modal v0.17.0, the modal states how long a deposit is expected to take and changes its framing when the wait runs long. The estimate is served by the deposit service — with the quote, and again on each read of an in-flight deposit — and is derived from recent completed deposits on the same route, falling back to the route’s own indicative estimate. It is an expectation, never a deadline, guarantee, or SLA. It is resolved per request, so two reads of the same deposit can return different numbers, and it is omitted whenever no estimate is available. Nothing about your integration changes. The estimate rides the existing POST /quotes/preview and GET /deposits routes your proxy already forwards — there is no new prop, proxy route, or host configuration.

While the deposit is processing

The pending screen shows one of three treatments, each replacing the previous one in place rather than accompanying it: Crossing a threshold drops the expectation instead of restating an estimate the deposit has already exceeded. The treatments only advance — a phase change or a late quote never walks the framing back. The estimate itself is latched for the widget session the first time it is seen, so a quote that resolves late can supply the numbers for the first time but can never change numbers the user has already been shown.

Before submission

The review and QR screens state the expected duration end to end rather than the bridging leg alone, using the served estimate and falling back to the route’s fill time when none was served. Sub-minute estimates read in seconds (~7 seconds) instead of collapsing to “less than a minute”.

In deposit history

An in-flight row in the history panel carries the same three treatments, computed from the deposit’s creation time against the served thresholds. History reads them from GET /deposits rather than from the current session, so the expectation survives a reload and is the same on another device or for a deposit started elsewhere.

When no estimate is served

Estimates are optional and route-dependent, and a read that cannot resolve one simply omits it. In that case the modal shows no expectation and no delay framing, and behaves exactly as it did before v0.17.0. The withdraw modal’s pending screen is served no estimate at all, so it is unchanged. See tracking a deposit through the API for the field on the deposit read, and the GET /deposits reference for its schema.

onLifecycle

onLifecycle receives a discriminated union — DepositLifecycleEvent on <DepositModal>, WithdrawLifecycleEvent on <WithdrawModal>. The two are similar but not identical; see withdraw events for the differences.

Deposit events

amount is in the source token’s base units — divide by sourceDecimals to display it. sourceDecimals is omitted when the token isn’t recognised, which happens for a QR deposit of an unlisted token. destinationAmount, when present, is the amount delivered in the destination token’s base units. Use it to reconcile a checkout payment; amount is the source amount sent. amountUsd is the USD value as entered in the modal. It is omitted for flows with no amount input: QR transfer, fiat on-ramp, and exchange connect.
sourceChain: "unknown" is deposit-only. When a webhook-detected deposit arrives without chain or token information, sourceChain is "unknown" and sourceToken is undefined — handle this branch so you don’t pick the wrong explorer URL.

Withdraw events

WithdrawLifecycleEvent carries the same type values minus "balance-changed" and "smart-account-changed". Its txHash is Hex, sourceChain is always a number, sourceToken / targetToken are Address, and "submitted" adds an accountAddress: Address field. "connected" means the deposit account for the chosen target is registered and fundable, not that a wallet connected.

onReady

Fires once when the modal is initialized and ready for interaction. No payload.

onError

Fires on errors at any stage — wallet connection, transaction signing, bridge setup — that prevent the deposit from being submitted. Distinct from the "failed" lifecycle event, which covers failures after the source transaction confirms.

Codes

PROCESS_TIMEOUT fires at most once per deposit. From v0.17.0 it fires at the served escalated threshold, five to ten minutes in practice, and keeps its previous fixed ten-minute threshold when no estimate is served. It does not fire for same-route transfers, and the withdraw modal keeps the fixed threshold. Errors without a code carry only message. For bridge-level codes, see deposit processing error codes.

Error handling

After the source chain transaction confirms, the deposit service may retry automatically before the "failed" event fires.