Skip to main content

v0.13.x → v0.14.0

v0.14.0 replaces the analytics contracts for all three modals and adds a session envelope. No flow, prop, or UI behavior changes, but annotated callback types and switches over old event names need updates.

Update callback and union handling

onEvent also delivers ingest failures. Widen explicitly annotated callbacks:
Do the same for WithdrawAnalyticsEvent and ClaimAnalyticsEvent. An inferred parameter needs no annotation change. This widening handles the diagnostic type only; it does not migrate switches over removed event names. ModalAnalyticsEvent now has four members: the three modal funnel unions plus AnalyticsIngestFailureEvent. Add an analytics_ingest_failure branch to exhaustive modal-wide switches.

Read funnel position from step

The v0.14.0 contracts remove last_step. Read step on every position-bearing event. Abandonment always has a non-null step. Close has the latest step, or step: null only when the session closes before entering the funnel. UI outcomes use the latest logical step and do not prove backend fulfillment.

Update reason routing

Friction means progress is blocked without a failed attempted operation. It carries step and reason, but no retryable. Failure means an attempted operation failed or the flow reached a terminal condition. It carries step, reason, and retryable. A retry is a subsequent explicit attempt after the reported reason. Bounded friction, failure, and retry reasons begin with an exported AnalyticsReasonFamily: account_setup, amount, exchange, lookup, migration, modal, processor, provider, quote, recipient, recovery, refund, regional_methods, registration, route, signature, submission, swapped, transfer, or wallet. Match the declared list longest-prefix first. Do not split at the first underscore. Abandonment, close-source, and ingest-diagnostic vocabularies are separate. Already-routable reason values are unchanged.

Replace deposit events

All eight v0.13.x events are removed. v0.14.0 uses a method-aware taxonomy with step and top-level funding_method. See the current deposit analytics contract. Balances, amounts, token labels, country, and shortcut CTA details leave the event stream with no replacement. Method selection and handoff now use method-aware identifier bags: Transfer handoff identities describe the source the user pays from, not the session target. source_chain is CAIP-2 (eip155:<id>, the Solana mainnet namespace, or hypercore:spot). source_token is the on-chain identity: lowercase for EVM and HyperCore hex values, case-sensitive for Solana mints, with native SOL represented by the system-program mint rather than native. The handoff uses a deposit_address correlator. Non-transfer handoffs use transaction_hash, deposit_id, or swapped_external_customer_id; those four correlator types can also appear on deposit_modal_correlator_observed. Do not treat the source displayed while the transfer picker is loading as the selected source. The handoff waits for the picker to settle and never publishes that temporary default. A session whose source never resolves emits step events but no transfer handoff; a later selection of a distinct source can emit another handoff. When rebuilding same-route deposit funnels, preserve the processing-step order. A transfer emits deposit_modal_step_open at transfer_tracking before deposit_modal_ui_outcome reports outcome: "completed" at that step. A wallet deposit does the same at wallet_processing. These completed outcomes are widget observations, not proof of backend or on-chain fulfillment. See the current deposit analytics contract.

Replace withdraw events

Both v0.13.x amount-screen events are removed. See the current withdraw events. Balances, amount, and token and chain labels leave the stream. withdraw_modal_handoff adds transaction_hash, managed_account, and same_route as the join identities and route classification. same_route is unknown before submit, pinned on handoff, and repeated on later events. Same-route withdrawals create no backend bridge row, so their UI outcome is only a client observation.

Replace claim events

All five v0.13.x events are replaced. See the current claim events. The handoff contains deposit_id, transaction_hash: string | null, and refund_transaction_hash. The first is the authoritative join key; transaction_hash is the deposit’s source transaction, and refund_transaction_hash is the submitted refund transaction. The searched hash, typed refund destination, deposit amount, chain, and HTTP status are not sent.

Read the session envelope

Every funnel event now carries session_id, modal, widget_version, timestamp, and modal-specific session_properties. Deposit events additionally carry top-level funding_method, which is null before method selection. The snapshot is taken when the modal opens and stays fixed for that session.
  • Deposit properties include enabled funding methods, wallet integration, fiat-method provenance, asset-migration providers and initial provider, gasless-wallet configuration, presentation, effective overlay-close configuration, prefill flags, and optional target dimensions.
  • Withdraw properties include presentation, effective overlay-close configuration, prefill flags, and optional target dimensions. The target is the destination on which the form opened, including its source fallback; flags record whether your app supplied it.
  • Claim properties include presentation, effective overlay-close configuration, and transaction-hash and refund-destination prefill flags.
target_chain is CAIP-2 and target_token is the on-chain identity. EVM and HyperCore hex values are lowercased; Solana mint case is preserved; native is allowed. Invalid target fields are omitted independently. Changing target props while the modal is open changes the flow but not this snapshot.

Forward POST /analytics/ingest-token

The browser mints a short-lived attribution token on your proxy. Events go directly to Rhinestone and never traverse your proxy. deposit-widget-proxy forwards this route already; for a custom proxy, add it to the allowlist and return Cache-Control: no-store. Skipping the route leaves sessions unattributed but does not stop collection or any modal flow. See the analytics token route.

Allow the ingest origin in connect-src

If your app sets an explicit content security policy, add https://v1.orchestrator.rhinestone.dev to connect-src. The directive must also include your proxy origin and any configured rpcUrls; see content security policy. A block surfaces only as analytics_ingest_failure with reason: "network". Apps without an explicit policy need no change.

v0.11.x → v0.12.0

Two changes: one proxy-side, one a single line to delete.

Forward GET /chains

The modal has read the chain set from /chains since v0.11.0, but it still carried a compiled-in table it fell back to. That table is gone. A proxy that does not forward the route no longer degrades to a built-in chain list — it leaves every picker empty, and the deposit flow reports that supported chains are unavailable. deposit-widget-proxy has forwarded it since 2026-08-11, so redeploying the packaged proxy is enough. A hand-written proxy needs the route added to its allowlist — see required routes. This is not a CORS change: it is a GET using headers the modal already sends, so it cannot break a preflight.
The upside of the removal is that the chain set is now whatever the backend serves, in both directions — a chain we add appears without a modal release, and one we withdraw stops being offered instead of lingering until you upgrade.

Delete uiConfig.showHistoryButton

Deposit history is now always available, so the flag has nothing left to switch. Delete the line; the button renders regardless, from the screen where the user picks a deposit method.
If your proxy does not forward GET /deposits, this is the release where that becomes visible: the panel is now reachable and shows the failure, where before the button could be switched off and the gap stayed hidden.

v0.8.x → v0.9.0

v0.9.0 moves both modals onto service-managed accounts, hands the withdrawal transfer to your app, and renames or removes props that no longer described what they did. The account and withdraw changes need code; the prop renames are mechanical.

Deploy your proxy first

Four of these changes are proxy-side and take effect the moment the new modal loads in a browser. None of them degrades — the request 404s, or the browser blocks it at preflight. See required routes for the full table. /setup-account and /register are no longer called and can be dropped once no older modal version is in use.
Bare Hono cors() is safe for the version header — with no allowHeaders it reflects whatever the preflight asks for. An explicit allow-list is what breaks, and it breaks on upgrade rather than on first deploy.
Both modals — the signerAddress and sessionChainIds props are gone, along with the DEFAULT_SIGNER_ADDRESS, EnableSessionDetails, and AccountInitData exports. Registration now goes through POST /register-managed, which a self-hosted proxy must forward before you ship — see required routes. There is no session key and no signature prompt during setup. <WithdrawModal> no longer moves funds. It previously built and submitted a Safe execTransaction, which only worked for apps whose funds sat in a Safe. It now asks your app to perform one transfer:
Send to to exactly, and return the on-chain transaction hash — not a userOp hash. See executing the transfer for both rules and a Safe-backed example, including how to keep gas sponsored. Also on <WithdrawModal>: onRequestConnect is removed (the modal needs no wallet, so there is no connect step — it opens on the withdraw form), the SafeTransactionRequest export is replaced by WithdrawTransferRequest, and the "submitted" lifecycle event renames safeAddress to accountAddress. POST /safe/withdraw still exists — the modal simply stopped calling it.

Renamed and removed props

Renames, plus the removal of config the server already owns. Nothing here changes what the modal can do.
dappAddress is removed with no replacement. The modal reads the address off walletClient.account, which nothing previously validated it against — so the modal could read balances for one address while the user was connected as another. allowedRoutes and the RouteConfig type are removed from both modals. They filtered the pickers client-side with nothing enforcing it, so a list that drifted from your deposit whitelist offered the user a source the processor then rejected. Set the whitelist via POST /setup; to offer a restricted subset, use an API key whose whitelist matches. enableSolana is removed for the same reason — Solana sources follow the deposit whitelist. uiConfig.checkLiquidity is removed. It cost an orchestrator round trip per continue to compute a warning the review screen never rendered. The cap is still checked and shown on the QR / transfer screen. rhinestoneApiKey is removed from both modals. It was never read — the key belongs on your backend proxy, which attaches it upstream. Delete it; nothing consumed it. FiatPaymentMethodOption is no longer exported. It described a row descriptor that fiatMethods no longer takes. backendUrl is now required on all three modals, and the DEFAULT_BACKEND_URL export is gone. The old default pointed at a Rhinestone-internal service running on our API key, so any integration that omitted the prop was silently routing its users’ deposits through it.
If you already set backendUrl, nothing changes. If you didn’t, you were on our key and need a proxy before upgrading. TypeScript flags the omission; each modal also logs a console.error when the value is missing, empty or whitespace, since backendUrl={process.env.X ?? ""} typechecks fine. fiatOnrampMethods becomes fiatMethods, a boolean map keyed by Swapped payment_group instead of a list of row descriptors:
The old prop made you supply each row’s label, sublabel and icon, which meant pasting our copy and freezing a claim like "Instant - $10,000 limit" into your bundle.
fiatOnrampMethods={[]} used to fall through to offering every payment method. An empty or all-false fiatMethods now offers none. If you computed the list dynamically and could produce an empty one, check which you wanted.

Optional where it was mandatory

<WithdrawModal>’s targetChain and targetToken are now optional. They only ever seeded the form — the user can pick any supported destination — so omitting them opens on a same-chain, same-token withdrawal. @reown/appkit and @reown/appkit-adapter-wagmi are now optional peer dependencies. An app that passes its own walletClient never opens AppKit and no longer needs it installed. See install.

New

  • enableWallet?: boolean (default true) on <DepositModal> — turn off to present a flow with no wallet even when a walletClient or reownAppId is supplied.
  • <ClaimModal> and the ./claim subpath — a user pastes a failed or rejected deposit’s transaction hash and gets the funds returned. The user’s own signature authorizes it, so this needs no backend of your own: pass signRecovery and forward POST /deposits/recover on your proxy. For recipients that can’t sign, @rhinestone/deposit-modal/server exports createRefundHandler to authorize against your own session instead.

Behavior changes worth checking

  • connected no longer fires for flows with no wallet (QR, fiat, exchange). It previously reported the declared address as though a wallet had connected. If you used it as a “flow started” signal, switch to onReady.
  • A QR-only integration no longer auto-locks to the wallet. The connect step’s auto-skip never accounted for enableQrTransfer or asset migrations, so it could skip past the only funding option you had enabled.
  • Logos load from Rhinestone’s asset CDN. Apps with an explicit img-src CSP must allow https://s3.rhinestone.dev — a blocked image fails silently. See content security policy.
  • HYPERCORE_RECIPIENT_NOT_EOA is no longer emitted. HyperCore deposits now accept a smart-account recipient, and the pre-screen that blocked one is gone. If you branch on that onError code, the branch is dead.
  • A chain your deposit whitelist allows nothing on is no longer offered in the QR flow’s chain picker, instead of appearing with built-in tokens the deposit would then be rejected for. Chains the shortlist says nothing about keep their existing set.
  • Fiat payment methods are personalized by region unless you pass fiatMethods. See regional payment methods.
  • The deposit review shows a single “Fees” row. The per-category breakdown and its tooltips are gone; uiConfig.feeSponsored and uiConfig.feeTooltip still apply on the processing and result screens.

Removed prop warnings

Both modals log a console.error naming the replacement when passed any prop removed in this release. TypeScript already catches these; the runtime warning is for plain JavaScript hosts, spread props, and loosely typed call sites, where several of the removals fail silently rather than visibly.

v0.1.x / v0.2.x → v0.3.0

v0.3.0 collapses each modal’s per-event callbacks into a single onLifecycle callback, renames the analytics event types, removes the /reown and /safe subpath entry points, and drops connectButtonLabel. <DepositModal> and <WithdrawModal> share the same callback shape, but their lifecycle payloads are not identical — see Asymmetries below.

Callback collapse — onLifecycle

Both modals replace their individual callbacks with one onLifecycle that receives a discriminated event. Switch on event.type; the payload fields keep the same names as before.
DepositModal
See status tracking for the full event payloads.

Asymmetries

The two unions look alike but differ — don’t assume one helper typechecks against both.
sourceChain: "unknown" is deposit-only. A webhook-detected deposit can arrive without chain or token info, in which case deposit events carry sourceChain: "unknown" and sourceToken: undefined. Handle this branch in your deposit onLifecycle switch — the wrong branch picks the wrong explorer URL. Withdraw flows always know the source chain.

Analytics type rename

The onEvent prop name is unchanged on both modals, but its parameter type was renamed. The payload shape is unchanged.

Removed

  • connectButtonLabel — gone from both modals. The connect-step copy is controlled internally; delete any consumer-side label, there is no replacement.
  • /reown and /safe subpath imports — they re-exported nothing that isn’t already on the root entry point.
    The ./deposit, ./withdraw, ./constants, and ./styles.css subpaths remain for tree-shaking.

Additive — no action required

New in v0.3.0; existing code keeps working:
  • appBalanceUsd?: number on <DepositModal> — renders a “Balance after deposit” row (appBalanceUsd + amount) instead of fetching a portfolio balance.
  • dappImports?: DappImportsConfig on <DepositModal> — pull balances from third-party apps. See migrating assets.
  • defaultAmount: "max" — defaults the input to the user’s full source-token balance.
  • Solana destinationstargetChain: Chain | number | "solana", targetToken: Address | string, recipient: Address | string.
  • New root exportsDepositLifecycleEvent, WithdrawLifecycleEvent, DappImportsConfig, OutputTokenRule, plus the renamed analytics types.

Unchanged

onError, onReady, onRequestConnect, the onEvent prop name, dappWalletClient / dappPublicClient / reownAppId, <WithdrawModal>’s onSignTransaction, and the @rhinestone/deposit-modal/styles.css export all keep their names and signatures.
Scoped to v0.3.0. Several of these changed again in v0.9.0 — see the v0.8.x → v0.9.0 section at the top of this page.