@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.js CHANGED
@@ -32,9 +32,11 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  BatchQueueProvider: () => BatchQueueProvider,
34
34
  BatchQueueWidget: () => BatchQueueWidget,
35
+ ETHEREUM_MESSAGE_PREFIX: () => ETHEREUM_MESSAGE_PREFIX,
35
36
  OneAuthClient: () => OneAuthClient,
36
37
  PASSKEY_MESSAGE_PREFIX: () => PASSKEY_MESSAGE_PREFIX,
37
38
  PasskeyProviderClient: () => OneAuthClient,
39
+ createOneAuthProvider: () => createOneAuthProvider,
38
40
  createPasskeyAccount: () => createPasskeyAccount,
39
41
  createPasskeyProvider: () => createPasskeyProvider,
40
42
  createPasskeyWalletClient: () => createPasskeyWalletClient,
@@ -156,7 +158,13 @@ function resolveTokenAddress(token, chainId) {
156
158
  if ((0, import_viem.isAddress)(token)) {
157
159
  return token;
158
160
  }
159
- return (0, import_sdk.getTokenAddress)(token.toUpperCase(), chainId);
161
+ const match = getSupportedTokens(chainId).find(
162
+ (t) => t.symbol.toUpperCase() === token.toUpperCase()
163
+ );
164
+ if (!match) {
165
+ return (0, import_sdk.getTokenAddress)(token, chainId);
166
+ }
167
+ return match.address;
160
168
  }
161
169
  function isTestnet(chainId) {
162
170
  try {
@@ -258,9 +266,7 @@ var OneAuthClient = class {
258
266
  const response = await fetch(
259
267
  `${this.config.providerUrl}/api/intent/status/${intentId}`,
260
268
  {
261
- headers: {
262
- "x-client-id": this.config.clientId
263
- }
269
+ headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
264
270
  }
265
271
  );
266
272
  if (response.ok) {
@@ -284,9 +290,11 @@ var OneAuthClient = class {
284
290
  async authWithModal(options) {
285
291
  const dialogUrl = this.getDialogUrl();
286
292
  const params = new URLSearchParams({
287
- clientId: this.config.clientId,
288
293
  mode: "iframe"
289
294
  });
295
+ if (this.config.clientId) {
296
+ params.set("clientId", this.config.clientId);
297
+ }
290
298
  if (options?.username) {
291
299
  params.set("username", options.username);
292
300
  }
@@ -302,6 +310,55 @@ var OneAuthClient = class {
302
310
  const { dialog, iframe, cleanup } = this.createModalDialog(url);
303
311
  return this.waitForModalAuthResponse(dialog, iframe, cleanup);
304
312
  }
313
+ /**
314
+ * Open the connect dialog (lightweight connection without passkey auth).
315
+ *
316
+ * This method shows a simple connection confirmation dialog that doesn't
317
+ * require a passkey signature. Users can optionally enable "auto-connect"
318
+ * to skip this dialog in the future.
319
+ *
320
+ * If the user has never connected before, this will return action: "switch"
321
+ * to indicate that the full auth modal should be opened instead.
322
+ *
323
+ * @example
324
+ * ```typescript
325
+ * const result = await client.connectWithModal();
326
+ *
327
+ * if (result.success) {
328
+ * console.log('Connected as:', result.username);
329
+ * } else if (result.action === 'switch') {
330
+ * // User needs to sign in first
331
+ * const authResult = await client.authWithModal();
332
+ * }
333
+ * ```
334
+ */
335
+ async connectWithModal(options) {
336
+ const dialogUrl = this.getDialogUrl();
337
+ const params = new URLSearchParams({
338
+ mode: "iframe"
339
+ });
340
+ if (this.config.clientId) {
341
+ params.set("clientId", this.config.clientId);
342
+ }
343
+ const themeParams = this.getThemeParams(options?.theme);
344
+ if (themeParams) {
345
+ const themeParsed = new URLSearchParams(themeParams);
346
+ themeParsed.forEach((value, key) => params.set(key, value));
347
+ }
348
+ const url = `${dialogUrl}/dialog/connect?${params.toString()}`;
349
+ const { dialog, iframe, cleanup } = this.createModalDialog(url);
350
+ const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
351
+ mode: "iframe"
352
+ });
353
+ if (!ready) {
354
+ return {
355
+ success: false,
356
+ action: "cancel",
357
+ error: { code: "USER_CANCELLED", message: "Connection was cancelled" }
358
+ };
359
+ }
360
+ return this.waitForConnectResponse(dialog, iframe, cleanup);
361
+ }
305
362
  /**
306
363
  * Authenticate a user with an optional challenge to sign.
307
364
  *
@@ -337,9 +394,11 @@ var OneAuthClient = class {
337
394
  async authenticate(options) {
338
395
  const dialogUrl = this.getDialogUrl();
339
396
  const params = new URLSearchParams({
340
- clientId: this.config.clientId,
341
397
  mode: "iframe"
342
398
  });
399
+ if (this.config.clientId) {
400
+ params.set("clientId", this.config.clientId);
401
+ }
343
402
  if (options?.challenge) {
344
403
  params.set("challenge", options.challenge);
345
404
  }
@@ -353,28 +412,30 @@ var OneAuthClient = class {
353
412
  return this.waitForAuthenticateResponse(dialog, iframe, cleanup);
354
413
  }
355
414
  /**
356
- * Show signing in a modal overlay (Porto-style iframe dialog)
415
+ * Show signing in a modal overlay (iframe dialog)
357
416
  */
358
417
  async signWithModal(options) {
359
418
  const dialogUrl = this.getDialogUrl();
360
419
  const themeParams = this.getThemeParams(options?.theme);
361
420
  const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
362
421
  const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
363
- const dialogOrigin = this.getDialogOrigin();
364
- await new Promise((resolve) => {
365
- iframe.onload = () => {
366
- iframe.contentWindow?.postMessage({
367
- type: "PASSKEY_INIT",
368
- mode: "iframe",
369
- challenge: options.challenge,
370
- username: options.username,
371
- description: options.description,
372
- transaction: options.transaction,
373
- metadata: options.metadata
374
- }, dialogOrigin);
375
- resolve();
376
- };
422
+ const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
423
+ mode: "iframe",
424
+ challenge: options.challenge,
425
+ username: options.username,
426
+ description: options.description,
427
+ transaction: options.transaction,
428
+ metadata: options.metadata
377
429
  });
430
+ if (!ready) {
431
+ return {
432
+ success: false,
433
+ error: {
434
+ code: "USER_REJECTED",
435
+ message: "User closed the dialog"
436
+ }
437
+ };
438
+ }
378
439
  return this.waitForSigningResponse(dialog, iframe, cleanup);
379
440
  }
380
441
  /**
@@ -447,16 +508,21 @@ var OneAuthClient = class {
447
508
  }
448
509
  };
449
510
  }
511
+ const serializedTokenRequests = options.tokenRequests?.map((r) => ({
512
+ token: r.token,
513
+ amount: r.amount.toString()
514
+ }));
450
515
  let prepareResponse;
516
+ const requestBody = signedIntent || {
517
+ username: options.username,
518
+ targetChain: options.targetChain,
519
+ calls: options.calls,
520
+ tokenRequests: serializedTokenRequests,
521
+ sourceAssets: options.sourceAssets,
522
+ sourceChainId: options.sourceChainId,
523
+ ...this.config.clientId && { clientId: this.config.clientId }
524
+ };
451
525
  try {
452
- const requestBody = signedIntent || {
453
- username: options.username,
454
- targetChain: options.targetChain,
455
- calls: options.calls,
456
- tokenRequests: options.tokenRequests,
457
- sourceAssets: options.sourceAssets,
458
- clientId: this.config.clientId
459
- };
460
526
  const response = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
461
527
  method: "POST",
462
528
  headers: {
@@ -493,112 +559,190 @@ var OneAuthClient = class {
493
559
  };
494
560
  }
495
561
  const dialogUrl = this.getDialogUrl();
496
- const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe`;
562
+ const themeParams = this.getThemeParams();
563
+ const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
497
564
  const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
498
565
  const dialogOrigin = this.getDialogOrigin();
499
- await new Promise((resolve) => {
500
- const handleReady = (event) => {
501
- if (event.origin !== dialogOrigin) return;
502
- if (event.data?.type === "PASSKEY_READY") {
503
- window.removeEventListener("message", handleReady);
504
- iframe.contentWindow?.postMessage({
505
- type: "PASSKEY_INIT",
506
- mode: "iframe",
507
- calls,
508
- chainId: targetChain,
509
- transaction: prepareResponse.transaction,
510
- challenge: prepareResponse.challenge,
511
- username,
512
- accountAddress: prepareResponse.accountAddress,
513
- intentId: prepareResponse.intentId
514
- }, dialogOrigin);
515
- resolve();
516
- }
517
- };
518
- window.addEventListener("message", handleReady);
566
+ const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
567
+ mode: "iframe",
568
+ calls,
569
+ chainId: targetChain,
570
+ transaction: prepareResponse.transaction,
571
+ challenge: prepareResponse.challenge,
572
+ username,
573
+ accountAddress: prepareResponse.accountAddress,
574
+ originMessages: prepareResponse.originMessages,
575
+ tokenRequests: serializedTokenRequests,
576
+ expiresAt: prepareResponse.expiresAt,
577
+ userId: prepareResponse.userId,
578
+ intentOp: prepareResponse.intentOp
519
579
  });
520
- const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
580
+ if (!ready) {
581
+ return {
582
+ success: false,
583
+ intentId: "",
584
+ status: "failed",
585
+ error: { code: "USER_CANCELLED", message: "User closed the dialog" }
586
+ };
587
+ }
588
+ const signingResult = await this.waitForSigningWithRefresh(
589
+ dialog,
590
+ iframe,
591
+ cleanup,
592
+ dialogOrigin,
593
+ // Refresh callback - called when dialog requests a quote refresh
594
+ async () => {
595
+ console.log("[SDK] Dialog requested quote refresh, re-preparing intent");
596
+ try {
597
+ const refreshResponse = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
598
+ method: "POST",
599
+ headers: { "Content-Type": "application/json" },
600
+ body: JSON.stringify(requestBody),
601
+ credentials: "include"
602
+ });
603
+ if (!refreshResponse.ok) {
604
+ console.error("[SDK] Quote refresh failed:", await refreshResponse.text());
605
+ return null;
606
+ }
607
+ const refreshedData = await refreshResponse.json();
608
+ prepareResponse = refreshedData;
609
+ return {
610
+ intentOp: refreshedData.intentOp,
611
+ expiresAt: refreshedData.expiresAt,
612
+ challenge: refreshedData.challenge,
613
+ originMessages: refreshedData.originMessages,
614
+ transaction: refreshedData.transaction
615
+ };
616
+ } catch (error) {
617
+ console.error("[SDK] Quote refresh error:", error);
618
+ return null;
619
+ }
620
+ }
621
+ );
521
622
  if (!signingResult.success) {
522
623
  return {
523
624
  success: false,
524
- intentId: prepareResponse.intentId,
625
+ intentId: "",
626
+ // No intentId yet - signing was cancelled before execute
525
627
  status: "failed",
526
628
  error: signingResult.error
527
629
  };
528
630
  }
631
+ const dialogExecutedIntent = "intentId" in signingResult && signingResult.intentId;
529
632
  let executeResponse;
530
- try {
531
- const response = await fetch(`${this.config.providerUrl}/api/intent/execute`, {
532
- method: "POST",
533
- headers: {
534
- "Content-Type": "application/json"
535
- },
536
- body: JSON.stringify({
537
- intentId: prepareResponse.intentId,
538
- signature: signingResult.signature,
539
- passkey: signingResult.passkey
540
- // Include passkey info for signature encoding
541
- })
542
- });
543
- if (!response.ok) {
544
- const errorData = await response.json().catch(() => ({}));
633
+ if (dialogExecutedIntent) {
634
+ executeResponse = {
635
+ success: true,
636
+ intentId: signingResult.intentId,
637
+ status: "pending"
638
+ };
639
+ } else {
640
+ try {
641
+ const response = await fetch(`${this.config.providerUrl}/api/intent/execute`, {
642
+ method: "POST",
643
+ headers: {
644
+ "Content-Type": "application/json"
645
+ },
646
+ body: JSON.stringify({
647
+ // Data from prepare response (no intentId yet - created on execute)
648
+ intentOp: prepareResponse.intentOp,
649
+ userId: prepareResponse.userId,
650
+ targetChain: prepareResponse.targetChain,
651
+ calls: prepareResponse.calls,
652
+ expiresAt: prepareResponse.expiresAt,
653
+ // Signature from dialog
654
+ signature: signingResult.signature,
655
+ passkey: signingResult.passkey
656
+ // Include passkey info for signature encoding
657
+ })
658
+ });
659
+ if (!response.ok) {
660
+ const errorData = await response.json().catch(() => ({}));
661
+ this.sendTransactionStatus(iframe, "failed");
662
+ await this.waitForDialogClose(dialog, cleanup);
663
+ return {
664
+ success: false,
665
+ intentId: "",
666
+ // No intentId - execute failed before creation
667
+ status: "failed",
668
+ error: {
669
+ code: "EXECUTE_FAILED",
670
+ message: errorData.error || "Failed to execute intent"
671
+ }
672
+ };
673
+ }
674
+ executeResponse = await response.json();
675
+ } catch (error) {
545
676
  this.sendTransactionStatus(iframe, "failed");
546
677
  await this.waitForDialogClose(dialog, cleanup);
547
678
  return {
548
679
  success: false,
549
- intentId: prepareResponse.intentId,
680
+ intentId: "",
681
+ // No intentId - network error before creation
550
682
  status: "failed",
551
683
  error: {
552
- code: "EXECUTE_FAILED",
553
- message: errorData.error || "Failed to execute intent"
684
+ code: "NETWORK_ERROR",
685
+ message: error instanceof Error ? error.message : "Network error"
554
686
  }
555
687
  };
556
688
  }
557
- executeResponse = await response.json();
558
- } catch (error) {
559
- this.sendTransactionStatus(iframe, "failed");
560
- await this.waitForDialogClose(dialog, cleanup);
561
- return {
562
- success: false,
563
- intentId: prepareResponse.intentId,
564
- status: "failed",
565
- error: {
566
- code: "NETWORK_ERROR",
567
- message: error instanceof Error ? error.message : "Network error"
568
- }
569
- };
570
689
  }
571
- const closeOn = options.closeOn || "preconfirmed";
572
- const acceptPreconfirmations = closeOn !== "completed";
573
690
  let finalStatus = executeResponse.status;
574
691
  let finalTxHash = executeResponse.transactionHash;
575
692
  if (finalStatus === "pending") {
576
- this.sendTransactionStatus(iframe, "processing");
577
- try {
578
- const waitResponse = await fetch(
579
- `${this.config.providerUrl}/api/intent/wait/${prepareResponse.intentId}?preconfirm=${acceptPreconfirmations}`,
580
- {
581
- headers: {
582
- "x-client-id": this.config.clientId
693
+ this.sendTransactionStatus(iframe, "pending");
694
+ const maxAttempts = 120;
695
+ const pollIntervalMs = 1500;
696
+ let lastStatus = "pending";
697
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
698
+ try {
699
+ const statusResponse = await fetch(
700
+ `${this.config.providerUrl}/api/intent/status/${executeResponse.intentId}`,
701
+ {
702
+ method: "GET",
703
+ headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
704
+ }
705
+ );
706
+ if (statusResponse.ok) {
707
+ const statusResult = await statusResponse.json();
708
+ finalStatus = statusResult.status;
709
+ finalTxHash = statusResult.transactionHash;
710
+ if (finalStatus !== lastStatus) {
711
+ this.sendTransactionStatus(iframe, finalStatus, finalTxHash);
712
+ lastStatus = finalStatus;
713
+ }
714
+ const closeOn2 = options.closeOn || "preconfirmed";
715
+ const successStatuses2 = {
716
+ claimed: ["claimed", "preconfirmed", "filled", "completed"],
717
+ preconfirmed: ["preconfirmed", "filled", "completed"],
718
+ filled: ["filled", "completed"],
719
+ completed: ["completed"]
720
+ };
721
+ const isTerminal = finalStatus === "failed" || finalStatus === "expired";
722
+ const isSuccess = successStatuses2[closeOn2]?.includes(finalStatus) ?? false;
723
+ if (isTerminal || isSuccess) {
724
+ break;
583
725
  }
584
726
  }
585
- );
586
- if (waitResponse.ok) {
587
- const waitResult = await waitResponse.json();
588
- finalStatus = waitResult.status === "preconfirmed" || waitResult.status === "completed" ? "completed" : waitResult.status;
589
- finalTxHash = waitResult.transactionHash;
590
- } else {
591
- console.error("Wait endpoint failed:", await waitResponse.text());
592
- }
593
- } catch (waitError) {
594
- console.error("Failed to wait for intent:", waitError);
727
+ } catch (pollError) {
728
+ console.error("Failed to poll intent status:", pollError);
729
+ }
730
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
595
731
  }
596
732
  }
597
- const displayStatus = finalStatus === "completed" ? "confirmed" : finalStatus;
733
+ const closeOn = options.closeOn || "preconfirmed";
734
+ const successStatuses = {
735
+ claimed: ["claimed", "preconfirmed", "filled", "completed"],
736
+ preconfirmed: ["preconfirmed", "filled", "completed"],
737
+ filled: ["filled", "completed"],
738
+ completed: ["completed"]
739
+ };
740
+ const isSuccessStatus = successStatuses[closeOn]?.includes(finalStatus) ?? false;
741
+ const displayStatus = isSuccessStatus ? "confirmed" : finalStatus;
598
742
  this.sendTransactionStatus(iframe, displayStatus, finalTxHash);
599
743
  await this.waitForDialogClose(dialog, cleanup);
600
744
  if (options.waitForHash && !finalTxHash) {
601
- const hash = await this.waitForTransactionHash(prepareResponse.intentId, {
745
+ const hash = await this.waitForTransactionHash(executeResponse.intentId, {
602
746
  timeoutMs: options.hashTimeoutMs,
603
747
  intervalMs: options.hashIntervalMs
604
748
  });
@@ -609,7 +753,7 @@ var OneAuthClient = class {
609
753
  finalStatus = "failed";
610
754
  return {
611
755
  success: false,
612
- intentId: prepareResponse.intentId,
756
+ intentId: executeResponse.intentId,
613
757
  status: finalStatus,
614
758
  transactionHash: finalTxHash,
615
759
  operationId: executeResponse.operationId,
@@ -621,14 +765,204 @@ var OneAuthClient = class {
621
765
  }
622
766
  }
623
767
  return {
624
- success: finalStatus === "completed",
625
- intentId: prepareResponse.intentId,
768
+ success: isSuccessStatus,
769
+ intentId: executeResponse.intentId,
626
770
  status: finalStatus,
627
771
  transactionHash: finalTxHash,
628
772
  operationId: executeResponse.operationId,
629
773
  error: executeResponse.error
630
774
  };
631
775
  }
776
+ /**
777
+ * Send a batch of intents for multi-chain execution with a single passkey tap.
778
+ *
779
+ * This method prepares multiple intents, shows a paginated review,
780
+ * and signs all intents with a single passkey tap via a shared merkle tree.
781
+ *
782
+ * @example
783
+ * ```typescript
784
+ * const result = await client.sendBatchIntent({
785
+ * username: 'alice',
786
+ * intents: [
787
+ * {
788
+ * targetChain: 8453, // Base
789
+ * calls: [{ to: '0x...', data: '0x...', label: 'Swap on Base' }],
790
+ * },
791
+ * {
792
+ * targetChain: 42161, // Arbitrum
793
+ * calls: [{ to: '0x...', data: '0x...', label: 'Mint on Arbitrum' }],
794
+ * },
795
+ * ],
796
+ * });
797
+ *
798
+ * if (result.success) {
799
+ * console.log(`${result.successCount} intents submitted`);
800
+ * }
801
+ * ```
802
+ */
803
+ async sendBatchIntent(options) {
804
+ if (!options.username) {
805
+ return {
806
+ success: false,
807
+ results: [],
808
+ successCount: 0,
809
+ failureCount: 0
810
+ };
811
+ }
812
+ if (!options.intents?.length) {
813
+ return {
814
+ success: false,
815
+ results: [],
816
+ successCount: 0,
817
+ failureCount: 0
818
+ };
819
+ }
820
+ const serializedIntents = options.intents.map((intent) => ({
821
+ targetChain: intent.targetChain,
822
+ calls: intent.calls,
823
+ tokenRequests: intent.tokenRequests?.map((r) => ({
824
+ token: r.token,
825
+ amount: r.amount.toString()
826
+ })),
827
+ sourceAssets: intent.sourceAssets,
828
+ sourceChainId: intent.sourceChainId
829
+ }));
830
+ const requestBody = {
831
+ username: options.username,
832
+ intents: serializedIntents,
833
+ ...this.config.clientId && { clientId: this.config.clientId }
834
+ };
835
+ let prepareResponse;
836
+ try {
837
+ const response = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
838
+ method: "POST",
839
+ headers: { "Content-Type": "application/json" },
840
+ body: JSON.stringify(requestBody)
841
+ });
842
+ if (!response.ok) {
843
+ const errorData = await response.json().catch(() => ({}));
844
+ const errorMessage = errorData.error || "Failed to prepare batch intent";
845
+ if (errorMessage.includes("User not found")) {
846
+ localStorage.removeItem("1auth-user");
847
+ }
848
+ return {
849
+ success: false,
850
+ results: [],
851
+ successCount: 0,
852
+ failureCount: 0
853
+ };
854
+ }
855
+ prepareResponse = await response.json();
856
+ } catch {
857
+ return {
858
+ success: false,
859
+ results: [],
860
+ successCount: 0,
861
+ failureCount: 0
862
+ };
863
+ }
864
+ const dialogUrl = this.getDialogUrl();
865
+ const themeParams = this.getThemeParams();
866
+ const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
867
+ const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
868
+ const dialogOrigin = this.getDialogOrigin();
869
+ const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
870
+ mode: "iframe",
871
+ batchMode: true,
872
+ batchIntents: prepareResponse.intents,
873
+ challenge: prepareResponse.challenge,
874
+ username: options.username,
875
+ accountAddress: prepareResponse.accountAddress,
876
+ userId: prepareResponse.userId,
877
+ expiresAt: prepareResponse.expiresAt
878
+ });
879
+ if (!ready) {
880
+ return {
881
+ success: false,
882
+ results: [],
883
+ successCount: 0,
884
+ failureCount: 0
885
+ };
886
+ }
887
+ const batchResult = await new Promise((resolve) => {
888
+ const handleMessage = async (event) => {
889
+ if (event.origin !== dialogOrigin) return;
890
+ const message = event.data;
891
+ if (message?.type === "PASSKEY_REFRESH_QUOTE") {
892
+ console.log("[SDK] Batch dialog requested quote refresh, re-preparing all intents");
893
+ try {
894
+ const refreshResponse = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
895
+ method: "POST",
896
+ headers: { "Content-Type": "application/json" },
897
+ body: JSON.stringify(requestBody)
898
+ });
899
+ if (refreshResponse.ok) {
900
+ const refreshed = await refreshResponse.json();
901
+ prepareResponse = refreshed;
902
+ iframe.contentWindow?.postMessage({
903
+ type: "PASSKEY_REFRESH_COMPLETE",
904
+ batchIntents: refreshed.intents,
905
+ challenge: refreshed.challenge,
906
+ expiresAt: refreshed.expiresAt
907
+ }, dialogOrigin);
908
+ } else {
909
+ iframe.contentWindow?.postMessage({
910
+ type: "PASSKEY_REFRESH_ERROR",
911
+ error: "Failed to refresh batch quotes"
912
+ }, dialogOrigin);
913
+ }
914
+ } catch {
915
+ iframe.contentWindow?.postMessage({
916
+ type: "PASSKEY_REFRESH_ERROR",
917
+ error: "Failed to refresh batch quotes"
918
+ }, dialogOrigin);
919
+ }
920
+ return;
921
+ }
922
+ if (message?.type === "PASSKEY_SIGNING_RESULT") {
923
+ window.removeEventListener("message", handleMessage);
924
+ if (message.success && message.data?.batchResults) {
925
+ const rawResults = message.data.batchResults;
926
+ const results = rawResults.map((r) => ({
927
+ index: r.index,
928
+ success: r.success ?? r.status !== "FAILED",
929
+ intentId: r.intentId || r.operationId || "",
930
+ status: r.status === "FAILED" ? "failed" : "pending",
931
+ error: r.error ? { code: "EXECUTE_FAILED", message: r.error } : void 0
932
+ }));
933
+ const successCount = results.filter((r) => r.success).length;
934
+ await this.waitForDialogClose(dialog, cleanup);
935
+ resolve({
936
+ success: successCount === results.length,
937
+ results,
938
+ successCount,
939
+ failureCount: results.length - successCount
940
+ });
941
+ } else {
942
+ cleanup();
943
+ resolve({
944
+ success: false,
945
+ results: [],
946
+ successCount: 0,
947
+ failureCount: 0
948
+ });
949
+ }
950
+ }
951
+ if (message?.type === "PASSKEY_CLOSE") {
952
+ window.removeEventListener("message", handleMessage);
953
+ cleanup();
954
+ resolve({
955
+ success: false,
956
+ results: [],
957
+ successCount: 0,
958
+ failureCount: 0
959
+ });
960
+ }
961
+ };
962
+ window.addEventListener("message", handleMessage);
963
+ });
964
+ return batchResult;
965
+ }
632
966
  /**
633
967
  * Send transaction status to the dialog iframe
634
968
  */
@@ -705,7 +1039,77 @@ var OneAuthClient = class {
705
1039
  const payload = message?.data;
706
1040
  if (message?.type === "PASSKEY_SIGNING_RESULT") {
707
1041
  window.removeEventListener("message", handleMessage);
708
- if (message.success && payload?.signature) {
1042
+ if (message.success && payload?.intentId) {
1043
+ resolve({
1044
+ success: true,
1045
+ intentId: payload.intentId
1046
+ });
1047
+ } else if (message.success && payload?.signature) {
1048
+ resolve({
1049
+ success: true,
1050
+ signature: payload.signature,
1051
+ passkey: payload.passkey,
1052
+ signedHash: payload.signedHash
1053
+ });
1054
+ } else {
1055
+ resolve({
1056
+ success: false,
1057
+ error: message.error || {
1058
+ code: "SIGNING_FAILED",
1059
+ message: "Signing failed"
1060
+ }
1061
+ });
1062
+ }
1063
+ } else if (message?.type === "PASSKEY_CLOSE") {
1064
+ window.removeEventListener("message", handleMessage);
1065
+ cleanup();
1066
+ resolve({
1067
+ success: false,
1068
+ error: {
1069
+ code: "USER_REJECTED",
1070
+ message: "User closed the dialog"
1071
+ }
1072
+ });
1073
+ }
1074
+ };
1075
+ window.addEventListener("message", handleMessage);
1076
+ });
1077
+ }
1078
+ /**
1079
+ * Wait for signing result with auto-refresh support
1080
+ * This method handles both signing results and quote refresh requests from the dialog
1081
+ */
1082
+ waitForSigningWithRefresh(dialog, iframe, cleanup, dialogOrigin, onRefresh) {
1083
+ console.log("[SDK] waitForSigningWithRefresh, expecting origin:", dialogOrigin);
1084
+ return new Promise((resolve) => {
1085
+ const handleMessage = async (event) => {
1086
+ if (event.origin !== dialogOrigin) return;
1087
+ const message = event.data;
1088
+ if (message?.type === "PASSKEY_REFRESH_QUOTE") {
1089
+ console.log("[SDK] Received quote refresh request from dialog");
1090
+ const refreshedData = await onRefresh();
1091
+ if (refreshedData) {
1092
+ iframe.contentWindow?.postMessage({
1093
+ type: "PASSKEY_REFRESH_COMPLETE",
1094
+ ...refreshedData
1095
+ }, dialogOrigin);
1096
+ } else {
1097
+ iframe.contentWindow?.postMessage({
1098
+ type: "PASSKEY_REFRESH_ERROR",
1099
+ error: "Failed to refresh quote"
1100
+ }, dialogOrigin);
1101
+ }
1102
+ return;
1103
+ }
1104
+ const payload = message?.data;
1105
+ if (message?.type === "PASSKEY_SIGNING_RESULT") {
1106
+ window.removeEventListener("message", handleMessage);
1107
+ if (message.success && payload?.intentId) {
1108
+ resolve({
1109
+ success: true,
1110
+ intentId: payload.intentId
1111
+ });
1112
+ } else if (message.success && payload?.signature) {
709
1113
  resolve({
710
1114
  success: true,
711
1115
  signature: payload.signature,
@@ -771,9 +1175,7 @@ var OneAuthClient = class {
771
1175
  const response = await fetch(
772
1176
  `${this.config.providerUrl}/api/intent/status/${intentId}`,
773
1177
  {
774
- headers: {
775
- "x-client-id": this.config.clientId
776
- }
1178
+ headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
777
1179
  }
778
1180
  );
779
1181
  if (!response.ok) {
@@ -808,6 +1210,43 @@ var OneAuthClient = class {
808
1210
  };
809
1211
  }
810
1212
  }
1213
+ /**
1214
+ * Get the history of intents for the authenticated user.
1215
+ *
1216
+ * Requires an active session (user must be logged in).
1217
+ *
1218
+ * @example
1219
+ * ```typescript
1220
+ * // Get recent intents
1221
+ * const history = await client.getIntentHistory({ limit: 10 });
1222
+ *
1223
+ * // Filter by status
1224
+ * const pending = await client.getIntentHistory({ status: 'pending' });
1225
+ *
1226
+ * // Filter by date range
1227
+ * const lastWeek = await client.getIntentHistory({
1228
+ * from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
1229
+ * });
1230
+ * ```
1231
+ */
1232
+ async getIntentHistory(options) {
1233
+ const queryParams = new URLSearchParams();
1234
+ if (options?.limit) queryParams.set("limit", String(options.limit));
1235
+ if (options?.offset) queryParams.set("offset", String(options.offset));
1236
+ if (options?.status) queryParams.set("status", options.status);
1237
+ if (options?.from) queryParams.set("from", options.from);
1238
+ if (options?.to) queryParams.set("to", options.to);
1239
+ const url = `${this.config.providerUrl}/api/intent/history${queryParams.toString() ? `?${queryParams}` : ""}`;
1240
+ const response = await fetch(url, {
1241
+ headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {},
1242
+ credentials: "include"
1243
+ });
1244
+ if (!response.ok) {
1245
+ const errorData = await response.json().catch(() => ({}));
1246
+ throw new Error(errorData.error || "Failed to get intent history");
1247
+ }
1248
+ return response.json();
1249
+ }
811
1250
  /**
812
1251
  * Send a swap intent through the Rhinestone orchestrator
813
1252
  *
@@ -870,17 +1309,21 @@ var OneAuthClient = class {
870
1309
  };
871
1310
  }
872
1311
  };
873
- const fromTokenResult = resolveToken(options.fromToken, "fromToken");
874
- if (!fromTokenResult.address) {
875
- return {
876
- success: false,
877
- intentId: "",
878
- status: "failed",
879
- error: {
880
- code: "INVALID_TOKEN",
881
- message: fromTokenResult.error || `Unknown fromToken: ${options.fromToken}`
882
- }
883
- };
1312
+ let fromTokenAddress;
1313
+ if (options.fromToken) {
1314
+ const fromTokenResult = resolveToken(options.fromToken, "fromToken");
1315
+ if (!fromTokenResult.address) {
1316
+ return {
1317
+ success: false,
1318
+ intentId: "",
1319
+ status: "failed",
1320
+ error: {
1321
+ code: "INVALID_TOKEN",
1322
+ message: fromTokenResult.error || `Unknown fromToken: ${options.fromToken}`
1323
+ }
1324
+ };
1325
+ }
1326
+ fromTokenAddress = fromTokenResult.address;
884
1327
  }
885
1328
  const toTokenResult = resolveToken(options.toToken, "toToken");
886
1329
  if (!toTokenResult.address) {
@@ -894,18 +1337,17 @@ var OneAuthClient = class {
894
1337
  }
895
1338
  };
896
1339
  }
897
- const fromTokenAddress = fromTokenResult.address;
898
1340
  const toTokenAddress = toTokenResult.address;
899
1341
  console.log("[SDK sendSwap] Token resolution:", {
900
- fromToken: options.fromToken,
901
- fromTokenAddress,
1342
+ fromToken: options.fromToken ?? "Any",
1343
+ fromTokenAddress: fromTokenAddress ?? "orchestrator picks",
902
1344
  toToken: options.toToken,
903
1345
  toTokenAddress,
904
1346
  targetChain: options.targetChain
905
1347
  });
906
1348
  const formatTokenLabel = (token, fallback) => {
907
1349
  if (!token.startsWith("0x")) {
908
- return token.toUpperCase();
1350
+ return token;
909
1351
  }
910
1352
  try {
911
1353
  return getTokenSymbol(token, options.targetChain);
@@ -913,15 +1355,11 @@ var OneAuthClient = class {
913
1355
  return fallback;
914
1356
  }
915
1357
  };
916
- const fromSymbol = formatTokenLabel(
917
- options.fromToken,
918
- `${options.fromToken.slice(0, 6)}...${options.fromToken.slice(-4)}`
919
- );
920
1358
  const toSymbol = formatTokenLabel(
921
1359
  options.toToken,
922
1360
  `${options.toToken.slice(0, 6)}...${options.toToken.slice(-4)}`
923
1361
  );
924
- const isFromNativeEth = fromTokenAddress === "0x0000000000000000000000000000000000000000";
1362
+ const isFromNativeEth = fromTokenAddress ? fromTokenAddress === "0x0000000000000000000000000000000000000000" : false;
925
1363
  const isToNativeEth = toTokenAddress === "0x0000000000000000000000000000000000000000";
926
1364
  const KNOWN_DECIMALS = {
927
1365
  ETH: 18,
@@ -931,31 +1369,33 @@ var OneAuthClient = class {
931
1369
  USDT0: 6
932
1370
  };
933
1371
  const getDecimals = (symbol, chainId) => {
934
- const upperSymbol = symbol.toUpperCase();
935
1372
  try {
936
- const decimals = (0, import_sdk.getTokenDecimals)(upperSymbol, chainId);
937
- console.log(`[SDK] getTokenDecimals(${upperSymbol}, ${chainId}) = ${decimals}`);
1373
+ const match = getSupportedTokens(chainId).find(
1374
+ (t) => t.symbol.toUpperCase() === symbol.toUpperCase()
1375
+ );
1376
+ if (match) {
1377
+ console.log(`[SDK] getTokenDecimals(${match.symbol}, ${chainId}) = ${match.decimals}`);
1378
+ return match.decimals;
1379
+ }
1380
+ const decimals = (0, import_sdk.getTokenDecimals)(symbol, chainId);
1381
+ console.log(`[SDK] getTokenDecimals(${symbol}, ${chainId}) = ${decimals}`);
938
1382
  return decimals;
939
1383
  } catch (e) {
940
- console.warn(`[SDK] getTokenDecimals failed for ${upperSymbol}, using fallback`, e);
1384
+ const upperSymbol = symbol.toUpperCase();
1385
+ console.warn(`[SDK] getTokenDecimals failed for ${symbol}, using fallback`, e);
941
1386
  return KNOWN_DECIMALS[upperSymbol] ?? 18;
942
1387
  }
943
1388
  };
944
- const fromDecimals = getDecimals(options.fromToken, options.targetChain);
945
1389
  const toDecimals = getDecimals(options.toToken, options.targetChain);
946
- const isBridge = options.fromToken.toUpperCase() === options.toToken.toUpperCase();
947
- let tokenRequests;
948
- if (!isToNativeEth) {
949
- tokenRequests = [{
950
- token: toTokenAddress,
951
- amount: (0, import_viem2.parseUnits)(options.amount, toDecimals).toString()
952
- }];
953
- }
1390
+ const isBridge = options.fromToken ? options.fromToken.toUpperCase() === options.toToken.toUpperCase() : false;
1391
+ const tokenRequests = [{
1392
+ token: toTokenAddress,
1393
+ amount: (0, import_viem2.parseUnits)(options.amount, toDecimals)
1394
+ }];
954
1395
  console.log("[SDK sendSwap] Building intent:", {
955
1396
  isBridge,
956
1397
  isFromNativeEth,
957
1398
  isToNativeEth,
958
- fromDecimals,
959
1399
  toDecimals,
960
1400
  tokenRequests
961
1401
  });
@@ -966,15 +1406,20 @@ var OneAuthClient = class {
966
1406
  {
967
1407
  // Minimal call - just signals to orchestrator we want the tokenRequests delivered
968
1408
  to: toTokenAddress,
969
- value: "0"
1409
+ value: "0",
1410
+ // SDK provides labels so dialog shows "Buy ETH" not "Send ETH / To: 0x000..."
1411
+ label: `Buy ${toSymbol}`,
1412
+ sublabel: `${options.amount} ${toSymbol}`
970
1413
  }
971
1414
  ],
972
1415
  // Request specific output tokens - this is what actually matters for swaps
973
1416
  tokenRequests,
974
1417
  // Constrain orchestrator to use only the fromToken as input
975
1418
  // This ensures the swap uses the correct source token
976
- // Pass the symbol (not address) so orchestrator can resolve per-chain
977
- sourceAssets: options.sourceAssets || [options.fromToken.toUpperCase()],
1419
+ // Use canonical symbol casing from registry (e.g. "MockUSD" not "MOCKUSD")
1420
+ sourceAssets: options.sourceAssets || (options.fromToken ? [options.fromToken] : void 0),
1421
+ // Pass source chain ID so orchestrator knows which chain to look for tokens on
1422
+ sourceChainId: options.sourceChainId,
978
1423
  closeOn: options.closeOn || "preconfirmed",
979
1424
  waitForHash: options.waitForHash,
980
1425
  hashTimeoutMs: options.hashTimeoutMs,
@@ -983,7 +1428,7 @@ var OneAuthClient = class {
983
1428
  return {
984
1429
  ...result,
985
1430
  quote: result.success ? {
986
- fromToken: fromTokenAddress,
1431
+ fromToken: fromTokenAddress ?? options.fromToken,
987
1432
  toToken: toTokenAddress,
988
1433
  amountIn: options.amount,
989
1434
  amountOut: "",
@@ -1025,26 +1470,23 @@ var OneAuthClient = class {
1025
1470
  const themeParams = this.getThemeParams(options?.theme);
1026
1471
  const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
1027
1472
  const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
1028
- const dialogOrigin = this.getDialogOrigin();
1029
- await new Promise((resolve) => {
1030
- const handleReady = (event) => {
1031
- if (event.origin !== dialogOrigin) return;
1032
- if (event.data?.type === "PASSKEY_READY") {
1033
- window.removeEventListener("message", handleReady);
1034
- iframe.contentWindow?.postMessage({
1035
- type: "PASSKEY_INIT",
1036
- mode: "iframe",
1037
- message: options.message,
1038
- challenge: options.challenge || options.message,
1039
- username: options.username,
1040
- description: options.description,
1041
- metadata: options.metadata
1042
- }, dialogOrigin);
1043
- resolve();
1473
+ const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
1474
+ mode: "iframe",
1475
+ message: options.message,
1476
+ challenge: options.challenge || options.message,
1477
+ username: options.username,
1478
+ description: options.description,
1479
+ metadata: options.metadata
1480
+ });
1481
+ if (!ready) {
1482
+ return {
1483
+ success: false,
1484
+ error: {
1485
+ code: "USER_REJECTED",
1486
+ message: "User closed the dialog"
1044
1487
  }
1045
1488
  };
1046
- window.addEventListener("message", handleReady);
1047
- });
1489
+ }
1048
1490
  const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
1049
1491
  cleanup();
1050
1492
  if (signingResult.success) {
@@ -1113,31 +1555,28 @@ var OneAuthClient = class {
1113
1555
  const themeParams = this.getThemeParams(options?.theme);
1114
1556
  const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
1115
1557
  const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
1116
- const dialogOrigin = this.getDialogOrigin();
1117
- await new Promise((resolve) => {
1118
- const handleReady = (event) => {
1119
- if (event.origin !== dialogOrigin) return;
1120
- if (event.data?.type === "PASSKEY_READY") {
1121
- window.removeEventListener("message", handleReady);
1122
- iframe.contentWindow?.postMessage({
1123
- type: "PASSKEY_INIT",
1124
- mode: "iframe",
1125
- signingMode: "typedData",
1126
- typedData: {
1127
- domain: options.domain,
1128
- types: options.types,
1129
- primaryType: options.primaryType,
1130
- message: options.message
1131
- },
1132
- challenge: signedHash,
1133
- username: options.username,
1134
- description: options.description
1135
- }, dialogOrigin);
1136
- resolve();
1558
+ const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
1559
+ mode: "iframe",
1560
+ signingMode: "typedData",
1561
+ typedData: {
1562
+ domain: options.domain,
1563
+ types: options.types,
1564
+ primaryType: options.primaryType,
1565
+ message: options.message
1566
+ },
1567
+ challenge: signedHash,
1568
+ username: options.username,
1569
+ description: options.description
1570
+ });
1571
+ if (!ready) {
1572
+ return {
1573
+ success: false,
1574
+ error: {
1575
+ code: "USER_REJECTED",
1576
+ message: "User closed the dialog"
1137
1577
  }
1138
1578
  };
1139
- window.addEventListener("message", handleReady);
1140
- });
1579
+ }
1141
1580
  const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
1142
1581
  cleanup();
1143
1582
  if (signingResult.success) {
@@ -1291,9 +1730,7 @@ var OneAuthClient = class {
1291
1730
  const response = await fetch(
1292
1731
  `${this.config.providerUrl}/api/users/${encodeURIComponent(username)}/passkeys`,
1293
1732
  {
1294
- headers: {
1295
- "x-client-id": this.config.clientId
1296
- }
1733
+ headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
1297
1734
  }
1298
1735
  );
1299
1736
  if (!response.ok) {
@@ -1312,7 +1749,7 @@ var OneAuthClient = class {
1312
1749
  "Content-Type": "application/json"
1313
1750
  },
1314
1751
  body: JSON.stringify({
1315
- clientId: this.config.clientId,
1752
+ ...this.config.clientId && { clientId: this.config.clientId },
1316
1753
  username: options.username,
1317
1754
  challenge: options.challenge,
1318
1755
  description: options.description,
@@ -1338,6 +1775,44 @@ var OneAuthClient = class {
1338
1775
  `width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`
1339
1776
  );
1340
1777
  }
1778
+ /**
1779
+ * Wait for the dialog iframe to signal ready, then send init data.
1780
+ * Also handles early close (X button, escape, backdrop) during the ready phase.
1781
+ * Returns true if dialog is ready, false if it was closed before becoming ready.
1782
+ */
1783
+ waitForDialogReady(dialog, iframe, cleanup, initMessage) {
1784
+ const dialogOrigin = this.getDialogOrigin();
1785
+ return new Promise((resolve) => {
1786
+ let settled = false;
1787
+ const teardown = () => {
1788
+ if (settled) return;
1789
+ settled = true;
1790
+ window.removeEventListener("message", handleMessage);
1791
+ dialog.removeEventListener("close", handleClose);
1792
+ };
1793
+ const handleMessage = (event) => {
1794
+ if (event.origin !== dialogOrigin) return;
1795
+ if (event.data?.type === "PASSKEY_READY") {
1796
+ teardown();
1797
+ iframe.contentWindow?.postMessage({
1798
+ type: "PASSKEY_INIT",
1799
+ ...initMessage
1800
+ }, dialogOrigin);
1801
+ resolve(true);
1802
+ } else if (event.data?.type === "PASSKEY_CLOSE") {
1803
+ teardown();
1804
+ cleanup();
1805
+ resolve(false);
1806
+ }
1807
+ };
1808
+ const handleClose = () => {
1809
+ teardown();
1810
+ resolve(false);
1811
+ };
1812
+ window.addEventListener("message", handleMessage);
1813
+ dialog.addEventListener("close", handleClose);
1814
+ });
1815
+ }
1341
1816
  /**
1342
1817
  * Create a modal dialog with an iframe inside.
1343
1818
  */
@@ -1482,12 +1957,24 @@ var OneAuthClient = class {
1482
1957
  };
1483
1958
  return { dialog, iframe, cleanup };
1484
1959
  }
1485
- waitForModalAuthResponse(_dialog, _iframe, cleanup) {
1960
+ waitForModalAuthResponse(_dialog, iframe, cleanup) {
1486
1961
  const dialogOrigin = this.getDialogOrigin();
1487
1962
  return new Promise((resolve) => {
1963
+ let dialogReady = false;
1488
1964
  const handleMessage = (event) => {
1489
1965
  if (event.origin !== dialogOrigin) return;
1490
1966
  const data = event.data;
1967
+ if (data?.type === "PASSKEY_READY") {
1968
+ dialogReady = true;
1969
+ iframe.contentWindow?.postMessage({
1970
+ type: "PASSKEY_INIT",
1971
+ mode: "iframe"
1972
+ }, dialogOrigin);
1973
+ return;
1974
+ }
1975
+ if (!dialogReady && data?.type === "PASSKEY_CLOSE") {
1976
+ return;
1977
+ }
1491
1978
  if (data?.type === "PASSKEY_LOGIN_RESULT") {
1492
1979
  window.removeEventListener("message", handleMessage);
1493
1980
  cleanup();
@@ -1517,6 +2004,11 @@ var OneAuthClient = class {
1517
2004
  error: data.error
1518
2005
  });
1519
2006
  }
2007
+ } else if (data?.type === "PASSKEY_RETRY_POPUP") {
2008
+ window.removeEventListener("message", handleMessage);
2009
+ cleanup();
2010
+ const popupUrl = data.data?.url?.replace("mode=iframe", "mode=popup") || `${this.getDialogUrl()}/dialog/auth?mode=popup${this.config.clientId ? `&clientId=${this.config.clientId}` : ""}`;
2011
+ this.waitForPopupAuthResponse(popupUrl).then(resolve);
1520
2012
  } else if (data?.type === "PASSKEY_CLOSE") {
1521
2013
  window.removeEventListener("message", handleMessage);
1522
2014
  cleanup();
@@ -1532,6 +2024,77 @@ var OneAuthClient = class {
1532
2024
  window.addEventListener("message", handleMessage);
1533
2025
  });
1534
2026
  }
2027
+ /**
2028
+ * Open a popup for auth and wait for the result.
2029
+ * Used when iframe mode fails (e.g., due to password manager interference).
2030
+ */
2031
+ waitForPopupAuthResponse(url) {
2032
+ const dialogOrigin = this.getDialogOrigin();
2033
+ const popup = this.openPopup(url);
2034
+ return new Promise((resolve) => {
2035
+ const pollTimer = setInterval(() => {
2036
+ if (popup?.closed) {
2037
+ clearInterval(pollTimer);
2038
+ window.removeEventListener("message", handleMessage);
2039
+ resolve({
2040
+ success: false,
2041
+ error: {
2042
+ code: "USER_CANCELLED",
2043
+ message: "Authentication was cancelled"
2044
+ }
2045
+ });
2046
+ }
2047
+ }, 500);
2048
+ const handleMessage = (event) => {
2049
+ if (event.origin !== dialogOrigin) return;
2050
+ const data = event.data;
2051
+ if (data?.type === "PASSKEY_LOGIN_RESULT") {
2052
+ clearInterval(pollTimer);
2053
+ window.removeEventListener("message", handleMessage);
2054
+ popup?.close();
2055
+ if (data.success) {
2056
+ resolve({
2057
+ success: true,
2058
+ username: data.data?.username,
2059
+ user: data.data?.user
2060
+ });
2061
+ } else {
2062
+ resolve({
2063
+ success: false,
2064
+ error: data.error
2065
+ });
2066
+ }
2067
+ } else if (data?.type === "PASSKEY_REGISTER_RESULT") {
2068
+ clearInterval(pollTimer);
2069
+ window.removeEventListener("message", handleMessage);
2070
+ popup?.close();
2071
+ if (data.success) {
2072
+ resolve({
2073
+ success: true,
2074
+ username: data.data?.username
2075
+ });
2076
+ } else {
2077
+ resolve({
2078
+ success: false,
2079
+ error: data.error
2080
+ });
2081
+ }
2082
+ } else if (data?.type === "PASSKEY_CLOSE") {
2083
+ clearInterval(pollTimer);
2084
+ window.removeEventListener("message", handleMessage);
2085
+ popup?.close();
2086
+ resolve({
2087
+ success: false,
2088
+ error: {
2089
+ code: "USER_CANCELLED",
2090
+ message: "Authentication was cancelled"
2091
+ }
2092
+ });
2093
+ }
2094
+ };
2095
+ window.addEventListener("message", handleMessage);
2096
+ });
2097
+ }
1535
2098
  waitForAuthenticateResponse(_dialog, _iframe, cleanup) {
1536
2099
  const dialogOrigin = this.getDialogOrigin();
1537
2100
  return new Promise((resolve) => {
@@ -1571,6 +2134,44 @@ var OneAuthClient = class {
1571
2134
  window.addEventListener("message", handleMessage);
1572
2135
  });
1573
2136
  }
2137
+ waitForConnectResponse(_dialog, _iframe, cleanup) {
2138
+ const dialogOrigin = this.getDialogOrigin();
2139
+ return new Promise((resolve) => {
2140
+ const handleMessage = (event) => {
2141
+ if (event.origin !== dialogOrigin) return;
2142
+ const data = event.data;
2143
+ if (data?.type === "PASSKEY_CONNECT_RESULT") {
2144
+ window.removeEventListener("message", handleMessage);
2145
+ cleanup();
2146
+ if (data.success) {
2147
+ resolve({
2148
+ success: true,
2149
+ username: data.data?.username,
2150
+ autoConnected: data.data?.autoConnected
2151
+ });
2152
+ } else {
2153
+ resolve({
2154
+ success: false,
2155
+ action: data.data?.action,
2156
+ error: data.error
2157
+ });
2158
+ }
2159
+ } else if (data?.type === "PASSKEY_CLOSE") {
2160
+ window.removeEventListener("message", handleMessage);
2161
+ cleanup();
2162
+ resolve({
2163
+ success: false,
2164
+ action: "cancel",
2165
+ error: {
2166
+ code: "USER_CANCELLED",
2167
+ message: "Connection was cancelled"
2168
+ }
2169
+ });
2170
+ }
2171
+ };
2172
+ window.addEventListener("message", handleMessage);
2173
+ });
2174
+ }
1574
2175
  waitForModalSigningResponse(requestId, _dialog, _iframe, cleanup) {
1575
2176
  const dialogOrigin = this.getDialogOrigin();
1576
2177
  return new Promise((resolve) => {
@@ -1659,9 +2260,7 @@ var OneAuthClient = class {
1659
2260
  const response = await fetch(
1660
2261
  `${this.config.providerUrl}/api/sign/request/${requestId}`,
1661
2262
  {
1662
- headers: {
1663
- "x-client-id": this.config.clientId
1664
- }
2263
+ headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
1665
2264
  }
1666
2265
  );
1667
2266
  if (!response.ok) {
@@ -1765,7 +2364,7 @@ function buildTransactionReview(calls) {
1765
2364
 
1766
2365
  // src/provider.ts
1767
2366
  var DEFAULT_STORAGE_KEY = "1auth-user";
1768
- function createPasskeyProvider(options) {
2367
+ function createOneAuthProvider(options) {
1769
2368
  const { client } = options;
1770
2369
  let chainId = options.chainId;
1771
2370
  const storageKey = options.storageKey || DEFAULT_STORAGE_KEY;
@@ -1796,12 +2395,11 @@ function createPasskeyProvider(options) {
1796
2395
  localStorage.removeItem(storageKey);
1797
2396
  };
1798
2397
  const resolveAccountAddress = async (username) => {
2398
+ const clientId = client.getClientId();
1799
2399
  const response = await fetch(
1800
2400
  `${client.getProviderUrl()}/api/users/${encodeURIComponent(username)}/account`,
1801
2401
  {
1802
- headers: {
1803
- "x-client-id": client.getClientId()
1804
- }
2402
+ headers: clientId ? { "x-client-id": clientId } : {}
1805
2403
  }
1806
2404
  );
1807
2405
  if (!response.ok) {
@@ -1816,12 +2414,21 @@ function createPasskeyProvider(options) {
1816
2414
  if (stored) {
1817
2415
  return [stored.address];
1818
2416
  }
1819
- const result = await client.authWithModal();
1820
- if (!result.success || !result.username) {
1821
- throw new Error(result.error?.message || "Authentication failed");
2417
+ const connectResult = await client.connectWithModal();
2418
+ let username;
2419
+ if (connectResult.success && connectResult.username) {
2420
+ username = connectResult.username;
2421
+ } else if (connectResult.action === "switch") {
2422
+ const authResult = await client.authWithModal();
2423
+ if (!authResult.success || !authResult.username) {
2424
+ throw new Error(authResult.error?.message || "Authentication failed");
2425
+ }
2426
+ username = authResult.username;
2427
+ } else {
2428
+ throw new Error(connectResult.error?.message || "Connection cancelled");
1822
2429
  }
1823
- const address = await resolveAccountAddress(result.username);
1824
- setStoredUser({ username: result.username, address });
2430
+ const address = await resolveAccountAddress(username);
2431
+ setStoredUser({ username, address });
1825
2432
  emit("accountsChanged", [address]);
1826
2433
  emit("connect", { chainId: (0, import_viem4.numberToHex)(chainId) });
1827
2434
  return [address];
@@ -1878,6 +2485,16 @@ function createPasskeyProvider(options) {
1878
2485
  };
1879
2486
  });
1880
2487
  };
2488
+ const normalizeTokenRequests = (requests) => {
2489
+ if (!Array.isArray(requests)) return void 0;
2490
+ return requests.map((r) => {
2491
+ const req = r;
2492
+ return {
2493
+ token: req.token,
2494
+ amount: typeof req.amount === "bigint" ? req.amount : BigInt(String(req.amount || "0"))
2495
+ };
2496
+ });
2497
+ };
1881
2498
  const decodeMessage = (value) => {
1882
2499
  if (!(0, import_viem4.isHex)(value)) return value;
1883
2500
  try {
@@ -1917,31 +2534,35 @@ function createPasskeyProvider(options) {
1917
2534
  return {
1918
2535
  username: payload.username,
1919
2536
  targetChain: payload.targetChain,
1920
- calls: payload.calls
2537
+ calls: payload.calls,
2538
+ tokenRequests: payload.tokenRequests
1921
2539
  };
1922
2540
  }
1923
2541
  const signedIntent = await options.signIntent({
1924
2542
  username: payload.username,
1925
2543
  accountAddress: payload.accountAddress,
1926
2544
  targetChain: payload.targetChain,
1927
- calls: payload.calls
2545
+ calls: payload.calls,
2546
+ tokenRequests: payload.tokenRequests
1928
2547
  });
1929
2548
  return { signedIntent };
1930
2549
  };
1931
2550
  const sendIntent = async (payload) => {
1932
- const closeOn = options.waitForHash ?? true ? "completed" : "preconfirmed";
2551
+ const closeOn = options.closeOn ?? (options.waitForHash ?? true ? "completed" : "preconfirmed");
1933
2552
  const intentPayload = await resolveIntentPayload(payload);
1934
2553
  const result = await client.sendIntent({
1935
2554
  ...intentPayload,
2555
+ tokenRequests: payload.tokenRequests,
2556
+ sourceChainId: payload.sourceChainId,
1936
2557
  closeOn,
1937
2558
  waitForHash: options.waitForHash ?? true,
1938
2559
  hashTimeoutMs: options.hashTimeoutMs,
1939
2560
  hashIntervalMs: options.hashIntervalMs
1940
2561
  });
1941
- if (!result.success || !result.transactionHash) {
2562
+ if (!result.success) {
1942
2563
  throw new Error(result.error?.message || "Transaction failed");
1943
2564
  }
1944
- return result.transactionHash;
2565
+ return result.intentId;
1945
2566
  };
1946
2567
  const request = async ({ method, params }) => {
1947
2568
  switch (method) {
@@ -1994,11 +2615,15 @@ function createPasskeyProvider(options) {
1994
2615
  const user = await ensureUser();
1995
2616
  const targetChain = parseChainId(tx.chainId) ?? chainId;
1996
2617
  const calls = normalizeCalls([tx]);
2618
+ const tokenRequests = normalizeTokenRequests(tx.tokenRequests);
2619
+ const txSourceChainId = parseChainId(tx.sourceChainId);
1997
2620
  return sendIntent({
1998
2621
  username: user.username,
1999
2622
  accountAddress: user.address,
2000
2623
  targetChain,
2001
- calls
2624
+ calls,
2625
+ tokenRequests,
2626
+ sourceChainId: txSourceChainId
2002
2627
  });
2003
2628
  }
2004
2629
  case "wallet_sendCalls": {
@@ -2007,22 +2632,128 @@ function createPasskeyProvider(options) {
2007
2632
  const user = await ensureUser();
2008
2633
  const targetChain = parseChainId(payload.chainId) ?? chainId;
2009
2634
  const calls = normalizeCalls(payload.calls || []);
2635
+ const tokenRequests = normalizeTokenRequests(payload.tokenRequests);
2636
+ const sourceChainId = parseChainId(payload.sourceChainId);
2010
2637
  if (!calls.length) throw new Error("No calls provided");
2011
2638
  return sendIntent({
2012
2639
  username: user.username,
2013
2640
  accountAddress: user.address,
2014
2641
  targetChain,
2015
- calls
2642
+ calls,
2643
+ tokenRequests,
2644
+ sourceChainId
2016
2645
  });
2017
2646
  }
2018
2647
  case "wallet_getCapabilities": {
2648
+ const paramList = Array.isArray(params) ? params : [];
2649
+ const requestedChains = paramList[1];
2019
2650
  const chainIds = getSupportedChainIds();
2020
- const tokensByChain = Object.fromEntries(
2021
- chainIds.map((id) => [id, getSupportedTokens(id)])
2651
+ const capabilities = {};
2652
+ for (const chainId2 of chainIds) {
2653
+ const hexChainId = `0x${chainId2.toString(16)}`;
2654
+ if (requestedChains && !requestedChains.includes(hexChainId)) {
2655
+ continue;
2656
+ }
2657
+ capabilities[hexChainId] = {
2658
+ atomic: { status: "supported" },
2659
+ paymasterService: { supported: true },
2660
+ auxiliaryFunds: { supported: true }
2661
+ };
2662
+ }
2663
+ return capabilities;
2664
+ }
2665
+ case "wallet_getAssets": {
2666
+ const { username } = await ensureUser();
2667
+ const clientId = client.getClientId();
2668
+ const response = await fetch(
2669
+ `${client.getProviderUrl()}/api/users/${encodeURIComponent(username)}/portfolio`,
2670
+ {
2671
+ headers: clientId ? { "x-client-id": clientId } : {}
2672
+ }
2673
+ );
2674
+ if (!response.ok) {
2675
+ const data = await response.json().catch(() => ({}));
2676
+ throw new Error(data.error || "Failed to get assets");
2677
+ }
2678
+ return response.json();
2679
+ }
2680
+ case "wallet_getCallsStatus": {
2681
+ const paramList = Array.isArray(params) ? params : [];
2682
+ const callsId = paramList[0];
2683
+ if (!callsId) {
2684
+ throw new Error("callsId is required");
2685
+ }
2686
+ const statusClientId = client.getClientId();
2687
+ const response = await fetch(
2688
+ `${client.getProviderUrl()}/api/intent/status/${encodeURIComponent(callsId)}`,
2689
+ {
2690
+ headers: statusClientId ? { "x-client-id": statusClientId } : {}
2691
+ }
2022
2692
  );
2693
+ if (!response.ok) {
2694
+ const data2 = await response.json().catch(() => ({}));
2695
+ throw new Error(data2.error || "Failed to get calls status");
2696
+ }
2697
+ const data = await response.json();
2698
+ const statusMap = {
2699
+ pending: "PENDING",
2700
+ preconfirmed: "PENDING",
2701
+ completed: "CONFIRMED",
2702
+ failed: "CONFIRMED",
2703
+ expired: "CONFIRMED"
2704
+ };
2023
2705
  return {
2024
- chains: chainIds,
2025
- tokens: tokensByChain
2706
+ status: statusMap[data.status] || "PENDING",
2707
+ receipts: data.transactionHash ? [
2708
+ {
2709
+ logs: [],
2710
+ status: data.status === "completed" ? "0x1" : "0x0",
2711
+ blockHash: data.blockHash,
2712
+ blockNumber: data.blockNumber,
2713
+ transactionHash: data.transactionHash
2714
+ }
2715
+ ] : []
2716
+ };
2717
+ }
2718
+ case "wallet_getCallsHistory": {
2719
+ const paramList = Array.isArray(params) ? params : [];
2720
+ const options2 = paramList[0] || {};
2721
+ const queryParams = new URLSearchParams();
2722
+ if (options2.limit) queryParams.set("limit", String(options2.limit));
2723
+ if (options2.offset) queryParams.set("offset", String(options2.offset));
2724
+ if (options2.status) queryParams.set("status", options2.status);
2725
+ if (options2.from) queryParams.set("from", options2.from);
2726
+ if (options2.to) queryParams.set("to", options2.to);
2727
+ const url = `${client.getProviderUrl()}/api/intent/history${queryParams.toString() ? `?${queryParams}` : ""}`;
2728
+ const historyClientId = client.getClientId();
2729
+ const response = await fetch(url, {
2730
+ headers: historyClientId ? { "x-client-id": historyClientId } : {},
2731
+ credentials: "include"
2732
+ });
2733
+ if (!response.ok) {
2734
+ const data2 = await response.json().catch(() => ({}));
2735
+ throw new Error(data2.error || "Failed to get calls history");
2736
+ }
2737
+ const data = await response.json();
2738
+ const statusMap = {
2739
+ pending: "PENDING",
2740
+ preconfirmed: "PENDING",
2741
+ completed: "CONFIRMED",
2742
+ failed: "CONFIRMED",
2743
+ expired: "CONFIRMED"
2744
+ };
2745
+ return {
2746
+ calls: data.intents.map(
2747
+ (intent) => ({
2748
+ callsId: intent.intentId,
2749
+ // intentId is the orchestrator's ID
2750
+ status: statusMap[intent.status] || "PENDING",
2751
+ receipts: intent.transactionHash ? [{ transactionHash: intent.transactionHash }] : [],
2752
+ chainId: `0x${intent.targetChain.toString(16)}`
2753
+ })
2754
+ ),
2755
+ total: data.total,
2756
+ hasMore: data.hasMore
2026
2757
  };
2027
2758
  }
2028
2759
  default:
@@ -2045,6 +2776,7 @@ function createPasskeyProvider(options) {
2045
2776
  disconnect
2046
2777
  };
2047
2778
  }
2779
+ var createPasskeyProvider = createOneAuthProvider;
2048
2780
 
2049
2781
  // src/account.ts
2050
2782
  var import_viem5 = require("viem");
@@ -2147,6 +2879,9 @@ function createPasskeyWalletClient(config) {
2147
2879
  if (!result.success) {
2148
2880
  throw new Error(result.error?.message || "Signing failed");
2149
2881
  }
2882
+ if (!result.signature) {
2883
+ throw new Error("No signature received");
2884
+ }
2150
2885
  return encodeWebAuthnSignature(result.signature);
2151
2886
  };
2152
2887
  const signTransactionImpl = async (transaction) => {
@@ -2167,6 +2902,9 @@ function createPasskeyWalletClient(config) {
2167
2902
  if (!result.success) {
2168
2903
  throw new Error(result.error?.message || "Signing failed");
2169
2904
  }
2905
+ if (!result.signature) {
2906
+ throw new Error("No signature received");
2907
+ }
2170
2908
  return encodeWebAuthnSignature(result.signature);
2171
2909
  };
2172
2910
  const signTypedDataImpl = async (typedData) => {
@@ -2188,6 +2926,9 @@ function createPasskeyWalletClient(config) {
2188
2926
  if (!result.success) {
2189
2927
  throw new Error(result.error?.message || "Signing failed");
2190
2928
  }
2929
+ if (!result.signature) {
2930
+ throw new Error("No signature received");
2931
+ }
2191
2932
  return encodeWebAuthnSignature(result.signature);
2192
2933
  };
2193
2934
  const buildIntentPayload = async (calls, targetChainOverride) => {
@@ -2256,11 +2997,12 @@ function createPasskeyWalletClient(config) {
2256
2997
  * Send multiple calls as a single batched transaction
2257
2998
  */
2258
2999
  async sendCalls(params) {
2259
- const { calls, chainId: targetChain } = params;
3000
+ const { calls, chainId: targetChain, tokenRequests } = params;
2260
3001
  const closeOn = config.waitForHash ?? true ? "completed" : "preconfirmed";
2261
3002
  const intentPayload = await buildIntentPayload(calls, targetChain);
2262
3003
  const result = await provider.sendIntent({
2263
3004
  ...intentPayload,
3005
+ tokenRequests,
2264
3006
  closeOn,
2265
3007
  waitForHash: config.waitForHash ?? true,
2266
3008
  hashTimeoutMs: config.hashTimeoutMs,
@@ -2704,9 +3446,10 @@ function BatchQueueWidget({ onSignAll }) {
2704
3446
 
2705
3447
  // src/verify.ts
2706
3448
  var import_viem7 = require("viem");
2707
- var PASSKEY_MESSAGE_PREFIX = "Passkey Signed Message:\n";
3449
+ var ETHEREUM_MESSAGE_PREFIX = "Ethereum Signed Message:\n";
3450
+ var PASSKEY_MESSAGE_PREFIX = ETHEREUM_MESSAGE_PREFIX;
2708
3451
  function hashMessage2(message) {
2709
- const prefixed = PASSKEY_MESSAGE_PREFIX + message.length.toString() + message;
3452
+ const prefixed = ETHEREUM_MESSAGE_PREFIX + message.length.toString() + message;
2710
3453
  return (0, import_viem7.keccak256)((0, import_viem7.toBytes)(prefixed));
2711
3454
  }
2712
3455
  function verifyMessageHash(message, signedHash) {
@@ -2718,9 +3461,11 @@ function verifyMessageHash(message, signedHash) {
2718
3461
  0 && (module.exports = {
2719
3462
  BatchQueueProvider,
2720
3463
  BatchQueueWidget,
3464
+ ETHEREUM_MESSAGE_PREFIX,
2721
3465
  OneAuthClient,
2722
3466
  PASSKEY_MESSAGE_PREFIX,
2723
3467
  PasskeyProviderClient,
3468
+ createOneAuthProvider,
2724
3469
  createPasskeyAccount,
2725
3470
  createPasskeyProvider,
2726
3471
  createPasskeyWalletClient,