@thru/wallet 0.2.27 → 0.2.29
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 +3 -2
- package/app.plugin.cjs +1 -1
- package/dist/{BrowserSDK-CpRFiJsW.d.ts → BrowserSDK-CRQTOT8S.d.ts} +178 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +376 -12
- package/dist/index.js.map +1 -1
- package/dist/native/react/transparent.d.ts +104 -0
- package/dist/native/react/transparent.js +2210 -0
- package/dist/native/react/transparent.js.map +1 -0
- package/dist/native/react.d.ts +5 -90
- package/dist/native/react.js +768 -35
- package/dist/native/react.js.map +1 -1
- package/dist/native.d.ts +106 -2
- package/dist/native.js +524 -34
- package/dist/native.js.map +1 -1
- package/dist/react-ui.js +5 -0
- package/dist/react-ui.js.map +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/react.js +376 -12
- package/dist/react.js.map +1 -1
- package/package.json +8 -2
- package/src/BrowserSDK.ts +32 -1
- package/src/encoding.ts +39 -0
- package/src/index.ts +5 -1
- package/src/interfaces/IThruChain.ts +50 -1
- package/src/interfaces/types.ts +52 -0
- package/src/native/NativeSDK.test.ts +200 -1
- package/src/native/NativeSDK.ts +125 -11
- package/src/native/index.ts +12 -0
- package/src/native/provider/NativeProvider.ts +109 -8
- package/src/native/provider/WebViewBridge.test.ts +24 -3
- package/src/native/provider/WebViewBridge.ts +18 -8
- package/src/native/provider/chains/ThruChain.ts +215 -5
- package/src/native/provider/shell.test.ts +3 -3
- package/src/native/provider/shell.ts +1 -1
- package/src/native/react/ThruContext.ts +3 -1
- package/src/native/react/ThruProvider.tsx +25 -0
- package/src/native/react/ThruTransparentWalletBridge.tsx +281 -0
- package/src/native/react/hooks/useWallet.ts +12 -1
- package/src/native/react/index.ts +11 -0
- package/src/native/react/transparent.ts +35 -0
- package/src/protocol/postMessage.ts +127 -2
- package/src/provider/EmbeddedProvider.ts +7 -1
- package/src/provider/IframeManager.test.ts +18 -0
- package/src/provider/IframeManager.ts +8 -1
- package/src/provider/chains/ThruChain.ts +210 -4
- package/src/provider/types/messages.ts +16 -0
- package/src/react/index.ts +6 -0
- package/src/signing-sessions.test.ts +182 -0
- package/src/signing-sessions.ts +204 -0
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
import { type ReactNode, useCallback, useEffect, useState } from "react";
|
|
7
7
|
import {
|
|
8
8
|
NativeSDK,
|
|
9
|
+
type CreateAccountOptions,
|
|
9
10
|
type NativeSDKConfig,
|
|
10
11
|
type WalletAvailability,
|
|
11
12
|
} from "../NativeSDK";
|
|
12
13
|
import type { WalletAccount } from "../../interfaces";
|
|
14
|
+
import type { CreateAccountResult } from "../../protocol";
|
|
13
15
|
import { CHECKING_WALLET_AVAILABILITY, ThruContext } from "./ThruContext";
|
|
14
16
|
|
|
15
17
|
export interface ThruProviderProps {
|
|
@@ -147,6 +149,28 @@ export function ThruProvider({ children, config }: ThruProviderProps) {
|
|
|
147
149
|
}
|
|
148
150
|
}, [sdk]);
|
|
149
151
|
|
|
152
|
+
const createAccount = useCallback(
|
|
153
|
+
async (options?: CreateAccountOptions): Promise<CreateAccountResult> => {
|
|
154
|
+
if (!sdk) throw new Error("NativeSDK not initialized");
|
|
155
|
+
try {
|
|
156
|
+
const result = await sdk.createAccount(options);
|
|
157
|
+
setSelectedAccount(result.selectedAccount);
|
|
158
|
+
setAccounts(result.accounts);
|
|
159
|
+
setIsConnected(true);
|
|
160
|
+
setIsConnecting(false);
|
|
161
|
+
setWalletAvailability(sdk.getWalletAvailability());
|
|
162
|
+
return result;
|
|
163
|
+
} catch (err) {
|
|
164
|
+
setError(
|
|
165
|
+
err instanceof Error ? err : new Error("createAccount failed"),
|
|
166
|
+
);
|
|
167
|
+
setIsConnecting(false);
|
|
168
|
+
throw err;
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
[sdk],
|
|
172
|
+
);
|
|
173
|
+
|
|
150
174
|
return (
|
|
151
175
|
<ThruContext.Provider
|
|
152
176
|
value={{
|
|
@@ -159,6 +183,7 @@ export function ThruProvider({ children, config }: ThruProviderProps) {
|
|
|
159
183
|
selectedAccount,
|
|
160
184
|
walletAvailability,
|
|
161
185
|
selectAccount,
|
|
186
|
+
createAccount,
|
|
162
187
|
manageAccounts,
|
|
163
188
|
}}
|
|
164
189
|
>
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/* Hidden native wallet WebView for transparent integrations. Hosts compose
|
|
2
|
+
this alongside <ThruProvider config={{ walletExperience: "transparent" }}>
|
|
3
|
+
so wallet requests can run without opening bottom-sheet UI. */
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
useCallback,
|
|
7
|
+
useContext,
|
|
8
|
+
useEffect,
|
|
9
|
+
useMemo,
|
|
10
|
+
useRef,
|
|
11
|
+
useState,
|
|
12
|
+
type ComponentProps,
|
|
13
|
+
} from "react";
|
|
14
|
+
import {
|
|
15
|
+
Platform,
|
|
16
|
+
StyleSheet,
|
|
17
|
+
View,
|
|
18
|
+
type LayoutChangeEvent,
|
|
19
|
+
type StyleProp,
|
|
20
|
+
type ViewStyle,
|
|
21
|
+
} from "react-native";
|
|
22
|
+
import {
|
|
23
|
+
WebView,
|
|
24
|
+
type WebViewMessageEvent,
|
|
25
|
+
type WebView as WebViewType,
|
|
26
|
+
} from "react-native-webview";
|
|
27
|
+
import { getShellHtml } from "../provider/shell";
|
|
28
|
+
import type { NativeSDK } from "../NativeSDK";
|
|
29
|
+
import type { WebViewRefLike } from "../provider/WebViewBridge";
|
|
30
|
+
import { enableWebAuthnSupport } from "./android-webauthn";
|
|
31
|
+
import { ThruContext } from "./ThruContext";
|
|
32
|
+
|
|
33
|
+
type WebViewLoadEndEvent = Parameters<
|
|
34
|
+
NonNullable<ComponentProps<typeof WebView>["onLoadEnd"]>
|
|
35
|
+
>[0];
|
|
36
|
+
|
|
37
|
+
export interface ThruTransparentWalletBridgeProps {
|
|
38
|
+
wallet?: NativeSDK | null;
|
|
39
|
+
style?: StyleProp<ViewStyle>;
|
|
40
|
+
webViewProps?: Partial<ComponentProps<typeof WebView>>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function ThruTransparentWalletBridge({
|
|
44
|
+
wallet: walletProp,
|
|
45
|
+
style,
|
|
46
|
+
webViewProps,
|
|
47
|
+
}: ThruTransparentWalletBridgeProps) {
|
|
48
|
+
const thruContext = useContext(ThruContext);
|
|
49
|
+
const wallet = walletProp ?? thruContext?.wallet ?? null;
|
|
50
|
+
const webViewRef = useRef<WebViewType | null>(null);
|
|
51
|
+
const webViewNativeTagRef = useRef<number | null>(null);
|
|
52
|
+
const didRefreshWalletAvailabilityRef = useRef(false);
|
|
53
|
+
const [isFocusSurfaceActive, setIsFocusSurfaceActive] = useState(false);
|
|
54
|
+
|
|
55
|
+
const attachIfReady = useCallback(() => {
|
|
56
|
+
if (!wallet || !webViewRef.current) return;
|
|
57
|
+
const ref: WebViewRefLike = {
|
|
58
|
+
injectJavaScript: (script: string) => {
|
|
59
|
+
webViewRef.current?.injectJavaScript(script);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
wallet.attachWebView(ref);
|
|
63
|
+
}, [wallet]);
|
|
64
|
+
|
|
65
|
+
const enableAndroidWebAuthnIfNeeded = useCallback(async () => {
|
|
66
|
+
if (Platform.OS !== "android") return false;
|
|
67
|
+
const enabled = await enableWebAuthnSupport(webViewNativeTagRef.current);
|
|
68
|
+
webViewRef.current?.injectJavaScript(
|
|
69
|
+
"window.dispatchEvent(new Event('thru:native-webauthn-ready')); true;",
|
|
70
|
+
);
|
|
71
|
+
return enabled;
|
|
72
|
+
}, []);
|
|
73
|
+
|
|
74
|
+
const focusWebViewDocument = useCallback(() => {
|
|
75
|
+
const webView = webViewRef.current as (WebViewType & {
|
|
76
|
+
requestFocus?: () => void;
|
|
77
|
+
}) | null;
|
|
78
|
+
webView?.requestFocus?.();
|
|
79
|
+
webViewRef.current?.injectJavaScript(
|
|
80
|
+
"try { window.focus(); document.body && document.body.focus && document.body.focus(); } catch (_) {} true;",
|
|
81
|
+
);
|
|
82
|
+
}, []);
|
|
83
|
+
|
|
84
|
+
const refreshWalletAvailabilityIfReady = useCallback(() => {
|
|
85
|
+
if (!wallet || didRefreshWalletAvailabilityRef.current) return;
|
|
86
|
+
didRefreshWalletAvailabilityRef.current = true;
|
|
87
|
+
void wallet.refreshWalletAvailability();
|
|
88
|
+
}, [wallet]);
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!wallet) return;
|
|
92
|
+
wallet.setUiHandlers({
|
|
93
|
+
onShowRequested: () => {
|
|
94
|
+
setIsFocusSurfaceActive(true);
|
|
95
|
+
},
|
|
96
|
+
onHideRequested: () => {
|
|
97
|
+
setIsFocusSurfaceActive(false);
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
return () => {
|
|
101
|
+
wallet.clearUiHandlers();
|
|
102
|
+
};
|
|
103
|
+
}, [focusWebViewDocument, wallet]);
|
|
104
|
+
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
if (!isFocusSurfaceActive) return;
|
|
107
|
+
const timers = [0, 50, 120, 250, 500].map((delay) =>
|
|
108
|
+
setTimeout(focusWebViewDocument, delay),
|
|
109
|
+
);
|
|
110
|
+
return () => {
|
|
111
|
+
timers.forEach(clearTimeout);
|
|
112
|
+
};
|
|
113
|
+
}, [focusWebViewDocument, isFocusSurfaceActive]);
|
|
114
|
+
|
|
115
|
+
const webViewSource = useMemo(() => {
|
|
116
|
+
if (!wallet) return null;
|
|
117
|
+
if (Platform.OS === "ios" && wallet.getIosWebViewMode() === "direct") {
|
|
118
|
+
return { uri: wallet.getIframeSrc() };
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
html: getShellHtml({
|
|
122
|
+
walletUrl: wallet.getIframeSrc(),
|
|
123
|
+
walletOrigin: wallet.getWalletOrigin(),
|
|
124
|
+
}),
|
|
125
|
+
baseUrl: wallet.getWalletOrigin(),
|
|
126
|
+
};
|
|
127
|
+
}, [wallet]);
|
|
128
|
+
|
|
129
|
+
const isDirectWalletSource = Boolean(
|
|
130
|
+
wallet &&
|
|
131
|
+
Platform.OS === "ios" &&
|
|
132
|
+
wallet.getIosWebViewMode() === "direct",
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
didRefreshWalletAvailabilityRef.current = false;
|
|
137
|
+
}, [webViewSource]);
|
|
138
|
+
|
|
139
|
+
const handleWebViewLayout = useCallback(
|
|
140
|
+
(event: LayoutChangeEvent) => {
|
|
141
|
+
const target = (event.nativeEvent as { target?: unknown }).target;
|
|
142
|
+
webViewNativeTagRef.current =
|
|
143
|
+
typeof target === "number" ? target : webViewNativeTagRef.current;
|
|
144
|
+
void enableAndroidWebAuthnIfNeeded();
|
|
145
|
+
webViewProps?.onLayout?.(event);
|
|
146
|
+
},
|
|
147
|
+
[enableAndroidWebAuthnIfNeeded, webViewProps],
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const handleLoadEnd = useCallback(
|
|
151
|
+
(event: WebViewLoadEndEvent) => {
|
|
152
|
+
attachIfReady();
|
|
153
|
+
if (isDirectWalletSource) {
|
|
154
|
+
void enableAndroidWebAuthnIfNeeded().finally(
|
|
155
|
+
refreshWalletAvailabilityIfReady,
|
|
156
|
+
);
|
|
157
|
+
} else {
|
|
158
|
+
void enableAndroidWebAuthnIfNeeded();
|
|
159
|
+
}
|
|
160
|
+
webViewProps?.onLoadEnd?.(event);
|
|
161
|
+
},
|
|
162
|
+
[
|
|
163
|
+
attachIfReady,
|
|
164
|
+
enableAndroidWebAuthnIfNeeded,
|
|
165
|
+
isDirectWalletSource,
|
|
166
|
+
refreshWalletAvailabilityIfReady,
|
|
167
|
+
webViewProps,
|
|
168
|
+
],
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const handleMessage = useCallback(
|
|
172
|
+
(event: WebViewMessageEvent) => {
|
|
173
|
+
let shouldRefreshAfterBridgeReady = false;
|
|
174
|
+
let shouldCollapseFocusSurface = false;
|
|
175
|
+
try {
|
|
176
|
+
const data = JSON.parse(event.nativeEvent.data) as {
|
|
177
|
+
id?: unknown;
|
|
178
|
+
success?: unknown;
|
|
179
|
+
type?: string;
|
|
180
|
+
};
|
|
181
|
+
shouldRefreshAfterBridgeReady = data.type === "iframe:ready";
|
|
182
|
+
shouldCollapseFocusSurface =
|
|
183
|
+
typeof data.id === "string" && typeof data.success === "boolean";
|
|
184
|
+
} catch {
|
|
185
|
+
/* Let the bridge ignore malformed messages. */
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (shouldCollapseFocusSurface) {
|
|
189
|
+
setIsFocusSurfaceActive(false);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
wallet?.onMessage({
|
|
193
|
+
nativeEvent: { data: event.nativeEvent.data },
|
|
194
|
+
});
|
|
195
|
+
webViewProps?.onMessage?.(event);
|
|
196
|
+
|
|
197
|
+
if (shouldRefreshAfterBridgeReady) {
|
|
198
|
+
void enableAndroidWebAuthnIfNeeded().finally(
|
|
199
|
+
refreshWalletAvailabilityIfReady,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
[
|
|
204
|
+
enableAndroidWebAuthnIfNeeded,
|
|
205
|
+
refreshWalletAvailabilityIfReady,
|
|
206
|
+
wallet,
|
|
207
|
+
webViewProps,
|
|
208
|
+
],
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
if (!webViewSource) return null;
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
<View
|
|
215
|
+
collapsable={false}
|
|
216
|
+
pointerEvents={isFocusSurfaceActive ? "auto" : "none"}
|
|
217
|
+
style={[
|
|
218
|
+
styles.container,
|
|
219
|
+
isFocusSurfaceActive ? styles.activeContainer : null,
|
|
220
|
+
style,
|
|
221
|
+
]}
|
|
222
|
+
>
|
|
223
|
+
<WebView
|
|
224
|
+
{...webViewProps}
|
|
225
|
+
ref={webViewRef}
|
|
226
|
+
source={webViewSource}
|
|
227
|
+
originWhitelist={["*"]}
|
|
228
|
+
javaScriptEnabled
|
|
229
|
+
domStorageEnabled
|
|
230
|
+
webviewDebuggingEnabled={__DEV__}
|
|
231
|
+
sharedCookiesEnabled
|
|
232
|
+
allowsInlineMediaPlayback
|
|
233
|
+
mediaPlaybackRequiresUserAction={false}
|
|
234
|
+
limitsNavigationsToAppBoundDomains={isDirectWalletSource}
|
|
235
|
+
onLoadStart={(event) => {
|
|
236
|
+
attachIfReady();
|
|
237
|
+
void enableAndroidWebAuthnIfNeeded();
|
|
238
|
+
webViewProps?.onLoadStart?.(event);
|
|
239
|
+
}}
|
|
240
|
+
onLoadEnd={handleLoadEnd}
|
|
241
|
+
onLayout={handleWebViewLayout}
|
|
242
|
+
onMessage={handleMessage}
|
|
243
|
+
style={[
|
|
244
|
+
styles.webview,
|
|
245
|
+
isFocusSurfaceActive ? styles.activeWebview : null,
|
|
246
|
+
webViewProps?.style,
|
|
247
|
+
]}
|
|
248
|
+
/>
|
|
249
|
+
</View>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const styles = StyleSheet.create({
|
|
254
|
+
container: {
|
|
255
|
+
height: 1,
|
|
256
|
+
left: 0,
|
|
257
|
+
opacity: 0,
|
|
258
|
+
overflow: "hidden",
|
|
259
|
+
position: "absolute",
|
|
260
|
+
top: 0,
|
|
261
|
+
width: 1,
|
|
262
|
+
},
|
|
263
|
+
activeContainer: {
|
|
264
|
+
bottom: 0,
|
|
265
|
+
height: "100%",
|
|
266
|
+
opacity: 1,
|
|
267
|
+
right: 0,
|
|
268
|
+
width: "100%",
|
|
269
|
+
zIndex: 2147483647,
|
|
270
|
+
},
|
|
271
|
+
webview: {
|
|
272
|
+
backgroundColor: "transparent",
|
|
273
|
+
height: 1,
|
|
274
|
+
width: 1,
|
|
275
|
+
},
|
|
276
|
+
activeWebview: {
|
|
277
|
+
flex: 1,
|
|
278
|
+
height: "100%",
|
|
279
|
+
width: "100%",
|
|
280
|
+
},
|
|
281
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef } from 'react';
|
|
2
2
|
import type { ConnectResult, IThruChain } from "../../../interfaces";
|
|
3
|
-
import type {
|
|
3
|
+
import type { CreateAccountResult } from "../../../protocol";
|
|
4
|
+
import type { ConnectOptions, CreateAccountOptions, SignInOptions } from "../../NativeSDK";
|
|
4
5
|
import { useThru } from './useThru';
|
|
5
6
|
import { waitForWallet } from './waitForWallet';
|
|
6
7
|
|
|
@@ -38,6 +39,15 @@ export function useWallet() {
|
|
|
38
39
|
return ready.signIn(options);
|
|
39
40
|
}, []);
|
|
40
41
|
|
|
42
|
+
const createTransparentAccount = useCallback(
|
|
43
|
+
async (options?: CreateAccountOptions): Promise<CreateAccountResult> => {
|
|
44
|
+
const ready =
|
|
45
|
+
walletRef.current ?? (await waitForWallet(() => walletRef.current));
|
|
46
|
+
return ready.createAccount(options);
|
|
47
|
+
},
|
|
48
|
+
[],
|
|
49
|
+
);
|
|
50
|
+
|
|
41
51
|
const disconnect = useCallback(async (): Promise<void> => {
|
|
42
52
|
const ready =
|
|
43
53
|
walletRef.current ?? (await waitForWallet(() => walletRef.current));
|
|
@@ -56,6 +66,7 @@ export function useWallet() {
|
|
|
56
66
|
accounts,
|
|
57
67
|
connect,
|
|
58
68
|
signIn,
|
|
69
|
+
createAccount: createTransparentAccount,
|
|
59
70
|
disconnect,
|
|
60
71
|
isConnected: isConnected && !!wallet,
|
|
61
72
|
isConnecting,
|
|
@@ -8,6 +8,8 @@ export type {
|
|
|
8
8
|
ThruWalletSheetProps,
|
|
9
9
|
ThruWalletSheetHandle,
|
|
10
10
|
} from './ThruWalletSheet';
|
|
11
|
+
export { ThruTransparentWalletBridge } from './ThruTransparentWalletBridge';
|
|
12
|
+
export type { ThruTransparentWalletBridgeProps } from './ThruTransparentWalletBridge';
|
|
11
13
|
|
|
12
14
|
export { useWallet } from './hooks/useWallet';
|
|
13
15
|
export { useWalletAvailability } from './hooks/useWalletAvailability';
|
|
@@ -19,11 +21,20 @@ export { enableWebAuthnSupport } from './android-webauthn';
|
|
|
19
21
|
export type {
|
|
20
22
|
WalletAccount,
|
|
21
23
|
ConnectResult,
|
|
24
|
+
ThruSigningSession,
|
|
25
|
+
ThruSigningSessionCreateOptions,
|
|
26
|
+
ThruSigningSessionDescriptor,
|
|
27
|
+
ThruSigningSessionInstruction,
|
|
28
|
+
ThruSigningSessionInstructionCreateOptions,
|
|
29
|
+
ThruSigningSessionTimestamp,
|
|
30
|
+
ThruTransactionIntent,
|
|
22
31
|
} from "../../interfaces";
|
|
32
|
+
export type { SigningSessionStorage } from "../../signing-sessions";
|
|
23
33
|
|
|
24
34
|
export type {
|
|
25
35
|
IosWebViewMode,
|
|
26
36
|
NativeSDKStorage,
|
|
37
|
+
NativeWalletExperience,
|
|
27
38
|
WalletAvailability,
|
|
28
39
|
} from "../NativeSDK";
|
|
29
40
|
export type { ManageAccountsResult } from "../../protocol";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export { ThruProvider } from './ThruProvider';
|
|
2
|
+
export type { ThruProviderProps } from './ThruProvider';
|
|
3
|
+
export { ThruContext } from './ThruContext';
|
|
4
|
+
export type { ThruContextValue } from './ThruContext';
|
|
5
|
+
|
|
6
|
+
export { ThruTransparentWalletBridge } from './ThruTransparentWalletBridge';
|
|
7
|
+
export type { ThruTransparentWalletBridgeProps } from './ThruTransparentWalletBridge';
|
|
8
|
+
|
|
9
|
+
export { useWallet } from './hooks/useWallet';
|
|
10
|
+
export { useWalletAvailability } from './hooks/useWalletAvailability';
|
|
11
|
+
export { useAccounts } from './hooks/useAccounts';
|
|
12
|
+
export { useThru } from './hooks/useThru';
|
|
13
|
+
|
|
14
|
+
export { enableWebAuthnSupport } from './android-webauthn';
|
|
15
|
+
|
|
16
|
+
export type {
|
|
17
|
+
WalletAccount,
|
|
18
|
+
ConnectResult,
|
|
19
|
+
ThruSigningSession,
|
|
20
|
+
ThruSigningSessionCreateOptions,
|
|
21
|
+
ThruSigningSessionDescriptor,
|
|
22
|
+
ThruSigningSessionInstruction,
|
|
23
|
+
ThruSigningSessionInstructionCreateOptions,
|
|
24
|
+
ThruSigningSessionTimestamp,
|
|
25
|
+
ThruTransactionIntent,
|
|
26
|
+
} from "../../interfaces";
|
|
27
|
+
export type { SigningSessionStorage } from "../../signing-sessions";
|
|
28
|
+
|
|
29
|
+
export type {
|
|
30
|
+
IosWebViewMode,
|
|
31
|
+
NativeSDKStorage,
|
|
32
|
+
NativeWalletExperience,
|
|
33
|
+
WalletAvailability,
|
|
34
|
+
} from "../NativeSDK";
|
|
35
|
+
export type { ManageAccountsResult } from "../../protocol";
|
|
@@ -7,14 +7,20 @@ import type {
|
|
|
7
7
|
|
|
8
8
|
export const POST_MESSAGE_REQUEST_TYPES = {
|
|
9
9
|
CONNECT: "connect",
|
|
10
|
+
CREATE_ACCOUNT: "createAccount",
|
|
10
11
|
DISCONNECT: "disconnect",
|
|
11
12
|
SIGN_MESSAGE: "signMessage",
|
|
12
13
|
SIGN_TRANSACTION: "signTransaction",
|
|
14
|
+
SIGN_PASSKEY_CHALLENGE: "signPasskeyChallenge",
|
|
13
15
|
GET_ACCOUNTS: "getAccounts",
|
|
14
16
|
GET_CONNECTION_STATE: "getConnectionState",
|
|
15
17
|
GET_SIGNING_CONTEXT: "getSigningContext",
|
|
16
18
|
SELECT_ACCOUNT: "selectAccount",
|
|
17
19
|
MANAGE_ACCOUNTS: "manageAccounts",
|
|
20
|
+
CREATE_SIGNING_SESSION: "createSigningSession",
|
|
21
|
+
CREATE_SIGNING_SESSION_INSTRUCTION: "createSigningSessionInstruction",
|
|
22
|
+
CONFIRM_SIGNING_SESSION: "confirmSigningSession",
|
|
23
|
+
REVOKE_SIGNING_SESSION: "revokeSigningSession",
|
|
18
24
|
} as const;
|
|
19
25
|
|
|
20
26
|
export type RequestType =
|
|
@@ -38,7 +44,7 @@ export const POST_MESSAGE_EVENT_TYPE = "event" as const;
|
|
|
38
44
|
|
|
39
45
|
export const IFRAME_READY_EVENT = "iframe:ready" as const;
|
|
40
46
|
|
|
41
|
-
export const DEFAULT_IFRAME_URL = "http://localhost:
|
|
47
|
+
export const DEFAULT_IFRAME_URL = "http://localhost:3010/embedded";
|
|
42
48
|
|
|
43
49
|
const REQUEST_ID_PREFIX = "req";
|
|
44
50
|
|
|
@@ -62,6 +68,11 @@ export interface DisconnectRequestMessage extends BaseRequest {
|
|
|
62
68
|
payload?: undefined;
|
|
63
69
|
}
|
|
64
70
|
|
|
71
|
+
export interface CreateAccountRequestMessage extends BaseRequest {
|
|
72
|
+
type: typeof POST_MESSAGE_REQUEST_TYPES.CREATE_ACCOUNT;
|
|
73
|
+
payload: CreateAccountPayload;
|
|
74
|
+
}
|
|
75
|
+
|
|
65
76
|
export interface SignMessageRequestMessage extends BaseRequest {
|
|
66
77
|
type: typeof POST_MESSAGE_REQUEST_TYPES.SIGN_MESSAGE;
|
|
67
78
|
payload: SignMessagePayload;
|
|
@@ -72,6 +83,11 @@ export interface SignTransactionRequestMessage extends BaseRequest {
|
|
|
72
83
|
payload: SignTransactionPayload;
|
|
73
84
|
}
|
|
74
85
|
|
|
86
|
+
export interface SignPasskeyChallengeRequestMessage extends BaseRequest {
|
|
87
|
+
type: typeof POST_MESSAGE_REQUEST_TYPES.SIGN_PASSKEY_CHALLENGE;
|
|
88
|
+
payload: SignPasskeyChallengePayload;
|
|
89
|
+
}
|
|
90
|
+
|
|
75
91
|
export interface GetAccountsRequestMessage extends BaseRequest {
|
|
76
92
|
type: typeof POST_MESSAGE_REQUEST_TYPES.GET_ACCOUNTS;
|
|
77
93
|
payload?: undefined;
|
|
@@ -97,21 +113,62 @@ export interface ManageAccountsRequestMessage extends BaseRequest {
|
|
|
97
113
|
payload?: undefined;
|
|
98
114
|
}
|
|
99
115
|
|
|
116
|
+
export interface CreateSigningSessionRequestMessage extends BaseRequest {
|
|
117
|
+
type: typeof POST_MESSAGE_REQUEST_TYPES.CREATE_SIGNING_SESSION;
|
|
118
|
+
payload: CreateSigningSessionPayload;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface CreateSigningSessionInstructionRequestMessage extends BaseRequest {
|
|
122
|
+
type: typeof POST_MESSAGE_REQUEST_TYPES.CREATE_SIGNING_SESSION_INSTRUCTION;
|
|
123
|
+
payload: CreateSigningSessionInstructionPayload;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface ConfirmSigningSessionRequestMessage extends BaseRequest {
|
|
127
|
+
type: typeof POST_MESSAGE_REQUEST_TYPES.CONFIRM_SIGNING_SESSION;
|
|
128
|
+
payload: ConfirmSigningSessionPayload;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface RevokeSigningSessionRequestMessage extends BaseRequest {
|
|
132
|
+
type: typeof POST_MESSAGE_REQUEST_TYPES.REVOKE_SIGNING_SESSION;
|
|
133
|
+
payload: RevokeSigningSessionPayload;
|
|
134
|
+
}
|
|
135
|
+
|
|
100
136
|
export type PostMessageRequest =
|
|
101
137
|
| ConnectRequestMessage
|
|
138
|
+
| CreateAccountRequestMessage
|
|
102
139
|
| DisconnectRequestMessage
|
|
103
140
|
| SignMessageRequestMessage
|
|
104
141
|
| SignTransactionRequestMessage
|
|
142
|
+
| SignPasskeyChallengeRequestMessage
|
|
105
143
|
| GetAccountsRequestMessage
|
|
106
144
|
| GetConnectionStateRequestMessage
|
|
107
145
|
| GetSigningContextRequestMessage
|
|
108
146
|
| SelectAccountRequestMessage
|
|
109
|
-
| ManageAccountsRequestMessage
|
|
147
|
+
| ManageAccountsRequestMessage
|
|
148
|
+
| CreateSigningSessionRequestMessage
|
|
149
|
+
| CreateSigningSessionInstructionRequestMessage
|
|
150
|
+
| ConfirmSigningSessionRequestMessage
|
|
151
|
+
| RevokeSigningSessionRequestMessage;
|
|
110
152
|
|
|
111
153
|
export interface DisconnectResult {
|
|
112
154
|
// Empty object keeps compatibility with existing consumers expecting a success payload
|
|
113
155
|
}
|
|
114
156
|
|
|
157
|
+
export interface CreateAccountPayload {
|
|
158
|
+
accountName?: string;
|
|
159
|
+
metadata?: ConnectMetadataInput;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export interface CreateAccountResult {
|
|
163
|
+
account: WalletAccount;
|
|
164
|
+
accounts: WalletAccount[];
|
|
165
|
+
selectedAccount: WalletAccount;
|
|
166
|
+
signature: string | null;
|
|
167
|
+
vmError: string | null;
|
|
168
|
+
userErrorCode: string | null;
|
|
169
|
+
executionResult: string | null;
|
|
170
|
+
}
|
|
171
|
+
|
|
115
172
|
export interface GetAccountsResult {
|
|
116
173
|
accounts: WalletAccount[];
|
|
117
174
|
}
|
|
@@ -142,14 +199,20 @@ export interface ManageAccountsResult {
|
|
|
142
199
|
|
|
143
200
|
type RequestResultMap = {
|
|
144
201
|
[POST_MESSAGE_REQUEST_TYPES.CONNECT]: ConnectResult;
|
|
202
|
+
[POST_MESSAGE_REQUEST_TYPES.CREATE_ACCOUNT]: CreateAccountResult;
|
|
145
203
|
[POST_MESSAGE_REQUEST_TYPES.DISCONNECT]: DisconnectResult;
|
|
146
204
|
[POST_MESSAGE_REQUEST_TYPES.SIGN_MESSAGE]: SignMessageResult;
|
|
147
205
|
[POST_MESSAGE_REQUEST_TYPES.SIGN_TRANSACTION]: SignTransactionResult;
|
|
206
|
+
[POST_MESSAGE_REQUEST_TYPES.SIGN_PASSKEY_CHALLENGE]: SignPasskeyChallengeResult;
|
|
148
207
|
[POST_MESSAGE_REQUEST_TYPES.GET_ACCOUNTS]: GetAccountsResult;
|
|
149
208
|
[POST_MESSAGE_REQUEST_TYPES.GET_CONNECTION_STATE]: GetConnectionStateResult;
|
|
150
209
|
[POST_MESSAGE_REQUEST_TYPES.GET_SIGNING_CONTEXT]: GetSigningContextResult;
|
|
151
210
|
[POST_MESSAGE_REQUEST_TYPES.SELECT_ACCOUNT]: SelectAccountResult;
|
|
152
211
|
[POST_MESSAGE_REQUEST_TYPES.MANAGE_ACCOUNTS]: ManageAccountsResult;
|
|
212
|
+
[POST_MESSAGE_REQUEST_TYPES.CREATE_SIGNING_SESSION]: CreateSigningSessionResult;
|
|
213
|
+
[POST_MESSAGE_REQUEST_TYPES.CREATE_SIGNING_SESSION_INSTRUCTION]: CreateSigningSessionInstructionResult;
|
|
214
|
+
[POST_MESSAGE_REQUEST_TYPES.CONFIRM_SIGNING_SESSION]: ConfirmSigningSessionResult;
|
|
215
|
+
[POST_MESSAGE_REQUEST_TYPES.REVOKE_SIGNING_SESSION]: RevokeSigningSessionResult;
|
|
153
216
|
};
|
|
154
217
|
|
|
155
218
|
interface ResponseErrorPayload {
|
|
@@ -248,12 +311,74 @@ export interface SignTransactionPayload {
|
|
|
248
311
|
readWriteAddresses?: string[];
|
|
249
312
|
readOnlyAddresses?: string[];
|
|
250
313
|
review?: TransactionReviewPayload;
|
|
314
|
+
signingSessionId?: string;
|
|
251
315
|
}
|
|
252
316
|
|
|
253
317
|
export interface SignTransactionResult {
|
|
254
318
|
signedTransaction: string;
|
|
255
319
|
}
|
|
256
320
|
|
|
321
|
+
export interface SignPasskeyChallengePayload {
|
|
322
|
+
/** base64url-encoded challenge bytes from a backend passkey-manager flow. */
|
|
323
|
+
challenge: string;
|
|
324
|
+
/** Optional expected wallet address for the selected transparent account. */
|
|
325
|
+
walletAddress?: string;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export interface SignPasskeyChallengeResult {
|
|
329
|
+
signatureR: string;
|
|
330
|
+
signatureS: string;
|
|
331
|
+
authenticatorData: string;
|
|
332
|
+
clientDataJSON: string;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export interface CreateSigningSessionPayload {
|
|
336
|
+
walletAddress?: string;
|
|
337
|
+
expiresAt: string;
|
|
338
|
+
review?: TransactionReviewPayload;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export interface CreateSigningSessionInstructionPayload {
|
|
342
|
+
walletAddress?: string;
|
|
343
|
+
expiresAt: string;
|
|
344
|
+
walletAccountIdx: number;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export interface SigningSessionDescriptorPayload {
|
|
348
|
+
id: string;
|
|
349
|
+
walletAddress: string;
|
|
350
|
+
publicKey: string;
|
|
351
|
+
authIdx: number;
|
|
352
|
+
expiresAt: string;
|
|
353
|
+
createdAt: string;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export interface CreateSigningSessionResult {
|
|
357
|
+
session: SigningSessionDescriptorPayload;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export interface CreateSigningSessionInstructionResult {
|
|
361
|
+
session: SigningSessionDescriptorPayload;
|
|
362
|
+
programAddress: string;
|
|
363
|
+
instructionData: string;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export interface ConfirmSigningSessionPayload {
|
|
367
|
+
sessionId: string;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export interface ConfirmSigningSessionResult {
|
|
371
|
+
session: SigningSessionDescriptorPayload;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export interface RevokeSigningSessionPayload {
|
|
375
|
+
sessionId: string;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export interface RevokeSigningSessionResult {
|
|
379
|
+
// Empty object keeps compatibility with existing consumers expecting a success payload
|
|
380
|
+
}
|
|
381
|
+
|
|
257
382
|
export interface TransactionReviewSimulation {
|
|
258
383
|
before?: string;
|
|
259
384
|
after?: string;
|
|
@@ -20,10 +20,12 @@ import {
|
|
|
20
20
|
} from '../protocol';
|
|
21
21
|
import { IframeManager } from './IframeManager';
|
|
22
22
|
import { EmbeddedThruChain } from './chains/ThruChain';
|
|
23
|
+
import type { SigningSessionDescriptorStore } from '../signing-sessions';
|
|
23
24
|
|
|
24
25
|
export interface EmbeddedProviderConfig {
|
|
25
26
|
iframeUrl?: string;
|
|
26
27
|
addressTypes?: AddressTypeValue[];
|
|
28
|
+
signingSessions?: SigningSessionDescriptorStore;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
export interface ConnectOptions {
|
|
@@ -77,7 +79,11 @@ export class EmbeddedProvider {
|
|
|
77
79
|
// Create chain instances
|
|
78
80
|
const addressTypes = config.addressTypes || [AddressType.THRU];
|
|
79
81
|
if (addressTypes.includes(AddressType.THRU)) {
|
|
80
|
-
this._thruChain = new EmbeddedThruChain(
|
|
82
|
+
this._thruChain = new EmbeddedThruChain(
|
|
83
|
+
this.iframeManager,
|
|
84
|
+
this,
|
|
85
|
+
config.signingSessions,
|
|
86
|
+
);
|
|
81
87
|
}
|
|
82
88
|
}
|
|
83
89
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { IframeManager } from './IframeManager';
|
|
3
|
+
|
|
4
|
+
describe('IframeManager', () => {
|
|
5
|
+
it('allows trusted production wallet origins', () => {
|
|
6
|
+
const thruBridge = new IframeManager('https://app.tid.sh/embedded');
|
|
7
|
+
const tidBridge = new IframeManager('https://wallet.tid.sh/embedded');
|
|
8
|
+
|
|
9
|
+
expect(thruBridge).toBeInstanceOf(IframeManager);
|
|
10
|
+
expect(tidBridge).toBeInstanceOf(IframeManager);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('rejects untrusted production wallet origins', () => {
|
|
14
|
+
expect(
|
|
15
|
+
() => new IframeManager('https://evil.example.com/embedded')
|
|
16
|
+
).toThrow(/Untrusted iframe origin/);
|
|
17
|
+
});
|
|
18
|
+
});
|