@phantom/react-native-sdk 1.0.7 → 2.0.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.mjs CHANGED
@@ -3,9 +3,10 @@ import { useState as useState5, useEffect as useEffect2, useMemo as useMemo2, us
3
3
  import { EmbeddedProvider } from "@phantom/embedded-provider-core";
4
4
  import {
5
5
  ANALYTICS_HEADERS,
6
+ DEFAULT_AUTH_API_BASE_URL,
6
7
  DEFAULT_WALLET_API_URL,
7
8
  DEFAULT_EMBEDDED_WALLET_TYPE,
8
- DEFAULT_AUTH_URL as DEFAULT_AUTH_URL2
9
+ DEFAULT_AUTH_URL
9
10
  } from "@phantom/constants";
10
11
  import { ThemeProvider, darkTheme } from "@phantom/wallet-sdk-ui";
11
12
 
@@ -488,115 +489,13 @@ var ExpoSecureStorage = class {
488
489
  }
489
490
  };
490
491
 
491
- // src/providers/embedded/auth.ts
492
- import * as WebBrowser from "expo-web-browser";
493
- import { Platform } from "react-native";
494
- import { DEFAULT_AUTH_URL } from "@phantom/constants";
495
- var ExpoAuthProvider = class {
496
- async authenticate(options) {
497
- if ("jwtToken" in options) {
498
- return;
499
- }
500
- const phantomOptions = options;
501
- const { authUrl, redirectUrl, publicKey, sessionId, provider, appId } = phantomOptions;
502
- if (!redirectUrl) {
503
- throw new Error("redirectUrl is required for web browser authentication");
504
- }
505
- if (!publicKey || !sessionId || !appId) {
506
- throw new Error("publicKey, sessionId and appId are required for authentication");
507
- }
508
- try {
509
- const baseUrl = authUrl || DEFAULT_AUTH_URL;
510
- const params = new URLSearchParams({
511
- public_key: publicKey,
512
- app_id: appId,
513
- redirect_uri: redirectUrl,
514
- session_id: sessionId,
515
- // OAuth session management - defaults to allow refresh unless explicitly clearing after logout
516
- clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
517
- allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
518
- sdk_version: "1.0.7",
519
- sdk_type: "react-native",
520
- platform: Platform.OS
521
- });
522
- if (provider) {
523
- console.log("[ExpoAuthProvider] Provider specified, will skip selection", { provider });
524
- params.append("provider", provider);
525
- } else {
526
- console.log("[ExpoAuthProvider] No provider specified, defaulting to Google");
527
- params.append("provider", "google");
528
- }
529
- const fullAuthUrl = `${baseUrl}?${params.toString()}`;
530
- console.log("[ExpoAuthProvider] Starting authentication", {
531
- baseUrl,
532
- redirectUrl,
533
- publicKey,
534
- sessionId,
535
- provider
536
- });
537
- await WebBrowser.warmUpAsync();
538
- const result = await WebBrowser.openAuthSessionAsync(fullAuthUrl, redirectUrl, {
539
- // Use system browser on iOS for ASWebAuthenticationSession
540
- preferEphemeralSession: false
541
- });
542
- console.log("[ExpoAuthProvider] Authentication result", {
543
- type: result.type,
544
- url: result.type === "success" && result.url ? result.url.substring(0, 100) + "..." : void 0
545
- });
546
- if (result.type === "success" && result.url) {
547
- const url = new URL(result.url);
548
- const walletId = url.searchParams.get("wallet_id");
549
- const organizationId = url.searchParams.get("organization_id");
550
- const accountDerivationIndex = url.searchParams.get("selected_account_index");
551
- const expiresInMs = url.searchParams.get("expires_in_ms");
552
- const authUserId = url.searchParams.get("auth_user_id");
553
- if (!walletId) {
554
- throw new Error("Authentication failed: no walletId in redirect URL");
555
- }
556
- if (!organizationId) {
557
- console.error("[ExpoAuthProvider] Missing organizationId in redirect URL", { url: result.url });
558
- throw new Error("Authentication failed: no organizationId in redirect URL");
559
- }
560
- console.log("[ExpoAuthProvider] Auth redirect parameters", {
561
- walletId,
562
- organizationId,
563
- provider,
564
- accountDerivationIndex,
565
- expiresInMs,
566
- authUserId
567
- });
568
- return {
569
- walletId,
570
- organizationId,
571
- provider: provider || void 0,
572
- accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : 0,
573
- expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0,
574
- authUserId: authUserId || void 0
575
- };
576
- } else if (result.type === "cancel") {
577
- throw new Error("User cancelled authentication");
578
- } else {
579
- throw new Error("Authentication failed");
580
- }
581
- } catch (error) {
582
- console.error("[ExpoAuthProvider] Authentication error", error);
583
- throw error;
584
- } finally {
585
- await WebBrowser.coolDownAsync();
586
- }
587
- }
588
- isAvailable() {
589
- return Promise.resolve(true);
590
- }
591
- };
592
-
593
492
  // src/providers/embedded/ExpoAuth2AuthProvider.ts
594
- import * as WebBrowser2 from "expo-web-browser";
493
+ import * as WebBrowser from "expo-web-browser";
595
494
  import {
596
- createCodeVerifier,
597
- exchangeAuthCode,
598
495
  Auth2KmsRpcClient,
599
- createConnectStartUrl
496
+ prepareAuth2Flow,
497
+ validateAuth2Callback,
498
+ completeAuth2Exchange
600
499
  } from "@phantom/auth2";
601
500
  var ExpoAuth2AuthProvider = class {
602
501
  constructor(stamper, auth2ProviderOptions, kmsClientOptions) {
@@ -612,246 +511,111 @@ var ExpoAuth2AuthProvider = class {
612
511
  * so the token exchange and KMS calls all happen here before returning AuthResult.
613
512
  */
614
513
  async authenticate(options) {
615
- if (!this.stamper.getKeyInfo()) {
616
- await this.stamper.init();
617
- }
618
- const keyPair = this.stamper.getCryptoKeyPair();
619
- if (!keyPair) {
620
- throw new Error("Stamper key pair not found.");
621
- }
622
- const codeVerifier = createCodeVerifier();
623
- const url = await createConnectStartUrl({
624
- keyPair,
625
- connectLoginUrl: this.auth2ProviderOptions.connectLoginUrl,
626
- clientId: this.auth2ProviderOptions.clientId,
627
- redirectUri: this.auth2ProviderOptions.redirectUri,
514
+ const { url, codeVerifier } = await prepareAuth2Flow({
515
+ stamper: this.stamper,
516
+ auth2Options: this.auth2ProviderOptions,
628
517
  sessionId: options.sessionId,
629
- provider: options.provider,
630
- codeVerifier,
631
- // The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
632
- salt: ""
518
+ provider: options.provider
633
519
  });
634
- await WebBrowser2.warmUpAsync();
520
+ await WebBrowser.warmUpAsync();
635
521
  let result;
636
522
  try {
637
- result = await WebBrowser2.openAuthSessionAsync(url, this.auth2ProviderOptions.redirectUri);
523
+ result = await WebBrowser.openAuthSessionAsync(url, this.auth2ProviderOptions.redirectUri);
638
524
  } finally {
639
- await WebBrowser2.coolDownAsync();
525
+ await WebBrowser.coolDownAsync();
640
526
  }
641
527
  if (!result.url) {
642
528
  throw new Error("Authentication failed");
643
529
  }
644
530
  const callbackUrl = new URL(result.url);
645
- const state = callbackUrl.searchParams.get("state");
646
- if (state && state !== options.sessionId) {
647
- throw new Error("Auth2 state mismatch \u2014 possible CSRF attack.");
648
- }
649
- const error = callbackUrl.searchParams.get("error");
650
- if (error) {
651
- const description = callbackUrl.searchParams.get("error_description");
652
- throw new Error(`Auth2 callback error: ${description ?? error}`);
653
- }
654
- const code = callbackUrl.searchParams.get("code");
655
- if (!code) {
656
- throw new Error("Auth2 callback missing authorization code");
657
- }
658
- const { idToken, bearerToken, authUserId, expiresInMs, refreshToken } = await exchangeAuthCode({
659
- authApiBaseUrl: this.auth2ProviderOptions.authApiBaseUrl,
660
- clientId: this.auth2ProviderOptions.clientId,
661
- redirectUri: this.auth2ProviderOptions.redirectUri,
531
+ const code = validateAuth2Callback({
532
+ getParam: (key) => callbackUrl.searchParams.get(key),
533
+ expectedSessionId: options.sessionId
534
+ });
535
+ return completeAuth2Exchange({
536
+ stamper: this.stamper,
537
+ kms: this.kms,
538
+ auth2Options: this.auth2ProviderOptions,
662
539
  code,
663
- codeVerifier
540
+ codeVerifier,
541
+ provider: options.provider
664
542
  });
665
- await this.stamper.setTokens({ idToken, bearerToken, refreshToken, expiresInMs });
666
- const { organizationId, walletId } = await this.kms.discoverOrganizationAndWalletId(bearerToken, authUserId);
667
- return {
668
- walletId,
669
- organizationId,
670
- provider: options.provider,
671
- accountDerivationIndex: 0,
672
- // discoverWalletId uses derivation index of 0.
673
- expiresInMs,
674
- authUserId,
675
- bearerToken
676
- };
677
543
  }
678
544
  };
679
545
 
680
- // src/providers/embedded/ExpoAuth2Stamper.ts
546
+ // src/PhantomProvider.tsx
547
+ import { Auth2Stamper } from "@phantom/auth2";
548
+
549
+ // src/providers/embedded/SecureStoreAuth2StamperStorage.ts
681
550
  import * as SecureStore2 from "expo-secure-store";
682
551
  import bs58 from "bs58";
683
552
  import { Buffer } from "buffer";
684
- import { base64urlEncode } from "@phantom/base64url";
685
- import { Algorithm } from "@phantom/sdk-types";
686
- import { refreshToken as refreshTokenRequest } from "@phantom/auth2";
687
- import { TOKEN_REFRESH_BUFFER_MS } from "@phantom/constants";
688
- var ExpoAuth2Stamper = class {
689
- /**
690
- * @param storageKey - expo-secure-store key used to persist the P-256 private key.
691
- * Use a unique key per app, e.g. `phantom-auth2-<appId>`.
692
- * @param refreshConfig - When provided, the stamper will automatically refresh
693
- * the id_token using the refresh_token before it expires.
694
- */
695
- constructor(storageKey, refreshConfig) {
553
+ var SecureStoreAuth2StamperStorage = class {
554
+ constructor(storageKey) {
696
555
  this.storageKey = storageKey;
697
- this.refreshConfig = refreshConfig;
698
- this._keyPair = null;
699
- this._keyInfo = null;
700
- this._idToken = null;
701
- this._bearerToken = null;
702
- this._refreshToken = null;
703
- this._tokenExpiresAt = null;
704
- this.algorithm = Algorithm.secp256r1;
705
- this.type = "OIDC";
556
+ this.requiresExtractableKeys = true;
706
557
  }
707
- async init() {
708
- const stored = await this.loadRecord();
709
- if (stored) {
710
- this._keyPair = {
711
- privateKey: await this.importPrivateKey(stored.privateKeyPkcs8),
712
- publicKey: await this.importPublicKeyFromBase58(stored.keyInfo.publicKey)
713
- };
714
- this._keyInfo = stored.keyInfo;
715
- if (stored.idToken) {
716
- this._idToken = stored.idToken;
717
- }
718
- if (stored.bearerToken) {
719
- this._bearerToken = stored.bearerToken;
720
- }
721
- if (stored.refreshToken) {
722
- this._refreshToken = stored.refreshToken;
723
- }
724
- if (stored.tokenExpiresAt) {
725
- this._tokenExpiresAt = stored.tokenExpiresAt;
726
- }
727
- return this._keyInfo;
558
+ async load() {
559
+ const raw = await SecureStore2.getItemAsync(this.storageKey);
560
+ if (raw === null) {
561
+ return null;
728
562
  }
729
- return this.generateAndStore();
730
- }
731
- getKeyInfo() {
732
- return this._keyInfo;
733
- }
734
- getCryptoKeyPair() {
735
- return this._keyPair;
736
- }
737
- /**
738
- * Returns the current token state (refreshing proactively if near expiry),
739
- * or null if no token has been set yet.
740
- */
741
- async getTokens() {
742
- if (this.refreshConfig && this._refreshToken && this._tokenExpiresAt !== null && Date.now() >= this._tokenExpiresAt - TOKEN_REFRESH_BUFFER_MS) {
743
- const refreshed = await refreshTokenRequest({
744
- authApiBaseUrl: this.refreshConfig.authApiBaseUrl,
745
- clientId: this.refreshConfig.clientId,
746
- redirectUri: this.refreshConfig.redirectUri,
747
- refreshToken: this._refreshToken
748
- });
749
- await this.setTokens(refreshed);
563
+ let record;
564
+ try {
565
+ record = JSON.parse(raw);
566
+ } catch (err) {
567
+ await SecureStore2.deleteItemAsync(this.storageKey);
568
+ throw new Error(`SecureStoreAuth2StamperStorage: corrupt stored record (JSON parse failed): ${err}`);
750
569
  }
751
- if (!this._idToken || !this._bearerToken) {
752
- return null;
570
+ let privateKey;
571
+ let publicKey;
572
+ try {
573
+ privateKey = await this.importPrivateKey(record.privateKeyPkcs8);
574
+ publicKey = await this.importPublicKeyFromBase58(record.keyInfo.publicKey);
575
+ } catch (err) {
576
+ await SecureStore2.deleteItemAsync(this.storageKey);
577
+ throw new Error(`SecureStoreAuth2StamperStorage: corrupt stored record (key import failed): ${err}`);
753
578
  }
754
579
  return {
755
- idToken: this._idToken,
756
- bearerToken: this._bearerToken,
757
- refreshToken: this._refreshToken ?? void 0
580
+ keyPair: { privateKey, publicKey },
581
+ keyInfo: record.keyInfo,
582
+ accessToken: record.accessToken,
583
+ idType: record.idType,
584
+ refreshToken: record.refreshToken,
585
+ tokenExpiresAt: record.tokenExpiresAt
758
586
  };
759
587
  }
760
- /**
761
- * Arms the stamper with the OIDC token data for subsequent KMS stamp() calls.
762
- *
763
- * Persists the tokens to SecureStore alongside the key pair so that
764
- * auto-connect can restore them on the next app launch without a new login.
765
- *
766
- * @param refreshToken - When provided alongside a `refreshConfig`, enables
767
- * silent token refresh before the token expires.
768
- * @param expiresInMs - Token lifetime in milliseconds (from `expires_in * 1000`).
769
- * Used to compute the absolute expiry time for proactive refresh.
770
- */
771
- async setTokens({
772
- idToken,
773
- bearerToken,
774
- refreshToken,
775
- expiresInMs
776
- }) {
777
- this._idToken = idToken;
778
- this._bearerToken = bearerToken;
779
- this._refreshToken = refreshToken ?? null;
780
- this._tokenExpiresAt = expiresInMs != null ? Date.now() + expiresInMs : null;
781
- const existing = await this.loadRecord();
782
- if (existing) {
783
- await this.storeRecord({
784
- ...existing,
785
- idToken,
786
- bearerToken,
787
- refreshToken,
788
- tokenExpiresAt: this._tokenExpiresAt ?? void 0
789
- });
790
- }
791
- }
792
- async stamp(params) {
793
- if (!this._keyPair || !this._keyInfo || this._idToken === null) {
794
- throw new Error("ExpoAuth2Stamper not initialized. Call init() first.");
795
- }
796
- const signatureRaw = await crypto.subtle.sign(
797
- { name: "ECDSA", hash: "SHA-256" },
798
- this._keyPair.privateKey,
799
- new Uint8Array(params.data)
800
- );
801
- const rawPublicKey = bs58.decode(this._keyInfo.publicKey);
802
- const stampData = {
803
- kind: this.type,
804
- idToken: this._idToken,
805
- publicKey: base64urlEncode(rawPublicKey),
806
- algorithm: this.algorithm,
807
- // The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
808
- salt: "",
809
- signature: base64urlEncode(new Uint8Array(signatureRaw))
588
+ async save(record) {
589
+ const pkcs8Buffer = await crypto.subtle.exportKey("pkcs8", record.keyPair.privateKey);
590
+ const privateKeyPkcs8 = Buffer.from(pkcs8Buffer).toString("base64");
591
+ const serialized = {
592
+ privateKeyPkcs8,
593
+ keyInfo: record.keyInfo,
594
+ accessToken: record.accessToken,
595
+ idType: record.idType,
596
+ refreshToken: record.refreshToken,
597
+ tokenExpiresAt: record.tokenExpiresAt
810
598
  };
811
- return base64urlEncode(new TextEncoder().encode(JSON.stringify(stampData)));
812
- }
813
- async resetKeyPair() {
814
- await this.clear();
815
- return this.generateAndStore();
599
+ await SecureStore2.setItemAsync(this.storageKey, JSON.stringify(serialized), {
600
+ requireAuthentication: false
601
+ });
816
602
  }
817
603
  async clear() {
818
- await this.clearStoredRecord();
819
- this._keyPair = null;
820
- this._keyInfo = null;
821
- this._idToken = null;
822
- this._bearerToken = null;
823
- this._refreshToken = null;
824
- this._tokenExpiresAt = null;
825
- }
826
- // Auth2 doesn't use key rotation; minimal no-op implementations.
827
- async rotateKeyPair() {
828
- return this.init();
829
- }
830
- // eslint-disable-next-line @typescript-eslint/require-await
831
- async commitRotation(authenticatorId) {
832
- if (this._keyInfo) {
833
- this._keyInfo.authenticatorId = authenticatorId;
604
+ try {
605
+ await SecureStore2.deleteItemAsync(this.storageKey);
606
+ } catch {
834
607
  }
835
608
  }
836
- async rollbackRotation() {
837
- }
838
- async generateAndStore() {
839
- const keyPair = await crypto.subtle.generateKey(
609
+ async importPrivateKey(pkcs8Base64) {
610
+ const pkcs8Bytes = Buffer.from(pkcs8Base64, "base64");
611
+ return crypto.subtle.importKey(
612
+ "pkcs8",
613
+ pkcs8Bytes,
840
614
  { name: "ECDSA", namedCurve: "P-256" },
841
- true,
842
- // extractable needed to export PKCS#8 for SecureStore
843
- ["sign", "verify"]
615
+ this.requiresExtractableKeys,
616
+ // extractable so save() can re-export via pkcs8
617
+ ["sign"]
844
618
  );
845
- const rawPublicKey = new Uint8Array(await crypto.subtle.exportKey("raw", keyPair.publicKey));
846
- const publicKeyBase58 = bs58.encode(rawPublicKey);
847
- const keyIdBuffer = await crypto.subtle.digest("SHA-256", rawPublicKey.buffer);
848
- const keyId = base64urlEncode(new Uint8Array(keyIdBuffer)).substring(0, 16);
849
- this._keyPair = keyPair;
850
- this._keyInfo = { keyId, publicKey: publicKeyBase58, createdAt: Date.now() };
851
- const pkcs8Buffer = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
852
- const privateKeyPkcs8 = Buffer.from(pkcs8Buffer).toString("base64");
853
- await this.storeRecord({ privateKeyPkcs8, keyInfo: this._keyInfo });
854
- return this._keyInfo;
855
619
  }
856
620
  async importPublicKeyFromBase58(base58PublicKey) {
857
621
  const rawBytes = bs58.decode(base58PublicKey);
@@ -864,34 +628,6 @@ var ExpoAuth2Stamper = class {
864
628
  ["verify"]
865
629
  );
866
630
  }
867
- async importPrivateKey(pkcs8Base64) {
868
- const pkcs8Bytes = Buffer.from(pkcs8Base64, "base64");
869
- return crypto.subtle.importKey(
870
- "pkcs8",
871
- pkcs8Bytes,
872
- { name: "ECDSA", namedCurve: "P-256" },
873
- false,
874
- // non-extractable once loaded into memory
875
- ["sign"]
876
- );
877
- }
878
- async loadRecord() {
879
- try {
880
- const raw = await SecureStore2.getItemAsync(this.storageKey);
881
- return raw ? JSON.parse(raw) : null;
882
- } catch {
883
- return null;
884
- }
885
- }
886
- async storeRecord(record) {
887
- await SecureStore2.setItemAsync(this.storageKey, JSON.stringify(record), { requireAuthentication: false });
888
- }
889
- async clearStoredRecord() {
890
- try {
891
- await SecureStore2.deleteItemAsync(this.storageKey);
892
- } catch {
893
- }
894
- }
895
631
  };
896
632
 
897
633
  // src/providers/embedded/url-params.ts
@@ -962,180 +698,6 @@ var ExpoURLParamsAccessor = class {
962
698
  }
963
699
  };
964
700
 
965
- // src/providers/embedded/stamper.ts
966
- import * as SecureStore3 from "expo-secure-store";
967
- import { ApiKeyStamper } from "@phantom/api-key-stamper";
968
- import { DEFAULT_AUTHENTICATOR_ALGORITHM } from "@phantom/constants";
969
- import { generateKeyPair } from "@phantom/crypto";
970
- import { base64urlEncode as base64urlEncode2 } from "@phantom/base64url";
971
- var ReactNativeStamper = class {
972
- // Optional for PKI, required for OIDC
973
- constructor(config = {}) {
974
- this.activeKeyRecord = null;
975
- this.pendingKeyRecord = null;
976
- this.algorithm = DEFAULT_AUTHENTICATOR_ALGORITHM;
977
- this.type = "PKI";
978
- this.keyPrefix = config.keyPrefix || "phantom-rn-stamper";
979
- this.appId = config.appId || "default";
980
- }
981
- /**
982
- * Initialize the stamper and generate/load cryptographic keys
983
- */
984
- async init() {
985
- this.activeKeyRecord = await this.loadActiveKeyRecord();
986
- if (!this.activeKeyRecord) {
987
- this.activeKeyRecord = await this.generateAndStoreNewKeyRecord("active");
988
- }
989
- this.pendingKeyRecord = await this.loadPendingKeyRecord();
990
- return this.activeKeyRecord.keyInfo;
991
- }
992
- /**
993
- * Get the current key information
994
- */
995
- getKeyInfo() {
996
- return this.activeKeyRecord?.keyInfo || null;
997
- }
998
- /**
999
- * Generate and store a new key pair, replacing any existing keys
1000
- */
1001
- async resetKeyPair() {
1002
- await this.clear();
1003
- this.activeKeyRecord = await this.generateAndStoreNewKeyRecord("active");
1004
- this.pendingKeyRecord = null;
1005
- return this.activeKeyRecord.keyInfo;
1006
- }
1007
- /**
1008
- * Create X-Phantom-Stamp header value using stored secret key
1009
- * @param params - Parameters object with data to sign and optional override params
1010
- * @returns Complete X-Phantom-Stamp header value
1011
- */
1012
- async stamp(params) {
1013
- if (!this.activeKeyRecord) {
1014
- throw new Error("Stamper not initialized. Call init() first.");
1015
- }
1016
- const apiKeyStamper = new ApiKeyStamper({ apiSecretKey: this.activeKeyRecord.secretKey });
1017
- return await apiKeyStamper.stamp(params);
1018
- }
1019
- /**
1020
- * Clear all stored keys from SecureStore
1021
- */
1022
- async clear() {
1023
- const activeKey = this.getActiveKeyName();
1024
- const pendingKey = this.getPendingKeyName();
1025
- try {
1026
- await SecureStore3.deleteItemAsync(activeKey);
1027
- } catch (error) {
1028
- }
1029
- try {
1030
- await SecureStore3.deleteItemAsync(pendingKey);
1031
- } catch (error) {
1032
- }
1033
- this.activeKeyRecord = null;
1034
- this.pendingKeyRecord = null;
1035
- }
1036
- /**
1037
- * Generate a new keypair for rotation without making it active
1038
- */
1039
- async rotateKeyPair() {
1040
- this.pendingKeyRecord = await this.generateAndStoreNewKeyRecord("pending");
1041
- return this.pendingKeyRecord.keyInfo;
1042
- }
1043
- /**
1044
- * Switch to the pending keypair, making it active and cleaning up the old one
1045
- */
1046
- async commitRotation(authenticatorId) {
1047
- if (!this.pendingKeyRecord) {
1048
- throw new Error("No pending keypair to commit");
1049
- }
1050
- if (this.activeKeyRecord) {
1051
- try {
1052
- await SecureStore3.deleteItemAsync(this.getActiveKeyName());
1053
- } catch (error) {
1054
- }
1055
- }
1056
- this.pendingKeyRecord.status = "active";
1057
- this.pendingKeyRecord.authenticatorId = authenticatorId;
1058
- this.pendingKeyRecord.keyInfo.authenticatorId = authenticatorId;
1059
- this.activeKeyRecord = this.pendingKeyRecord;
1060
- this.pendingKeyRecord = null;
1061
- await this.storeKeyRecord(this.activeKeyRecord, "active");
1062
- try {
1063
- await SecureStore3.deleteItemAsync(this.getPendingKeyName());
1064
- } catch (error) {
1065
- }
1066
- }
1067
- /**
1068
- * Discard the pending keypair on rotation failure
1069
- */
1070
- async rollbackRotation() {
1071
- if (!this.pendingKeyRecord) {
1072
- return;
1073
- }
1074
- try {
1075
- await SecureStore3.deleteItemAsync(this.getPendingKeyName());
1076
- } catch (error) {
1077
- }
1078
- this.pendingKeyRecord = null;
1079
- }
1080
- async generateAndStoreNewKeyRecord(type) {
1081
- const keypair = generateKeyPair();
1082
- const keyId = this.createKeyId(keypair.publicKey);
1083
- const now = Date.now();
1084
- const keyInfo = {
1085
- keyId,
1086
- publicKey: keypair.publicKey,
1087
- createdAt: now
1088
- };
1089
- const record = {
1090
- keyInfo,
1091
- secretKey: keypair.secretKey,
1092
- createdAt: now,
1093
- expiresAt: 0,
1094
- // Not used anymore, kept for backward compatibility
1095
- status: type
1096
- };
1097
- await this.storeKeyRecord(record, type);
1098
- return record;
1099
- }
1100
- createKeyId(publicKey) {
1101
- return base64urlEncode2(new TextEncoder().encode(publicKey)).substring(0, 16);
1102
- }
1103
- async storeKeyRecord(record, type) {
1104
- const keyName = type === "active" ? this.getActiveKeyName() : this.getPendingKeyName();
1105
- await SecureStore3.setItemAsync(keyName, JSON.stringify(record), {
1106
- requireAuthentication: false
1107
- });
1108
- }
1109
- async loadActiveKeyRecord() {
1110
- try {
1111
- const activeKey = this.getActiveKeyName();
1112
- const storedRecord = await SecureStore3.getItemAsync(activeKey);
1113
- if (storedRecord) {
1114
- return JSON.parse(storedRecord);
1115
- }
1116
- } catch (error) {
1117
- }
1118
- return null;
1119
- }
1120
- async loadPendingKeyRecord() {
1121
- try {
1122
- const pendingKey = this.getPendingKeyName();
1123
- const storedRecord = await SecureStore3.getItemAsync(pendingKey);
1124
- if (storedRecord) {
1125
- return JSON.parse(storedRecord);
1126
- }
1127
- } catch (error) {
1128
- }
1129
- return null;
1130
- }
1131
- getActiveKeyName() {
1132
- return `${this.keyPrefix}-${this.appId}-active`;
1133
- }
1134
- getPendingKeyName() {
1135
- return `${this.keyPrefix}-${this.appId}-pending`;
1136
- }
1137
- };
1138
-
1139
701
  // src/providers/embedded/logger.ts
1140
702
  var ExpoLogger = class {
1141
703
  constructor(enabled = false) {
@@ -1178,7 +740,7 @@ var ReactNativePhantomAppProvider = class {
1178
740
  };
1179
741
 
1180
742
  // src/PhantomProvider.tsx
1181
- import { Platform as Platform2 } from "react-native";
743
+ import { Platform } from "react-native";
1182
744
  import { jsx as jsx6 } from "react/jsx-runtime";
1183
745
  function PhantomProvider({ children, config, debugConfig, theme, appIcon, appName }) {
1184
746
  const [isConnected, setIsConnected] = useState5(false);
@@ -1194,9 +756,9 @@ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appNam
1194
756
  apiBaseUrl: config.apiBaseUrl || DEFAULT_WALLET_API_URL,
1195
757
  embeddedWalletType: config.embeddedWalletType || DEFAULT_EMBEDDED_WALLET_TYPE,
1196
758
  authOptions: {
1197
- ...config.authOptions || {},
1198
759
  redirectUrl,
1199
- authUrl: config.authOptions?.authUrl || DEFAULT_AUTH_URL2
760
+ authUrl: config.authOptions?.authUrl || DEFAULT_AUTH_URL,
761
+ authApiBaseUrl: config.authOptions?.authApiBaseUrl || DEFAULT_AUTH_API_BASE_URL
1200
762
  }
1201
763
  };
1202
764
  }, [config]);
@@ -1204,28 +766,25 @@ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appNam
1204
766
  const storage = new ExpoSecureStorage();
1205
767
  const urlParamsAccessor = new ExpoURLParamsAccessor();
1206
768
  const logger = new ExpoLogger(debugConfig?.enabled || false);
1207
- const stamper = config.unstable__auth2Options && config.authOptions?.redirectUrl ? new ExpoAuth2Stamper(`phantom-auth2-${memoizedConfig.appId}`, {
1208
- authApiBaseUrl: config.unstable__auth2Options.authApiBaseUrl,
1209
- clientId: config.unstable__auth2Options.clientId,
1210
- redirectUri: config.authOptions.redirectUrl
1211
- }) : new ReactNativeStamper({
1212
- keyPrefix: `phantom-rn-${memoizedConfig.appId}`,
1213
- appId: memoizedConfig.appId
769
+ const stamper = new Auth2Stamper(new SecureStoreAuth2StamperStorage(`phantom-auth2-${memoizedConfig.appId}`), {
770
+ authApiBaseUrl: memoizedConfig.authOptions.authApiBaseUrl,
771
+ clientId: memoizedConfig.appId,
772
+ redirectUri: memoizedConfig.authOptions.redirectUrl
1214
773
  });
1215
- const authProvider = config.unstable__auth2Options && config.authOptions?.authUrl && config.authOptions?.redirectUrl && config.apiBaseUrl && stamper instanceof ExpoAuth2Stamper ? new ExpoAuth2AuthProvider(
774
+ const authProvider = new ExpoAuth2AuthProvider(
1216
775
  stamper,
1217
776
  {
1218
- redirectUri: config.authOptions.redirectUrl,
1219
- connectLoginUrl: config.authOptions.authUrl,
1220
- clientId: config.unstable__auth2Options.clientId,
1221
- authApiBaseUrl: config.unstable__auth2Options.authApiBaseUrl
777
+ redirectUri: memoizedConfig.authOptions.redirectUrl,
778
+ connectLoginUrl: memoizedConfig.authOptions.authUrl,
779
+ clientId: memoizedConfig.appId,
780
+ authApiBaseUrl: memoizedConfig.authOptions.authApiBaseUrl
1222
781
  },
1223
782
  {
1224
- apiBaseUrl: config.apiBaseUrl,
1225
- appId: config.appId
783
+ apiBaseUrl: memoizedConfig.apiBaseUrl,
784
+ appId: memoizedConfig.appId
1226
785
  }
1227
- ) : new ExpoAuthProvider();
1228
- const platformName = `${Platform2.OS}-${Platform2.Version}`;
786
+ );
787
+ const platformName = `${Platform.OS}-${Platform.Version}`;
1229
788
  const platform = {
1230
789
  storage,
1231
790
  authProvider,
@@ -1236,11 +795,11 @@ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appNam
1236
795
  analyticsHeaders: {
1237
796
  [ANALYTICS_HEADERS.SDK_TYPE]: "react-native",
1238
797
  [ANALYTICS_HEADERS.PLATFORM]: "ext-sdk",
1239
- [ANALYTICS_HEADERS.PLATFORM_VERSION]: `${Platform2.Version}`,
1240
- [ANALYTICS_HEADERS.CLIENT]: Platform2.OS,
798
+ [ANALYTICS_HEADERS.PLATFORM_VERSION]: `${Platform.Version}`,
799
+ [ANALYTICS_HEADERS.CLIENT]: Platform.OS,
1241
800
  [ANALYTICS_HEADERS.APP_ID]: config.appId,
1242
801
  [ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
1243
- [ANALYTICS_HEADERS.SDK_VERSION]: "1.0.7"
802
+ [ANALYTICS_HEADERS.SDK_VERSION]: "2.0.0"
1244
803
  // Replaced at build time
1245
804
  }
1246
805
  };
@@ -1355,14 +914,14 @@ function useEthereum() {
1355
914
  // src/index.ts
1356
915
  import { AddressType } from "@phantom/client";
1357
916
  import { NetworkId } from "@phantom/constants";
1358
- import { base64urlEncode as base64urlEncode3, base64urlDecode } from "@phantom/base64url";
917
+ import { base64urlEncode, base64urlDecode } from "@phantom/base64url";
1359
918
  import { darkTheme as darkTheme2, lightTheme } from "@phantom/wallet-sdk-ui";
1360
919
  export {
1361
920
  AddressType,
1362
921
  NetworkId,
1363
922
  PhantomProvider,
1364
923
  base64urlDecode,
1365
- base64urlEncode3 as base64urlEncode,
924
+ base64urlEncode,
1366
925
  darkTheme2 as darkTheme,
1367
926
  lightTheme,
1368
927
  useAccounts,