@dynamic-labs-sdk/legacy-embedded-wallet-upgrade
Helpers to upgrade a user's legacy Dynamic embedded wallets to Dynamic WaaS. Wraps the URL contract, the redirect round-trip, the iframe/WebView completion handshake, and the session refresh behind a small set of functions, so you only call functions — you never assemble the flow yourself.
The core entry point is framework-agnostic (no React). React hooks live in a separate /react subpath so non-React consumers never load them.
Installation
npm install @dynamic-labs-sdk/legacy-embedded-wallet-upgrade @dynamic-labs-sdk/client
Detecting wallets that require an upgrade
import {
getWalletsRequiringUpgrade,
hasWalletsRequiringUpgrade,
} from '@dynamic-labs-sdk/legacy-embedded-wallet-upgrade';
if (hasWalletsRequiringUpgrade()) {
const wallets = getWalletsRequiringUpgrade();
// show an upgrade entry point for these wallets
}
Redirect path
Navigate the top-level window to the hosted upgrade app, then handle the result when it returns. The return leg is two steps — detectUpgradeRedirect tells you whether the current URL is a return from the upgrade app, and completeUpgradeRedirect finishes it (refreshing the session on success) — mirroring how OAuth and device registration consume their redirects. You always pass the URL yourself (window.location.href in the browser, the deep-link URL in React Native).
import {
startUpgradeRedirect,
detectUpgradeRedirect,
completeUpgradeRedirect,
} from '@dynamic-labs-sdk/legacy-embedded-wallet-upgrade';
// on the "Upgrade via redirect" button
startUpgradeRedirect();
// once, on app load
if (detectUpgradeRedirect({ url: window.location.href })) {
const result = await completeUpgradeRedirect({ url: window.location.href });
if (result?.status === 'error') {
showErrorToast();
}
}
Iframe path
The package renders no UI of its own — you own the container (e.g. a bottom sheet). openUpgradeIframe appends the iframe into your container, listens for the origin-checked completion message, refreshes the session on success, and resolves.
import { openUpgradeIframe } from '@dynamic-labs-sdk/legacy-embedded-wallet-upgrade';
const session = openUpgradeIframe({ container: sheetBodyElement });
const { status } = await session.completed;
closeSheet();
// if the user dismisses the sheet first:
session.close();
React Native / custom mounts
Use the DOM-agnostic primitives when you mount the upgrade app yourself (e.g. a React Native WebView):
import {
buildUpgradeUrl,
subscribeToUpgradeCompletion,
} from '@dynamic-labs-sdk/legacy-embedded-wallet-upgrade';
const src = buildUpgradeUrl();
const unsubscribe = subscribeToUpgradeCompletion({
onComplete: ({ status }) => handleOutcome(status),
});
React
React hooks are exported from the /react subpath, with react as an optional peer dependency:
import {
useWalletsRequiringUpgrade,
useHasWalletsRequiringUpgrade,
useUpgradeRedirectResult,
useUpgradeIframe,
} from '@dynamic-labs-sdk/legacy-embedded-wallet-upgrade/react';
const needsUpgrade = useHasWalletsRequiringUpgrade();
useUpgradeRedirectResult({ onComplete: ({ status }) => handleOutcome(status) });
const { upgradeUrl } = useUpgradeIframe({
active: isSheetOpen,
onComplete: ({ status }) => closeSheet(status),
});
Multiple clients
Every function accepts the Dynamic client as an optional second argument and defaults to getDefaultClient(); pass it explicitly only when you use more than one client.