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.
Blink runs inside the modal
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”.
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.
Deposit Crypto now runs on Blink
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 itself — 999
(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 dev —
calm_public_(live|sandbox)_….