Prerequisites
Before this page: users who sign in to your app (see Post-login user setup) and a Dynamic environment with WaaS enabled.What you’ll build
Some users may still hold an earlier, legacy generation of embedded wallet. The JavaScript SDK does not support these wallets: no extension registers a provider for them, so a user on a legacy wallet cannot sign transactions or messages, and calls fail with aNoWalletProviderFoundError. Upgrading to Dynamic WaaS is required for those users to use their wallets.
Dynamic provides a ready-made upgrade app that performs the upgrade, and the wallet keeps the same address. Because the upgrade happens in a dedicated app (hosted by Dynamic or on your own domain, see Hosting the app), the only supported path is routing the user to it; there is no in-SDK upgrade.
The @dynamic-labs-sdk/legacy-embedded-wallet-upgrade package wraps the whole flow so you only call functions: it derives the environment from your client, builds the app URL, validates the return URL, and handles the completion handshake. Your job is to:
- Detect which users need the upgrade.
- Route them to the upgrade app (by redirect, by embedding it, or by popup).
- React when the upgrade finishes.
- Redirect to the app: navigate away and read the outcome on return.
- Embed the app (iframe): render it inline in a sheet or modal.
- Popup (new window): open it in a separate window. The recommended route for React Native, where it runs in the system auth session.
Install
The React entry point (
/react) has react and @dynamic-labs-sdk/react-hooks as optional peer dependencies. Non-React apps import the root and never pull React in.Detect who needs the upgrade
Two synchronous helpers tell you whether the signed-in user has any wallet that needs upgrading. They read the environment and wallets from your client, so single-client apps call them with no arguments.- TypeScript
- React / React Native
Choose a route
Hosting the app
By default every helper points at Dynamic’s hosted upgrade app. You can run it two ways:- Dynamic-hosted: use the shared Dynamic-hosted deployment as-is (the default; pass nothing).
- Your own subdomain: point a CNAME (e.g.
upgrade.yourapp.com) at the deployment so it runs on your own domain and branding.
baseUrl. Every entry point accepts it:
Passkey users require your own domain
Passkeys are bound to the domain they were registered on: the authenticator ties each passkey to the site’s domain (its WebAuthn relying party ID) and the browser only offers it back on that domain or its subdomains. A passkey created onyourapp.com works on upgrade.yourapp.com but not on upgrade-wallet.dynamic.dev, so passkey users cannot use it in the Dynamic-hosted app, whether as their sign-in credential or as an MFA factor.
If any of your users use passkeys for sign-in or MFA, serve the upgrade app on the domain your passkeys were registered on (such as a subdomain like upgrade.yourapp.com when they were registered on yourapp.com) and set baseUrl accordingly. This is a hard requirement, not a UX preference.
Required: allowlist the app’s origin (CORS)
Add the origin serving the app to your environment’s allowed origins in the Dynamic dashboard, or the app’s calls will be blocked:- Your own subdomain → add your origin (e.g.
https://upgrade.yourapp.com). - Dynamic-hosted → add
https://upgrade-wallet.dynamic.dev.
Custom auth domains
If your environment authenticates through a custom domain (e.g.auth.yourapp.com in place of Dynamic’s default API), the Dynamic-hosted upgrade app only authenticates against that domain once it’s on Dynamic’s internal allowlist. Environments that already had a custom domain configured when Dynamic v5 shipped are allowlisted, so the flow works out of the box.
Set up a custom domain more recently and the upgrade fails to reach your API? Reach out to Dynamic to have your domain added to the allowlist.
Smoother UX: skip the extra login
If you serve the app on a subdomain of your app’s domain and your environment uses cookie-based sessions, an already-signed-in user lands in the app already authenticated, with no second login. Otherwise the user simply signs in again with the same credential they use in your app.Handling errors
Route-specific errors (blocked/closed popups, etc.) are covered on each route’s page.
See also
- Post-login user setup: detect upgrade needs alongside other post-login steps
- Connected wallets management: the upgraded wallet appears here with the same address
- Wallet backup & recovery: back up the wallet after upgrading