@dynamic-labs-wallet/browser-wallet-client 0.0.0-beta.3 → 0.0.1

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.
Files changed (35) hide show
  1. package/index.cjs.js +504 -207
  2. package/index.esm.js +494 -185
  3. package/package.json +7 -6
  4. package/src/client/client.d.ts +37 -87
  5. package/src/client/client.d.ts.map +1 -1
  6. package/src/client/iframeManager/IframeManager.d.ts +90 -0
  7. package/src/client/iframeManager/IframeManager.d.ts.map +1 -0
  8. package/src/client/iframeManager/index.d.ts +2 -0
  9. package/src/client/iframeManager/index.d.ts.map +1 -0
  10. package/src/client/index.d.ts +1 -0
  11. package/src/client/index.d.ts.map +1 -1
  12. package/src/index.d.ts +0 -1
  13. package/src/index.d.ts.map +1 -1
  14. package/src/services/iframeMessageHandler.d.ts +9 -8
  15. package/src/services/iframeMessageHandler.d.ts.map +1 -1
  16. package/internal/core/bip340.d.ts +0 -22
  17. package/internal/core/bip340.js +0 -1
  18. package/internal/core/common.d.ts +0 -1
  19. package/internal/core/common.js +0 -1
  20. package/internal/core/ecdsa.d.ts +0 -23
  21. package/internal/core/ecdsa.js +0 -1
  22. package/internal/core/ed25519.d.ts +0 -22
  23. package/internal/core/ed25519.js +0 -1
  24. package/internal/core/index.d.ts +0 -6
  25. package/internal/core/index.js +0 -1
  26. package/internal/core/native.d.ts +0 -103
  27. package/internal/core/native.js +0 -1
  28. package/internal/core/types.d.ts +0 -58
  29. package/internal/core/types.js +0 -1
  30. package/internal/web/generated/libmpc_executor.d.ts +0 -130
  31. package/internal/web/generated/libmpc_executor.js +0 -2
  32. package/internal/web/generated/libmpc_executor_bg.wasm +0 -0
  33. package/internal/web/generated/libmpc_executor_bg.wasm.d.ts +0 -64
  34. package/internal/web/index.d.ts +0 -11
  35. package/internal/web/index.js +0 -1
package/index.esm.js CHANGED
@@ -1,64 +1,29 @@
1
- import { parseMessageTransportData, createRequestChannel, applyDefaultMessageOrigin, createMessageTransport } from '@dynamic-labs/message-transport';
1
+ import { AuthMode, getEnvironmentFromUrl, IFRAME_DOMAIN_MAP, WalletOperation } from '@dynamic-labs-wallet/core';
2
+ export { AuthMode, MPC_RELAY_PREPROD_API_URL, MPC_RELAY_PROD_API_URL, ThresholdSignatureScheme, WalletOperation } from '@dynamic-labs-wallet/core';
3
+ import { v4 } from 'uuid';
4
+ import { createRequestChannel, parseMessageTransportData, applyDefaultMessageOrigin, createMessageTransport } from '@dynamic-labs/message-transport';
2
5
  import { Logger } from '@dynamic-labs/logger';
3
- import { getEnvironmentFromUrl, IFRAME_DOMAIN_MAP } from '@dynamic-labs-wallet/core';
4
- export { MPC_RELAY_PREPROD_API_URL, MPC_RELAY_PROD_API_URL, ThresholdSignatureScheme, WalletOperation } from '@dynamic-labs-wallet/core';
5
- export { BIP340KeygenResult, EcdsaKeygenResult, EcdsaSignature, Ed25519KeygenResult } from './internal/web';
6
6
 
7
- const setupMessageTransportBridge = (messageTransport, iframe, iframeOrigin)=>{
8
- if (!(iframe == null ? void 0 : iframe.contentWindow)) {
9
- throw new Error('Iframe or contentWindow not available');
10
- }
11
- const logger = new Logger('debug');
12
- messageTransport.on((message)=>{
13
- // Forward the message to webview via postMessage
14
- if (message.origin === 'host') {
15
- var _iframe_contentWindow;
16
- iframe == null ? void 0 : (_iframe_contentWindow = iframe.contentWindow) == null ? void 0 : _iframe_contentWindow.postMessage(message, iframeOrigin);
17
- }
18
- });
19
- const handleIncomingMessage = (message)=>{
20
- const { data } = message;
21
- if (!data) return;
22
- if ((data == null ? void 0 : data.origin) !== 'webview') {
23
- return;
24
- }
25
- if (typeof data !== 'object') {
26
- return;
27
- }
28
- try {
29
- const message = parseMessageTransportData(data);
30
- messageTransport.emit(message);
31
- } catch (error) {
32
- if (!(error instanceof SyntaxError)) {
33
- logger.error('Error handling incoming message:', error);
34
- }
7
+ function _extends() {
8
+ _extends = Object.assign || function assign(target) {
9
+ for(var i = 1; i < arguments.length; i++){
10
+ var source = arguments[i];
11
+ for(var key in source)if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];
35
12
  }
13
+ return target;
36
14
  };
37
- /**
38
- * Handle incoming message from android client
39
- */ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
40
- // @ts-ignore
41
- document.addEventListener('message', handleIncomingMessage);
42
- /**
43
- * Handle incoming message from iOS client
44
- */ window.addEventListener('message', handleIncomingMessage);
45
- };
15
+ return _extends.apply(this, arguments);
16
+ }
46
17
 
47
18
  class iframeMessageHandler {
48
- async getWallets() {
49
- return this.requestChannel.request('getWallets');
19
+ async getWallets(request) {
20
+ return this.requestChannel.request('getWallets', request);
50
21
  }
51
- async getWallet({ accountAddress, walletOperation }) {
52
- return this.requestChannel.request('getWallet', {
53
- accountAddress,
54
- walletOperation
55
- });
22
+ async getWallet(request) {
23
+ return this.requestChannel.request('getWallet', request);
56
24
  }
57
- async createWalletAccount(thresholdSignatureScheme, password) {
58
- return this.requestChannel.request('createWalletAccount', {
59
- thresholdSignatureScheme,
60
- password
61
- });
25
+ async createWalletAccount(request) {
26
+ return this.requestChannel.request('createWalletAccount', request);
62
27
  }
63
28
  async requiresPasswordForOperation(request) {
64
29
  return this.requestChannel.request('requiresPasswordForOperation', request);
@@ -66,6 +31,9 @@ class iframeMessageHandler {
66
31
  async signMessage(request) {
67
32
  return this.requestChannel.request('signMessage', request);
68
33
  }
34
+ async signRawMessage(request) {
35
+ return this.requestChannel.request('signRawMessage', request);
36
+ }
69
37
  async signTransaction(request) {
70
38
  return this.requestChannel.request('signTransaction', request);
71
39
  }
@@ -75,6 +43,9 @@ class iframeMessageHandler {
75
43
  async backupKeySharesToGoogleDrive(request) {
76
44
  await this.requestChannel.request('backupKeySharesToGoogleDrive', request);
77
45
  }
46
+ async delegateKeyShares(request) {
47
+ return this.requestChannel.request('delegateKeyShares', request);
48
+ }
78
49
  async restoreBackupFromGoogleDrive(request) {
79
50
  return this.requestChannel.request('restoreBackupFromGoogleDrive', request);
80
51
  }
@@ -96,8 +67,8 @@ class iframeMessageHandler {
96
67
  async importPrivateKey(request) {
97
68
  return this.requestChannel.request('importPrivateKey', request);
98
69
  }
99
- async sendAuthToken(token) {
100
- return this.requestChannel.request('sendAuthToken', token);
70
+ async sendAuthToken(token, authMode) {
71
+ return this.requestChannel.request('sendAuthToken', token, authMode);
101
72
  }
102
73
  async exportClientKeyshares(request) {
103
74
  return this.requestChannel.request('exportClientKeyshares', request);
@@ -105,6 +76,12 @@ class iframeMessageHandler {
105
76
  async offlineExportPrivateKey(request) {
106
77
  return this.requestChannel.request('offlineExportPrivateKey', request);
107
78
  }
79
+ async signTypedData(request) {
80
+ return this.requestChannel.request('signTypedData', request);
81
+ }
82
+ async cleanup() {
83
+ return this.requestChannel.request('cleanup');
84
+ }
108
85
  constructor(messageTransport){
109
86
  this.requestChannel = createRequestChannel(messageTransport);
110
87
  }
@@ -112,7 +89,47 @@ class iframeMessageHandler {
112
89
 
113
90
  const logger = new Logger('DynamicWaasWalletClient');
114
91
 
115
- class DynamicWalletClient {
92
+ const setupMessageTransportBridge = (messageTransport, iframe, iframeOrigin)=>{
93
+ if (!(iframe == null ? void 0 : iframe.contentWindow)) {
94
+ throw new Error('Iframe or contentWindow not available');
95
+ }
96
+ const logger = new Logger('debug');
97
+ messageTransport.on((message)=>{
98
+ // Forward the message to webview via postMessage
99
+ if (message.origin === 'host') {
100
+ var _iframe_contentWindow;
101
+ iframe == null ? void 0 : (_iframe_contentWindow = iframe.contentWindow) == null ? void 0 : _iframe_contentWindow.postMessage(message, iframeOrigin);
102
+ }
103
+ });
104
+ const handleIncomingMessage = (message)=>{
105
+ const { data } = message;
106
+ if (!data) return;
107
+ if ((data == null ? void 0 : data.origin) !== 'webview') {
108
+ return;
109
+ }
110
+ if (typeof data !== 'object') {
111
+ return;
112
+ }
113
+ try {
114
+ const message = parseMessageTransportData(data);
115
+ messageTransport.emit(message);
116
+ } catch (error) {
117
+ if (!(error instanceof SyntaxError)) {
118
+ logger.error('Error handling incoming message:', error);
119
+ }
120
+ }
121
+ };
122
+ /**
123
+ * Handle incoming message from android client
124
+ */ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
125
+ // @ts-ignore
126
+ document.addEventListener('message', handleIncomingMessage);
127
+ /**
128
+ * Handle incoming message from iOS client
129
+ */ window.addEventListener('message', handleIncomingMessage);
130
+ };
131
+
132
+ class IframeManager {
116
133
  // Simply load the iframe from localhost
117
134
  async initialize() {
118
135
  await this.doInitializeIframeCommunication();
@@ -121,10 +138,10 @@ class DynamicWalletClient {
121
138
  * this is called on class construction time
122
139
  * @returns {Promise<void>} that resolves when the iframe is loaded and the message transport and iframe storage are initialized
123
140
  */ initializeIframeCommunication() {
124
- if (!this.iframeLoadPromise) {
125
- this.iframeLoadPromise = this.doInitializeIframeCommunication();
126
- }
127
- return this.iframeLoadPromise;
141
+ var _IframeManager;
142
+ var _iframeLoadPromise;
143
+ (_iframeLoadPromise = (_IframeManager = IframeManager).iframeLoadPromise) != null ? _iframeLoadPromise : _IframeManager.iframeLoadPromise = this.doInitializeIframeCommunication();
144
+ return IframeManager.iframeLoadPromise;
128
145
  }
129
146
  /**
130
147
  * initialize the iframe communication by awaiting the iframe load promise
@@ -132,8 +149,6 @@ class DynamicWalletClient {
132
149
  */ async doInitializeIframeCommunication() {
133
150
  try {
134
151
  await this.loadIframe();
135
- this.initializeMessageTransport();
136
- await this.initAuthToken();
137
152
  } catch (error) {
138
153
  this.logger.error('Error initializing iframe:', error);
139
154
  throw error;
@@ -141,7 +156,12 @@ class DynamicWalletClient {
141
156
  }
142
157
  /**
143
158
  * initialize the message transport after iframe is successfully loaded
144
- */ initializeMessageTransport() {
159
+ */ async initializeMessageTransport() {
160
+ if (this.messageTransport && this.iframeMessageHandler) {
161
+ this.logger.debug('Skipping initializeMessageTransport: transport and message handler already initialized');
162
+ return;
163
+ }
164
+ await this.initializeIframeCommunication();
145
165
  const transport = applyDefaultMessageOrigin({
146
166
  defaultOrigin: 'host',
147
167
  messageTransport: createMessageTransport()
@@ -152,6 +172,7 @@ class DynamicWalletClient {
152
172
  }
153
173
  setupMessageTransportBridge(this.messageTransport, this.iframe, this.iframeDomain);
154
174
  this.iframeMessageHandler = new iframeMessageHandler(this.messageTransport);
175
+ await this.initAuthToken();
155
176
  }
156
177
  /**
157
178
  * securely exchange the auth token with iframe securely
@@ -161,52 +182,150 @@ class DynamicWalletClient {
161
182
  }
162
183
  try {
163
184
  // Send auth token to iframe
164
- await this.iframeMessageHandler.sendAuthToken(this.authToken);
185
+ await this.iframeMessageHandler.sendAuthToken(this.authToken, this.authMode);
165
186
  } catch (error) {
166
187
  throw new Error('Failed to establish secure token exchange: ' + error);
167
188
  }
168
189
  }
169
- loadIframe() {
170
- return new Promise((resolve, reject)=>{
171
- const iframe = document.createElement('iframe');
172
- const iframeTimeoutId = setTimeout(()=>{
173
- reject(new Error('Iframe load timeout'));
174
- }, 10000);
175
- iframe.style.display = 'none';
176
- iframe.setAttribute('title', 'Dynamic Wallet Iframe');
177
- iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-downloads');
178
- iframe.setAttribute('referrerpolicy', 'origin');
179
- iframe.style.position = 'fixed';
180
- iframe.style.top = '0';
181
- iframe.style.left = '0';
182
- iframe.style.width = '0';
183
- iframe.style.height = '0';
184
- iframe.style.border = 'none';
185
- iframe.style.pointerEvents = 'none';
186
- var _this_instanceId;
187
- const params = new URLSearchParams({
188
- instanceId: (_this_instanceId = this.instanceId) != null ? _this_instanceId : '',
189
- hostOrigin: window.location.origin,
190
- environmentId: this.environmentId,
191
- authToken: this.authToken,
192
- baseApiUrl: this.baseApiUrl,
193
- baseMPCRelayApiUrl: this.baseMPCRelayApiUrl,
194
- chain: this.chainName
190
+ /**
191
+ * Reset the shared iframe and iframe load promise, and iframe instance count
192
+ */ async resetSharedIframe() {
193
+ IframeManager.sharedIframe = null;
194
+ IframeManager.iframeInstanceCount = 0;
195
+ IframeManager.iframeLoadPromise = null;
196
+ this.iframe = null;
197
+ this.iframeMessageHandler = null;
198
+ this.messageTransport = null;
199
+ // Double the timeout and cap at 60 seconds to give more time for slow networks
200
+ IframeManager.iframeLoadTimeout = Math.min(IframeManager.iframeLoadTimeout * 2, 60000);
201
+ }
202
+ async loadIframe() {
203
+ // If the iframe is already loaded, just assign and resolve
204
+ if (IframeManager.sharedIframe) {
205
+ this.assignExistingIframe();
206
+ return Promise.resolve();
207
+ }
208
+ // If a load is in progress, wait for it, then assign
209
+ if (IframeManager.iframeLoadPromise) {
210
+ return IframeManager.iframeLoadPromise.then(()=>{
211
+ this.assignExistingIframe();
195
212
  });
196
- iframe.src = `${this.iframeDomain}/waas/${this.environmentId}?${params.toString()}`;
197
- this.logger.debug('Creating iframe with src:', iframe.src);
198
- document.body.appendChild(iframe);
199
- iframe.onload = ()=>{
213
+ }
214
+ IframeManager.iframeLoadPromise = this.createIframeLoadPromise();
215
+ return IframeManager.iframeLoadPromise;
216
+ }
217
+ assignExistingIframe() {
218
+ this.iframe = IframeManager.sharedIframe;
219
+ IframeManager.iframeInstanceCount++;
220
+ }
221
+ createIframeLoadPromise() {
222
+ return new Promise((resolve, reject)=>{
223
+ const attemptLoad = ()=>{
224
+ IframeManager.iframeLoadAttempts++;
225
+ this.logger.info(`Loading iframe for waas wallet client... (attempt ${IframeManager.iframeLoadAttempts}/${IframeManager.maxRetryAttempts + 1})`, this.getIframeContext());
226
+ const iframe = document.createElement('iframe');
227
+ let messageListener = null;
228
+ const context = _extends({}, this.getIframeContext(), {
229
+ attempt: IframeManager.iframeLoadAttempts
230
+ });
231
+ // Set up timeout that will trigger iframe error, a retry will be triggered on this iframe error
232
+ const iframeTimeoutId = setTimeout(()=>{
233
+ if (iframe.onerror) {
234
+ iframe.onerror('Iframe load timeout');
235
+ }
236
+ }, IframeManager.iframeLoadTimeout);
237
+ messageListener = this.createMessageListener(iframe, iframeTimeoutId, resolve);
238
+ window.addEventListener('message', messageListener);
239
+ this.configureIframe(iframe);
240
+ this.setIframeSource(iframe);
241
+ this.logger.debug('Creating iframe with src:', iframe.src);
242
+ document.body.appendChild(iframe);
243
+ this.setupIframeEventHandlersWithRetry(iframe, messageListener, iframeTimeoutId, reject, attemptLoad, context);
244
+ };
245
+ // Start the first attempt
246
+ attemptLoad();
247
+ });
248
+ }
249
+ setupIframeEventHandlersWithRetry(iframe, messageListener, iframeTimeoutId, reject, attemptLoad, context) {
250
+ iframe.onload = ()=>{
251
+ this.logger.debug('Iframe onload fired, waiting for ready message...');
252
+ };
253
+ iframe.onerror = (error)=>{
254
+ if (messageListener) {
255
+ window.removeEventListener('message', messageListener);
256
+ }
257
+ clearTimeout(iframeTimeoutId);
258
+ // Check if we should retry
259
+ if (IframeManager.iframeLoadAttempts <= IframeManager.maxRetryAttempts) {
260
+ const errorMsg = error instanceof Error ? error.message : 'Unknown error occurred.';
261
+ this.logger.warn(`(loadIframe) Iframe failed to load on attempt ${IframeManager.iframeLoadAttempts}, retrying... context: ${JSON.stringify(context)}, error: ${errorMsg}`);
262
+ // Clean up current attempt
263
+ if (iframe.parentNode) {
264
+ iframe.parentNode.removeChild(iframe);
265
+ }
266
+ // Retry after a short delay
267
+ setTimeout(()=>{
268
+ attemptLoad();
269
+ }, 1000); // 1 second delay between retries
270
+ } else {
271
+ // Max retries reached, give up
272
+ this.logger.error('Iframe failed to load after all retry attempts: ', error);
273
+ this.resetSharedIframe();
274
+ IframeManager.iframeLoadAttempts = 0;
275
+ reject(new Error(`Failed to load iframe after all retry attempts... context: ${JSON.stringify(context)}`));
276
+ }
277
+ };
278
+ }
279
+ getIframeContext() {
280
+ var _this_sdkVersion;
281
+ return {
282
+ iframeDomain: this.iframeDomain,
283
+ environmentId: this.environmentId,
284
+ sdkVersion: (_this_sdkVersion = this.sdkVersion) != null ? _this_sdkVersion : '',
285
+ instanceId: this.instanceId,
286
+ chainName: this.chainName,
287
+ iframeLoadTimeout: IframeManager.iframeLoadTimeout
288
+ };
289
+ }
290
+ createMessageListener(iframe, iframeTimeoutId, resolve) {
291
+ const messageListener = (event)=>{
292
+ if (event.source === iframe.contentWindow && event.data === `iframe-ready-${this.instanceId}`) {
293
+ window.removeEventListener('message', messageListener);
200
294
  clearTimeout(iframeTimeoutId);
295
+ IframeManager.sharedIframe = iframe;
201
296
  this.iframe = iframe;
297
+ IframeManager.iframeInstanceCount++;
298
+ IframeManager.iframeLoadAttempts = 0; // Reset retry counter on success
202
299
  resolve();
203
- };
204
- iframe.onerror = (error)=>{
205
- clearTimeout(iframeTimeoutId);
206
- this.logger.error('Iframe failed to load:', error);
207
- reject(new Error('Failed to load iframe'));
208
- };
300
+ this.logger.info('Iframe loaded successfully...', this.getIframeContext());
301
+ }
302
+ };
303
+ return messageListener;
304
+ }
305
+ configureIframe(iframe) {
306
+ iframe.style.display = 'none';
307
+ iframe.setAttribute('title', 'Dynamic Wallet Iframe');
308
+ iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-downloads');
309
+ iframe.setAttribute('referrerpolicy', 'origin');
310
+ iframe.style.position = 'fixed';
311
+ iframe.style.top = '0';
312
+ iframe.style.left = '0';
313
+ iframe.style.width = '0';
314
+ iframe.style.height = '0';
315
+ iframe.style.border = 'none';
316
+ iframe.style.pointerEvents = 'none';
317
+ }
318
+ setIframeSource(iframe) {
319
+ var _this_instanceId, _this_sdkVersion;
320
+ const params = new URLSearchParams({
321
+ instanceId: (_this_instanceId = this.instanceId) != null ? _this_instanceId : '',
322
+ hostOrigin: window.location.origin,
323
+ environmentId: this.environmentId,
324
+ baseApiUrl: this.baseApiUrl,
325
+ baseMPCRelayApiUrl: this.baseMPCRelayApiUrl,
326
+ sdkVersion: (_this_sdkVersion = this.sdkVersion) != null ? _this_sdkVersion : ''
209
327
  });
328
+ iframe.src = `${this.iframeDomain}/waas-v1/${this.environmentId}?${params.toString()}`;
210
329
  }
211
330
  /**
212
331
  * Load an iframe for a specific container
@@ -214,41 +333,78 @@ class DynamicWalletClient {
214
333
  * @returns {Promise<HTMLIFrameElement>} that resolves when the iframe is loaded
215
334
  */ loadIframeForContainer(container) {
216
335
  return new Promise((resolve, reject)=>{
336
+ var _this_sdkVersion;
337
+ const context = {
338
+ iframeDomain: this.iframeDomain,
339
+ environmentId: this.environmentId,
340
+ sdkVersion: (_this_sdkVersion = this.sdkVersion) != null ? _this_sdkVersion : '',
341
+ instanceId: this.instanceId,
342
+ chainName: this.chainName,
343
+ iframeLoadTimeout: IframeManager.iframeLoadTimeout
344
+ };
345
+ this.logger.info(`Loading iframe for container...`, context);
217
346
  const iframe = document.createElement('iframe');
347
+ let messageListener = null;
218
348
  const iframeTimeoutId = setTimeout(()=>{
219
- reject(new Error('Iframe load timeout'));
220
- }, 10000);
349
+ if (messageListener) {
350
+ window.removeEventListener('message', messageListener);
351
+ }
352
+ this.logger.error(`(loadIframeForContainer) Iframe load timeout due to no handshake message from iframe, this could be network issues, incorrect iframe domain, or CORS errors that prevents iframe from being loaded or sending handshake message, context: ${JSON.stringify(context)}`);
353
+ reject(new Error(`(loadIframeForContainer) Iframe load timeout due to no handshake message from iframe, this could be network issues, incorrect iframe domain, or CORS errors that prevents iframe from being loaded or sending handshake message, context: ${JSON.stringify(context)}`));
354
+ }, IframeManager.iframeLoadTimeout);
221
355
  iframe.style.display = 'block';
222
356
  iframe.style.width = '100%';
223
357
  iframe.style.height = '100%';
224
358
  iframe.setAttribute('title', 'Dynamic Wallet Storage');
225
359
  iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
226
360
  iframe.setAttribute('referrerpolicy', 'origin');
227
- var _this_instanceId;
361
+ var _this_instanceId, _this_sdkVersion1;
228
362
  const params = new URLSearchParams({
229
363
  instanceId: (_this_instanceId = this.instanceId) != null ? _this_instanceId : '',
230
364
  hostOrigin: window.location.origin,
231
365
  environmentId: this.environmentId,
232
- authToken: this.authToken,
233
366
  baseApiUrl: this.baseApiUrl,
234
367
  baseMPCRelayApiUrl: this.baseMPCRelayApiUrl,
235
- chain: this.chainName
368
+ sdkVersion: (_this_sdkVersion1 = this.sdkVersion) != null ? _this_sdkVersion1 : ''
236
369
  });
237
- iframe.src = `${this.iframeDomain}/waas/${this.environmentId}?${params.toString()}`;
370
+ iframe.src = `${this.iframeDomain}/waas-v1/${this.environmentId}?${params.toString()}`;
238
371
  this.logger.debug('Creating iframe with src:', iframe.src);
239
372
  // Add iframe to the provided container
240
373
  container.appendChild(iframe);
241
374
  iframe.onload = ()=>{
242
- clearTimeout(iframeTimeoutId);
243
- this.logger.debug('Iframe loaded successfully');
244
- this.iframe = iframe;
245
- resolve(iframe);
375
+ // The message listener is already set up, so iframe can send ready message
376
+ this.logger.debug('Iframe onload fired, waiting for ready message...');
246
377
  };
247
378
  iframe.onerror = (error)=>{
379
+ if (messageListener) {
380
+ window.removeEventListener('message', messageListener);
381
+ }
248
382
  clearTimeout(iframeTimeoutId);
249
- this.logger.error('Iframe failed to load:', error);
250
- reject(new Error('Failed to load iframe'));
383
+ this.logger.error('Iframe failed to load due to errors: ', error);
384
+ reject(new Error('Failed to load iframe due to unknown load errors, this is likely a browser or network issue.'));
251
385
  };
386
+ // Set up message listener BEFORE creating iframe
387
+ messageListener = (event)=>{
388
+ if (event.source === iframe.contentWindow && event.data === `iframe-ready-${this.instanceId}`) {
389
+ if (messageListener) {
390
+ window.removeEventListener('message', messageListener);
391
+ }
392
+ clearTimeout(iframeTimeoutId);
393
+ IframeManager.sharedIframe = iframe;
394
+ this.iframe = iframe;
395
+ IframeManager.iframeInstanceCount++;
396
+ resolve(iframe);
397
+ var _this_sdkVersion;
398
+ this.logger.info('Iframe loaded successfully...', {
399
+ iframeDomain: this.iframeDomain,
400
+ environmentId: this.environmentId,
401
+ sdkVersion: (_this_sdkVersion = this.sdkVersion) != null ? _this_sdkVersion : '',
402
+ instanceId: this.instanceId,
403
+ chainName: this.chainName
404
+ });
405
+ }
406
+ };
407
+ window.addEventListener('message', messageListener);
252
408
  });
253
409
  }
254
410
  /**
@@ -268,8 +424,10 @@ class DynamicWalletClient {
268
424
  });
269
425
  setupMessageTransportBridge(transport, iframe, this.iframeDomain);
270
426
  const iframeDisplay = new iframeMessageHandler(transport);
271
- // Send auth token to iframe
272
- await iframeDisplay.sendAuthToken(this.authToken);
427
+ var _this_authMode;
428
+ // if authMode is header: inform iframe the authMode with auth token
429
+ // if authMode is cookie: inform iframe the authMode with empty authToken
430
+ await iframeDisplay.sendAuthToken(this.authToken, (_this_authMode = this.authMode) != null ? _this_authMode : AuthMode.HEADER);
273
431
  return {
274
432
  iframe,
275
433
  iframeDisplay,
@@ -282,58 +440,144 @@ class DynamicWalletClient {
282
440
  throw error;
283
441
  }
284
442
  }
443
+ async cleanup() {
444
+ await this.initializeMessageTransport();
445
+ if (!this.iframeMessageHandler) {
446
+ throw new Error('Iframe message handler not initialized');
447
+ }
448
+ await this.iframeMessageHandler.cleanup();
449
+ if (this.iframe) {
450
+ IframeManager.iframeInstanceCount--;
451
+ if (IframeManager.sharedIframe && IframeManager.iframeInstanceCount === 0) {
452
+ document.body.removeChild(IframeManager.sharedIframe);
453
+ IframeManager.sharedIframe = null;
454
+ IframeManager.iframeLoadPromise = null;
455
+ }
456
+ this.iframe = null;
457
+ }
458
+ }
459
+ constructor({ environmentId, baseApiUrl, baseMPCRelayApiUrl, chainName, sdkVersion, authMode = AuthMode.HEADER, authToken, debug }){
460
+ this.logger = logger;
461
+ this.instanceId = null;
462
+ this.iframeDomain = null;
463
+ this.messageTransport = null;
464
+ this.iframeMessageHandler = null;
465
+ this.iframe = null;
466
+ this.environmentId = environmentId;
467
+ this.authToken = authToken;
468
+ this.authMode = authMode;
469
+ this.baseApiUrl = baseApiUrl;
470
+ this.baseMPCRelayApiUrl = baseMPCRelayApiUrl;
471
+ this.chainName = chainName;
472
+ this.sdkVersion = sdkVersion;
473
+ const environment = getEnvironmentFromUrl(baseApiUrl);
474
+ this.iframeDomain = IFRAME_DOMAIN_MAP[environment];
475
+ if (this.authMode === AuthMode.COOKIE) {
476
+ this.iframeDomain = this.baseApiUrl;
477
+ }
478
+ // Generate unique instanceId when client is created
479
+ this.instanceId = v4();
480
+ this.debug = Boolean(debug);
481
+ this.logger.setLogLevel(this.debug ? 'DEBUG' : 'INFO');
482
+ }
483
+ }
484
+ IframeManager.iframeLoadPromise = null;
485
+ IframeManager.iframeLoadTimeout = 10000;
486
+ IframeManager.iframeLoadAttempts = 0;
487
+ IframeManager.maxRetryAttempts = 1;
488
+ IframeManager.sharedIframe = null;
489
+ IframeManager.iframeInstanceCount = 0;
490
+
491
+ class DynamicWalletClient extends IframeManager {
285
492
  async getWallets() {
286
- await this.initializeIframeCommunication();
493
+ await this.initializeMessageTransport();
287
494
  if (!this.iframeMessageHandler) {
288
495
  throw new Error('Iframe message handler not initialized');
289
496
  }
290
- return this.iframeMessageHandler.getWallets();
497
+ return this.iframeMessageHandler.getWallets({
498
+ chainName: this.chainName
499
+ });
291
500
  }
292
- async getWallet({ accountAddress, walletOperation }) {
293
- await this.initializeIframeCommunication();
501
+ async getWallet({ accountAddress, walletOperation = WalletOperation.NO_OPERATION, signedSessionId, authToken }) {
502
+ await this.initializeMessageTransport();
294
503
  if (!this.iframeMessageHandler) {
295
504
  throw new Error('Iframe message handler not initialized');
296
505
  }
297
506
  return this.iframeMessageHandler.getWallet({
507
+ chainName: this.chainName,
298
508
  accountAddress,
299
- walletOperation
509
+ walletOperation,
510
+ signedSessionId,
511
+ authToken
300
512
  });
301
513
  }
302
- async createWalletAccount({ thresholdSignatureScheme, password = undefined }) {
303
- await this.initializeIframeCommunication();
514
+ async createWalletAccount({ thresholdSignatureScheme, password = undefined, signedSessionId, authToken }) {
515
+ await this.initializeMessageTransport();
304
516
  if (!this.iframeMessageHandler) {
305
517
  throw new Error('Iframe message handler not initialized');
306
518
  }
307
- return this.iframeMessageHandler.createWalletAccount(thresholdSignatureScheme, password);
519
+ return this.iframeMessageHandler.createWalletAccount({
520
+ chainName: this.chainName,
521
+ thresholdSignatureScheme,
522
+ password,
523
+ signedSessionId,
524
+ authToken
525
+ });
308
526
  }
309
- async requiresPasswordForOperation({ accountAddress, walletOperation }) {
310
- await this.initializeIframeCommunication();
527
+ async requiresPasswordForOperation({ accountAddress, walletOperation = WalletOperation.REACH_THRESHOLD, authToken }) {
528
+ await this.initializeMessageTransport();
311
529
  if (!this.iframeMessageHandler) {
312
530
  throw new Error('Iframe message handler not initialized');
313
531
  }
314
532
  return this.iframeMessageHandler.requiresPasswordForOperation({
533
+ chainName: this.chainName,
315
534
  accountAddress,
316
- walletOperation
535
+ walletOperation,
536
+ authToken
317
537
  });
318
538
  }
319
- async isPasswordEncrypted({ accountAddress }) {
320
- await this.initializeIframeCommunication();
539
+ async isPasswordEncrypted({ accountAddress, authToken }) {
540
+ await this.initializeMessageTransport();
321
541
  if (!this.iframeMessageHandler) {
322
542
  throw new Error('Iframe message handler not initialized');
323
543
  }
324
544
  return this.iframeMessageHandler.isPasswordEncrypted({
325
- accountAddress
545
+ chainName: this.chainName,
546
+ accountAddress,
547
+ authToken
326
548
  });
327
549
  }
328
- async signMessage({ message, accountAddress, password = undefined }) {
329
- await this.initializeIframeCommunication();
550
+ async signMessage({ message, accountAddress, password = undefined, signedSessionId, authToken, mfaToken, context }) {
551
+ await this.initializeMessageTransport();
330
552
  if (!this.iframeMessageHandler) {
331
553
  throw new Error('Iframe message handler not initialized');
332
554
  }
555
+ const contextString = JSON.stringify(context, (_key, value)=>typeof value === 'bigint' ? value.toString() : value);
333
556
  return this.iframeMessageHandler.signMessage({
557
+ chainName: this.chainName,
334
558
  message,
335
559
  accountAddress,
336
- password
560
+ password,
561
+ signedSessionId,
562
+ authToken,
563
+ mfaToken,
564
+ context: contextString
565
+ });
566
+ }
567
+ async signRawMessage({ message, accountAddress, password = undefined, signedSessionId, authToken, mfaToken, context }) {
568
+ await this.initializeMessageTransport();
569
+ if (!this.iframeMessageHandler) {
570
+ throw new Error('Iframe message handler not initialized');
571
+ }
572
+ return this.iframeMessageHandler.signRawMessage({
573
+ chainName: this.chainName,
574
+ message,
575
+ accountAddress,
576
+ password,
577
+ signedSessionId,
578
+ authToken,
579
+ mfaToken,
580
+ context
337
581
  });
338
582
  }
339
583
  /**
@@ -346,25 +590,65 @@ class DynamicWalletClient {
346
590
  * SUI:
347
591
  * const txBytes = await txb.build({ client });
348
592
  * const txString = Buffer.from(txBytes).toString("hex");
349
- */ async signTransaction({ senderAddress, transaction, password = undefined }) {
350
- await this.initializeIframeCommunication();
593
+ */ async signTransaction({ senderAddress, transaction, password = undefined, signedSessionId, authToken, mfaToken, chainId }) {
594
+ await this.initializeMessageTransport();
351
595
  if (!this.iframeMessageHandler) {
352
596
  throw new Error('Iframe message handler not initialized');
353
597
  }
354
598
  return this.iframeMessageHandler.signTransaction({
599
+ chainName: this.chainName,
355
600
  senderAddress,
356
601
  transaction,
357
- password
602
+ password,
603
+ signedSessionId,
604
+ authToken,
605
+ mfaToken,
606
+ chainId
358
607
  });
359
608
  }
360
- async backupKeySharesToGoogleDrive(request) {
361
- await this.initializeIframeCommunication();
609
+ async signTypedData({ accountAddress, typedData, password = undefined, signedSessionId, authToken, mfaToken }) {
610
+ await this.initializeMessageTransport();
362
611
  if (!this.iframeMessageHandler) {
363
612
  throw new Error('Iframe message handler not initialized');
364
613
  }
365
- return this.iframeMessageHandler.backupKeySharesToGoogleDrive(request);
614
+ return this.iframeMessageHandler.signTypedData({
615
+ chainName: this.chainName,
616
+ accountAddress,
617
+ typedData: JSON.stringify(typedData),
618
+ password,
619
+ signedSessionId,
620
+ authToken,
621
+ mfaToken
622
+ });
366
623
  }
367
- async restoreBackupFromGoogleDrive({ accountAddress, oauthAccountId, displayContainer, name, password }) {
624
+ async backupKeySharesToGoogleDrive({ accountAddress, password = undefined, signedSessionId, authToken }) {
625
+ await this.initializeMessageTransport();
626
+ if (!this.iframeMessageHandler) {
627
+ throw new Error('Iframe message handler not initialized');
628
+ }
629
+ return this.iframeMessageHandler.backupKeySharesToGoogleDrive({
630
+ chainName: this.chainName,
631
+ accountAddress,
632
+ password,
633
+ signedSessionId,
634
+ authToken
635
+ });
636
+ }
637
+ async delegateKeyShares({ accountAddress, password, signedSessionId, authToken, mfaToken }) {
638
+ await this.initializeMessageTransport();
639
+ if (!this.iframeMessageHandler) {
640
+ throw new Error('Iframe message handler not initialized');
641
+ }
642
+ return this.iframeMessageHandler.delegateKeyShares({
643
+ chainName: this.chainName,
644
+ accountAddress,
645
+ password,
646
+ signedSessionId,
647
+ authToken,
648
+ mfaToken
649
+ });
650
+ }
651
+ async restoreBackupFromGoogleDrive({ accountAddress, displayContainer, password, signedSessionId, authToken }) {
368
652
  const { iframeDisplay } = await this.initializeIframeDisplayForContainer({
369
653
  container: displayContainer
370
654
  });
@@ -372,27 +656,44 @@ class DynamicWalletClient {
372
656
  throw new Error('Failed to initialize iframe handler with display functionality');
373
657
  }
374
658
  return iframeDisplay.restoreBackupFromGoogleDrive({
659
+ chainName: this.chainName,
375
660
  accountAddress,
376
- oauthAccountId,
377
- name,
378
- password
661
+ password,
662
+ signedSessionId,
663
+ authToken
379
664
  });
380
665
  }
381
- async refreshWalletAccountShares(request) {
382
- await this.initializeIframeCommunication();
666
+ async refreshWalletAccountShares({ accountAddress, password, signedSessionId, authToken, mfaToken }) {
667
+ await this.initializeMessageTransport();
383
668
  if (!this.iframeMessageHandler) {
384
669
  throw new Error('Iframe message handler not initialized');
385
670
  }
386
- return this.iframeMessageHandler.refreshWalletAccountShares(request);
671
+ return this.iframeMessageHandler.refreshWalletAccountShares({
672
+ chainName: this.chainName,
673
+ accountAddress: accountAddress,
674
+ password: password,
675
+ signedSessionId,
676
+ authToken,
677
+ mfaToken
678
+ });
387
679
  }
388
- async reshare(request) {
389
- await this.initializeIframeCommunication();
680
+ async reshare({ accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme, password, signedSessionId, authToken, mfaToken }) {
681
+ await this.initializeMessageTransport();
390
682
  if (!this.iframeMessageHandler) {
391
683
  throw new Error('Iframe message handler not initialized');
392
684
  }
393
- return this.iframeMessageHandler.reshare(request);
685
+ return this.iframeMessageHandler.reshare({
686
+ chainName: this.chainName,
687
+ accountAddress,
688
+ oldThresholdSignatureScheme,
689
+ newThresholdSignatureScheme,
690
+ password,
691
+ signedSessionId,
692
+ authToken,
693
+ mfaToken
694
+ });
394
695
  }
395
- async exportPrivateKey({ accountAddress, displayContainer, password }) {
696
+ async exportPrivateKey({ accountAddress, displayContainer, password, signedSessionId, authToken, mfaToken }) {
396
697
  const { iframeDisplay } = await this.initializeIframeDisplayForContainer({
397
698
  container: displayContainer
398
699
  });
@@ -400,55 +701,72 @@ class DynamicWalletClient {
400
701
  throw new Error('Failed to initialize iframe handler with display functionality');
401
702
  }
402
703
  return iframeDisplay.exportPrivateKey({
704
+ chainName: this.chainName,
403
705
  accountAddress,
404
- password
706
+ password,
707
+ signedSessionId,
708
+ authToken,
709
+ mfaToken
405
710
  });
406
711
  }
407
- async verifyPassword({ accountAddress, password, walletOperation }) {
408
- await this.initializeIframeCommunication();
712
+ async verifyPassword({ accountAddress, password, walletOperation = WalletOperation.NO_OPERATION, signedSessionId, authToken }) {
713
+ await this.initializeMessageTransport();
409
714
  if (!this.iframeMessageHandler) {
410
715
  throw new Error('Iframe message handler not initialized');
411
716
  }
412
717
  return this.iframeMessageHandler.verifyPassword({
718
+ chainName: this.chainName,
413
719
  accountAddress,
414
720
  password,
415
- walletOperation
721
+ walletOperation,
722
+ signedSessionId,
723
+ authToken
416
724
  });
417
725
  }
418
- async updatePassword({ accountAddress, existingPassword, newPassword }) {
419
- await this.initializeIframeCommunication();
726
+ async updatePassword({ accountAddress, existingPassword, newPassword, signedSessionId, authToken }) {
727
+ await this.initializeMessageTransport();
420
728
  if (!this.iframeMessageHandler) {
421
729
  throw new Error('Iframe message handler not initialized');
422
730
  }
423
731
  return this.iframeMessageHandler.updatePassword({
732
+ chainName: this.chainName,
424
733
  accountAddress,
425
734
  existingPassword,
426
- newPassword
735
+ newPassword,
736
+ signedSessionId,
737
+ authToken
427
738
  });
428
739
  }
429
- async importPrivateKey({ privateKey, chainName, thresholdSignatureScheme }) {
430
- await this.initializeIframeCommunication();
740
+ async importPrivateKey({ privateKey, thresholdSignatureScheme, signedSessionId, authToken }) {
741
+ await this.initializeMessageTransport();
431
742
  if (!this.iframeMessageHandler) {
432
743
  throw new Error('Iframe message handler not initialized');
433
744
  }
434
745
  return this.iframeMessageHandler.importPrivateKey({
746
+ chainName: this.chainName,
435
747
  privateKey,
436
- chainName,
437
- thresholdSignatureScheme
748
+ thresholdSignatureScheme,
749
+ signedSessionId,
750
+ authToken
438
751
  });
439
752
  }
440
- async exportClientKeyshares({ accountAddress, password }) {
441
- await this.initializeIframeCommunication();
753
+ async exportClientKeyshares({ accountAddress, password, signedSessionId, authToken }) {
754
+ await this.initializeMessageTransport();
442
755
  if (!this.iframeMessageHandler) {
443
756
  throw new Error('Iframe message handler not initialized');
444
757
  }
445
758
  return this.iframeMessageHandler.exportClientKeyshares({
759
+ chainName: this.chainName,
446
760
  accountAddress,
447
- password
761
+ password,
762
+ signedSessionId,
763
+ authToken
448
764
  });
449
765
  }
450
- async offlineExportPrivateKey({ keyShares, derivationPath }) {
451
- await this.initializeIframeCommunication();
766
+ /**
767
+ * keyShares is stringified list of EcdsaKeygenResult[] and Ed25519KeygenResult[]
768
+ */ async offlineExportPrivateKey({ keyShares, derivationPath }) {
769
+ await this.initializeMessageTransport();
452
770
  if (!this.iframeMessageHandler) {
453
771
  throw new Error('Iframe message handler not initialized');
454
772
  }
@@ -461,30 +779,21 @@ class DynamicWalletClient {
461
779
  const argsBuffer = new TextEncoder().encode(serializedArgs);
462
780
  const base64Args = Buffer.from(argsBuffer).toString('base64');
463
781
  return this.iframeMessageHandler.offlineExportPrivateKey({
782
+ chainName: this.chainName,
464
783
  base64Args
465
784
  });
466
785
  }
467
- constructor({ environmentId, authToken, baseApiUrl, baseMPCRelayApiUrl, chainName, debug }){
468
- this.logger = logger;
469
- this.instanceId = null;
470
- this.iframeDomain = null;
471
- this.messageTransport = null;
472
- this.iframeMessageHandler = null;
473
- this.iframeLoadPromise = null;
474
- this.iframe = null;
475
- this.environmentId = environmentId;
476
- this.authToken = authToken;
477
- this.baseApiUrl = baseApiUrl;
478
- this.baseMPCRelayApiUrl = baseMPCRelayApiUrl;
479
- this.chainName = chainName;
480
- const environment = getEnvironmentFromUrl(baseApiUrl);
481
- this.iframeDomain = IFRAME_DOMAIN_MAP[environment];
482
- // Generate unique instanceId when client is created
483
- this.instanceId = crypto.randomUUID();
484
- this.debug = Boolean(debug);
485
- this.logger.setLogLevel(this.debug ? 'DEBUG' : 'INFO');
486
- // initialize the client
487
- this.initialize();
786
+ constructor({ environmentId, authToken, baseApiUrl, baseMPCRelayApiUrl, chainName, sdkVersion, debug, authMode = AuthMode.HEADER }){
787
+ super({
788
+ environmentId,
789
+ authToken,
790
+ baseApiUrl,
791
+ baseMPCRelayApiUrl,
792
+ chainName,
793
+ sdkVersion,
794
+ debug,
795
+ authMode
796
+ });
488
797
  }
489
798
  }
490
799