@dynamic-labs-sdk/react-native-embedded-wallet-export 0.0.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -0
- package/dist/ExportPrivateKey/ExportPrivateKey.d.ts +26 -0
- package/dist/ExportPrivateKey/ExportPrivateKey.d.ts.map +1 -0
- package/dist/ExportPrivateKey/ExportPrivateKey.types.d.ts +33 -0
- package/dist/ExportPrivateKey/ExportPrivateKey.types.d.ts.map +1 -0
- package/dist/createWebViewWaasContainer/createWebViewWaasContainer.d.ts +54 -0
- package/dist/createWebViewWaasContainer/createWebViewWaasContainer.d.ts.map +1 -0
- package/dist/exports/index.d.ts +3 -0
- package/dist/exports/index.d.ts.map +1 -0
- package/dist/index.cjs +252 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.esm.js +224 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +44 -1
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# @dynamic-labs-sdk/react-native-embedded-wallet-export
|
|
2
|
+
|
|
3
|
+
Secure private-key export for Dynamic WaaS embedded wallets on **React Native**.
|
|
4
|
+
|
|
5
|
+
The export ceremony runs inside a visible, screenshot-protected native WebView
|
|
6
|
+
overlay. The private key is rendered entirely within the WebView's sandboxed
|
|
7
|
+
document — it is **never** sent across the bridge, so the host application's
|
|
8
|
+
JavaScript cannot read, log, or screenshot it. This is the intentional security
|
|
9
|
+
boundary: the developer integrating the SDK cannot access the raw key.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @dynamic-labs-sdk/react-native-embedded-wallet-export
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Requires `@dynamic-labs-sdk/client` (configured for React Native) and a React
|
|
18
|
+
Native host that links the `DynamicClientWebView` native module shipped with the
|
|
19
|
+
client package.
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
Mount `<ExportPrivateKey />` to present the export overlay; unmount it to
|
|
24
|
+
dismiss. The component owns the overlay's lifecycle.
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { ExportPrivateKey } from '@dynamic-labs-sdk/react-native-embedded-wallet-export';
|
|
28
|
+
import { getWalletAccounts } from '@dynamic-labs-sdk/client';
|
|
29
|
+
import { isWaasWalletAccount } from '@dynamic-labs-sdk/client/waas';
|
|
30
|
+
import { useState } from 'react';
|
|
31
|
+
import { Button, View } from 'react-native';
|
|
32
|
+
|
|
33
|
+
function ExportScreen() {
|
|
34
|
+
const [revealing, setRevealing] = useState(false);
|
|
35
|
+
const walletAccount = getWalletAccounts()[0];
|
|
36
|
+
|
|
37
|
+
if (!walletAccount || !isWaasWalletAccount({ walletAccount })) return null;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<View>
|
|
41
|
+
<Button title="Reveal private key" onPress={() => setRevealing(true)} />
|
|
42
|
+
{revealing && (
|
|
43
|
+
<ExportPrivateKey
|
|
44
|
+
walletAccount={walletAccount}
|
|
45
|
+
onError={(error) => {
|
|
46
|
+
// Log a non-sensitive representation only — never the raw error,
|
|
47
|
+
// which could carry key material or stack traces.
|
|
48
|
+
const errorType = error instanceof Error ? error.name : typeof error;
|
|
49
|
+
console.warn('Private key export failed', { errorType });
|
|
50
|
+
setRevealing(false);
|
|
51
|
+
}}
|
|
52
|
+
/>
|
|
53
|
+
)}
|
|
54
|
+
</View>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Showing a loading state
|
|
60
|
+
|
|
61
|
+
`ExportPrivateKey` is a minimal primitive: it renders the secure box plus the
|
|
62
|
+
native overlay and signals readiness via `onComplete` (fired once the key is
|
|
63
|
+
displayed). The loading UI is yours to style — render your own spinner and hide
|
|
64
|
+
it on `onComplete`. The native overlay is transparent while it loads, so a
|
|
65
|
+
spinner placed behind it shows through during the wait.
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
import { ActivityIndicator, StyleSheet, View } from 'react-native';
|
|
69
|
+
import { useState } from 'react';
|
|
70
|
+
|
|
71
|
+
function ExportWithSpinner({ walletAccount }) {
|
|
72
|
+
const [loading, setLoading] = useState(true);
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<View style={{ position: 'relative' }}>
|
|
76
|
+
<ExportPrivateKey
|
|
77
|
+
walletAccount={walletAccount}
|
|
78
|
+
onComplete={() => setLoading(false)}
|
|
79
|
+
onError={() => setLoading(false)}
|
|
80
|
+
/>
|
|
81
|
+
{loading && (
|
|
82
|
+
<View
|
|
83
|
+
pointerEvents="none"
|
|
84
|
+
style={[
|
|
85
|
+
StyleSheet.absoluteFillObject,
|
|
86
|
+
{ alignItems: 'center', justifyContent: 'center' },
|
|
87
|
+
]}
|
|
88
|
+
>
|
|
89
|
+
<ActivityIndicator size="large" />
|
|
90
|
+
</View>
|
|
91
|
+
)}
|
|
92
|
+
</View>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`pointerEvents="none"` keeps the spinner from intercepting taps meant for the
|
|
98
|
+
native key WebView (e.g. its copy button).
|
|
99
|
+
|
|
100
|
+
### Props
|
|
101
|
+
|
|
102
|
+
| Prop | Type | Description |
|
|
103
|
+
| --------------- | -------------------------- | ----------------------------------------------------------------- |
|
|
104
|
+
| `walletAccount` | `WalletAccount` | The WaaS wallet account whose private key is exported. Required. |
|
|
105
|
+
| `password` | `string` | Optional password to decrypt the private key. |
|
|
106
|
+
| `onComplete` | `() => void` | Called once the key is displayed in the overlay. No key material. |
|
|
107
|
+
| `onError` | `(error: unknown) => void` | Called on export failure. The error never contains key material. |
|
|
108
|
+
|
|
109
|
+
## Security
|
|
110
|
+
|
|
111
|
+
- The key is displayed only inside the native WebView overlay and never crosses
|
|
112
|
+
the JS bridge.
|
|
113
|
+
- The overlay enables screenshot/recording protection and disables web
|
|
114
|
+
inspection.
|
|
115
|
+
- Unmounting the component tears the overlay down, so the displayed key cannot
|
|
116
|
+
outlive the screen that requested it.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
import type { ExportPrivateKeyProps } from './ExportPrivateKey.types';
|
|
3
|
+
/**
|
|
4
|
+
* Renders a secure private-key export box for a WaaS wallet account on React
|
|
5
|
+
* Native. The key is rendered inside a `react-native-webview` that fills the
|
|
6
|
+
* box — the same split the web SDK uses (host renders the chrome, the
|
|
7
|
+
* cross-origin document renders only the key). Because it is a normal RN view,
|
|
8
|
+
* it scrolls, clips and positions like any view; no native overlay or manual
|
|
9
|
+
* window positioning is involved. The key is rendered inside the WebView and
|
|
10
|
+
* never crosses the bridge, so the host app's JS cannot read, log, or
|
|
11
|
+
* screenshot it. On Android, window-level screenshot/recording protection is
|
|
12
|
+
* enabled while the component is mounted.
|
|
13
|
+
*
|
|
14
|
+
* Place it inside your own modal/card and add your own close button — unmounting
|
|
15
|
+
* tears the WebView down. Size/position it with the `style` prop.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <Modal visible={revealing} onRequestClose={() => setRevealing(false)}>
|
|
20
|
+
* <ExportPrivateKey walletAccount={walletAccount} onError={setError} />
|
|
21
|
+
* <Button title="I'm Done" onPress={() => setRevealing(false)} />
|
|
22
|
+
* </Modal>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare const ExportPrivateKey: FC<ExportPrivateKeyProps>;
|
|
26
|
+
//# sourceMappingURL=ExportPrivateKey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExportPrivateKey.d.ts","sourceRoot":"","sources":["../../src/ExportPrivateKey/ExportPrivateKey.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAUhC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,gBAAgB,EAAE,EAAE,CAAC,qBAAqB,CA6GtD,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { WalletAccount } from '@dynamic-labs-sdk/client';
|
|
2
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
/**
|
|
4
|
+
* Props for the {@link ExportPrivateKey} component.
|
|
5
|
+
*
|
|
6
|
+
* The component owns the export view's lifecycle: the key is shown while the
|
|
7
|
+
* component is mounted and torn down on unmount. The private key is rendered
|
|
8
|
+
* entirely inside the `react-native-webview` and never reaches this (or any)
|
|
9
|
+
* host JS layer.
|
|
10
|
+
*/
|
|
11
|
+
export type ExportPrivateKeyProps = {
|
|
12
|
+
/**
|
|
13
|
+
* Called once the export ceremony has completed and the key is displayed in
|
|
14
|
+
* the WebView. Receives no key material.
|
|
15
|
+
*/
|
|
16
|
+
onComplete?: () => void;
|
|
17
|
+
/**
|
|
18
|
+
* Called if the export fails (load failure, step-up MFA failure, etc.). The
|
|
19
|
+
* error never contains key material.
|
|
20
|
+
*/
|
|
21
|
+
onError?: (error: unknown) => void;
|
|
22
|
+
/** Optional password to decrypt the private key. */
|
|
23
|
+
password?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Style for the box that hosts the key display (size, border, background).
|
|
26
|
+
* The WebView fills this box, so its dimensions control the display size.
|
|
27
|
+
* Defaults to a minimal white box with a min height; add your own chrome.
|
|
28
|
+
*/
|
|
29
|
+
style?: StyleProp<ViewStyle>;
|
|
30
|
+
/** The WaaS wallet account whose private key is exported. */
|
|
31
|
+
walletAccount: WalletAccount;
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=ExportPrivateKey.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExportPrivateKey.types.d.ts","sourceRoot":"","sources":["../../src/ExportPrivateKey/ExportPrivateKey.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,6DAA6D;IAC7D,aAAa,EAAE,aAAa,CAAC;CAC9B,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { WaasSDKContainer } from '@dynamic-labs-sdk/client/waas/core';
|
|
2
|
+
import type { RefObject } from 'react';
|
|
3
|
+
import type WebView from 'react-native-webview';
|
|
4
|
+
/**
|
|
5
|
+
* JS injected into the hosted export page before its own scripts run. The page
|
|
6
|
+
* is written for the browser iframe transport: it reaches the SDK host via
|
|
7
|
+
* `window.parent.postMessage` and listens with `window.addEventListener
|
|
8
|
+
* ('message', ...)`. In a top-level WebView `window.parent === window`, so we
|
|
9
|
+
* override `window.postMessage` to forward the page's outbound payload to React
|
|
10
|
+
* Native through `window.ReactNativeWebView.postMessage`. The reverse direction
|
|
11
|
+
* (host -> page) is delivered by `sendMessage` injecting a synthetic
|
|
12
|
+
* `MessageEvent`, so no inbound wiring is needed here. Mirrors the native
|
|
13
|
+
* `BRIDGE_USER_SCRIPT` in the client package's DynamicWebViewHost.
|
|
14
|
+
*/
|
|
15
|
+
export declare const WAAS_EXPORT_BRIDGE_SHIM = "\n(function () {\n if (window.__dynamicBridgeInstalled) { return; }\n window.__dynamicBridgeInstalled = true;\n var post = function (data) {\n try {\n var json = typeof data === 'string' ? data : JSON.stringify(data);\n window.ReactNativeWebView.postMessage(json);\n } catch (e) {}\n };\n try {\n Object.defineProperty(window, 'postMessage', {\n configurable: true,\n writable: true,\n value: function (data, targetOrigin, transfer) { post(data); }\n });\n } catch (e) {\n try { window.postMessage = function (d) { post(d); }; } catch (_) {}\n }\n})();\ntrue;\n";
|
|
16
|
+
/**
|
|
17
|
+
* Parameters for {@link createWebViewWaasContainer}.
|
|
18
|
+
*/
|
|
19
|
+
export type CreateWebViewWaasContainerParams = {
|
|
20
|
+
/**
|
|
21
|
+
* Sets the URL the hosted `<WebView>` should load. An empty string unmounts
|
|
22
|
+
* it (used by `destroy`). The component renders the WebView from this state.
|
|
23
|
+
*/
|
|
24
|
+
setUrl: (url: string) => void;
|
|
25
|
+
/** Ref to the rendered `react-native-webview` instance. */
|
|
26
|
+
webViewRef: RefObject<WebView | null>;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* A {@link WaasSDKContainer} bound to a `react-native-webview`, plus the event
|
|
30
|
+
* sinks the host component wires into the WebView's `onMessage`/`onError`.
|
|
31
|
+
*/
|
|
32
|
+
export type WebViewWaasContainer = {
|
|
33
|
+
/** The container handed to `exportWaasPrivateKeyToContainer`. */
|
|
34
|
+
container: WaasSDKContainer;
|
|
35
|
+
/** Feed a `<WebView>` load/HTTP error into the container's onError handlers. */
|
|
36
|
+
handleWebViewError: (error: unknown) => void;
|
|
37
|
+
/** Feed a raw `<WebView>` onMessage payload into the container's handlers. */
|
|
38
|
+
handleWebViewMessage: (raw: string) => void;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Creates a `WaasSDKContainer` backed by a `react-native-webview` rendered in
|
|
42
|
+
* the normal RN view tree (so it scrolls, clips, and positions like any view —
|
|
43
|
+
* no native overlay or manual window positioning). The private key is rendered
|
|
44
|
+
* inside the WebView's sandboxed document and never crosses the bridge; only
|
|
45
|
+
* the WaaS transport protocol messages do.
|
|
46
|
+
*
|
|
47
|
+
* The returned `container` is passed to `exportWaasPrivateKeyToContainer`; the
|
|
48
|
+
* `handleWebView*` sinks are wired into the WebView's `onMessage`/`onError` by
|
|
49
|
+
* the host component.
|
|
50
|
+
*
|
|
51
|
+
* @not-instrumented
|
|
52
|
+
*/
|
|
53
|
+
export declare const createWebViewWaasContainer: ({ setUrl, webViewRef, }: CreateWebViewWaasContainerParams) => WebViewWaasContainer;
|
|
54
|
+
//# sourceMappingURL=createWebViewWaasContainer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createWebViewWaasContainer.d.ts","sourceRoot":"","sources":["../../src/createWebViewWaasContainer/createWebViewWaasContainer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,OAAO,MAAM,sBAAsB,CAAC;AAEhD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,qmBAqBnC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gCAAgC,GAAG;IAC7C;;;OAGG;IACH,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,2DAA2D;IAC3D,UAAU,EAAE,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;CACvC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,iEAAiE;IACjE,SAAS,EAAE,gBAAgB,CAAC;IAC5B,gFAAgF;IAChF,kBAAkB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7C,8EAA8E;IAC9E,oBAAoB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,0BAA0B,4BAGpC,gCAAgC,KAAG,oBAiFrC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/exports/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AACxE,YAAY,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: ((k) => from[k]).bind(null, key),
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
+
value: mod,
|
|
24
|
+
enumerable: true
|
|
25
|
+
}) : target, mod));
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
let _dynamic_labs_sdk_assert_package_version = require("@dynamic-labs-sdk/assert-package-version");
|
|
29
|
+
let _dynamic_labs_sdk_client = require("@dynamic-labs-sdk/client");
|
|
30
|
+
let _dynamic_labs_sdk_client_core = require("@dynamic-labs-sdk/client/core");
|
|
31
|
+
let _dynamic_labs_sdk_client_waas_core = require("@dynamic-labs-sdk/client/waas/core");
|
|
32
|
+
let react = require("react");
|
|
33
|
+
let react_native = require("react-native");
|
|
34
|
+
let react_native_webview = require("react-native-webview");
|
|
35
|
+
react_native_webview = __toESM(react_native_webview);
|
|
36
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
37
|
+
|
|
38
|
+
//#region package.json
|
|
39
|
+
var name = "@dynamic-labs-sdk/react-native-embedded-wallet-export";
|
|
40
|
+
var version = "1.12.0";
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/createWebViewWaasContainer/createWebViewWaasContainer.ts
|
|
44
|
+
/**
|
|
45
|
+
* JS injected into the hosted export page before its own scripts run. The page
|
|
46
|
+
* is written for the browser iframe transport: it reaches the SDK host via
|
|
47
|
+
* `window.parent.postMessage` and listens with `window.addEventListener
|
|
48
|
+
* ('message', ...)`. In a top-level WebView `window.parent === window`, so we
|
|
49
|
+
* override `window.postMessage` to forward the page's outbound payload to React
|
|
50
|
+
* Native through `window.ReactNativeWebView.postMessage`. The reverse direction
|
|
51
|
+
* (host -> page) is delivered by `sendMessage` injecting a synthetic
|
|
52
|
+
* `MessageEvent`, so no inbound wiring is needed here. Mirrors the native
|
|
53
|
+
* `BRIDGE_USER_SCRIPT` in the client package's DynamicWebViewHost.
|
|
54
|
+
*/
|
|
55
|
+
const WAAS_EXPORT_BRIDGE_SHIM = `
|
|
56
|
+
(function () {
|
|
57
|
+
if (window.__dynamicBridgeInstalled) { return; }
|
|
58
|
+
window.__dynamicBridgeInstalled = true;
|
|
59
|
+
var post = function (data) {
|
|
60
|
+
try {
|
|
61
|
+
var json = typeof data === 'string' ? data : JSON.stringify(data);
|
|
62
|
+
window.ReactNativeWebView.postMessage(json);
|
|
63
|
+
} catch (e) {}
|
|
64
|
+
};
|
|
65
|
+
try {
|
|
66
|
+
Object.defineProperty(window, 'postMessage', {
|
|
67
|
+
configurable: true,
|
|
68
|
+
writable: true,
|
|
69
|
+
value: function (data, targetOrigin, transfer) { post(data); }
|
|
70
|
+
});
|
|
71
|
+
} catch (e) {
|
|
72
|
+
try { window.postMessage = function (d) { post(d); }; } catch (_) {}
|
|
73
|
+
}
|
|
74
|
+
})();
|
|
75
|
+
true;
|
|
76
|
+
`;
|
|
77
|
+
/**
|
|
78
|
+
* Creates a `WaasSDKContainer` backed by a `react-native-webview` rendered in
|
|
79
|
+
* the normal RN view tree (so it scrolls, clips, and positions like any view —
|
|
80
|
+
* no native overlay or manual window positioning). The private key is rendered
|
|
81
|
+
* inside the WebView's sandboxed document and never crosses the bridge; only
|
|
82
|
+
* the WaaS transport protocol messages do.
|
|
83
|
+
*
|
|
84
|
+
* The returned `container` is passed to `exportWaasPrivateKeyToContainer`; the
|
|
85
|
+
* `handleWebView*` sinks are wired into the WebView's `onMessage`/`onError` by
|
|
86
|
+
* the host component.
|
|
87
|
+
*
|
|
88
|
+
* @not-instrumented
|
|
89
|
+
*/
|
|
90
|
+
const createWebViewWaasContainer = ({ setUrl, webViewRef }) => {
|
|
91
|
+
const messageHandlers = [];
|
|
92
|
+
const errorHandlers = [];
|
|
93
|
+
let destroyed = false;
|
|
94
|
+
return {
|
|
95
|
+
container: {
|
|
96
|
+
destroy() {
|
|
97
|
+
if (destroyed) return;
|
|
98
|
+
destroyed = true;
|
|
99
|
+
messageHandlers.length = 0;
|
|
100
|
+
errorHandlers.length = 0;
|
|
101
|
+
setUrl("");
|
|
102
|
+
},
|
|
103
|
+
isAlive: () => !destroyed,
|
|
104
|
+
onError(handler) {
|
|
105
|
+
errorHandlers.push(handler);
|
|
106
|
+
return () => {
|
|
107
|
+
const index = errorHandlers.indexOf(handler);
|
|
108
|
+
if (index !== -1) errorHandlers.splice(index, 1);
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
onMessage(handler) {
|
|
112
|
+
messageHandlers.push(handler);
|
|
113
|
+
return () => {
|
|
114
|
+
const index = messageHandlers.indexOf(handler);
|
|
115
|
+
if (index !== -1) messageHandlers.splice(index, 1);
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
sendMessage(data) {
|
|
119
|
+
const payload = JSON.stringify(data);
|
|
120
|
+
webViewRef.current?.injectJavaScript(`window.dispatchEvent(new MessageEvent('message',{data:${payload},origin:window.location.origin}));true;`);
|
|
121
|
+
},
|
|
122
|
+
async setUrl(url) {
|
|
123
|
+
setUrl(url);
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
handleWebViewError(error) {
|
|
127
|
+
if (destroyed) return;
|
|
128
|
+
for (const handler of errorHandlers) handler(error);
|
|
129
|
+
},
|
|
130
|
+
handleWebViewMessage(raw) {
|
|
131
|
+
if (destroyed) return;
|
|
132
|
+
let data;
|
|
133
|
+
try {
|
|
134
|
+
data = JSON.parse(raw);
|
|
135
|
+
} catch {
|
|
136
|
+
data = raw;
|
|
137
|
+
}
|
|
138
|
+
for (const handler of messageHandlers) handler(data);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/ExportPrivateKey/ExportPrivateKey.tsx
|
|
145
|
+
/**
|
|
146
|
+
* Renders a secure private-key export box for a WaaS wallet account on React
|
|
147
|
+
* Native. The key is rendered inside a `react-native-webview` that fills the
|
|
148
|
+
* box — the same split the web SDK uses (host renders the chrome, the
|
|
149
|
+
* cross-origin document renders only the key). Because it is a normal RN view,
|
|
150
|
+
* it scrolls, clips and positions like any view; no native overlay or manual
|
|
151
|
+
* window positioning is involved. The key is rendered inside the WebView and
|
|
152
|
+
* never crosses the bridge, so the host app's JS cannot read, log, or
|
|
153
|
+
* screenshot it. On Android, window-level screenshot/recording protection is
|
|
154
|
+
* enabled while the component is mounted.
|
|
155
|
+
*
|
|
156
|
+
* Place it inside your own modal/card and add your own close button — unmounting
|
|
157
|
+
* tears the WebView down. Size/position it with the `style` prop.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```tsx
|
|
161
|
+
* <Modal visible={revealing} onRequestClose={() => setRevealing(false)}>
|
|
162
|
+
* <ExportPrivateKey walletAccount={walletAccount} onError={setError} />
|
|
163
|
+
* <Button title="I'm Done" onPress={() => setRevealing(false)} />
|
|
164
|
+
* </Modal>
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
const ExportPrivateKey = ({ walletAccount, password, onComplete, onError, style }) => {
|
|
168
|
+
const webViewRef = (0, react.useRef)(null);
|
|
169
|
+
const [url, setUrl] = (0, react.useState)(null);
|
|
170
|
+
const onCompleteRef = (0, react.useRef)(onComplete);
|
|
171
|
+
onCompleteRef.current = onComplete;
|
|
172
|
+
const onErrorRef = (0, react.useRef)(onError);
|
|
173
|
+
onErrorRef.current = onError;
|
|
174
|
+
const passwordRef = (0, react.useRef)(password);
|
|
175
|
+
passwordRef.current = password;
|
|
176
|
+
const connectorRef = (0, react.useRef)(null);
|
|
177
|
+
if (connectorRef.current === null) connectorRef.current = createWebViewWaasContainer({
|
|
178
|
+
setUrl: (next) => setUrl(next || null),
|
|
179
|
+
webViewRef
|
|
180
|
+
});
|
|
181
|
+
const { container, handleWebViewMessage, handleWebViewError } = connectorRef.current;
|
|
182
|
+
const aliveRef = (0, react.useRef)(true);
|
|
183
|
+
const startedRef = (0, react.useRef)(false);
|
|
184
|
+
(0, react.useEffect)(() => {
|
|
185
|
+
if (startedRef.current) return;
|
|
186
|
+
startedRef.current = true;
|
|
187
|
+
(0, _dynamic_labs_sdk_client_core.setScreenshotProtection)({ enabled: true });
|
|
188
|
+
(0, _dynamic_labs_sdk_client_waas_core.exportWaasPrivateKeyToContainer)({
|
|
189
|
+
container,
|
|
190
|
+
password: passwordRef.current,
|
|
191
|
+
walletAccount
|
|
192
|
+
}, (0, _dynamic_labs_sdk_client.getDefaultClient)()).then(() => {
|
|
193
|
+
if (aliveRef.current) onCompleteRef.current?.();
|
|
194
|
+
}).catch((error) => {
|
|
195
|
+
if (aliveRef.current) onErrorRef.current?.(error);
|
|
196
|
+
});
|
|
197
|
+
return () => {
|
|
198
|
+
aliveRef.current = false;
|
|
199
|
+
container.destroy();
|
|
200
|
+
(0, _dynamic_labs_sdk_client_core.setScreenshotProtection)({ enabled: false });
|
|
201
|
+
};
|
|
202
|
+
}, []);
|
|
203
|
+
const expectedOrigin = (0, react.useMemo)(() => {
|
|
204
|
+
if (!url) return null;
|
|
205
|
+
try {
|
|
206
|
+
return new URL(url).origin;
|
|
207
|
+
} catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}, [url]);
|
|
211
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
212
|
+
style: [styles.box, style],
|
|
213
|
+
children: url && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_webview.default, {
|
|
214
|
+
ref: webViewRef,
|
|
215
|
+
source: { uri: url },
|
|
216
|
+
injectedJavaScriptBeforeContentLoaded: WAAS_EXPORT_BRIDGE_SHIM,
|
|
217
|
+
onMessage: (event) => handleWebViewMessage(event.nativeEvent.data),
|
|
218
|
+
onError: (event) => handleWebViewError(event.nativeEvent),
|
|
219
|
+
onHttpError: (event) => handleWebViewError(event.nativeEvent),
|
|
220
|
+
originWhitelist: expectedOrigin ? [expectedOrigin] : [],
|
|
221
|
+
onShouldStartLoadWithRequest: (request) => {
|
|
222
|
+
if (!expectedOrigin) return true;
|
|
223
|
+
try {
|
|
224
|
+
return new URL(request.url).origin === expectedOrigin;
|
|
225
|
+
} catch {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
incognito: true,
|
|
230
|
+
style: styles.webView
|
|
231
|
+
})
|
|
232
|
+
});
|
|
233
|
+
};
|
|
234
|
+
const styles = react_native.StyleSheet.create({
|
|
235
|
+
box: {
|
|
236
|
+
backgroundColor: "#FFFFFF",
|
|
237
|
+
minHeight: 88,
|
|
238
|
+
overflow: "hidden"
|
|
239
|
+
},
|
|
240
|
+
webView: {
|
|
241
|
+
backgroundColor: "transparent",
|
|
242
|
+
flex: 1
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/exports/index.ts
|
|
248
|
+
(0, _dynamic_labs_sdk_assert_package_version.assertPackageVersion)(name, version);
|
|
249
|
+
|
|
250
|
+
//#endregion
|
|
251
|
+
exports.ExportPrivateKey = ExportPrivateKey;
|
|
252
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["messageHandlers: Array<(data: unknown) => void>","errorHandlers: Array<(error: unknown) => void>","data: unknown","ExportPrivateKey: FC<ExportPrivateKeyProps>","View","WebView","StyleSheet","packageName","packageVersion"],"sources":["../package.json","../src/createWebViewWaasContainer/createWebViewWaasContainer.ts","../src/ExportPrivateKey/ExportPrivateKey.tsx","../src/exports/index.ts"],"sourcesContent":["","import type { WaasSDKContainer } from '@dynamic-labs-sdk/client/waas/core';\nimport type { RefObject } from 'react';\nimport type WebView from 'react-native-webview';\n\n/**\n * JS injected into the hosted export page before its own scripts run. The page\n * is written for the browser iframe transport: it reaches the SDK host via\n * `window.parent.postMessage` and listens with `window.addEventListener\n * ('message', ...)`. In a top-level WebView `window.parent === window`, so we\n * override `window.postMessage` to forward the page's outbound payload to React\n * Native through `window.ReactNativeWebView.postMessage`. The reverse direction\n * (host -> page) is delivered by `sendMessage` injecting a synthetic\n * `MessageEvent`, so no inbound wiring is needed here. Mirrors the native\n * `BRIDGE_USER_SCRIPT` in the client package's DynamicWebViewHost.\n */\nexport const WAAS_EXPORT_BRIDGE_SHIM = `\n(function () {\n if (window.__dynamicBridgeInstalled) { return; }\n window.__dynamicBridgeInstalled = true;\n var post = function (data) {\n try {\n var json = typeof data === 'string' ? data : JSON.stringify(data);\n window.ReactNativeWebView.postMessage(json);\n } catch (e) {}\n };\n try {\n Object.defineProperty(window, 'postMessage', {\n configurable: true,\n writable: true,\n value: function (data, targetOrigin, transfer) { post(data); }\n });\n } catch (e) {\n try { window.postMessage = function (d) { post(d); }; } catch (_) {}\n }\n})();\ntrue;\n`;\n\n/**\n * Parameters for {@link createWebViewWaasContainer}.\n */\nexport type CreateWebViewWaasContainerParams = {\n /**\n * Sets the URL the hosted `<WebView>` should load. An empty string unmounts\n * it (used by `destroy`). The component renders the WebView from this state.\n */\n setUrl: (url: string) => void;\n /** Ref to the rendered `react-native-webview` instance. */\n webViewRef: RefObject<WebView | null>;\n};\n\n/**\n * A {@link WaasSDKContainer} bound to a `react-native-webview`, plus the event\n * sinks the host component wires into the WebView's `onMessage`/`onError`.\n */\nexport type WebViewWaasContainer = {\n /** The container handed to `exportWaasPrivateKeyToContainer`. */\n container: WaasSDKContainer;\n /** Feed a `<WebView>` load/HTTP error into the container's onError handlers. */\n handleWebViewError: (error: unknown) => void;\n /** Feed a raw `<WebView>` onMessage payload into the container's handlers. */\n handleWebViewMessage: (raw: string) => void;\n};\n\n/**\n * Creates a `WaasSDKContainer` backed by a `react-native-webview` rendered in\n * the normal RN view tree (so it scrolls, clips, and positions like any view —\n * no native overlay or manual window positioning). The private key is rendered\n * inside the WebView's sandboxed document and never crosses the bridge; only\n * the WaaS transport protocol messages do.\n *\n * The returned `container` is passed to `exportWaasPrivateKeyToContainer`; the\n * `handleWebView*` sinks are wired into the WebView's `onMessage`/`onError` by\n * the host component.\n *\n * @not-instrumented\n */\nexport const createWebViewWaasContainer = ({\n setUrl,\n webViewRef,\n}: CreateWebViewWaasContainerParams): WebViewWaasContainer => {\n // IframeManager registers more than one onMessage handler on the same\n // container (handshake wait + transport forwarding), so we fan out to all.\n const messageHandlers: Array<(data: unknown) => void> = [];\n const errorHandlers: Array<(error: unknown) => void> = [];\n let destroyed = false;\n\n const container: WaasSDKContainer = {\n destroy() {\n if (destroyed) return;\n destroyed = true;\n messageHandlers.length = 0;\n errorHandlers.length = 0;\n // Unmount the WebView so the displayed key cannot outlive the container.\n setUrl('');\n },\n\n isAlive: () => !destroyed,\n\n // Signature is dictated by the upstream WaasSDKContainer interface.\n // eslint-disable-next-line custom-rules/require-single-object-param\n onError(handler: (error: unknown) => void) {\n errorHandlers.push(handler);\n return () => {\n const index = errorHandlers.indexOf(handler);\n if (index !== -1) errorHandlers.splice(index, 1);\n };\n },\n\n // Signature is dictated by the upstream WaasSDKContainer interface.\n // eslint-disable-next-line custom-rules/require-single-object-param\n onMessage(handler: (data: unknown) => void) {\n messageHandlers.push(handler);\n return () => {\n const index = messageHandlers.indexOf(handler);\n if (index !== -1) messageHandlers.splice(index, 1);\n };\n },\n\n sendMessage(data: Record<string, unknown>) {\n // Deliver host -> page as a synthetic 'message' event — the shape the\n // page expects from window.parent.postMessage in a browser iframe.\n const payload = JSON.stringify(data);\n webViewRef.current?.injectJavaScript(\n `window.dispatchEvent(new MessageEvent('message',{data:${payload},origin:window.location.origin}));true;`\n );\n },\n\n // Signature is dictated by the upstream WaasSDKContainer interface.\n // eslint-disable-next-line custom-rules/require-single-object-param\n async setUrl(url: string) {\n setUrl(url);\n },\n };\n\n return {\n container,\n\n handleWebViewError(error: unknown) {\n if (destroyed) return;\n for (const handler of errorHandlers) handler(error);\n },\n\n // Single positional arg matches the WebView onMessage event sink shape.\n // eslint-disable-next-line custom-rules/require-single-object-param\n handleWebViewMessage(raw: string) {\n if (destroyed) return;\n // The WaaS transport posts BOTH objects (JSON) and plain strings (e.g. the\n // `iframe-ready-<instanceId>` handshake). Deliver parsed JSON when possible,\n // otherwise the raw string — mirroring browser postMessage semantics, where\n // a string stays a string and an object stays an object. Discarding non-JSON\n // here would drop the handshake and hang the export until timeout.\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch {\n data = raw;\n }\n for (const handler of messageHandlers) handler(data);\n },\n };\n};\n","import { getDefaultClient } from '@dynamic-labs-sdk/client';\nimport { setScreenshotProtection } from '@dynamic-labs-sdk/client/core';\nimport { exportWaasPrivateKeyToContainer } from '@dynamic-labs-sdk/client/waas/core';\nimport type { FC } from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { StyleSheet, View } from 'react-native';\nimport WebView from 'react-native-webview';\n\nimport {\n WAAS_EXPORT_BRIDGE_SHIM,\n createWebViewWaasContainer,\n} from '../createWebViewWaasContainer/createWebViewWaasContainer';\nimport type { WebViewWaasContainer } from '../createWebViewWaasContainer/createWebViewWaasContainer';\nimport type { ExportPrivateKeyProps } from './ExportPrivateKey.types';\n\n/**\n * Renders a secure private-key export box for a WaaS wallet account on React\n * Native. The key is rendered inside a `react-native-webview` that fills the\n * box — the same split the web SDK uses (host renders the chrome, the\n * cross-origin document renders only the key). Because it is a normal RN view,\n * it scrolls, clips and positions like any view; no native overlay or manual\n * window positioning is involved. The key is rendered inside the WebView and\n * never crosses the bridge, so the host app's JS cannot read, log, or\n * screenshot it. On Android, window-level screenshot/recording protection is\n * enabled while the component is mounted.\n *\n * Place it inside your own modal/card and add your own close button — unmounting\n * tears the WebView down. Size/position it with the `style` prop.\n *\n * @example\n * ```tsx\n * <Modal visible={revealing} onRequestClose={() => setRevealing(false)}>\n * <ExportPrivateKey walletAccount={walletAccount} onError={setError} />\n * <Button title=\"I'm Done\" onPress={() => setRevealing(false)} />\n * </Modal>\n * ```\n */\nexport const ExportPrivateKey: FC<ExportPrivateKeyProps> = ({\n walletAccount,\n password,\n onComplete,\n onError,\n style,\n}) => {\n const webViewRef = useRef<WebView | null>(null);\n const [url, setUrl] = useState<string | null>(null);\n\n // Hold callbacks + options in refs so re-renders don't restart the export.\n const onCompleteRef = useRef(onComplete);\n onCompleteRef.current = onComplete;\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const passwordRef = useRef(password);\n passwordRef.current = password;\n\n // The container handed to the SDK plus the sinks wired into the WebView. It\n // holds mutable state (handler arrays, destroyed flag), so it must be created\n // exactly once and stay stable for the component's lifetime — useRef, not\n // useMemo (which React may discard and recompute). setUrl drives whether (and\n // what) the WebView renders.\n const connectorRef = useRef<WebViewWaasContainer | null>(null);\n if (connectorRef.current === null) {\n connectorRef.current = createWebViewWaasContainer({\n setUrl: (next: string) => setUrl(next || null),\n webViewRef,\n });\n }\n const { container, handleWebViewMessage, handleWebViewError } =\n connectorRef.current;\n\n const aliveRef = useRef(true);\n const startedRef = useRef(false);\n\n useEffect(() => {\n // One-shot: the export ceremony runs once.\n if (startedRef.current) return;\n startedRef.current = true;\n\n // Window-level screenshot/recording protection while the key is visible\n // (Android FLAG_SECURE; no-op on iOS).\n setScreenshotProtection({ enabled: true });\n\n exportWaasPrivateKeyToContainer(\n {\n container,\n password: passwordRef.current,\n walletAccount,\n },\n // Internal API takes the client explicitly; the component defaults it.\n getDefaultClient()\n )\n .then(() => {\n if (aliveRef.current) onCompleteRef.current?.();\n })\n .catch((error: unknown) => {\n if (aliveRef.current) onErrorRef.current?.(error);\n });\n\n return () => {\n // Unmounting tears down the WebView + container and clears screenshot\n // protection so the displayed key cannot outlive the screen.\n aliveRef.current = false;\n container.destroy();\n setScreenshotProtection({ enabled: false });\n };\n // Effect intentionally runs once; inputs are captured in refs above.\n }, []);\n\n // Pin the WebView to the export URL's origin (navigation off it is blocked).\n const expectedOrigin = useMemo(() => {\n if (!url) return null;\n try {\n return new URL(url).origin;\n } catch {\n return null;\n }\n }, [url]);\n\n return (\n <View style={[styles.box, style]}>\n {url && (\n <WebView\n ref={webViewRef}\n source={{ uri: url }}\n injectedJavaScriptBeforeContentLoaded={WAAS_EXPORT_BRIDGE_SHIM}\n onMessage={(event) => handleWebViewMessage(event.nativeEvent.data)}\n onError={(event) => handleWebViewError(event.nativeEvent)}\n onHttpError={(event) => handleWebViewError(event.nativeEvent)}\n originWhitelist={expectedOrigin ? [expectedOrigin] : []}\n onShouldStartLoadWithRequest={(request) => {\n if (!expectedOrigin) return true;\n // Exact-origin match — a prefix check would let look-alike hosts\n // (e.g. https://export.example.com.evil.com) through.\n try {\n return new URL(request.url).origin === expectedOrigin;\n } catch {\n return false;\n }\n }}\n // A fresh, isolated, ephemeral store per export.\n incognito\n style={styles.webView}\n />\n )}\n </View>\n );\n};\n\nconst styles = StyleSheet.create({\n // Minimal default so the component stays \"headless\" — the host owns the\n // chrome (border, radius, etc.) via the `style` prop. We only keep an opaque\n // background, a min height so the box is measurable before the host sizes it,\n // and clipping for the WebView.\n box: {\n backgroundColor: '#FFFFFF',\n minHeight: 88,\n overflow: 'hidden',\n },\n webView: {\n backgroundColor: 'transparent',\n flex: 1,\n },\n});\n","import { assertPackageVersion } from '@dynamic-labs-sdk/assert-package-version';\n\nimport {\n name as packageName,\n version as packageVersion,\n} from '../../package.json';\nassertPackageVersion(packageName, packageVersion);\n\nexport { ExportPrivateKey } from '../ExportPrivateKey/ExportPrivateKey';\nexport type { ExportPrivateKeyProps } from '../ExportPrivateKey/ExportPrivateKey.types';\n"],"mappings":"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"}
|