Skip to main content

Prerequisites

Calm authenticates Dynamic users via the JWT Dynamic mints on sign-in. The JWT carries the user’s sub, verified_credentials (including the linked EVM wallet), and the Dynamic environment id — which is exactly what the SDK forwards to Calm. The only setup is making sure Dynamic is configured for EVM wallets so an Ethereum address appears in verified_credentials. Register the Ethereum connectors in your Dynamic config (see “Wrap App” below) — no Dynamic dashboard toggle is required. You’ll also need your public Dynamic environment ID — the same environmentId you pass to <DynamicContextProvider>, found in the Dynamic dashboard. Add it when you generate your Calm publishable key so the key is bound to your Dynamic environment.
If no EVM connector is registered, the SDK can’t start a session — the API can’t see an EVM wallet in the JWT claims and returns wallet_not_linked.

Generating the publishable key

The calmKey you pass to <DynamicCalmProvider> is a publishable key minted in the Calm dashboard. A Dynamic key is bound to your Dynamic environment ID, so Calm only accepts JWTs minted by that Dynamic environment.
1

Open the Calm dashboard

Sign in to the Calm dashboard and open Publishable keys.
2

Fill out "Generate a new key"

  • EnvironmentSandbox to start, Live for production. Each environment issues its own key (calm_public_sandbox_… / calm_public_live_…).
  • Wallet provider — select Dynamic.
  • Dynamic environment ID — paste your Dynamic environment ID (from the Dynamic dashboard). Required for Dynamic keys; it binds the key to your Dynamic environment.
  • Allowed origin (optional) — the origin your app is served from. Live keys require an https:// origin; in Sandbox you can leave it blank to skip the Origin check (e.g. for http://localhost).
Calm dashboard generate-key form with the Dynamic wallet provider and a Dynamic environment ID

Calm dashboard: Generate a new key, Dynamic selected

3

Generate and copy the key

Click Generate. The new key appears under Active keys — copy it and pass it as calmKey. It looks like calm_public_sandbox_<32 hex>.
A generated publishable key listed under Active keys with a copy button

Calm dashboard: the new key under Active keys

Installation

To add Calm to your project, install the required packages.
  • Dynamic is the identity provider the SDK reads the user, wallet, and JWT from.
  • @dynamic-labs/ethereum registers the EVM connectors so the user’s wallet rides inside the JWT’s verified_credentials.
  • TanStack Query is an async state manager that handles requests, caching, and more.

Import the stylesheet

Import the Calm stylesheet once at your app root (Next.js layout.tsx, React main.tsx):
or @import it from your own CSS file:

Wrap App in <DynamicCalmProvider>

Place <DynamicCalmProvider> inside <DynamicContextProvider> and <QueryClientProvider>.
app/layout.tsx
Mount <DynamicCalmProvider> only once primaryWallet is set — it reads the wallet address at mount and requires it to be defined. Gate the mount on useDynamicContext().primaryWallet?.address; mounting before the wallet resolves throws.

Open the onramp

The provider always renders its children. Wrap any trigger element in <CalmOnramp> to open the deposit modal, and gate it on the Calm session being ready via useSession — the trigger only enables once the session is live, so the modal’s data loads immediately on open. (A connected wallet alone isn’t enough; the session is what the modal’s requests are authenticated with.)

Props

string
required
Your publishable key — calm_public_(live|sandbox)_<32 hex>. Identifies the Calm tenant. Sent to the API on every session creation via the X-Calm-Publishable-Key header.
"usd" | "gbp" | "eur"
required
Source fiat currency for the bank-deposit onramp.
number
default:"1337"
Destination chain id for the delivered USDC. Defaults to HyperCore (1337).
string
default:"https://api.calmtreasury.xyz"
Override the Calm API root. Use https://api.sandbox.calmtreasury.xyz for the sandbox environment.
Partial<Record<number, string>>
required
Per-chain RPC URLs (keyed by chain id) used to read transaction receipts when confirming a swap. The receipt is read from the chain the swap transaction lands on (e.g. Base 8453), so each source chain needs a reliable RPC — reading through the wallet’s own endpoint can stall the confirm step. Provide an entry for every chain you support. Pass a stable (memoized) object.

Errors

The provider’s createSession throws on any non-2xx response. The useSession hook surfaces the error in result.error. See Errors for the full code table; the most common from this shell: