@thru/wallet 0.2.28 → 0.2.30
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 -3
- package/app.plugin.cjs +1 -1
- package/dist/{BrowserSDK-CRQTOT8S.d.ts → BrowserSDK-CQd2uC--.d.ts} +4 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/native/react/transparent.js +32 -7
- package/dist/native/react/transparent.js.map +1 -1
- package/dist/native/react.js +32 -7
- package/dist/native/react.js.map +1 -1
- package/dist/native.d.ts +13 -1
- package/dist/native.js +32 -7
- package/dist/native.js.map +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/react.js.map +1 -1
- package/package.json +2 -2
- package/src/native/NativeSDK.test.ts +88 -1
- package/src/native/NativeSDK.ts +33 -3
- package/src/native/provider/NativeProvider.ts +11 -3
- package/src/native/provider/WebViewBridge.test.ts +2 -2
- package/src/native/provider/WebViewBridge.ts +2 -2
- package/src/native/provider/shell.test.ts +3 -3
- package/src/native/provider/shell.ts +1 -1
- package/src/protocol/postMessage.ts +4 -0
- package/src/provider/IframeManager.test.ts +1 -1
- package/src/provider/IframeManager.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thru/wallet",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.30",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"README.md"
|
|
47
47
|
],
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@thru/sdk": "0.2.
|
|
49
|
+
"@thru/sdk": "0.2.30"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@svgr/core": "^8.1.0",
|
|
@@ -159,7 +159,7 @@ describe("NativeSDK", () => {
|
|
|
159
159
|
});
|
|
160
160
|
const iframeUrl = new URL(transparentSdk.getIframeSrc());
|
|
161
161
|
|
|
162
|
-
expect(iframeUrl.origin).toBe("https://
|
|
162
|
+
expect(iframeUrl.origin).toBe("https://app.tid.sh");
|
|
163
163
|
expect(iframeUrl.pathname).toBe("/embedded/native/transparent");
|
|
164
164
|
|
|
165
165
|
transparentSdk.destroy();
|
|
@@ -295,6 +295,93 @@ describe("NativeSDK", () => {
|
|
|
295
295
|
});
|
|
296
296
|
});
|
|
297
297
|
|
|
298
|
+
it("persists a bundled transparent createAccount signing session", async () => {
|
|
299
|
+
sdk.destroy();
|
|
300
|
+
const storage = new MockStorage();
|
|
301
|
+
sdk = new NativeSDK({
|
|
302
|
+
walletUrl: "http://localhost:3000/embedded/native/transparent",
|
|
303
|
+
walletExperience: "transparent",
|
|
304
|
+
origin: "thru-mobile://token-dummy",
|
|
305
|
+
storage,
|
|
306
|
+
});
|
|
307
|
+
webView = new MockWebView();
|
|
308
|
+
sdk.attachWebView(webView);
|
|
309
|
+
const nowSeconds = 1_800_000_000;
|
|
310
|
+
|
|
311
|
+
const frameId = frameIdFor(sdk);
|
|
312
|
+
const promise = sdk.createAccount({
|
|
313
|
+
accountName: "JCoin Account",
|
|
314
|
+
metadata: {
|
|
315
|
+
appId: "token_dummy_app",
|
|
316
|
+
appName: "Token Dummy App",
|
|
317
|
+
appUrl: "thru-mobile://token-dummy",
|
|
318
|
+
},
|
|
319
|
+
createSigningSession: { expiresAt: nowSeconds + 120 },
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
sdk.onMessage(readyMessage(frameId));
|
|
323
|
+
await flush();
|
|
324
|
+
|
|
325
|
+
const request = parseInjectedRequest(await waitForInjectedRequest(webView));
|
|
326
|
+
expect(request.type).toBe(POST_MESSAGE_REQUEST_TYPES.CREATE_ACCOUNT);
|
|
327
|
+
expect(request.payload).toEqual({
|
|
328
|
+
accountName: "JCoin Account",
|
|
329
|
+
metadata: {
|
|
330
|
+
appId: "token_dummy_app",
|
|
331
|
+
appName: "Token Dummy App",
|
|
332
|
+
appUrl: "thru-mobile://token-dummy",
|
|
333
|
+
},
|
|
334
|
+
createSigningSession: {
|
|
335
|
+
expiresAt: String(nowSeconds + 120),
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const result = {
|
|
340
|
+
account: {
|
|
341
|
+
accountType: "thru",
|
|
342
|
+
address: "thru_created_address",
|
|
343
|
+
label: "JCoin Account",
|
|
344
|
+
},
|
|
345
|
+
accounts: [
|
|
346
|
+
{
|
|
347
|
+
accountType: "thru",
|
|
348
|
+
address: "thru_created_address",
|
|
349
|
+
label: "JCoin Account",
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
selectedAccount: {
|
|
353
|
+
accountType: "thru",
|
|
354
|
+
address: "thru_created_address",
|
|
355
|
+
label: "JCoin Account",
|
|
356
|
+
},
|
|
357
|
+
signature: "thru_signature",
|
|
358
|
+
vmError: "0",
|
|
359
|
+
userErrorCode: "0",
|
|
360
|
+
executionResult: "0",
|
|
361
|
+
signingSession: {
|
|
362
|
+
id: "session_1",
|
|
363
|
+
walletAddress: "thru_created_address",
|
|
364
|
+
publicKey: "thru_session_address",
|
|
365
|
+
authIdx: 1,
|
|
366
|
+
expiresAt: String(nowSeconds + 120),
|
|
367
|
+
createdAt: String(nowSeconds),
|
|
368
|
+
},
|
|
369
|
+
};
|
|
370
|
+
sdk.onMessage(responseMessage(frameId, request.id, result));
|
|
371
|
+
|
|
372
|
+
await expect(promise).resolves.toEqual(result);
|
|
373
|
+
await expect(sdk.thru.getSigningSessions()).resolves.toEqual([
|
|
374
|
+
expect.objectContaining({
|
|
375
|
+
id: "session_1",
|
|
376
|
+
walletAddress: "thru_created_address",
|
|
377
|
+
publicKey: "thru_session_address",
|
|
378
|
+
authIdx: 1,
|
|
379
|
+
expiresAt: nowSeconds + 120,
|
|
380
|
+
createdAt: nowSeconds,
|
|
381
|
+
}),
|
|
382
|
+
]);
|
|
383
|
+
});
|
|
384
|
+
|
|
298
385
|
it("sends transparent passkey challenge signing requests through the wallet WebView", async () => {
|
|
299
386
|
sdk.destroy();
|
|
300
387
|
sdk = new NativeSDK({
|
package/src/native/NativeSDK.ts
CHANGED
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
type AddressType as AddressTypeValue,
|
|
5
5
|
type ConnectResult,
|
|
6
6
|
type IThruChain,
|
|
7
|
+
type ThruSigningSessionCreateOptions,
|
|
8
|
+
type ThruSigningSessionDescriptor,
|
|
7
9
|
type WalletAccount,
|
|
8
10
|
normalizeActiveWalletAccounts,
|
|
9
11
|
normalizeWalletAccountResult,
|
|
@@ -16,6 +18,7 @@ import {
|
|
|
16
18
|
type CreateAccountResult,
|
|
17
19
|
type GetConnectionStateResult,
|
|
18
20
|
type ManageAccountsResult,
|
|
21
|
+
type SigningSessionDescriptorPayload,
|
|
19
22
|
normalizeConnectionStateResult,
|
|
20
23
|
} from "../protocol";
|
|
21
24
|
import { NativeProvider } from "./provider/NativeProvider";
|
|
@@ -113,6 +116,7 @@ export interface ConnectOptions {
|
|
|
113
116
|
export interface CreateAccountOptions {
|
|
114
117
|
accountName?: string;
|
|
115
118
|
metadata?: ConnectMetadataInput;
|
|
119
|
+
createSigningSession?: Omit<ThruSigningSessionCreateOptions, "walletAddress" | "review">;
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
export interface RestoreConnectionOptions {
|
|
@@ -144,9 +148,9 @@ export interface NativeSDKUiHandlers {
|
|
|
144
148
|
const DEFAULT_STORAGE_KEY = "thru.native-sdk.connection.v1";
|
|
145
149
|
const SELECTED_ACCOUNT_STORAGE_KEY_SUFFIX = ".selected-account.v1";
|
|
146
150
|
const SIGNING_SESSION_STORAGE_KEY_SUFFIX = ".signing-sessions.v1";
|
|
147
|
-
const DEFAULT_NATIVE_WALLET_URL = "https://
|
|
151
|
+
const DEFAULT_NATIVE_WALLET_URL = "https://app.tid.sh/embedded/native";
|
|
148
152
|
const DEFAULT_TRANSPARENT_WALLET_URL =
|
|
149
|
-
"https://
|
|
153
|
+
"https://app.tid.sh/embedded/native/transparent";
|
|
150
154
|
|
|
151
155
|
const CHECKING_WALLET_AVAILABILITY: WalletAvailability = {
|
|
152
156
|
status: "checking",
|
|
@@ -175,6 +179,19 @@ function completeAppMetadata(
|
|
|
175
179
|
};
|
|
176
180
|
}
|
|
177
181
|
|
|
182
|
+
function signingSessionDescriptorFromWire(
|
|
183
|
+
session: SigningSessionDescriptorPayload,
|
|
184
|
+
): ThruSigningSessionDescriptor {
|
|
185
|
+
return {
|
|
186
|
+
id: session.id,
|
|
187
|
+
walletAddress: session.walletAddress,
|
|
188
|
+
publicKey: session.publicKey,
|
|
189
|
+
authIdx: session.authIdx,
|
|
190
|
+
expiresAt: Number(BigInt(session.expiresAt)),
|
|
191
|
+
createdAt: Number(BigInt(session.createdAt)),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
178
195
|
interface PersistedSelectedAccountSnapshot {
|
|
179
196
|
version: 1;
|
|
180
197
|
origin: string;
|
|
@@ -205,6 +222,7 @@ export class NativeSDK {
|
|
|
205
222
|
private readonly iosWebViewMode: IosWebViewMode;
|
|
206
223
|
private readonly walletExperience: NativeWalletExperience;
|
|
207
224
|
private readonly defaultMetadata?: ConnectMetadataInput;
|
|
225
|
+
private readonly signingSessions?: SigningSessionDescriptorStore;
|
|
208
226
|
|
|
209
227
|
constructor(config: NativeSDKConfig = {}) {
|
|
210
228
|
this.origin = config.origin ?? "thru-mobile://app";
|
|
@@ -235,6 +253,7 @@ export class NativeSDK {
|
|
|
235
253
|
}),
|
|
236
254
|
)
|
|
237
255
|
: undefined;
|
|
256
|
+
this.signingSessions = signingSessions;
|
|
238
257
|
this.provider = new NativeProvider({
|
|
239
258
|
walletUrl,
|
|
240
259
|
origin: this.origin,
|
|
@@ -268,7 +287,7 @@ export class NativeSDK {
|
|
|
268
287
|
return this.provider.getIframeSrc();
|
|
269
288
|
}
|
|
270
289
|
|
|
271
|
-
/** Wallet origin (e.g. https://
|
|
290
|
+
/** Wallet origin (e.g. https://app.tid.sh). */
|
|
272
291
|
getWalletOrigin(): string {
|
|
273
292
|
return this.provider.getWalletOrigin();
|
|
274
293
|
}
|
|
@@ -394,6 +413,9 @@ export class NativeSDK {
|
|
|
394
413
|
const result = await this.provider.createAccount({
|
|
395
414
|
...(options.accountName ? { accountName: options.accountName } : {}),
|
|
396
415
|
...(metadata ? { metadata } : {}),
|
|
416
|
+
...(options.createSigningSession
|
|
417
|
+
? { createSigningSession: options.createSigningSession }
|
|
418
|
+
: {}),
|
|
397
419
|
});
|
|
398
420
|
const selectedAccount = result.selectedAccount ?? result.account;
|
|
399
421
|
const activeResult: CreateAccountResult = {
|
|
@@ -412,6 +434,14 @@ export class NativeSDK {
|
|
|
412
434
|
await this.persistSelectedAccountAddress(
|
|
413
435
|
activeResult.selectedAccount.address,
|
|
414
436
|
);
|
|
437
|
+
if (activeResult.signingSession) {
|
|
438
|
+
if (!this.signingSessions) {
|
|
439
|
+
throw new Error("NativeSDKStorage is required for signing sessions");
|
|
440
|
+
}
|
|
441
|
+
await this.signingSessions.saveReplacingWalletSessions(
|
|
442
|
+
signingSessionDescriptorFromWire(activeResult.signingSession),
|
|
443
|
+
);
|
|
444
|
+
}
|
|
415
445
|
await this.clearPersistedConnection();
|
|
416
446
|
this.setWalletAvailability(
|
|
417
447
|
walletAvailabilityFromConnectResult(completedResult),
|
|
@@ -28,13 +28,15 @@ import {
|
|
|
28
28
|
type WebViewMessageEventLike,
|
|
29
29
|
type WebViewRefLike,
|
|
30
30
|
} from "./WebViewBridge";
|
|
31
|
+
import { resolveSessionExpirySeconds } from "../../signing-sessions";
|
|
32
|
+
import type { ThruSigningSessionCreateOptions } from "../../interfaces";
|
|
31
33
|
|
|
32
|
-
const DEFAULT_WALLET_URL = "https://
|
|
34
|
+
const DEFAULT_WALLET_URL = "https://app.tid.sh/embedded/native";
|
|
33
35
|
const DEFAULT_ORIGIN = "thru-mobile://app";
|
|
34
36
|
const TRANSPARENT_FOCUS_SETTLE_MS = 500;
|
|
35
37
|
|
|
36
38
|
export interface NativeProviderConfig {
|
|
37
|
-
/**
|
|
39
|
+
/** app.tid.sh/embedded/native URL to load. */
|
|
38
40
|
walletUrl?: string;
|
|
39
41
|
/** Standard bottom-sheet wallet or transparent auto-signing wallet. */
|
|
40
42
|
walletExperience?: "standard" | "transparent";
|
|
@@ -56,6 +58,7 @@ export interface ConnectOptions {
|
|
|
56
58
|
export interface CreateAccountOptions {
|
|
57
59
|
accountName?: string;
|
|
58
60
|
metadata?: ConnectMetadataInput;
|
|
61
|
+
createSigningSession?: Omit<ThruSigningSessionCreateOptions, "walletAddress" | "review">;
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
export type NativeProviderEvent = EmbeddedProviderEvent;
|
|
@@ -153,7 +156,7 @@ export class NativeProvider {
|
|
|
153
156
|
return this.bridge.getIframeSrc();
|
|
154
157
|
}
|
|
155
158
|
|
|
156
|
-
/** Wallet origin (e.g. https://
|
|
159
|
+
/** Wallet origin (e.g. https://app.tid.sh). The shell template
|
|
157
160
|
should substitute this for WALLET_ORIGIN_PLACEHOLDER. */
|
|
158
161
|
getWalletOrigin(): string {
|
|
159
162
|
return this.bridge.walletOrigin;
|
|
@@ -238,6 +241,11 @@ export class NativeProvider {
|
|
|
238
241
|
const payload: CreateAccountPayload = {};
|
|
239
242
|
if (options?.accountName) payload.accountName = options.accountName;
|
|
240
243
|
if (options?.metadata) payload.metadata = options.metadata;
|
|
244
|
+
if (options?.createSigningSession) {
|
|
245
|
+
payload.createSigningSession = {
|
|
246
|
+
expiresAt: String(resolveSessionExpirySeconds(options.createSigningSession)),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
241
249
|
|
|
242
250
|
const response = await this.bridge.sendMessage({
|
|
243
251
|
id: createRequestId(),
|
|
@@ -154,9 +154,9 @@ describe('WebViewBridge', () => {
|
|
|
154
154
|
process.env.NODE_ENV = 'production';
|
|
155
155
|
|
|
156
156
|
const productionBridge = new WebViewBridge({
|
|
157
|
-
walletUrl: 'https://
|
|
157
|
+
walletUrl: 'https://app.tid.sh/embedded',
|
|
158
158
|
});
|
|
159
|
-
expect(productionBridge.walletOrigin).toBe('https://
|
|
159
|
+
expect(productionBridge.walletOrigin).toBe('https://app.tid.sh');
|
|
160
160
|
productionBridge.destroy();
|
|
161
161
|
|
|
162
162
|
expect(
|
|
@@ -12,12 +12,12 @@ import {
|
|
|
12
12
|
|
|
13
13
|
/* RN-side analog of `web/packages/embedded-provider/src/IframeManager.ts`.
|
|
14
14
|
The wallet ships unchanged. The shell HTML (src/shell.html) hosts an
|
|
15
|
-
<iframe src="
|
|
15
|
+
<iframe src="app.tid.sh/embedded/native"> and forwards
|
|
16
16
|
iframe<->ReactNativeWebView postMessage traffic. This bridge only
|
|
17
17
|
speaks the RN side: webView.injectJavaScript out, onMessage in. */
|
|
18
18
|
|
|
19
19
|
const PRODUCTION_WALLET_ORIGINS = [
|
|
20
|
-
'https://
|
|
20
|
+
'https://app.tid.sh',
|
|
21
21
|
'https://wallet.tid.sh',
|
|
22
22
|
];
|
|
23
23
|
|
|
@@ -6,8 +6,8 @@ import { getShellHtml } from './shell';
|
|
|
6
6
|
|
|
7
7
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
8
|
const TEST_SHELL_OPTIONS = {
|
|
9
|
-
walletUrl: 'https://
|
|
10
|
-
walletOrigin: 'https://
|
|
9
|
+
walletUrl: 'https://app.tid.sh/embedded?tn_frame_id=frame_test',
|
|
10
|
+
walletOrigin: 'https://app.tid.sh',
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
describe('native shell HTML', () => {
|
|
@@ -36,7 +36,7 @@ describe('native shell HTML', () => {
|
|
|
36
36
|
|
|
37
37
|
it('substitutes shell placeholders without reprocessing inserted values', () => {
|
|
38
38
|
const walletUrl =
|
|
39
|
-
'https://
|
|
39
|
+
'https://app.tid.sh/embedded?marker=WALLET_ORIGIN_PLACEHOLDER';
|
|
40
40
|
const walletOrigin = 'thru-mobile://WALLET_URL_PLACEHOLDER/$&';
|
|
41
41
|
|
|
42
42
|
const html = getShellHtml({ walletUrl, walletOrigin });
|
|
@@ -95,7 +95,7 @@ export interface ShellOptions {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
|
-
* Returns the shell HTML for loading
|
|
98
|
+
* Returns the shell HTML for loading app.tid.sh/embedded inside a
|
|
99
99
|
* react-native-webview. The shell hosts an <iframe> pointing at the wallet
|
|
100
100
|
* and bridges window.postMessage traffic between the iframe and the
|
|
101
101
|
* react-native-webview's onMessage / injectJavaScript channels.
|
|
@@ -157,6 +157,9 @@ export interface DisconnectResult {
|
|
|
157
157
|
export interface CreateAccountPayload {
|
|
158
158
|
accountName?: string;
|
|
159
159
|
metadata?: ConnectMetadataInput;
|
|
160
|
+
createSigningSession?: {
|
|
161
|
+
expiresAt: string;
|
|
162
|
+
};
|
|
160
163
|
}
|
|
161
164
|
|
|
162
165
|
export interface CreateAccountResult {
|
|
@@ -167,6 +170,7 @@ export interface CreateAccountResult {
|
|
|
167
170
|
vmError: string | null;
|
|
168
171
|
userErrorCode: string | null;
|
|
169
172
|
executionResult: string | null;
|
|
173
|
+
signingSession?: SigningSessionDescriptorPayload;
|
|
170
174
|
}
|
|
171
175
|
|
|
172
176
|
export interface GetAccountsResult {
|
|
@@ -3,7 +3,7 @@ import { IframeManager } from './IframeManager';
|
|
|
3
3
|
|
|
4
4
|
describe('IframeManager', () => {
|
|
5
5
|
it('allows trusted production wallet origins', () => {
|
|
6
|
-
const thruBridge = new IframeManager('https://
|
|
6
|
+
const thruBridge = new IframeManager('https://app.tid.sh/embedded');
|
|
7
7
|
const tidBridge = new IframeManager('https://wallet.tid.sh/embedded');
|
|
8
8
|
|
|
9
9
|
expect(thruBridge).toBeInstanceOf(IframeManager);
|