@dynamic-labs-sdk/react-native-embedded-wallet-export
v1.12.2
Published
Secure private-key export for Dynamic WaaS embedded wallets on **React Native**.
Readme
@dynamic-labs-sdk/react-native-embedded-wallet-export
Secure private-key export for Dynamic WaaS embedded wallets on React Native.
The export ceremony runs inside a visible, screenshot-protected native WebView overlay. The private key is rendered entirely within the WebView's sandboxed document — it is never sent across the bridge, so the host application's JavaScript cannot read, log, or screenshot it. This is the intentional security boundary: the developer integrating the SDK cannot access the raw key.
Installation
pnpm add @dynamic-labs-sdk/react-native-embedded-wallet-exportRequires @dynamic-labs-sdk/client (configured for React Native) and a React
Native host that links the DynamicClientWebView native module shipped with the
client package.
Usage
Mount <ExportPrivateKey /> to present the export overlay; unmount it to
dismiss. The component owns the overlay's lifecycle.
import { ExportPrivateKey } from '@dynamic-labs-sdk/react-native-embedded-wallet-export';
import { getWalletAccounts } from '@dynamic-labs-sdk/client';
import { isWaasWalletAccount } from '@dynamic-labs-sdk/client/waas';
import { useState } from 'react';
import { Button, View } from 'react-native';
function ExportScreen() {
const [revealing, setRevealing] = useState(false);
const walletAccount = getWalletAccounts()[0];
if (!walletAccount || !isWaasWalletAccount({ walletAccount })) return null;
return (
<View>
<Button title="Reveal private key" onPress={() => setRevealing(true)} />
{revealing && (
<ExportPrivateKey
walletAccount={walletAccount}
onError={(error) => {
// Log a non-sensitive representation only — never the raw error,
// which could carry key material or stack traces.
const errorType = error instanceof Error ? error.name : typeof error;
console.warn('Private key export failed', { errorType });
setRevealing(false);
}}
/>
)}
</View>
);
}Showing a loading state
ExportPrivateKey is a minimal primitive: it renders the secure box plus the
native overlay and signals readiness via onComplete (fired once the key is
displayed). The loading UI is yours to style — render your own spinner and hide
it on onComplete. The native overlay is transparent while it loads, so a
spinner placed behind it shows through during the wait.
import { ActivityIndicator, StyleSheet, View } from 'react-native';
import { useState } from 'react';
function ExportWithSpinner({ walletAccount }) {
const [loading, setLoading] = useState(true);
return (
<View style={{ position: 'relative' }}>
<ExportPrivateKey
walletAccount={walletAccount}
onComplete={() => setLoading(false)}
onError={() => setLoading(false)}
/>
{loading && (
<View
pointerEvents="none"
style={[
StyleSheet.absoluteFillObject,
{ alignItems: 'center', justifyContent: 'center' },
]}
>
<ActivityIndicator size="large" />
</View>
)}
</View>
);
}pointerEvents="none" keeps the spinner from intercepting taps meant for the
native key WebView (e.g. its copy button).
Props
| Prop | Type | Description |
| --------------- | -------------------------- | ----------------------------------------------------------------- |
| walletAccount | WalletAccount | The WaaS wallet account whose private key is exported. Required. |
| password | string | Optional password to decrypt the private key. |
| onComplete | () => void | Called once the key is displayed in the overlay. No key material. |
| onError | (error: unknown) => void | Called on export failure. The error never contains key material. |
Security
- The key is displayed only inside the native WebView overlay and never crosses the JS bridge.
- The overlay enables screenshot/recording protection and disables web inspection.
- Unmounting the component tears the overlay down, so the displayed key cannot outlive the screen that requested it.
