@rhinestone/1auth 0.1.1 → 0.1.2

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/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  buildTransactionReview,
3
+ createOneAuthProvider,
3
4
  createPasskeyProvider,
4
5
  encodeWebAuthnSignature,
5
6
  getAllSupportedChainsAndTokens,
@@ -18,7 +19,7 @@ import {
18
19
  isTestnet,
19
20
  isTokenAddressSupported,
20
21
  resolveTokenAddress
21
- } from "./chunk-U7KZ4XMQ.mjs";
22
+ } from "./chunk-TACK3LJN.mjs";
22
23
 
23
24
  // src/client.ts
24
25
  import { parseUnits, hashTypedData } from "viem";
@@ -95,9 +96,7 @@ var OneAuthClient = class {
95
96
  const response = await fetch(
96
97
  `${this.config.providerUrl}/api/intent/status/${intentId}`,
97
98
  {
98
- headers: {
99
- "x-client-id": this.config.clientId
100
- }
99
+ headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
101
100
  }
102
101
  );
103
102
  if (response.ok) {
@@ -121,9 +120,11 @@ var OneAuthClient = class {
121
120
  async authWithModal(options) {
122
121
  const dialogUrl = this.getDialogUrl();
123
122
  const params = new URLSearchParams({
124
- clientId: this.config.clientId,
125
123
  mode: "iframe"
126
124
  });
125
+ if (this.config.clientId) {
126
+ params.set("clientId", this.config.clientId);
127
+ }
127
128
  if (options?.username) {
128
129
  params.set("username", options.username);
129
130
  }
@@ -139,6 +140,55 @@ var OneAuthClient = class {
139
140
  const { dialog, iframe, cleanup } = this.createModalDialog(url);
140
141
  return this.waitForModalAuthResponse(dialog, iframe, cleanup);
141
142
  }
143
+ /**
144
+ * Open the connect dialog (lightweight connection without passkey auth).
145
+ *
146
+ * This method shows a simple connection confirmation dialog that doesn't
147
+ * require a passkey signature. Users can optionally enable "auto-connect"
148
+ * to skip this dialog in the future.
149
+ *
150
+ * If the user has never connected before, this will return action: "switch"
151
+ * to indicate that the full auth modal should be opened instead.
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * const result = await client.connectWithModal();
156
+ *
157
+ * if (result.success) {
158
+ * console.log('Connected as:', result.username);
159
+ * } else if (result.action === 'switch') {
160
+ * // User needs to sign in first
161
+ * const authResult = await client.authWithModal();
162
+ * }
163
+ * ```
164
+ */
165
+ async connectWithModal(options) {
166
+ const dialogUrl = this.getDialogUrl();
167
+ const params = new URLSearchParams({
168
+ mode: "iframe"
169
+ });
170
+ if (this.config.clientId) {
171
+ params.set("clientId", this.config.clientId);
172
+ }
173
+ const themeParams = this.getThemeParams(options?.theme);
174
+ if (themeParams) {
175
+ const themeParsed = new URLSearchParams(themeParams);
176
+ themeParsed.forEach((value, key) => params.set(key, value));
177
+ }
178
+ const url = `${dialogUrl}/dialog/connect?${params.toString()}`;
179
+ const { dialog, iframe, cleanup } = this.createModalDialog(url);
180
+ const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
181
+ mode: "iframe"
182
+ });
183
+ if (!ready) {
184
+ return {
185
+ success: false,
186
+ action: "cancel",
187
+ error: { code: "USER_CANCELLED", message: "Connection was cancelled" }
188
+ };
189
+ }
190
+ return this.waitForConnectResponse(dialog, iframe, cleanup);
191
+ }
142
192
  /**
143
193
  * Authenticate a user with an optional challenge to sign.
144
194
  *
@@ -174,9 +224,11 @@ var OneAuthClient = class {
174
224
  async authenticate(options) {
175
225
  const dialogUrl = this.getDialogUrl();
176
226
  const params = new URLSearchParams({
177
- clientId: this.config.clientId,
178
227
  mode: "iframe"
179
228
  });
229
+ if (this.config.clientId) {
230
+ params.set("clientId", this.config.clientId);
231
+ }
180
232
  if (options?.challenge) {
181
233
  params.set("challenge", options.challenge);
182
234
  }
@@ -190,28 +242,30 @@ var OneAuthClient = class {
190
242
  return this.waitForAuthenticateResponse(dialog, iframe, cleanup);
191
243
  }
192
244
  /**
193
- * Show signing in a modal overlay (Porto-style iframe dialog)
245
+ * Show signing in a modal overlay (iframe dialog)
194
246
  */
195
247
  async signWithModal(options) {
196
248
  const dialogUrl = this.getDialogUrl();
197
249
  const themeParams = this.getThemeParams(options?.theme);
198
250
  const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
199
251
  const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
200
- const dialogOrigin = this.getDialogOrigin();
201
- await new Promise((resolve) => {
202
- iframe.onload = () => {
203
- iframe.contentWindow?.postMessage({
204
- type: "PASSKEY_INIT",
205
- mode: "iframe",
206
- challenge: options.challenge,
207
- username: options.username,
208
- description: options.description,
209
- transaction: options.transaction,
210
- metadata: options.metadata
211
- }, dialogOrigin);
212
- resolve();
213
- };
252
+ const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
253
+ mode: "iframe",
254
+ challenge: options.challenge,
255
+ username: options.username,
256
+ description: options.description,
257
+ transaction: options.transaction,
258
+ metadata: options.metadata
214
259
  });
260
+ if (!ready) {
261
+ return {
262
+ success: false,
263
+ error: {
264
+ code: "USER_REJECTED",
265
+ message: "User closed the dialog"
266
+ }
267
+ };
268
+ }
215
269
  return this.waitForSigningResponse(dialog, iframe, cleanup);
216
270
  }
217
271
  /**
@@ -284,16 +338,21 @@ var OneAuthClient = class {
284
338
  }
285
339
  };
286
340
  }
341
+ const serializedTokenRequests = options.tokenRequests?.map((r) => ({
342
+ token: r.token,
343
+ amount: r.amount.toString()
344
+ }));
287
345
  let prepareResponse;
346
+ const requestBody = signedIntent || {
347
+ username: options.username,
348
+ targetChain: options.targetChain,
349
+ calls: options.calls,
350
+ tokenRequests: serializedTokenRequests,
351
+ sourceAssets: options.sourceAssets,
352
+ sourceChainId: options.sourceChainId,
353
+ ...this.config.clientId && { clientId: this.config.clientId }
354
+ };
288
355
  try {
289
- const requestBody = signedIntent || {
290
- username: options.username,
291
- targetChain: options.targetChain,
292
- calls: options.calls,
293
- tokenRequests: options.tokenRequests,
294
- sourceAssets: options.sourceAssets,
295
- clientId: this.config.clientId
296
- };
297
356
  const response = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
298
357
  method: "POST",
299
358
  headers: {
@@ -330,112 +389,190 @@ var OneAuthClient = class {
330
389
  };
331
390
  }
332
391
  const dialogUrl = this.getDialogUrl();
333
- const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe`;
392
+ const themeParams = this.getThemeParams();
393
+ const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
334
394
  const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
335
395
  const dialogOrigin = this.getDialogOrigin();
336
- await new Promise((resolve) => {
337
- const handleReady = (event) => {
338
- if (event.origin !== dialogOrigin) return;
339
- if (event.data?.type === "PASSKEY_READY") {
340
- window.removeEventListener("message", handleReady);
341
- iframe.contentWindow?.postMessage({
342
- type: "PASSKEY_INIT",
343
- mode: "iframe",
344
- calls,
345
- chainId: targetChain,
346
- transaction: prepareResponse.transaction,
347
- challenge: prepareResponse.challenge,
348
- username,
349
- accountAddress: prepareResponse.accountAddress,
350
- intentId: prepareResponse.intentId
351
- }, dialogOrigin);
352
- resolve();
353
- }
354
- };
355
- window.addEventListener("message", handleReady);
396
+ const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
397
+ mode: "iframe",
398
+ calls,
399
+ chainId: targetChain,
400
+ transaction: prepareResponse.transaction,
401
+ challenge: prepareResponse.challenge,
402
+ username,
403
+ accountAddress: prepareResponse.accountAddress,
404
+ originMessages: prepareResponse.originMessages,
405
+ tokenRequests: serializedTokenRequests,
406
+ expiresAt: prepareResponse.expiresAt,
407
+ userId: prepareResponse.userId,
408
+ intentOp: prepareResponse.intentOp
356
409
  });
357
- const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
410
+ if (!ready) {
411
+ return {
412
+ success: false,
413
+ intentId: "",
414
+ status: "failed",
415
+ error: { code: "USER_CANCELLED", message: "User closed the dialog" }
416
+ };
417
+ }
418
+ const signingResult = await this.waitForSigningWithRefresh(
419
+ dialog,
420
+ iframe,
421
+ cleanup,
422
+ dialogOrigin,
423
+ // Refresh callback - called when dialog requests a quote refresh
424
+ async () => {
425
+ console.log("[SDK] Dialog requested quote refresh, re-preparing intent");
426
+ try {
427
+ const refreshResponse = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
428
+ method: "POST",
429
+ headers: { "Content-Type": "application/json" },
430
+ body: JSON.stringify(requestBody),
431
+ credentials: "include"
432
+ });
433
+ if (!refreshResponse.ok) {
434
+ console.error("[SDK] Quote refresh failed:", await refreshResponse.text());
435
+ return null;
436
+ }
437
+ const refreshedData = await refreshResponse.json();
438
+ prepareResponse = refreshedData;
439
+ return {
440
+ intentOp: refreshedData.intentOp,
441
+ expiresAt: refreshedData.expiresAt,
442
+ challenge: refreshedData.challenge,
443
+ originMessages: refreshedData.originMessages,
444
+ transaction: refreshedData.transaction
445
+ };
446
+ } catch (error) {
447
+ console.error("[SDK] Quote refresh error:", error);
448
+ return null;
449
+ }
450
+ }
451
+ );
358
452
  if (!signingResult.success) {
359
453
  return {
360
454
  success: false,
361
- intentId: prepareResponse.intentId,
455
+ intentId: "",
456
+ // No intentId yet - signing was cancelled before execute
362
457
  status: "failed",
363
458
  error: signingResult.error
364
459
  };
365
460
  }
461
+ const dialogExecutedIntent = "intentId" in signingResult && signingResult.intentId;
366
462
  let executeResponse;
367
- try {
368
- const response = await fetch(`${this.config.providerUrl}/api/intent/execute`, {
369
- method: "POST",
370
- headers: {
371
- "Content-Type": "application/json"
372
- },
373
- body: JSON.stringify({
374
- intentId: prepareResponse.intentId,
375
- signature: signingResult.signature,
376
- passkey: signingResult.passkey
377
- // Include passkey info for signature encoding
378
- })
379
- });
380
- if (!response.ok) {
381
- const errorData = await response.json().catch(() => ({}));
463
+ if (dialogExecutedIntent) {
464
+ executeResponse = {
465
+ success: true,
466
+ intentId: signingResult.intentId,
467
+ status: "pending"
468
+ };
469
+ } else {
470
+ try {
471
+ const response = await fetch(`${this.config.providerUrl}/api/intent/execute`, {
472
+ method: "POST",
473
+ headers: {
474
+ "Content-Type": "application/json"
475
+ },
476
+ body: JSON.stringify({
477
+ // Data from prepare response (no intentId yet - created on execute)
478
+ intentOp: prepareResponse.intentOp,
479
+ userId: prepareResponse.userId,
480
+ targetChain: prepareResponse.targetChain,
481
+ calls: prepareResponse.calls,
482
+ expiresAt: prepareResponse.expiresAt,
483
+ // Signature from dialog
484
+ signature: signingResult.signature,
485
+ passkey: signingResult.passkey
486
+ // Include passkey info for signature encoding
487
+ })
488
+ });
489
+ if (!response.ok) {
490
+ const errorData = await response.json().catch(() => ({}));
491
+ this.sendTransactionStatus(iframe, "failed");
492
+ await this.waitForDialogClose(dialog, cleanup);
493
+ return {
494
+ success: false,
495
+ intentId: "",
496
+ // No intentId - execute failed before creation
497
+ status: "failed",
498
+ error: {
499
+ code: "EXECUTE_FAILED",
500
+ message: errorData.error || "Failed to execute intent"
501
+ }
502
+ };
503
+ }
504
+ executeResponse = await response.json();
505
+ } catch (error) {
382
506
  this.sendTransactionStatus(iframe, "failed");
383
507
  await this.waitForDialogClose(dialog, cleanup);
384
508
  return {
385
509
  success: false,
386
- intentId: prepareResponse.intentId,
510
+ intentId: "",
511
+ // No intentId - network error before creation
387
512
  status: "failed",
388
513
  error: {
389
- code: "EXECUTE_FAILED",
390
- message: errorData.error || "Failed to execute intent"
514
+ code: "NETWORK_ERROR",
515
+ message: error instanceof Error ? error.message : "Network error"
391
516
  }
392
517
  };
393
518
  }
394
- executeResponse = await response.json();
395
- } catch (error) {
396
- this.sendTransactionStatus(iframe, "failed");
397
- await this.waitForDialogClose(dialog, cleanup);
398
- return {
399
- success: false,
400
- intentId: prepareResponse.intentId,
401
- status: "failed",
402
- error: {
403
- code: "NETWORK_ERROR",
404
- message: error instanceof Error ? error.message : "Network error"
405
- }
406
- };
407
519
  }
408
- const closeOn = options.closeOn || "preconfirmed";
409
- const acceptPreconfirmations = closeOn !== "completed";
410
520
  let finalStatus = executeResponse.status;
411
521
  let finalTxHash = executeResponse.transactionHash;
412
522
  if (finalStatus === "pending") {
413
- this.sendTransactionStatus(iframe, "processing");
414
- try {
415
- const waitResponse = await fetch(
416
- `${this.config.providerUrl}/api/intent/wait/${prepareResponse.intentId}?preconfirm=${acceptPreconfirmations}`,
417
- {
418
- headers: {
419
- "x-client-id": this.config.clientId
523
+ this.sendTransactionStatus(iframe, "pending");
524
+ const maxAttempts = 120;
525
+ const pollIntervalMs = 1500;
526
+ let lastStatus = "pending";
527
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
528
+ try {
529
+ const statusResponse = await fetch(
530
+ `${this.config.providerUrl}/api/intent/status/${executeResponse.intentId}`,
531
+ {
532
+ method: "GET",
533
+ headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
534
+ }
535
+ );
536
+ if (statusResponse.ok) {
537
+ const statusResult = await statusResponse.json();
538
+ finalStatus = statusResult.status;
539
+ finalTxHash = statusResult.transactionHash;
540
+ if (finalStatus !== lastStatus) {
541
+ this.sendTransactionStatus(iframe, finalStatus, finalTxHash);
542
+ lastStatus = finalStatus;
543
+ }
544
+ const closeOn2 = options.closeOn || "preconfirmed";
545
+ const successStatuses2 = {
546
+ claimed: ["claimed", "preconfirmed", "filled", "completed"],
547
+ preconfirmed: ["preconfirmed", "filled", "completed"],
548
+ filled: ["filled", "completed"],
549
+ completed: ["completed"]
550
+ };
551
+ const isTerminal = finalStatus === "failed" || finalStatus === "expired";
552
+ const isSuccess = successStatuses2[closeOn2]?.includes(finalStatus) ?? false;
553
+ if (isTerminal || isSuccess) {
554
+ break;
420
555
  }
421
556
  }
422
- );
423
- if (waitResponse.ok) {
424
- const waitResult = await waitResponse.json();
425
- finalStatus = waitResult.status === "preconfirmed" || waitResult.status === "completed" ? "completed" : waitResult.status;
426
- finalTxHash = waitResult.transactionHash;
427
- } else {
428
- console.error("Wait endpoint failed:", await waitResponse.text());
557
+ } catch (pollError) {
558
+ console.error("Failed to poll intent status:", pollError);
429
559
  }
430
- } catch (waitError) {
431
- console.error("Failed to wait for intent:", waitError);
560
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
432
561
  }
433
562
  }
434
- const displayStatus = finalStatus === "completed" ? "confirmed" : finalStatus;
563
+ const closeOn = options.closeOn || "preconfirmed";
564
+ const successStatuses = {
565
+ claimed: ["claimed", "preconfirmed", "filled", "completed"],
566
+ preconfirmed: ["preconfirmed", "filled", "completed"],
567
+ filled: ["filled", "completed"],
568
+ completed: ["completed"]
569
+ };
570
+ const isSuccessStatus = successStatuses[closeOn]?.includes(finalStatus) ?? false;
571
+ const displayStatus = isSuccessStatus ? "confirmed" : finalStatus;
435
572
  this.sendTransactionStatus(iframe, displayStatus, finalTxHash);
436
573
  await this.waitForDialogClose(dialog, cleanup);
437
574
  if (options.waitForHash && !finalTxHash) {
438
- const hash = await this.waitForTransactionHash(prepareResponse.intentId, {
575
+ const hash = await this.waitForTransactionHash(executeResponse.intentId, {
439
576
  timeoutMs: options.hashTimeoutMs,
440
577
  intervalMs: options.hashIntervalMs
441
578
  });
@@ -446,7 +583,7 @@ var OneAuthClient = class {
446
583
  finalStatus = "failed";
447
584
  return {
448
585
  success: false,
449
- intentId: prepareResponse.intentId,
586
+ intentId: executeResponse.intentId,
450
587
  status: finalStatus,
451
588
  transactionHash: finalTxHash,
452
589
  operationId: executeResponse.operationId,
@@ -458,14 +595,204 @@ var OneAuthClient = class {
458
595
  }
459
596
  }
460
597
  return {
461
- success: finalStatus === "completed",
462
- intentId: prepareResponse.intentId,
598
+ success: isSuccessStatus,
599
+ intentId: executeResponse.intentId,
463
600
  status: finalStatus,
464
601
  transactionHash: finalTxHash,
465
602
  operationId: executeResponse.operationId,
466
603
  error: executeResponse.error
467
604
  };
468
605
  }
606
+ /**
607
+ * Send a batch of intents for multi-chain execution with a single passkey tap.
608
+ *
609
+ * This method prepares multiple intents, shows a paginated review,
610
+ * and signs all intents with a single passkey tap via a shared merkle tree.
611
+ *
612
+ * @example
613
+ * ```typescript
614
+ * const result = await client.sendBatchIntent({
615
+ * username: 'alice',
616
+ * intents: [
617
+ * {
618
+ * targetChain: 8453, // Base
619
+ * calls: [{ to: '0x...', data: '0x...', label: 'Swap on Base' }],
620
+ * },
621
+ * {
622
+ * targetChain: 42161, // Arbitrum
623
+ * calls: [{ to: '0x...', data: '0x...', label: 'Mint on Arbitrum' }],
624
+ * },
625
+ * ],
626
+ * });
627
+ *
628
+ * if (result.success) {
629
+ * console.log(`${result.successCount} intents submitted`);
630
+ * }
631
+ * ```
632
+ */
633
+ async sendBatchIntent(options) {
634
+ if (!options.username) {
635
+ return {
636
+ success: false,
637
+ results: [],
638
+ successCount: 0,
639
+ failureCount: 0
640
+ };
641
+ }
642
+ if (!options.intents?.length) {
643
+ return {
644
+ success: false,
645
+ results: [],
646
+ successCount: 0,
647
+ failureCount: 0
648
+ };
649
+ }
650
+ const serializedIntents = options.intents.map((intent) => ({
651
+ targetChain: intent.targetChain,
652
+ calls: intent.calls,
653
+ tokenRequests: intent.tokenRequests?.map((r) => ({
654
+ token: r.token,
655
+ amount: r.amount.toString()
656
+ })),
657
+ sourceAssets: intent.sourceAssets,
658
+ sourceChainId: intent.sourceChainId
659
+ }));
660
+ const requestBody = {
661
+ username: options.username,
662
+ intents: serializedIntents,
663
+ ...this.config.clientId && { clientId: this.config.clientId }
664
+ };
665
+ let prepareResponse;
666
+ try {
667
+ const response = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
668
+ method: "POST",
669
+ headers: { "Content-Type": "application/json" },
670
+ body: JSON.stringify(requestBody)
671
+ });
672
+ if (!response.ok) {
673
+ const errorData = await response.json().catch(() => ({}));
674
+ const errorMessage = errorData.error || "Failed to prepare batch intent";
675
+ if (errorMessage.includes("User not found")) {
676
+ localStorage.removeItem("1auth-user");
677
+ }
678
+ return {
679
+ success: false,
680
+ results: [],
681
+ successCount: 0,
682
+ failureCount: 0
683
+ };
684
+ }
685
+ prepareResponse = await response.json();
686
+ } catch {
687
+ return {
688
+ success: false,
689
+ results: [],
690
+ successCount: 0,
691
+ failureCount: 0
692
+ };
693
+ }
694
+ const dialogUrl = this.getDialogUrl();
695
+ const themeParams = this.getThemeParams();
696
+ const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
697
+ const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
698
+ const dialogOrigin = this.getDialogOrigin();
699
+ const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
700
+ mode: "iframe",
701
+ batchMode: true,
702
+ batchIntents: prepareResponse.intents,
703
+ challenge: prepareResponse.challenge,
704
+ username: options.username,
705
+ accountAddress: prepareResponse.accountAddress,
706
+ userId: prepareResponse.userId,
707
+ expiresAt: prepareResponse.expiresAt
708
+ });
709
+ if (!ready) {
710
+ return {
711
+ success: false,
712
+ results: [],
713
+ successCount: 0,
714
+ failureCount: 0
715
+ };
716
+ }
717
+ const batchResult = await new Promise((resolve) => {
718
+ const handleMessage = async (event) => {
719
+ if (event.origin !== dialogOrigin) return;
720
+ const message = event.data;
721
+ if (message?.type === "PASSKEY_REFRESH_QUOTE") {
722
+ console.log("[SDK] Batch dialog requested quote refresh, re-preparing all intents");
723
+ try {
724
+ const refreshResponse = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
725
+ method: "POST",
726
+ headers: { "Content-Type": "application/json" },
727
+ body: JSON.stringify(requestBody)
728
+ });
729
+ if (refreshResponse.ok) {
730
+ const refreshed = await refreshResponse.json();
731
+ prepareResponse = refreshed;
732
+ iframe.contentWindow?.postMessage({
733
+ type: "PASSKEY_REFRESH_COMPLETE",
734
+ batchIntents: refreshed.intents,
735
+ challenge: refreshed.challenge,
736
+ expiresAt: refreshed.expiresAt
737
+ }, dialogOrigin);
738
+ } else {
739
+ iframe.contentWindow?.postMessage({
740
+ type: "PASSKEY_REFRESH_ERROR",
741
+ error: "Failed to refresh batch quotes"
742
+ }, dialogOrigin);
743
+ }
744
+ } catch {
745
+ iframe.contentWindow?.postMessage({
746
+ type: "PASSKEY_REFRESH_ERROR",
747
+ error: "Failed to refresh batch quotes"
748
+ }, dialogOrigin);
749
+ }
750
+ return;
751
+ }
752
+ if (message?.type === "PASSKEY_SIGNING_RESULT") {
753
+ window.removeEventListener("message", handleMessage);
754
+ if (message.success && message.data?.batchResults) {
755
+ const rawResults = message.data.batchResults;
756
+ const results = rawResults.map((r) => ({
757
+ index: r.index,
758
+ success: r.success ?? r.status !== "FAILED",
759
+ intentId: r.intentId || r.operationId || "",
760
+ status: r.status === "FAILED" ? "failed" : "pending",
761
+ error: r.error ? { code: "EXECUTE_FAILED", message: r.error } : void 0
762
+ }));
763
+ const successCount = results.filter((r) => r.success).length;
764
+ await this.waitForDialogClose(dialog, cleanup);
765
+ resolve({
766
+ success: successCount === results.length,
767
+ results,
768
+ successCount,
769
+ failureCount: results.length - successCount
770
+ });
771
+ } else {
772
+ cleanup();
773
+ resolve({
774
+ success: false,
775
+ results: [],
776
+ successCount: 0,
777
+ failureCount: 0
778
+ });
779
+ }
780
+ }
781
+ if (message?.type === "PASSKEY_CLOSE") {
782
+ window.removeEventListener("message", handleMessage);
783
+ cleanup();
784
+ resolve({
785
+ success: false,
786
+ results: [],
787
+ successCount: 0,
788
+ failureCount: 0
789
+ });
790
+ }
791
+ };
792
+ window.addEventListener("message", handleMessage);
793
+ });
794
+ return batchResult;
795
+ }
469
796
  /**
470
797
  * Send transaction status to the dialog iframe
471
798
  */
@@ -542,7 +869,77 @@ var OneAuthClient = class {
542
869
  const payload = message?.data;
543
870
  if (message?.type === "PASSKEY_SIGNING_RESULT") {
544
871
  window.removeEventListener("message", handleMessage);
545
- if (message.success && payload?.signature) {
872
+ if (message.success && payload?.intentId) {
873
+ resolve({
874
+ success: true,
875
+ intentId: payload.intentId
876
+ });
877
+ } else if (message.success && payload?.signature) {
878
+ resolve({
879
+ success: true,
880
+ signature: payload.signature,
881
+ passkey: payload.passkey,
882
+ signedHash: payload.signedHash
883
+ });
884
+ } else {
885
+ resolve({
886
+ success: false,
887
+ error: message.error || {
888
+ code: "SIGNING_FAILED",
889
+ message: "Signing failed"
890
+ }
891
+ });
892
+ }
893
+ } else if (message?.type === "PASSKEY_CLOSE") {
894
+ window.removeEventListener("message", handleMessage);
895
+ cleanup();
896
+ resolve({
897
+ success: false,
898
+ error: {
899
+ code: "USER_REJECTED",
900
+ message: "User closed the dialog"
901
+ }
902
+ });
903
+ }
904
+ };
905
+ window.addEventListener("message", handleMessage);
906
+ });
907
+ }
908
+ /**
909
+ * Wait for signing result with auto-refresh support
910
+ * This method handles both signing results and quote refresh requests from the dialog
911
+ */
912
+ waitForSigningWithRefresh(dialog, iframe, cleanup, dialogOrigin, onRefresh) {
913
+ console.log("[SDK] waitForSigningWithRefresh, expecting origin:", dialogOrigin);
914
+ return new Promise((resolve) => {
915
+ const handleMessage = async (event) => {
916
+ if (event.origin !== dialogOrigin) return;
917
+ const message = event.data;
918
+ if (message?.type === "PASSKEY_REFRESH_QUOTE") {
919
+ console.log("[SDK] Received quote refresh request from dialog");
920
+ const refreshedData = await onRefresh();
921
+ if (refreshedData) {
922
+ iframe.contentWindow?.postMessage({
923
+ type: "PASSKEY_REFRESH_COMPLETE",
924
+ ...refreshedData
925
+ }, dialogOrigin);
926
+ } else {
927
+ iframe.contentWindow?.postMessage({
928
+ type: "PASSKEY_REFRESH_ERROR",
929
+ error: "Failed to refresh quote"
930
+ }, dialogOrigin);
931
+ }
932
+ return;
933
+ }
934
+ const payload = message?.data;
935
+ if (message?.type === "PASSKEY_SIGNING_RESULT") {
936
+ window.removeEventListener("message", handleMessage);
937
+ if (message.success && payload?.intentId) {
938
+ resolve({
939
+ success: true,
940
+ intentId: payload.intentId
941
+ });
942
+ } else if (message.success && payload?.signature) {
546
943
  resolve({
547
944
  success: true,
548
945
  signature: payload.signature,
@@ -608,9 +1005,7 @@ var OneAuthClient = class {
608
1005
  const response = await fetch(
609
1006
  `${this.config.providerUrl}/api/intent/status/${intentId}`,
610
1007
  {
611
- headers: {
612
- "x-client-id": this.config.clientId
613
- }
1008
+ headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
614
1009
  }
615
1010
  );
616
1011
  if (!response.ok) {
@@ -645,6 +1040,43 @@ var OneAuthClient = class {
645
1040
  };
646
1041
  }
647
1042
  }
1043
+ /**
1044
+ * Get the history of intents for the authenticated user.
1045
+ *
1046
+ * Requires an active session (user must be logged in).
1047
+ *
1048
+ * @example
1049
+ * ```typescript
1050
+ * // Get recent intents
1051
+ * const history = await client.getIntentHistory({ limit: 10 });
1052
+ *
1053
+ * // Filter by status
1054
+ * const pending = await client.getIntentHistory({ status: 'pending' });
1055
+ *
1056
+ * // Filter by date range
1057
+ * const lastWeek = await client.getIntentHistory({
1058
+ * from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
1059
+ * });
1060
+ * ```
1061
+ */
1062
+ async getIntentHistory(options) {
1063
+ const queryParams = new URLSearchParams();
1064
+ if (options?.limit) queryParams.set("limit", String(options.limit));
1065
+ if (options?.offset) queryParams.set("offset", String(options.offset));
1066
+ if (options?.status) queryParams.set("status", options.status);
1067
+ if (options?.from) queryParams.set("from", options.from);
1068
+ if (options?.to) queryParams.set("to", options.to);
1069
+ const url = `${this.config.providerUrl}/api/intent/history${queryParams.toString() ? `?${queryParams}` : ""}`;
1070
+ const response = await fetch(url, {
1071
+ headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {},
1072
+ credentials: "include"
1073
+ });
1074
+ if (!response.ok) {
1075
+ const errorData = await response.json().catch(() => ({}));
1076
+ throw new Error(errorData.error || "Failed to get intent history");
1077
+ }
1078
+ return response.json();
1079
+ }
648
1080
  /**
649
1081
  * Send a swap intent through the Rhinestone orchestrator
650
1082
  *
@@ -707,17 +1139,21 @@ var OneAuthClient = class {
707
1139
  };
708
1140
  }
709
1141
  };
710
- const fromTokenResult = resolveToken(options.fromToken, "fromToken");
711
- if (!fromTokenResult.address) {
712
- return {
713
- success: false,
714
- intentId: "",
715
- status: "failed",
716
- error: {
717
- code: "INVALID_TOKEN",
718
- message: fromTokenResult.error || `Unknown fromToken: ${options.fromToken}`
719
- }
720
- };
1142
+ let fromTokenAddress;
1143
+ if (options.fromToken) {
1144
+ const fromTokenResult = resolveToken(options.fromToken, "fromToken");
1145
+ if (!fromTokenResult.address) {
1146
+ return {
1147
+ success: false,
1148
+ intentId: "",
1149
+ status: "failed",
1150
+ error: {
1151
+ code: "INVALID_TOKEN",
1152
+ message: fromTokenResult.error || `Unknown fromToken: ${options.fromToken}`
1153
+ }
1154
+ };
1155
+ }
1156
+ fromTokenAddress = fromTokenResult.address;
721
1157
  }
722
1158
  const toTokenResult = resolveToken(options.toToken, "toToken");
723
1159
  if (!toTokenResult.address) {
@@ -731,18 +1167,17 @@ var OneAuthClient = class {
731
1167
  }
732
1168
  };
733
1169
  }
734
- const fromTokenAddress = fromTokenResult.address;
735
1170
  const toTokenAddress = toTokenResult.address;
736
1171
  console.log("[SDK sendSwap] Token resolution:", {
737
- fromToken: options.fromToken,
738
- fromTokenAddress,
1172
+ fromToken: options.fromToken ?? "Any",
1173
+ fromTokenAddress: fromTokenAddress ?? "orchestrator picks",
739
1174
  toToken: options.toToken,
740
1175
  toTokenAddress,
741
1176
  targetChain: options.targetChain
742
1177
  });
743
1178
  const formatTokenLabel = (token, fallback) => {
744
1179
  if (!token.startsWith("0x")) {
745
- return token.toUpperCase();
1180
+ return token;
746
1181
  }
747
1182
  try {
748
1183
  return getTokenSymbol(token, options.targetChain);
@@ -750,15 +1185,11 @@ var OneAuthClient = class {
750
1185
  return fallback;
751
1186
  }
752
1187
  };
753
- const fromSymbol = formatTokenLabel(
754
- options.fromToken,
755
- `${options.fromToken.slice(0, 6)}...${options.fromToken.slice(-4)}`
756
- );
757
1188
  const toSymbol = formatTokenLabel(
758
1189
  options.toToken,
759
1190
  `${options.toToken.slice(0, 6)}...${options.toToken.slice(-4)}`
760
1191
  );
761
- const isFromNativeEth = fromTokenAddress === "0x0000000000000000000000000000000000000000";
1192
+ const isFromNativeEth = fromTokenAddress ? fromTokenAddress === "0x0000000000000000000000000000000000000000" : false;
762
1193
  const isToNativeEth = toTokenAddress === "0x0000000000000000000000000000000000000000";
763
1194
  const KNOWN_DECIMALS = {
764
1195
  ETH: 18,
@@ -768,31 +1199,33 @@ var OneAuthClient = class {
768
1199
  USDT0: 6
769
1200
  };
770
1201
  const getDecimals = (symbol, chainId) => {
771
- const upperSymbol = symbol.toUpperCase();
772
1202
  try {
773
- const decimals = getTokenDecimals(upperSymbol, chainId);
774
- console.log(`[SDK] getTokenDecimals(${upperSymbol}, ${chainId}) = ${decimals}`);
1203
+ const match = getSupportedTokens(chainId).find(
1204
+ (t) => t.symbol.toUpperCase() === symbol.toUpperCase()
1205
+ );
1206
+ if (match) {
1207
+ console.log(`[SDK] getTokenDecimals(${match.symbol}, ${chainId}) = ${match.decimals}`);
1208
+ return match.decimals;
1209
+ }
1210
+ const decimals = getTokenDecimals(symbol, chainId);
1211
+ console.log(`[SDK] getTokenDecimals(${symbol}, ${chainId}) = ${decimals}`);
775
1212
  return decimals;
776
1213
  } catch (e) {
777
- console.warn(`[SDK] getTokenDecimals failed for ${upperSymbol}, using fallback`, e);
1214
+ const upperSymbol = symbol.toUpperCase();
1215
+ console.warn(`[SDK] getTokenDecimals failed for ${symbol}, using fallback`, e);
778
1216
  return KNOWN_DECIMALS[upperSymbol] ?? 18;
779
1217
  }
780
1218
  };
781
- const fromDecimals = getDecimals(options.fromToken, options.targetChain);
782
1219
  const toDecimals = getDecimals(options.toToken, options.targetChain);
783
- const isBridge = options.fromToken.toUpperCase() === options.toToken.toUpperCase();
784
- let tokenRequests;
785
- if (!isToNativeEth) {
786
- tokenRequests = [{
787
- token: toTokenAddress,
788
- amount: parseUnits(options.amount, toDecimals).toString()
789
- }];
790
- }
1220
+ const isBridge = options.fromToken ? options.fromToken.toUpperCase() === options.toToken.toUpperCase() : false;
1221
+ const tokenRequests = [{
1222
+ token: toTokenAddress,
1223
+ amount: parseUnits(options.amount, toDecimals)
1224
+ }];
791
1225
  console.log("[SDK sendSwap] Building intent:", {
792
1226
  isBridge,
793
1227
  isFromNativeEth,
794
1228
  isToNativeEth,
795
- fromDecimals,
796
1229
  toDecimals,
797
1230
  tokenRequests
798
1231
  });
@@ -803,15 +1236,20 @@ var OneAuthClient = class {
803
1236
  {
804
1237
  // Minimal call - just signals to orchestrator we want the tokenRequests delivered
805
1238
  to: toTokenAddress,
806
- value: "0"
1239
+ value: "0",
1240
+ // SDK provides labels so dialog shows "Buy ETH" not "Send ETH / To: 0x000..."
1241
+ label: `Buy ${toSymbol}`,
1242
+ sublabel: `${options.amount} ${toSymbol}`
807
1243
  }
808
1244
  ],
809
1245
  // Request specific output tokens - this is what actually matters for swaps
810
1246
  tokenRequests,
811
1247
  // Constrain orchestrator to use only the fromToken as input
812
1248
  // This ensures the swap uses the correct source token
813
- // Pass the symbol (not address) so orchestrator can resolve per-chain
814
- sourceAssets: options.sourceAssets || [options.fromToken.toUpperCase()],
1249
+ // Use canonical symbol casing from registry (e.g. "MockUSD" not "MOCKUSD")
1250
+ sourceAssets: options.sourceAssets || (options.fromToken ? [options.fromToken] : void 0),
1251
+ // Pass source chain ID so orchestrator knows which chain to look for tokens on
1252
+ sourceChainId: options.sourceChainId,
815
1253
  closeOn: options.closeOn || "preconfirmed",
816
1254
  waitForHash: options.waitForHash,
817
1255
  hashTimeoutMs: options.hashTimeoutMs,
@@ -820,7 +1258,7 @@ var OneAuthClient = class {
820
1258
  return {
821
1259
  ...result,
822
1260
  quote: result.success ? {
823
- fromToken: fromTokenAddress,
1261
+ fromToken: fromTokenAddress ?? options.fromToken,
824
1262
  toToken: toTokenAddress,
825
1263
  amountIn: options.amount,
826
1264
  amountOut: "",
@@ -862,26 +1300,23 @@ var OneAuthClient = class {
862
1300
  const themeParams = this.getThemeParams(options?.theme);
863
1301
  const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
864
1302
  const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
865
- const dialogOrigin = this.getDialogOrigin();
866
- await new Promise((resolve) => {
867
- const handleReady = (event) => {
868
- if (event.origin !== dialogOrigin) return;
869
- if (event.data?.type === "PASSKEY_READY") {
870
- window.removeEventListener("message", handleReady);
871
- iframe.contentWindow?.postMessage({
872
- type: "PASSKEY_INIT",
873
- mode: "iframe",
874
- message: options.message,
875
- challenge: options.challenge || options.message,
876
- username: options.username,
877
- description: options.description,
878
- metadata: options.metadata
879
- }, dialogOrigin);
880
- resolve();
1303
+ const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
1304
+ mode: "iframe",
1305
+ message: options.message,
1306
+ challenge: options.challenge || options.message,
1307
+ username: options.username,
1308
+ description: options.description,
1309
+ metadata: options.metadata
1310
+ });
1311
+ if (!ready) {
1312
+ return {
1313
+ success: false,
1314
+ error: {
1315
+ code: "USER_REJECTED",
1316
+ message: "User closed the dialog"
881
1317
  }
882
1318
  };
883
- window.addEventListener("message", handleReady);
884
- });
1319
+ }
885
1320
  const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
886
1321
  cleanup();
887
1322
  if (signingResult.success) {
@@ -950,31 +1385,28 @@ var OneAuthClient = class {
950
1385
  const themeParams = this.getThemeParams(options?.theme);
951
1386
  const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
952
1387
  const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
953
- const dialogOrigin = this.getDialogOrigin();
954
- await new Promise((resolve) => {
955
- const handleReady = (event) => {
956
- if (event.origin !== dialogOrigin) return;
957
- if (event.data?.type === "PASSKEY_READY") {
958
- window.removeEventListener("message", handleReady);
959
- iframe.contentWindow?.postMessage({
960
- type: "PASSKEY_INIT",
961
- mode: "iframe",
962
- signingMode: "typedData",
963
- typedData: {
964
- domain: options.domain,
965
- types: options.types,
966
- primaryType: options.primaryType,
967
- message: options.message
968
- },
969
- challenge: signedHash,
970
- username: options.username,
971
- description: options.description
972
- }, dialogOrigin);
973
- resolve();
1388
+ const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
1389
+ mode: "iframe",
1390
+ signingMode: "typedData",
1391
+ typedData: {
1392
+ domain: options.domain,
1393
+ types: options.types,
1394
+ primaryType: options.primaryType,
1395
+ message: options.message
1396
+ },
1397
+ challenge: signedHash,
1398
+ username: options.username,
1399
+ description: options.description
1400
+ });
1401
+ if (!ready) {
1402
+ return {
1403
+ success: false,
1404
+ error: {
1405
+ code: "USER_REJECTED",
1406
+ message: "User closed the dialog"
974
1407
  }
975
1408
  };
976
- window.addEventListener("message", handleReady);
977
- });
1409
+ }
978
1410
  const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
979
1411
  cleanup();
980
1412
  if (signingResult.success) {
@@ -1128,9 +1560,7 @@ var OneAuthClient = class {
1128
1560
  const response = await fetch(
1129
1561
  `${this.config.providerUrl}/api/users/${encodeURIComponent(username)}/passkeys`,
1130
1562
  {
1131
- headers: {
1132
- "x-client-id": this.config.clientId
1133
- }
1563
+ headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
1134
1564
  }
1135
1565
  );
1136
1566
  if (!response.ok) {
@@ -1149,7 +1579,7 @@ var OneAuthClient = class {
1149
1579
  "Content-Type": "application/json"
1150
1580
  },
1151
1581
  body: JSON.stringify({
1152
- clientId: this.config.clientId,
1582
+ ...this.config.clientId && { clientId: this.config.clientId },
1153
1583
  username: options.username,
1154
1584
  challenge: options.challenge,
1155
1585
  description: options.description,
@@ -1175,6 +1605,44 @@ var OneAuthClient = class {
1175
1605
  `width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`
1176
1606
  );
1177
1607
  }
1608
+ /**
1609
+ * Wait for the dialog iframe to signal ready, then send init data.
1610
+ * Also handles early close (X button, escape, backdrop) during the ready phase.
1611
+ * Returns true if dialog is ready, false if it was closed before becoming ready.
1612
+ */
1613
+ waitForDialogReady(dialog, iframe, cleanup, initMessage) {
1614
+ const dialogOrigin = this.getDialogOrigin();
1615
+ return new Promise((resolve) => {
1616
+ let settled = false;
1617
+ const teardown = () => {
1618
+ if (settled) return;
1619
+ settled = true;
1620
+ window.removeEventListener("message", handleMessage);
1621
+ dialog.removeEventListener("close", handleClose);
1622
+ };
1623
+ const handleMessage = (event) => {
1624
+ if (event.origin !== dialogOrigin) return;
1625
+ if (event.data?.type === "PASSKEY_READY") {
1626
+ teardown();
1627
+ iframe.contentWindow?.postMessage({
1628
+ type: "PASSKEY_INIT",
1629
+ ...initMessage
1630
+ }, dialogOrigin);
1631
+ resolve(true);
1632
+ } else if (event.data?.type === "PASSKEY_CLOSE") {
1633
+ teardown();
1634
+ cleanup();
1635
+ resolve(false);
1636
+ }
1637
+ };
1638
+ const handleClose = () => {
1639
+ teardown();
1640
+ resolve(false);
1641
+ };
1642
+ window.addEventListener("message", handleMessage);
1643
+ dialog.addEventListener("close", handleClose);
1644
+ });
1645
+ }
1178
1646
  /**
1179
1647
  * Create a modal dialog with an iframe inside.
1180
1648
  */
@@ -1319,12 +1787,24 @@ var OneAuthClient = class {
1319
1787
  };
1320
1788
  return { dialog, iframe, cleanup };
1321
1789
  }
1322
- waitForModalAuthResponse(_dialog, _iframe, cleanup) {
1790
+ waitForModalAuthResponse(_dialog, iframe, cleanup) {
1323
1791
  const dialogOrigin = this.getDialogOrigin();
1324
1792
  return new Promise((resolve) => {
1793
+ let dialogReady = false;
1325
1794
  const handleMessage = (event) => {
1326
1795
  if (event.origin !== dialogOrigin) return;
1327
1796
  const data = event.data;
1797
+ if (data?.type === "PASSKEY_READY") {
1798
+ dialogReady = true;
1799
+ iframe.contentWindow?.postMessage({
1800
+ type: "PASSKEY_INIT",
1801
+ mode: "iframe"
1802
+ }, dialogOrigin);
1803
+ return;
1804
+ }
1805
+ if (!dialogReady && data?.type === "PASSKEY_CLOSE") {
1806
+ return;
1807
+ }
1328
1808
  if (data?.type === "PASSKEY_LOGIN_RESULT") {
1329
1809
  window.removeEventListener("message", handleMessage);
1330
1810
  cleanup();
@@ -1354,6 +1834,11 @@ var OneAuthClient = class {
1354
1834
  error: data.error
1355
1835
  });
1356
1836
  }
1837
+ } else if (data?.type === "PASSKEY_RETRY_POPUP") {
1838
+ window.removeEventListener("message", handleMessage);
1839
+ cleanup();
1840
+ const popupUrl = data.data?.url?.replace("mode=iframe", "mode=popup") || `${this.getDialogUrl()}/dialog/auth?mode=popup${this.config.clientId ? `&clientId=${this.config.clientId}` : ""}`;
1841
+ this.waitForPopupAuthResponse(popupUrl).then(resolve);
1357
1842
  } else if (data?.type === "PASSKEY_CLOSE") {
1358
1843
  window.removeEventListener("message", handleMessage);
1359
1844
  cleanup();
@@ -1369,6 +1854,77 @@ var OneAuthClient = class {
1369
1854
  window.addEventListener("message", handleMessage);
1370
1855
  });
1371
1856
  }
1857
+ /**
1858
+ * Open a popup for auth and wait for the result.
1859
+ * Used when iframe mode fails (e.g., due to password manager interference).
1860
+ */
1861
+ waitForPopupAuthResponse(url) {
1862
+ const dialogOrigin = this.getDialogOrigin();
1863
+ const popup = this.openPopup(url);
1864
+ return new Promise((resolve) => {
1865
+ const pollTimer = setInterval(() => {
1866
+ if (popup?.closed) {
1867
+ clearInterval(pollTimer);
1868
+ window.removeEventListener("message", handleMessage);
1869
+ resolve({
1870
+ success: false,
1871
+ error: {
1872
+ code: "USER_CANCELLED",
1873
+ message: "Authentication was cancelled"
1874
+ }
1875
+ });
1876
+ }
1877
+ }, 500);
1878
+ const handleMessage = (event) => {
1879
+ if (event.origin !== dialogOrigin) return;
1880
+ const data = event.data;
1881
+ if (data?.type === "PASSKEY_LOGIN_RESULT") {
1882
+ clearInterval(pollTimer);
1883
+ window.removeEventListener("message", handleMessage);
1884
+ popup?.close();
1885
+ if (data.success) {
1886
+ resolve({
1887
+ success: true,
1888
+ username: data.data?.username,
1889
+ user: data.data?.user
1890
+ });
1891
+ } else {
1892
+ resolve({
1893
+ success: false,
1894
+ error: data.error
1895
+ });
1896
+ }
1897
+ } else if (data?.type === "PASSKEY_REGISTER_RESULT") {
1898
+ clearInterval(pollTimer);
1899
+ window.removeEventListener("message", handleMessage);
1900
+ popup?.close();
1901
+ if (data.success) {
1902
+ resolve({
1903
+ success: true,
1904
+ username: data.data?.username
1905
+ });
1906
+ } else {
1907
+ resolve({
1908
+ success: false,
1909
+ error: data.error
1910
+ });
1911
+ }
1912
+ } else if (data?.type === "PASSKEY_CLOSE") {
1913
+ clearInterval(pollTimer);
1914
+ window.removeEventListener("message", handleMessage);
1915
+ popup?.close();
1916
+ resolve({
1917
+ success: false,
1918
+ error: {
1919
+ code: "USER_CANCELLED",
1920
+ message: "Authentication was cancelled"
1921
+ }
1922
+ });
1923
+ }
1924
+ };
1925
+ window.addEventListener("message", handleMessage);
1926
+ });
1927
+ }
1372
1928
  waitForAuthenticateResponse(_dialog, _iframe, cleanup) {
1373
1929
  const dialogOrigin = this.getDialogOrigin();
1374
1930
  return new Promise((resolve) => {
@@ -1408,6 +1964,44 @@ var OneAuthClient = class {
1408
1964
  window.addEventListener("message", handleMessage);
1409
1965
  });
1410
1966
  }
1967
+ waitForConnectResponse(_dialog, _iframe, cleanup) {
1968
+ const dialogOrigin = this.getDialogOrigin();
1969
+ return new Promise((resolve) => {
1970
+ const handleMessage = (event) => {
1971
+ if (event.origin !== dialogOrigin) return;
1972
+ const data = event.data;
1973
+ if (data?.type === "PASSKEY_CONNECT_RESULT") {
1974
+ window.removeEventListener("message", handleMessage);
1975
+ cleanup();
1976
+ if (data.success) {
1977
+ resolve({
1978
+ success: true,
1979
+ username: data.data?.username,
1980
+ autoConnected: data.data?.autoConnected
1981
+ });
1982
+ } else {
1983
+ resolve({
1984
+ success: false,
1985
+ action: data.data?.action,
1986
+ error: data.error
1987
+ });
1988
+ }
1989
+ } else if (data?.type === "PASSKEY_CLOSE") {
1990
+ window.removeEventListener("message", handleMessage);
1991
+ cleanup();
1992
+ resolve({
1993
+ success: false,
1994
+ action: "cancel",
1995
+ error: {
1996
+ code: "USER_CANCELLED",
1997
+ message: "Connection was cancelled"
1998
+ }
1999
+ });
2000
+ }
2001
+ };
2002
+ window.addEventListener("message", handleMessage);
2003
+ });
2004
+ }
1411
2005
  waitForModalSigningResponse(requestId, _dialog, _iframe, cleanup) {
1412
2006
  const dialogOrigin = this.getDialogOrigin();
1413
2007
  return new Promise((resolve) => {
@@ -1496,9 +2090,7 @@ var OneAuthClient = class {
1496
2090
  const response = await fetch(
1497
2091
  `${this.config.providerUrl}/api/sign/request/${requestId}`,
1498
2092
  {
1499
- headers: {
1500
- "x-client-id": this.config.clientId
1501
- }
2093
+ headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
1502
2094
  }
1503
2095
  );
1504
2096
  if (!response.ok) {
@@ -1640,6 +2232,9 @@ function createPasskeyWalletClient(config) {
1640
2232
  if (!result.success) {
1641
2233
  throw new Error(result.error?.message || "Signing failed");
1642
2234
  }
2235
+ if (!result.signature) {
2236
+ throw new Error("No signature received");
2237
+ }
1643
2238
  return encodeWebAuthnSignature(result.signature);
1644
2239
  };
1645
2240
  const signTransactionImpl = async (transaction) => {
@@ -1660,6 +2255,9 @@ function createPasskeyWalletClient(config) {
1660
2255
  if (!result.success) {
1661
2256
  throw new Error(result.error?.message || "Signing failed");
1662
2257
  }
2258
+ if (!result.signature) {
2259
+ throw new Error("No signature received");
2260
+ }
1663
2261
  return encodeWebAuthnSignature(result.signature);
1664
2262
  };
1665
2263
  const signTypedDataImpl = async (typedData) => {
@@ -1681,6 +2279,9 @@ function createPasskeyWalletClient(config) {
1681
2279
  if (!result.success) {
1682
2280
  throw new Error(result.error?.message || "Signing failed");
1683
2281
  }
2282
+ if (!result.signature) {
2283
+ throw new Error("No signature received");
2284
+ }
1684
2285
  return encodeWebAuthnSignature(result.signature);
1685
2286
  };
1686
2287
  const buildIntentPayload = async (calls, targetChainOverride) => {
@@ -1749,11 +2350,12 @@ function createPasskeyWalletClient(config) {
1749
2350
  * Send multiple calls as a single batched transaction
1750
2351
  */
1751
2352
  async sendCalls(params) {
1752
- const { calls, chainId: targetChain } = params;
2353
+ const { calls, chainId: targetChain, tokenRequests } = params;
1753
2354
  const closeOn = config.waitForHash ?? true ? "completed" : "preconfirmed";
1754
2355
  const intentPayload = await buildIntentPayload(calls, targetChain);
1755
2356
  const result = await provider.sendIntent({
1756
2357
  ...intentPayload,
2358
+ tokenRequests,
1757
2359
  closeOn,
1758
2360
  waitForHash: config.waitForHash ?? true,
1759
2361
  hashTimeoutMs: config.hashTimeoutMs,
@@ -2197,9 +2799,10 @@ function BatchQueueWidget({ onSignAll }) {
2197
2799
 
2198
2800
  // src/verify.ts
2199
2801
  import { keccak256, toBytes } from "viem";
2200
- var PASSKEY_MESSAGE_PREFIX = "Passkey Signed Message:\n";
2802
+ var ETHEREUM_MESSAGE_PREFIX = "Ethereum Signed Message:\n";
2803
+ var PASSKEY_MESSAGE_PREFIX = ETHEREUM_MESSAGE_PREFIX;
2201
2804
  function hashMessage2(message) {
2202
- const prefixed = PASSKEY_MESSAGE_PREFIX + message.length.toString() + message;
2805
+ const prefixed = ETHEREUM_MESSAGE_PREFIX + message.length.toString() + message;
2203
2806
  return keccak256(toBytes(prefixed));
2204
2807
  }
2205
2808
  function verifyMessageHash(message, signedHash) {
@@ -2210,9 +2813,11 @@ function verifyMessageHash(message, signedHash) {
2210
2813
  export {
2211
2814
  BatchQueueProvider,
2212
2815
  BatchQueueWidget,
2816
+ ETHEREUM_MESSAGE_PREFIX,
2213
2817
  OneAuthClient,
2214
2818
  PASSKEY_MESSAGE_PREFIX,
2215
2819
  OneAuthClient as PasskeyProviderClient,
2820
+ createOneAuthProvider,
2216
2821
  createPasskeyAccount,
2217
2822
  createPasskeyProvider,
2218
2823
  createPasskeyWalletClient,