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.

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.

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. 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.

Claim events

ClaimLifecycleEvent is a separate union — lookup, refund_requested, complete, failed. See claim modal.

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

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.

Analytics

The onEvent callback delivers modal funnel events and analytics delivery diagnostics. Funnel events use DepositAnalyticsEvent, WithdrawAnalyticsEvent, or ClaimAnalyticsEvent; diagnostics use AnalyticsIngestFailureEvent.
All three funnels use the same event taxonomy where it applies: open, step open and complete, friction, failure, retry, handoff, UI outcome, abandonment, and close. Each modal also has events and fields specific to its flow. Every position-bearing event uses step. Abandonment carries the latest reached step. Close carries that step, or step: null only when the session closes before entering the funnel. A UI outcome reports what the widget observed, not backend or on-chain fulfillment truth.

The session envelope

Every funnel event carries these fields in addition to its event-specific payload: Deposit funnel events also carry top-level funding_method, which is null before method selection. AnalyticsIngestFailureEvent is a delivery diagnostic, not a funnel event, so it has no session_properties. Session properties stay fixed for the session even if props change while the modal is open.

Deposit session properties

The target is the configured deposit destination.

Withdraw session properties

The target is the destination on which the form opened, including the source-chain and source-token fallback when no target was supplied. The prefill flags separately record whether your app supplied each value.

Claim session properties

overlay_close_enabled reports the effective configuration. Backdrop closing is opt-in because closeOnOverlayClick defaults to false.

Target identities

target_chain uses CAIP-2: eip155:<id>, solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp, or hypercore:spot. target_token is the on-chain token identity. EVM addresses and HyperCore hex token IDs are normalized to lowercase, Solana mints preserve case, and the literal native is allowed. Invalid chain and token values are omitted independently, so one invalid value does not remove the other or invalidate the session.

Friction, failure, and retry

  • Friction means progress is blocked without an attempted operation failing. Its payload carries step and reason.
  • Failure means an attempted operation failed or the flow reached a terminal condition. Its payload carries step, reason, and retryable.
  • Retry is a subsequent explicit attempt after a reported reason. Its payload carries step and reason.
retryable means the widget judged another attempt safe or offered that affordance. It appears only on failure events. Reasons used by friction, failure, and retry form bounded vocabularies. Each starts with one declared AnalyticsReasonFamily: account_setup, amount, exchange, lookup, migration, modal, processor, provider, quote, recipient, recovery, refund, regional_methods, registration, route, signature, submission, swapped, transfer, or wallet. Route reasons by matching this declared list longest-prefix first. Do not split at the first underscore: account_setup_* and regional_methods_* are multiword families. Abandonment reasons, close sources, and ingest-delivery reasons are separate vocabularies and do not follow this convention.

Deposit analytics

Funding methods are wallet, transfer, fiat_onramp, exchange_connect, and asset_migration.

Deposit steps

Deposit events

All payloads below also carry the envelope and top-level funding_method. entry_source is user or initial_config. Method-selection identifiers are discriminated by funding_method: A transfer handoff requires correlator: { type: "deposit_address", value } and identifiers: { source_chain, source_token }. Those identities describe the payment source, not the session target. Non-transfer handoffs cannot use deposit_address and have no identifier bag. The transfer picker can show a temporary default while its source options load. The modal waits until the picker settles on the source presented to the user before emitting a handoff, so it never publishes that temporary source. If no source identity resolves, the step events remain but no transfer handoff is emitted. A later selection of a distinct source can emit another handoff. For same-route deposits, the processing step opens before the completed outcome. A transfer emits deposit_modal_step_open at transfer_tracking, followed by deposit_modal_ui_outcome with outcome: "completed" and step: "transfer_tracking". A wallet deposit emits the same events in that order at wallet_processing. These outcomes report what the widget observed, not backend or on-chain fulfillment. Correlator types are deposit_address, transaction_hash, deposit_id, and swapped_external_customer_id. Abandonment reasons are back, modal_close, provider_back, wallet_disconnect, method_replacement, and initial_migration_fallback. Close sources are header_button, escape, overlay, host_controlled, provider_back, native_dismiss_request, success_done, failure_cancel, and new_deposit_reset.

Deposit reasons

Withdraw analytics

Steps are form, review, submit, and processing. submit is the asynchronous boundary opened by the review CTA, not a separate screen. same_route is unknown before submit, pinned on handoff, and repeated on later events. A same-route withdrawal creates no backend bridge row, so its UI outcome remains a client observation. Close sources are header_button, escape, overlay, host_controlled, success_done, and failure_cancel.

Withdraw reasons

Claim analytics

Steps are lookup, select, review, and submit. submit is the asynchronous boundary opened by the review CTA, not a separate screen. Most events arrive at most once per attempt. claim_modal_lookup_result arrives once per submitted search. The searched hash, refund destination, and deposit amount are not sent. Identities enter the stream only after a lookup matches a backend deposit row. transaction_hash is the deposit’s source transaction hash; refund_transaction_hash is the submitted refund transaction. A missing source hash is null, never an empty string, and deposit_id is the authoritative join key. A claim UI outcome is what the widget observed at submit, not on-chain settlement. Close sources are header_button, escape, overlay, host_controlled, success_done, and failure_cancel.

Claim reasons

These are not the claim lifecycle events. onLifecycle reports where the claim itself got to; onEvent reports funnel behavior.

Opting out

enableAnalyticsIngest={false} on any modal turns Rhinestone collection off entirely. onEvent receives the same events either way, so your own pipeline is unaffected. Collection and attribution are separate: leaving the POST /analytics/ingest-token route off your proxy leaves sessions unattributed but does not stop collection.

Ingest failures

When delivery to Rhinestone fails, onEvent receives an AnalyticsIngestFailureEvent. It carries type, session_id, modal, widget_version, timestamp, reason, dropped_events, and status when an HTTP response exists. It has no session_properties and is never sent to Rhinestone. It fires at most once per distinct reason per session, with a running dropped_events count.
Analytics never blocks or interrupts a deposit, withdrawal, or claim. Ignoring a delivery diagnostic costs analytics only.