Skip to main content

v0.13.0 — 2026-09-09

The largest release so far. Wallet authentication is removed, every route moves, the card flow becomes a hosted purchase, and there is a React Native package. Read the first two sections before upgrading — both need a change on your side.

Wallet authentication is gone

There is no SIWE handshake, no Privy or Dynamic identity-token exchange, and no calm_session / calm_refresh cookie. The /v1/session/* routes are removed. Every route now identifies the caller with the publishable key alone, sent as X-Calm-Publishable-Key and checked against that key’s Origin allowlist. useSession, the SessionResponse type and CreateSession are removed. Hooks that used to wait for a session fire as soon as an address is present, so a cold open reaches its first request a round trip earlier. CalmProvider no longer takes getIdentityToken.

Every route is one segment shorter

The wallet is a query parameter or a body field, not a path segment:
If you use @calm-xyz/core’s generated clients or the React hooks, this is handled for you. If you call the API directly, every path changes.

React Native

@calm-xyz/react-native ships the same flows on iOS and Android, over a shared wagmi shell. The iOS bundle needed two workarounds Hermes forces — Intl.NumberFormat.prototype.formatToParts and EIP-6963 discovery are absent — both handled inside the package.

The card flow is a hosted purchase

The buyer completes the payment in the card provider’s own page, rendered in an iframe inside the modal, rather than on a form we collect. POST /v1/card/checkout answers { order_id, url }: the order row is written before the URL is handed out, and card/orders/{id} polls it. getCardQuote is replaced by createCardQuote (POST /v1/card/quote), which prices the whole purchase — either a fiat spend or a target crypto amount — rather than a dollar figure. The picker now offers a row per payment method: Card, Apple Pay and Google Pay. The wallets are hidden in markets where the provider does not carry them. getCardConfig and getCardCustomer are gone with the form they configured.

The buyer can set their country

The flow header draws a country control beside the close button. country is now the initial value rather than the fixed truth — useCalm() returns setCountry beside it, the same shape chain/setChain already had. country is also optional. Omit it and the SDK reads the region off the browser’s locale (en-GB gives GB), falling back to US where there is none. currency is optional too and follows the country: GB gives GBP, the euro area EUR, everywhere else USD. Pass one and it is pinned. COUNTRY_CODES is no longer the ISO standard — it is the 53 markets the card rail serves. Offering the other 196 offered dead ends. The control draws flags, which are fetched rather than bundled. This is the SDK’s first network-loaded image, so img-src https://cdn.jsdelivr.net becomes a Content-Security-Policy requirement. Every way it can fail — a 404, a throttle, a CSP refusal — renders the two-letter country code instead, so a partner who cannot allow-list it loses nothing but the picture. CalmProvider takes assetUrl to serve them from your own host, or "" to fetch nothing.

An inline surface

CalmEmbed renders the deposit flow inline instead of in a dialog. It takes flows and no trigger, mounts into whatever container you give it, and draws a back button above the current step — no card, no title, no balance line.

Amounts follow a locale

numberFormat is replaced by locale: pass a BCP-47 tag and separators, grouping and decimal handling follow that locale’s own conventions, so ja-JP and es-ES are reachable for the first time. Omit it and the SDK uses the browser’s own. The number-format globe is removed from the modal header — the format comes from the provider now, not a control inside the flow.

Naming the payout

destinationToken is required on every provider, spelled the way the rest of the SDK spells it: USDC, USDC.e, USDm, AUSD. It was optional, and a mount that left it out got the chain’s default. The Blink crypto deposit renders in a panel that follows the height Blink reports and picks up the modal’s colors, instead of taking the page over.

Arbitrum

Arbitrum (42161) is a supported chain again — a destination for every flow, a swap source, a Blink deposit network and a Coinbase withdrawal origin. Balances read it by default; it was in the catalog but absent from the readable-chain list, so balances came back empty rather than erroring.

Fixes

  • Cash App is hidden outside the US instead of failing at the amount step. It settles through a US-only rail, so the row was never completable elsewhere.
  • Registration moves under the virtual-account resource (POST /v1/virtual-accounts/register), joining the terms-of-service and identity-verification steps that already lived there. The two submit endpoints are replaced by polling the status.
  • The bank account details panel renders on the standard surface tint again, including its loading skeleton.
  • The Swap row hides when balances settle with nothing swappable, and a skeleton covers the settling window.
  • Navigating between views no longer flashes the wrong tab.

v0.12.0 — 2026-09-01

Reworks how the modal moves between the picker and a flow, fixes six things partners can see, and removes the module-global configuration from @calm-xyz/core.

Back returns to where you came from

The modal’s navigation is a stack of pages, and each tab is a page of its own. Leaving a flow returns to the tab it was opened from rather than to the default tab, so a deposit started from Cash no longer lands the buyer back on Crypto. The Blink deposit’s Done button returns to the picker instead of closing the modal. Back and close are hidden while a request is in flight, rather than staying visible and rejecting the press. The modal is about 1.5 kB smaller gzipped.

Fixes

  • The card checkout follows the modal’s theme. It rendered light inside a dark modal.
  • A receipt names the payout token you configured. An integration paying out AUSD saw a receipt that said USDC.
  • A buyer whose identity document is rejected stays on the document step. They were skipped past it when the rejection arrived while the status screen was open.
  • The swap token list shows its loading state on a cold open instead of an empty list.
  • Session-expiry copy appears for the responses that mean the session ended: session_invalid, token_expired and invalid_token. unauthorized, which the API returns only on a server fault, now reads as a server problem instead of asking the buyer to sign in again.
  • The Privy provider throws when no identity token is available, matching how it already handles a missing wallet address. It previously opened a session the API rejects unconditionally.

@calm-xyz/core removes module-global configuration

configureCalm, getConfig and setConfig are removed, along with the responseType request option that nothing read. Every operation already takes apiUrl per request, which is how the SDK has called them throughout. This affects you only if you import @calm-xyz/core directly. @calm-xyz/react is unchanged.

v0.11.0 — 2026-08-25

Adds an optional crypto/cash split to the picker, lets the modal render and open before a wallet is connected (a connect state stands in for the picker until one is), and adds a provider that takes a partner-supplied address.

Grouped flows

flows now also takes { cash, crypto }, where each section lists its own methods in render order:
The grouped form is what turns the sections on. Each one gets a tab, crypto first, and the modal opens there. The tab bar appears only when both sections render a row. While the user is on the crypto tab, a dot on the cash tab flags a bank verification that needs attention, so that state is never buried behind the tab. Each array may only hold methods from its own section; a misplaced one throws. Passing the flat Flow[] renders exactly as it did before: one list, no tabs, in the order you wrote it. That includes the default, which is still ["swap", "bank"], so an integration that doesn’t adopt the grouped form sees no change at all. New integrations should use the grouped form. Section, CashFlow, CryptoFlow, FlowSections, FlowConfig, and ResolvedFlows are exported alongside Flow.

The modal connects the wallet itself

The provider no longer throws when no wallet is connected. The modal renders and opens signed-out — to a connect state in place of the picker: where the identity provider has a sign-in flow of its own the modal offers a button named for it (Sign in with Privy, Sign in with Dynamic) that opens that flow, and a plain wallet-signature setup gets an inline notice while your app’s own UI does the connecting. The deposit methods appear the moment an address exists. A connected wallet is all the methods need — they open on the first click, waiting on the session inside the flow if it is still being created. Apps that gate <CalmOnramp> on their own connection state can keep doing so — behavior for an already-connected wallet is unchanged.

A provider for partner-supplied addresses

AddressCalmProvider, exported from the package root, takes the wallet address straight from your app — for wallet stacks the SDK has no provider for. It has no signer and no sign-in of its own: sessions come only from renewal, and flows that must sign transactions need one of the wallet providers. See “Supplying the address yourself”.

Configured flows gate their own setup

The Stripe card flow used to read its config and inject the vendor script on every USD picker. It now does that only when card[stripe] is configured.

v0.10.2 — 2026-08-10

Fixes @calm-xyz/react installation in package managers that strictly resolve package metadata before reading the tarball contents. No SDK API changes are required.

v0.9.0 — 2026-08-07

Blink takes over the Deposit Crypto row, and the old QR deposit flow is removed. The blink row is labeled Deposit Crypto and carries Blink’s brand mark, replacing the previous “Deposit with Blink” label. Choosing it opens Blink’s hosted flow, and USDC arrives in the connected wallet.

The QR deposit is gone

Breaking: Flow no longer accepts "crypto", and the default flows is now ["swap", "bank"] instead of ["swap", "crypto", "bank"]. The view that showed a deposit address and QR code for sending from another wallet is removed with it. If you relied on the default set, that row disappears on upgrade; add "blink" to flows for the replacement. If you listed "crypto" explicitly, drop it or swap it for "blink".

v0.8.0 — 2026-07-14

Polishes the Cash App onramp: a branded pay button, a visible quote and back navigation on the pay step, and a $3 minimum.

Pay step

  • The QR step’s button is now labeled Pay with Cash App and uses Cash App’s own brand color, distinguishing it from the SDK’s other onramp actions.
  • A You Receive row now appears under the QR code, showing the amount from the order that was just created.
  • The back chevron is now visible on the pay step, returning to the amount step instead of being hidden.
  • Once payment is opened, the button itself shows the order’s status (e.g. “Confirming payment”, “Bridging”) and disables — replacing the separate status badge.

$3 minimum

Cash App deposits now require at least $3. Amounts below that show an inline error before submitting instead of failing after a round trip.

v0.7.0 — 2026-07-06

Requires a connected wallet, and fails fast with clear errors when the provider is set up wrong.

Requires a connected wallet

Breaking: the Calm provider now requires a connected wallet. Mounted without one it throws instead of rendering in a broken half-state — mount it only once a wallet is connected, or gate it on your app’s connection state. Previously it mounted and only failed later, on the first onramp action.

Clearer setup errors

A missing required provider now throws an actionable error at mount instead of a late or cryptic failure:
  • No <QueryClientProvider> above the Calm provider → a message telling you to add one.
  • <WagmiCalmProvider> outside a wagmi provider → a message telling you to wrap it in <WagmiProvider> (or Reown AppKit / RainbowKit).

v0.6.0 — 2026-06-25

Adds a Connect exchange onramp, a flows prop for curating the deposit picker, and a required rpcUrls prop on the Privy and Dynamic providers.

Connect exchange

The user connects a Coinbase account, picks an origin chain (Ethereum, Base, or Arbitrum) and an amount, and withdraws USDC into their Calm wallet.

Choosing flows

<CalmOnramp flows={[...]}> takes an ordered list of the methods to show — swap, deposit_crypto, bank, cashapp, exchange, and card. Omit it for the default set: Swap, Crypto deposit, and Bank transfer. The Cash App, exchange, and card rows are opt-in.
Cash App and the exchange onramp are available but not yet recommended for production.

Per-chain RPC URLs

<PrivyCalmProvider> and <DynamicCalmProvider> now take a required rpcUrls prop — a map of chain id to RPC URL — used to read swap transaction receipts from a reliable per-chain endpoint (reading through the wallet’s own endpoint could stall the confirm step). Breaking: add rpcUrls when you upgrade.

v0.5.2 — 2026-06-25

Re-enables the Swap to USDC onramp. Fixes a transaction-encoding issue that caused swaps to fail in some wallets before the first confirmation.

v0.5.1 — 2026-06-25

Temporarily removes the Swap to USDC row from the deposit picker while an issue with the swap flow is addressed. The other onramps — Crypto deposit, Cash App, and Bank transfer — are unchanged.

v0.5.0 — 2026-06-24

Adds a Cash App onramp and a third auth modality (Dynamic), and drops the separate Hyperliquid mode concept — the execution layer is now just the destination chain id.

Cash App onramp

A new fiat onramp: the user enters an amount, pays in Cash App, and USDC lands in their Calm wallet. USD only. It appears as its own row in the deposit picker (shown when currency === "usd"), alongside Swap, Crypto deposit, and Bank transfer.

Dynamic provider

<DynamicCalmProvider> (@calm-xyz/react/dynamic) — for apps authenticated with Dynamic. Joins the existing <PrivyCalmProvider> and <WagmiCalmProvider>. Bind the key to your public Dynamic environment id when you generate it in the dashboard.

Hyperliquid: mode is gone

The Hyperliquid execution layer is now the chain id itself999 (HyperEVM) vs 1337 (HyperCore) — so the separate mode concept is removed across the API and SDK:
  • Removed: the Mode type, useCalm().mode / setMode, the providers’ initialMode prop, and the mode field on the wallet record and swap quote.
  • To deliver to HyperCore, set the destination chain to 1337; for HyperEVM, 999.

Providers + useCalm()

  • The destination-chain prop is now chain (was initialChain), defaulting to 1337 (HyperCore). useCalm() exposes chain + setChain.
  • useCalm() is configuration only now — address, apiUrl, chain, setChain, currency. The session/transaction callbacks and logout it used to surface are gone; drive sessions through useSession and clear them with useSession({ address }).clear().
  • useReady() is removed. Gate UI on the session being live instead — const ready = !!session.data && !session.isError, where session = useSession({ address }).

Publishable key

The sandbox environment prefix is sandbox, not devcalm_public_(live|sandbox)_….