Skip to main content
Migrate an existing Fireblocks Non-Custodial Wallet (NCW) into a Dynamic WaaS wallet. Migration takes over the Fireblocks key material and provisions WaaS wallet accounts for every supported chain in a single cross-chain call, preserving the addresses the user already had. Before this: create and initialize a Dynamic client (see Creating a Dynamic Client, Initializing the Dynamic Client) and add a WaaS extension for at least one chain.
Before you start. This is for apps already running a Fireblocks NCW integration. The user must be authenticated with Dynamic — migration creates the WaaS wallet for the current user, and your Fireblocks wallet is left untouched. You supply the existing device’s deviceId, password, and storage from your NCW integration, plus a jwt from your own auth. Migration currently supports EVM and Solana; other chains come back as a per-chain failure (see Result).

Prerequisites

Fireblocks migration is behind a feature flag. Contact support to enable it for your environment.
Set up the migration backend before users migrate — see Migrate from Fireblocks for the full setup. In short:
  • Dashboard — configure the migration backend base URL and environment (sandbox / production) in your environment settings.
  • Backend — expose POST /api/devices/{deviceId}/rpc that relays NCW messages to Fireblocks, and enable CORS for the Dynamic iframe origin https://app.dynamicauth.com.
Fireblocks migration settings in the Dynamic dashboard

Usage

Parameters

  • deviceId: string — The Fireblocks NCW device id being migrated.
  • jwt: string — Your application’s auth token for the user (for example, from social login). It’s forwarded as the bearer token to your backend’s RPC endpoint, which authenticates the request. This is not a Fireblocks credential — your Fireblocks API auth stays on the backend.
  • password: string — The password that encrypted the NCW device storage. Required.
  • storage: Record<string, string> — The device’s persisted Fireblocks NCW entries (every NCW-* key), verbatim — the encrypted values exactly as stored. See Building the storage map.
  • expectedAddresses: Record<string, string> — Required. The wallet address Fireblocks held per chain name (for example, { EVM: "0x…" }). Its keys are the chains migration runs on — only those chains are migrated — and each imported Dynamic address is verified against the value, so a wrong derivation fails loudly instead of producing an unusable wallet.
Pass a client as the second argument only when using multiple Dynamic clients.

Building the storage map

storage must contain the device’s persisted Fireblocks NCW entries (every NCW-* key) verbatim — the encrypted values exactly as stored. The migration iframe re-hydrates this map and decrypts the secure entries itself using the same password and deviceId, so the values must stay as ciphertext. Build it in your application, where your Fireblocks NCW integration persisted the device. The snippet below reads the default browser storage; if you configured a custom NCW storage provider, read the device’s entries from there instead.
Enumerate the base BrowserLocalStorageProvider so secure entries stay encrypted. Do not read entries through a secure-storage provider (ISecureStorageProvider, e.g. PasswordEncryptedLocalStorage) — its get() decrypts to plaintext, which will not round-trip through the takeover.

Result

Migration is per chain, so inspect wallets to see which chains migrated. account is the created WaaS wallet account; fireblocksAlgorithm and fireblocksKeyId identify the source Fireblocks key, useful for logging and reconciliation. Re-running migration is safe: a chain already imported on a prior run comes back as { ok: true, alreadyMigrated: true }. A { ok: false } entry means that chain failed while others may have succeeded — surface it and let the user retry. Possible errorCode values: Treat any other ok: false as a failure to surface and retry.

Error Handling

These are thrown from the call itself (distinct from the per-chain errorCode above):
  • If no WaaS wallet provider is registered, it throws a NoWalletProviderFoundError.
  • If the resolved provider is not a WaaS provider, it throws a NotWaasWalletProviderError.
  • A wrong password fails the takeover; it does not create a usable wallet.
Last modified on June 25, 2026