@fourt/sdk 1.1.6 → 1.2.0

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
@@ -1,16 +1,15 @@
1
1
  // src/session/index.ts
2
2
  import { createStore } from "zustand";
3
- import { createJSONStorage, persist } from "zustand/middleware";
3
+ import { persist, createJSONStorage } from "zustand/middleware";
4
4
  var SessionStore = class {
5
5
  _store;
6
- /**
7
- * Initializes a new instance of the `SessionStore` class by creating a new `zustand`store with the initial state.
8
- */
9
6
  constructor() {
10
7
  this._store = createStore()(
11
8
  persist(this._getInitialState, {
12
- name: "fourt.io-signer-session",
13
- storage: createJSONStorage(() => localStorage)
9
+ name: "fourt-session",
10
+ storage: createJSONStorage(() => localStorage),
11
+ // keep only these keys in persisted storage
12
+ partialize: (state) => ({ bundle: state.bundle, type: state.type })
14
13
  })
15
14
  );
16
15
  }
@@ -366,26 +365,29 @@ var UserModule = class {
366
365
  this._webSignerClient = _webSignerClient;
367
366
  }
368
367
  /**
369
- * Gets the user information.
370
- *
371
- * @returns {User | undefined} user information.
368
+ * Retrieves information for the authenticated user.
369
+ * Assumes a user is already logged in, otherwise it will throw an error.
372
370
  */
373
- get info() {
374
- return this._webSignerClient.user;
371
+ async getInfo() {
372
+ return this._webSignerClient.getUser();
375
373
  }
376
- /** Gets the user token.
377
- *
378
- * @returns {string | undefined} user token.
374
+ /**
375
+ * Checks if a user is currently logged in to the fourt.io SDK.
379
376
  */
380
- get token() {
377
+ async isLoggedIn() {
378
+ return this._webSignerClient.isLoggedIn();
379
+ }
380
+ /**
381
+ * Generates an access token with a lifespan of 15 minutes.
382
+ * Assumes a user is already logged in, otherwise it will throw an error.
383
+ */
384
+ async getToken() {
381
385
  return this._webSignerClient.getToken();
382
386
  }
383
387
  /**
384
388
  * Logs out the user.
385
- *
386
- * @returns {void}
387
389
  */
388
- logout() {
390
+ async logout() {
389
391
  return this._webSignerClient.logout();
390
392
  }
391
393
  };
@@ -452,24 +454,28 @@ var UnauthenticatedError = class _UnauthenticatedError extends SDKError {
452
454
  }
453
455
  };
454
456
 
455
- // src/types/Routes.ts
457
+ // src/types/routes.ts
456
458
  var ROUTE_METHOD_MAP = {
457
459
  "/v1/signup": "POST",
458
460
  "/v1/email-auth": "POST",
459
461
  "/v1/lookup": "POST",
460
462
  "/v1/signin": "POST",
461
463
  "/v1/sign": "POST",
462
- "v1/oauth/init": "POST"
464
+ "/v1/oauth/init": "POST",
465
+ "/v1/refresh": "POST",
466
+ "/v1/logout": "POST",
467
+ "/v1/me": "GET"
463
468
  };
464
469
 
465
470
  // src/signer/index.ts
466
471
  import { jwtDecode } from "jwt-decode";
467
- import { isPast, secondsToMilliseconds } from "date-fns";
472
+ import { differenceInMilliseconds, isBefore, subMinutes } from "date-fns";
468
473
  var SignerClient = class {
469
474
  _turnkeyClient;
470
475
  _configuration;
471
476
  _sessionStore;
472
- _user;
477
+ _refreshPromise;
478
+ _refreshTimer;
473
479
  constructor({
474
480
  stamper,
475
481
  configuration: { apiUrl, paymasterRpcUrl, ...requiredConfiguration }
@@ -485,54 +491,97 @@ var SignerClient = class {
485
491
  };
486
492
  this._sessionStore = new SessionStore();
487
493
  }
488
- logout() {
489
- this._user = void 0;
490
- this.sessionStore.clearAll();
491
- }
492
494
  get configuration() {
493
495
  return this._configuration;
494
496
  }
495
- get user() {
496
- if (this._user) return this._user;
497
- if (!this.sessionStore.token) {
498
- this.sessionStore.clearAll();
499
- return void 0;
500
- }
501
- const decodedToken = jwtDecode(this.sessionStore.token);
502
- if (decodedToken.exp && isPast(new Date(secondsToMilliseconds(decodedToken.exp)))) {
503
- this.sessionStore.clearAll();
504
- return void 0;
497
+ async getUser() {
498
+ if (this._sessionStore.user) return this._sessionStore.user;
499
+ try {
500
+ const user = await this.request("/v1/me");
501
+ this._sessionStore.user = user;
502
+ return user;
503
+ } catch (error) {
504
+ if (error instanceof UnauthorizedError) {
505
+ try {
506
+ await this._refreshToken();
507
+ const user = await this.request("/v1/me");
508
+ this._sessionStore.user = user;
509
+ return user;
510
+ } catch (error2) {
511
+ throw error2;
512
+ }
513
+ }
514
+ throw error;
505
515
  }
506
- if (this.sessionStore.user) this._user = this.sessionStore.user;
507
- return this._user;
508
516
  }
509
- set user(value) {
510
- this._user = value;
517
+ async isLoggedIn() {
518
+ const token = this._sessionStore.token;
519
+ if (token && !this._isTokenExpired(token)) return true;
520
+ try {
521
+ await this._refreshToken();
522
+ return !!this._sessionStore.token;
523
+ } catch {
524
+ return false;
525
+ }
511
526
  }
512
- set stamper(stamper) {
513
- this._turnkeyClient.stamper = stamper;
527
+ async getToken() {
528
+ if (!this._sessionStore.token) {
529
+ try {
530
+ await this._refreshToken();
531
+ } catch {
532
+ throw new UnauthorizedError({
533
+ message: "No token found, user might not be logged in"
534
+ });
535
+ }
536
+ } else if (this._isTokenExpired(this._sessionStore.token)) {
537
+ try {
538
+ await this._refreshToken();
539
+ } catch {
540
+ throw new UnauthorizedError({
541
+ message: "Token expired and refresh failed"
542
+ });
543
+ }
544
+ }
545
+ const token = this._sessionStore.token;
546
+ if (!token) {
547
+ throw new UnauthorizedError({
548
+ message: "No token found, user might not be logged in"
549
+ });
550
+ }
551
+ return token;
514
552
  }
515
- get stamper() {
516
- return this._turnkeyClient.stamper;
553
+ _isTokenExpired(token) {
554
+ try {
555
+ const decoded = jwtDecode(token);
556
+ if (decoded.exp) {
557
+ return decoded.exp * 1e3 <= Date.now();
558
+ }
559
+ return true;
560
+ } catch {
561
+ return true;
562
+ }
517
563
  }
518
- get sessionStore() {
519
- return this._sessionStore;
564
+ async logout() {
565
+ if (this._refreshTimer) clearTimeout(this._refreshTimer);
566
+ this._refreshTimer = void 0;
567
+ await this.request("/v1/logout");
568
+ this._sessionStore.clearAll();
520
569
  }
521
570
  async signRawMessage(msg) {
522
- if (!this._user) {
571
+ if (!this._sessionStore.token || !this._sessionStore.user) {
523
572
  throw new UnauthorizedError({
524
573
  message: "SignerClient must be authenticated to sign a message"
525
574
  });
526
575
  }
527
576
  const stampedRequest = await this._turnkeyClient.stampSignRawPayload({
528
- organizationId: this._user.subOrgId,
577
+ organizationId: this._sessionStore.user.subOrgId,
529
578
  type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2",
530
579
  timestampMs: Date.now().toString(),
531
580
  parameters: {
532
581
  encoding: "PAYLOAD_ENCODING_HEXADECIMAL",
533
582
  hashFunction: "HASH_FUNCTION_NO_OP",
534
583
  payload: msg,
535
- signWith: this._user.walletAddress
584
+ signWith: this._sessionStore.user.walletAddress
536
585
  }
537
586
  });
538
587
  const { signature } = await this.request("/v1/sign", {
@@ -540,8 +589,11 @@ var SignerClient = class {
540
589
  });
541
590
  return signature;
542
591
  }
543
- getToken() {
544
- return this._sessionStore.token;
592
+ set stamper(stamper) {
593
+ this._turnkeyClient.stamper = stamper;
594
+ }
595
+ get stamper() {
596
+ return this._turnkeyClient.stamper;
545
597
  }
546
598
  async lookUpUser(email) {
547
599
  try {
@@ -561,7 +613,7 @@ var SignerClient = class {
561
613
  }
562
614
  }
563
615
  async whoAmI(subOrgId) {
564
- const orgId = subOrgId || this._user?.subOrgId;
616
+ const orgId = subOrgId || this._sessionStore.user?.subOrgId;
565
617
  if (!orgId) throw new BadRequestError({ message: "No orgId provided" });
566
618
  const stampedRequest = await this._turnkeyClient.stampGetWhoami({
567
619
  organizationId: orgId
@@ -576,19 +628,20 @@ var SignerClient = class {
576
628
  return void 0;
577
629
  }
578
630
  })();
579
- this._user = {
631
+ this._sessionStore.user = {
580
632
  ...user,
581
633
  credentialId
582
634
  };
583
- this.sessionStore.user = this.user;
584
- this.sessionStore.token = token;
635
+ this._sessionStore.token = token;
636
+ this._scheduleRefresh(token);
585
637
  }
586
638
  async request(route, body) {
587
639
  const url = new URL(`${route}`, this._configuration.apiUrl);
588
- const token = this.sessionStore.token;
640
+ const token = this._sessionStore.token;
589
641
  const headers = {
590
642
  "Content-Type": "application/json",
591
- "X-FOURT-KEY": this._configuration.apiKey
643
+ "X-FOURT-KEY": this._configuration.apiKey,
644
+ "X-CSRF-TOKEN": this._getCookie("csrfToken") ?? ""
592
645
  };
593
646
  if (token) {
594
647
  headers["Authorization"] = `Bearer ${token}`;
@@ -603,7 +656,6 @@ var SignerClient = class {
603
656
  if (error) {
604
657
  switch (error.kind) {
605
658
  case "UnauthorizedError": {
606
- this.logout();
607
659
  throw new UnauthorizedError({ message: error.message });
608
660
  }
609
661
  case "NotFoundError": {
@@ -619,11 +671,88 @@ var SignerClient = class {
619
671
  }
620
672
  return { ...data };
621
673
  }
674
+ _scheduleRefresh(token) {
675
+ try {
676
+ const decoded = jwtDecode(token);
677
+ if (!decoded.exp) return;
678
+ const expiryDate = new Date(decoded.exp * 1e3);
679
+ const refreshDate = subMinutes(expiryDate, 2);
680
+ const delay = isBefore(refreshDate, /* @__PURE__ */ new Date()) ? 0 : differenceInMilliseconds(refreshDate, /* @__PURE__ */ new Date());
681
+ if (this._refreshTimer) clearTimeout(this._refreshTimer);
682
+ this._refreshTimer = setTimeout(() => {
683
+ this._refreshTimer = void 0;
684
+ this._refreshToken();
685
+ }, delay);
686
+ } catch {
687
+ }
688
+ }
689
+ async _refreshToken() {
690
+ if (this._refreshPromise) return this._refreshPromise;
691
+ this._refreshPromise = (async () => {
692
+ const TIMEOUT_MS = 1e4;
693
+ const RETRY_DELAY_MS = 5e3;
694
+ try {
695
+ const refreshPromise = this.request("/v1/refresh");
696
+ const data = await Promise.race([
697
+ refreshPromise,
698
+ new Promise(
699
+ (_, reject) => setTimeout(() => reject(new Error("Refresh timeout")), TIMEOUT_MS)
700
+ )
701
+ ]);
702
+ if (!data || !data.token) {
703
+ throw new UnauthorizedError({
704
+ message: "Refresh did not return a token"
705
+ });
706
+ }
707
+ this._sessionStore.token = data.token;
708
+ this._scheduleRefresh(data.token);
709
+ } catch (error) {
710
+ if (error instanceof UnauthorizedError) {
711
+ try {
712
+ this._sessionStore.clearAll();
713
+ } catch {
714
+ }
715
+ throw error;
716
+ }
717
+ if (this._refreshTimer) clearTimeout(this._refreshTimer);
718
+ const MAX_RETRIES = 5;
719
+ let retryCount = 0;
720
+ this._refreshTimer = setTimeout(() => {
721
+ this._refreshTimer = void 0;
722
+ void this._refreshToken().catch(() => {
723
+ retryCount++;
724
+ if (retryCount <= MAX_RETRIES) {
725
+ const nextDelay = Math.min(
726
+ RETRY_DELAY_MS * 2 ** (retryCount - 1),
727
+ 6e4
728
+ );
729
+ this._refreshTimer = setTimeout(() => {
730
+ this._refreshTimer = void 0;
731
+ void this._refreshToken().catch(() => {
732
+ });
733
+ }, nextDelay);
734
+ }
735
+ });
736
+ }, RETRY_DELAY_MS);
737
+ throw error;
738
+ } finally {
739
+ this._refreshPromise = void 0;
740
+ }
741
+ })();
742
+ return this._refreshPromise;
743
+ }
744
+ _getCookie(name) {
745
+ if (typeof document === "undefined") return null;
746
+ const match = document.cookie.match(new RegExp("(^| )" + name + "=([^;]+)"));
747
+ return match ? decodeURIComponent(match[2]) : null;
748
+ }
622
749
  };
623
750
 
624
751
  // src/signer/web.ts
625
752
  var WebSignerClient = class extends SignerClient {
626
- _stampers;
753
+ iframeStamper;
754
+ webauthnStamper;
755
+ iframeConfig;
627
756
  oauthConfiguration;
628
757
  /**
629
758
  * Initializes a new instance of the `WebSignerClient` class.
@@ -636,54 +765,56 @@ var WebSignerClient = class extends SignerClient {
636
765
  iframe,
637
766
  oauth
638
767
  }) {
639
- const iframeContainerId = iframe?.iframeContainerId ?? "fourt-signer-iframe-container";
768
+ const iframeConfig = {
769
+ iframeElementId: iframe?.iframeElementId ?? "turnkey-iframe",
770
+ iframeContainerId: iframe?.iframeContainerId ?? "signer-iframe-container"
771
+ };
640
772
  const iframeContainer = document.createElement("div");
641
- iframeContainer.id = iframeContainerId;
773
+ iframeContainer.id = iframeConfig.iframeContainerId;
642
774
  iframeContainer.style.display = "none";
643
775
  document.body.appendChild(iframeContainer);
644
- const webauthnStamper = new WebauthnStamper({ rpId: webauthn.rpId });
645
776
  const iframeStamper = new IframeStamper({
646
777
  iframeUrl: "https://auth.turnkey.com",
647
- iframeElementId: iframe?.iframeElementId ?? "turnkey-iframe",
648
- iframeContainer: document.getElementById(iframeContainerId)
778
+ iframeElementId: iframeConfig.iframeElementId,
779
+ iframeContainer: document.getElementById(iframeConfig.iframeContainerId)
649
780
  });
650
781
  super({
651
782
  stamper: iframeStamper,
652
783
  // Initialized to iframeStamper; can be either webauthnStamper or iframeStamper
653
784
  configuration
654
785
  });
655
- this._stampers = {
656
- webauthn: webauthnStamper,
657
- iframe: iframeStamper
658
- };
786
+ this.iframeStamper = iframeStamper;
787
+ this.iframeConfig = iframeConfig;
788
+ this.webauthnStamper = new WebauthnStamper({ rpId: webauthn.rpId });
659
789
  this.oauthConfiguration = oauth;
660
790
  }
791
+ async logout() {
792
+ super.logout();
793
+ this.iframeStamper.clear();
794
+ const stamper = new IframeStamper({
795
+ iframeUrl: "https://auth.turnkey.com",
796
+ iframeElementId: this.iframeConfig.iframeElementId,
797
+ iframeContainer: document.getElementById(
798
+ this.iframeConfig.iframeContainerId
799
+ )
800
+ });
801
+ this.iframeStamper = stamper;
802
+ await this._initIframeStamper();
803
+ }
661
804
  async signRawMessage(msg) {
662
- await this.updateStamper();
805
+ await this._updateStamper();
663
806
  return super.signRawMessage(msg);
664
807
  }
665
- async logout() {
666
- this._stampers.iframe.clear();
667
- await this._stampers.iframe.init();
668
- return super.logout();
669
- }
670
808
  /**
671
- * Checks for an existing session and if exists, updates the stamper accordingly.
809
+ * Get the pre-filled URL for initiating oauth with a specific provider.
672
810
  *
811
+ * @param {string} provider provider for which we are getting the URL, currently google or apple
673
812
  */
674
- async updateStamper() {
675
- if (this._sessionStore.type === void 0 && (this._sessionStore.bundle === void 0 || this._sessionStore.token === void 0))
676
- return;
677
- if (this._sessionStore.type === "passkeys" /* Passkeys */) {
678
- this.stamper = this._stampers.webauthn;
679
- } else {
680
- this.stamper = this._stampers.iframe;
681
- await this.completeAuthWithBundle({
682
- bundle: this._sessionStore.bundle,
683
- subOrgId: this.user?.subOrgId,
684
- sessionType: this._sessionStore.type
685
- });
686
- }
813
+ async getOAuthInitUrl(provider) {
814
+ const { url } = await this.request("/v1/oauth/init", {
815
+ provider
816
+ });
817
+ return url;
687
818
  }
688
819
  /**
689
820
  * Signs in a user with webauthn.
@@ -695,15 +826,15 @@ var WebSignerClient = class extends SignerClient {
695
826
  if (!existingUserSubOrgId) {
696
827
  await this._createAccount({ method: "webauthn", email });
697
828
  } else {
698
- this.stamper = this._stampers.webauthn;
829
+ this.stamper = this.webauthnStamper;
699
830
  await this.whoAmI(existingUserSubOrgId);
700
831
  this._sessionStore.type = "passkeys" /* Passkeys */;
701
- if (!this.user || !this.user.credentialId) {
832
+ if (!this._sessionStore.user || !this._sessionStore.user.credentialId) {
702
833
  return;
703
834
  }
704
- this._stampers.webauthn.allowCredentials = [
835
+ this.webauthnStamper.allowCredentials = [
705
836
  {
706
- id: LibBase64.toBuffer(this.user.credentialId),
837
+ id: LibBase64.toBuffer(this._sessionStore.user.credentialId),
707
838
  type: "public-key",
708
839
  transports: ["internal", "usb"]
709
840
  }
@@ -726,23 +857,6 @@ var WebSignerClient = class extends SignerClient {
726
857
  async getIframePublicKey() {
727
858
  return await this._initIframeStamper();
728
859
  }
729
- /**
730
- * Signs in a user with email.
731
- *
732
- * @param {EmailInitializeAuthParams} params params for the sign in
733
- */
734
- async _signInWithEmail({
735
- email,
736
- expirationSeconds,
737
- redirectUrl
738
- }) {
739
- return this.request("/v1/email-auth", {
740
- email,
741
- targetPublicKey: await this.getIframePublicKey(),
742
- expirationSeconds,
743
- redirectUrl: redirectUrl.toString()
744
- });
745
- }
746
860
  /**
747
861
  * Completes the authentication process with a credential bundle.
748
862
  *
@@ -754,7 +868,7 @@ var WebSignerClient = class extends SignerClient {
754
868
  sessionType
755
869
  }) {
756
870
  await this._initIframeStamper();
757
- const result = await this._stampers.iframe.injectCredentialBundle(bundle);
871
+ const result = await this.iframeStamper.injectCredentialBundle(bundle);
758
872
  if (!result) {
759
873
  throw new Error("Failed to inject credential bundle");
760
874
  }
@@ -762,6 +876,40 @@ var WebSignerClient = class extends SignerClient {
762
876
  this._sessionStore.type = sessionType;
763
877
  this._sessionStore.bundle = bundle;
764
878
  }
879
+ /**
880
+ * Checks for an existing session and if exists, updates the stamper accordingly.
881
+ */
882
+ async _updateStamper() {
883
+ if (this._sessionStore.type === void 0 && (this._sessionStore.bundle === void 0 || this._sessionStore.token === void 0))
884
+ return;
885
+ if (this._sessionStore.type === "passkeys" /* Passkeys */) {
886
+ this.stamper = this.webauthnStamper;
887
+ } else {
888
+ this.stamper = this.iframeStamper;
889
+ await this.completeAuthWithBundle({
890
+ bundle: this._sessionStore.bundle,
891
+ subOrgId: this._sessionStore.user?.subOrgId,
892
+ sessionType: this._sessionStore.type
893
+ });
894
+ }
895
+ }
896
+ /**
897
+ * Signs in a user with email.
898
+ *
899
+ * @param {EmailInitializeAuthParams} params params for the sign in
900
+ */
901
+ async _signInWithEmail({
902
+ email,
903
+ expirationSeconds,
904
+ redirectUrl
905
+ }) {
906
+ return this.request("/v1/email-auth", {
907
+ email,
908
+ targetPublicKey: await this.getIframePublicKey(),
909
+ expirationSeconds,
910
+ redirectUrl: redirectUrl.toString()
911
+ });
912
+ }
765
913
  /**
766
914
  * Creates a passkey account using the webauthn stamper.
767
915
  *
@@ -771,28 +919,20 @@ var WebSignerClient = class extends SignerClient {
771
919
  const { challenge, attestation } = await this._webauthnGenerateAttestation(
772
920
  params.email
773
921
  );
774
- const {
775
- token,
776
- user: { id, email, subOrgId, walletAddress, salt, smartAccountAddress }
777
- } = await this.request("/v1/signup", {
922
+ const { token, user } = await this.request("/v1/signup", {
778
923
  passkey: {
779
924
  challenge: LibBase64.fromBuffer(challenge),
780
925
  attestation
781
926
  },
782
927
  email: params.email
783
928
  });
784
- this.user = {
785
- id,
786
- email,
787
- subOrgId,
788
- walletAddress,
789
- salt,
790
- smartAccountAddress,
929
+ this._sessionStore.user = {
930
+ ...user,
791
931
  credentialId: attestation.credentialId
792
932
  };
793
- this._sessionStore.user = this.user;
794
933
  this._sessionStore.type = "passkeys" /* Passkeys */;
795
934
  this._sessionStore.token = token;
935
+ this._scheduleRefresh(token);
796
936
  }
797
937
  /**
798
938
  * Creates an email account using the iframe stamper.
@@ -863,22 +1003,11 @@ var WebSignerClient = class extends SignerClient {
863
1003
  return { challenge, attestation, authenticatorUserId };
864
1004
  }
865
1005
  async _initIframeStamper() {
866
- if (!this._stampers.iframe.publicKey()) {
867
- await this._stampers.iframe.init();
1006
+ if (!this.iframeStamper.publicKey()) {
1007
+ await this.iframeStamper.init();
868
1008
  }
869
- this.stamper = this._stampers.iframe;
870
- return this._stampers.iframe.publicKey();
871
- }
872
- /**
873
- * Get the pre-filled URL for initiating oauth with a specific provider.
874
- *
875
- * @param {string} provider provider for which we are getting the URL, currently google or apple
876
- */
877
- async getOAuthInitUrl(provider) {
878
- const { url } = await this.request("v1/oauth/init", {
879
- provider
880
- });
881
- return url;
1009
+ this.stamper = this.iframeStamper;
1010
+ return this.iframeStamper.publicKey();
882
1011
  }
883
1012
  };
884
1013
 
@@ -902,13 +1031,13 @@ var ViemModule = class {
902
1031
  this._signerClient = _signerClient;
903
1032
  }
904
1033
  async toLocalAccount() {
905
- const user = this._signerClient.user;
1034
+ const user = await this._signerClient.getUser();
906
1035
  if (!user) {
907
1036
  throw new UnauthenticatedError({ message: "Signer not authenticated" });
908
1037
  }
909
1038
  return toAccount({
910
1039
  address: user.walletAddress,
911
- signMessage: (msg) => this.signMessage(msg.message),
1040
+ signMessage: ({ message }) => this.signMessage(message),
912
1041
  signTypedData: (typedDataDefinition) => this.signTypedData(typedDataDefinition),
913
1042
  signTransaction: this.signTransaction
914
1043
  });
@@ -917,7 +1046,7 @@ var ViemModule = class {
917
1046
  client,
918
1047
  owner
919
1048
  }) {
920
- const user = this._signerClient.user;
1049
+ const user = await this._signerClient.getUser();
921
1050
  if (!user) {
922
1051
  throw new UnauthenticatedError({ message: "Signer not authenticated" });
923
1052
  }
@@ -953,7 +1082,7 @@ var ViemModule = class {
953
1082
  }
954
1083
  async signTransaction(transaction, options) {
955
1084
  const serializeFn = options?.serializer ?? serializeTransaction;
956
- const serializedTx = serializeFn(transaction);
1085
+ const serializedTx = await serializeFn(transaction);
957
1086
  const signatureHex = await this._signerClient.signRawMessage(
958
1087
  keccak256(serializedTx)
959
1088
  );