Skip to main content

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 that should be moved onto Dynamic WaaS. Dynamic provides a ready-made upgrade app that performs the upgrade — the wallet keeps the same address. 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:
  1. Detect which users need the upgrade.
  2. Route them to the upgrade app — by redirect, by embedding it, or by popup.
  3. React when the upgrade finishes.

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.

1. 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.
The upgrade app only accepts an https return URL. Serve your app over https (in local development, expose it with a tunnel such as ngrok) or the flow will be rejected.

Option A — Redirect to the app

Navigate the user to the upgrade app, then read the outcome when they return. startUpgradeRedirect builds the URL and navigates; consumeUpgradeRedirect reads and strips the result parameter, refreshes the session on success, and returns the outcome (null when the user didn’t just come back from an upgrade).

Option B — Embed the app (iframe / WebView)

Render the app in an iframe or WebView instead of navigating away. When embedded, the app posts an origin-checked completion message instead of redirecting, so your page can react and close the embed. The package owns the message contract; you keep full control of the surrounding UI (e.g. your bottom sheet or modal).
openUpgradeIframe appends an iframe into a container you provide and resolves completed when the app reports done (refreshing the session on success). Call close() if the user dismisses your sheet first.

Option C — Popup (new window)

Open the app in a popup window instead of navigating away or embedding it. Like the iframe path, the app posts an origin-checked completion message back — here to the window that opened it — so the current page never unloads. Reach for this when a full-page redirect is too heavy but you don’t want the app inline.
Browsers block popups that aren’t opened from a user gesture. Call openUpgradePopup (or the hook’s open) synchronously inside the click handler — don’t await anything before it, or the popup is blocked and you get an UpgradePopupBlockedError.
openUpgradePopup opens the window synchronously and resolves completed when the app reports done (refreshing the session on success). completed rejects with an UpgradePopupClosedError if the user closes the popup first, and the call throws an UpgradePopupBlockedError if the browser blocks it.

Hosting the app

By default every helper points at Dynamic’s hosted upgrade app. You can run it three ways:
  1. Dynamic-hosted — use the shared Dynamic-hosted deployment as-is (the default; pass nothing). It lives at https://waas-migration-dynamic-xyz.vercel.app.
  2. Your own subdomain — point a CNAME (e.g. upgrade.yourapp.com) at the Dynamic-hosted app (waas-migration-dynamic-xyz.vercel.app) so it runs on your own domain and branding, then reach out to Dynamic so we can add your custom domain to the deployment (otherwise it won’t serve on your CNAME).
  3. Fork & self-host — clone the upgrade app repo, build, and host it yourself.
For options 2 and 3, tell the package where the app lives with baseUrl — every entry point accepts it:

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 / self-host → add your origin (e.g. https://upgrade.yourapp.com).
  • Dynamic-hosted → add https://waas-migration-dynamic-xyz.vercel.app.
If the app can’t reach Dynamic, it shows an “add your domain to allowed origins” screen — a signal that the serving origin isn’t allowlisted.

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 — no second login. Otherwise the user simply signs in again with the same credential they use in your app.
Tell the just-arrived user to sign in with the same credential they use in your app — that’s how the app finds the wallet to upgrade.

Handling errors

See also

Last modified on July 22, 2026