@phantom/react-native-sdk 1.0.3 → 1.0.5
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.d.ts +5 -0
- package/dist/index.js +267 -17
- package/dist/index.mjs +272 -17
- package/package.json +11 -10
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,11 @@ interface PhantomSDKConfig extends Omit<EmbeddedProviderConfig, "apiBaseUrl" | "
|
|
|
25
25
|
authUrl?: string;
|
|
26
26
|
redirectUrl?: string;
|
|
27
27
|
};
|
|
28
|
+
/** When also provided, the Auth2 PKCE flow is used instead of the legacy Phantom Connect flow. */
|
|
29
|
+
unstable__auth2Options?: {
|
|
30
|
+
authApiBaseUrl: string;
|
|
31
|
+
clientId: string;
|
|
32
|
+
};
|
|
28
33
|
}
|
|
29
34
|
interface ConnectOptions {
|
|
30
35
|
/** OAuth provider to use (required) */
|
package/dist/index.js
CHANGED
|
@@ -557,7 +557,7 @@ var ExpoAuthProvider = class {
|
|
|
557
557
|
// OAuth session management - defaults to allow refresh unless explicitly clearing after logout
|
|
558
558
|
clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
|
|
559
559
|
allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
|
|
560
|
-
sdk_version: "1.0.
|
|
560
|
+
sdk_version: "1.0.5",
|
|
561
561
|
sdk_type: "react-native",
|
|
562
562
|
platform: import_react_native5.Platform.OS
|
|
563
563
|
});
|
|
@@ -632,6 +632,243 @@ var ExpoAuthProvider = class {
|
|
|
632
632
|
}
|
|
633
633
|
};
|
|
634
634
|
|
|
635
|
+
// src/providers/embedded/ExpoAuth2AuthProvider.ts
|
|
636
|
+
var WebBrowser2 = __toESM(require("expo-web-browser"));
|
|
637
|
+
var import_auth2 = require("@phantom/auth2");
|
|
638
|
+
var ExpoAuth2AuthProvider = class {
|
|
639
|
+
constructor(stamper, auth2ProviderOptions, kmsClientOptions) {
|
|
640
|
+
this.stamper = stamper;
|
|
641
|
+
this.auth2ProviderOptions = auth2ProviderOptions;
|
|
642
|
+
this.kms = new import_auth2.Auth2KmsRpcClient(stamper, kmsClientOptions);
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Runs the full PKCE Auth2 flow inline using expo-web-browser.
|
|
646
|
+
*
|
|
647
|
+
* Unlike the browser flow (which requires a page redirect and resumeAuthFromRedirect),
|
|
648
|
+
* expo-web-browser intercepts the OAuth callback URL and returns it synchronously,
|
|
649
|
+
* so the token exchange and KMS calls all happen here before returning AuthResult.
|
|
650
|
+
*/
|
|
651
|
+
async authenticate(options) {
|
|
652
|
+
if (!this.stamper.getKeyInfo()) {
|
|
653
|
+
await this.stamper.init();
|
|
654
|
+
}
|
|
655
|
+
const keyPair = this.stamper.getCryptoKeyPair();
|
|
656
|
+
if (!keyPair) {
|
|
657
|
+
throw new Error("Stamper key pair not found.");
|
|
658
|
+
}
|
|
659
|
+
const codeVerifier = (0, import_auth2.createCodeVerifier)();
|
|
660
|
+
const url = await (0, import_auth2.createConnectStartUrl)({
|
|
661
|
+
keyPair,
|
|
662
|
+
connectLoginUrl: this.auth2ProviderOptions.connectLoginUrl,
|
|
663
|
+
clientId: this.auth2ProviderOptions.clientId,
|
|
664
|
+
redirectUri: this.auth2ProviderOptions.redirectUri,
|
|
665
|
+
sessionId: options.sessionId,
|
|
666
|
+
provider: options.provider,
|
|
667
|
+
codeVerifier,
|
|
668
|
+
// The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
|
|
669
|
+
salt: ""
|
|
670
|
+
});
|
|
671
|
+
await WebBrowser2.warmUpAsync();
|
|
672
|
+
let result;
|
|
673
|
+
try {
|
|
674
|
+
result = await WebBrowser2.openAuthSessionAsync(url, this.auth2ProviderOptions.redirectUri);
|
|
675
|
+
} finally {
|
|
676
|
+
await WebBrowser2.coolDownAsync();
|
|
677
|
+
}
|
|
678
|
+
if (!result.url) {
|
|
679
|
+
throw new Error("Authentication failed");
|
|
680
|
+
}
|
|
681
|
+
const callbackUrl = new URL(result.url);
|
|
682
|
+
const state = callbackUrl.searchParams.get("state");
|
|
683
|
+
if (state && state !== options.sessionId) {
|
|
684
|
+
throw new Error("Auth2 state mismatch \u2014 possible CSRF attack.");
|
|
685
|
+
}
|
|
686
|
+
const error = callbackUrl.searchParams.get("error");
|
|
687
|
+
if (error) {
|
|
688
|
+
const description = callbackUrl.searchParams.get("error_description");
|
|
689
|
+
throw new Error(`Auth2 callback error: ${description ?? error}`);
|
|
690
|
+
}
|
|
691
|
+
const code = callbackUrl.searchParams.get("code");
|
|
692
|
+
if (!code) {
|
|
693
|
+
throw new Error("Auth2 callback missing authorization code");
|
|
694
|
+
}
|
|
695
|
+
const { idToken, bearerToken, authUserId, expiresInMs } = await (0, import_auth2.exchangeAuthCode)({
|
|
696
|
+
authApiBaseUrl: this.auth2ProviderOptions.authApiBaseUrl,
|
|
697
|
+
clientId: this.auth2ProviderOptions.clientId,
|
|
698
|
+
redirectUri: this.auth2ProviderOptions.redirectUri,
|
|
699
|
+
code,
|
|
700
|
+
codeVerifier
|
|
701
|
+
});
|
|
702
|
+
await this.stamper.setIdToken(idToken);
|
|
703
|
+
const { organizationId, walletId } = await this.kms.discoverOrganizationAndWalletId(bearerToken, authUserId);
|
|
704
|
+
return {
|
|
705
|
+
walletId,
|
|
706
|
+
organizationId,
|
|
707
|
+
provider: options.provider,
|
|
708
|
+
accountDerivationIndex: 0,
|
|
709
|
+
// discoverWalletId uses derivation index of 0.
|
|
710
|
+
expiresInMs,
|
|
711
|
+
authUserId,
|
|
712
|
+
bearerToken
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
// src/providers/embedded/ExpoAuth2Stamper.ts
|
|
718
|
+
var SecureStore2 = __toESM(require("expo-secure-store"));
|
|
719
|
+
var import_bs58 = __toESM(require("bs58"));
|
|
720
|
+
var import_buffer = require("buffer");
|
|
721
|
+
var import_base64url = require("@phantom/base64url");
|
|
722
|
+
var import_sdk_types = require("@phantom/sdk-types");
|
|
723
|
+
var ExpoAuth2Stamper = class {
|
|
724
|
+
/**
|
|
725
|
+
* @param storageKey - expo-secure-store key used to persist the P-256 private key.
|
|
726
|
+
* Use a unique key per app, e.g. `phantom-auth2-<appId>`.
|
|
727
|
+
*/
|
|
728
|
+
constructor(storageKey) {
|
|
729
|
+
this.storageKey = storageKey;
|
|
730
|
+
this._keyPair = null;
|
|
731
|
+
this._keyInfo = null;
|
|
732
|
+
this._idToken = null;
|
|
733
|
+
this.algorithm = import_sdk_types.Algorithm.secp256r1;
|
|
734
|
+
this.type = "OIDC";
|
|
735
|
+
}
|
|
736
|
+
async init() {
|
|
737
|
+
const stored = await this.loadRecord();
|
|
738
|
+
if (stored) {
|
|
739
|
+
this._keyPair = {
|
|
740
|
+
privateKey: await this.importPrivateKey(stored.privateKeyPkcs8),
|
|
741
|
+
publicKey: await this.importPublicKeyFromBase58(stored.keyInfo.publicKey)
|
|
742
|
+
};
|
|
743
|
+
this._keyInfo = stored.keyInfo;
|
|
744
|
+
if (stored.idToken) {
|
|
745
|
+
this._idToken = stored.idToken;
|
|
746
|
+
}
|
|
747
|
+
return this._keyInfo;
|
|
748
|
+
}
|
|
749
|
+
return this.generateAndStore();
|
|
750
|
+
}
|
|
751
|
+
getKeyInfo() {
|
|
752
|
+
return this._keyInfo;
|
|
753
|
+
}
|
|
754
|
+
getCryptoKeyPair() {
|
|
755
|
+
return this._keyPair;
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Arms the stamper with the OIDC id token for subsequent KMS stamp() calls.
|
|
759
|
+
*
|
|
760
|
+
* Persists the token to SecureStore alongside the key pair so that
|
|
761
|
+
* auto-connect can restore it on the next app launch without a new login.
|
|
762
|
+
*/
|
|
763
|
+
async setIdToken(idToken) {
|
|
764
|
+
this._idToken = idToken;
|
|
765
|
+
const existing = await this.loadRecord();
|
|
766
|
+
if (existing) {
|
|
767
|
+
await this.storeRecord({ ...existing, idToken });
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
async stamp(params) {
|
|
771
|
+
if (!this._keyPair || !this._keyInfo || this._idToken === null) {
|
|
772
|
+
throw new Error("ExpoAuth2Stamper not initialized. Call init() first.");
|
|
773
|
+
}
|
|
774
|
+
const signatureRaw = await crypto.subtle.sign(
|
|
775
|
+
{ name: "ECDSA", hash: "SHA-256" },
|
|
776
|
+
this._keyPair.privateKey,
|
|
777
|
+
new Uint8Array(params.data)
|
|
778
|
+
);
|
|
779
|
+
const rawPublicKey = import_bs58.default.decode(this._keyInfo.publicKey);
|
|
780
|
+
const stampData = {
|
|
781
|
+
kind: this.type,
|
|
782
|
+
idToken: this._idToken,
|
|
783
|
+
publicKey: (0, import_base64url.base64urlEncode)(rawPublicKey),
|
|
784
|
+
algorithm: this.algorithm,
|
|
785
|
+
// The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
|
|
786
|
+
salt: "",
|
|
787
|
+
signature: (0, import_base64url.base64urlEncode)(new Uint8Array(signatureRaw))
|
|
788
|
+
};
|
|
789
|
+
return (0, import_base64url.base64urlEncode)(new TextEncoder().encode(JSON.stringify(stampData)));
|
|
790
|
+
}
|
|
791
|
+
async resetKeyPair() {
|
|
792
|
+
await this.clear();
|
|
793
|
+
return this.generateAndStore();
|
|
794
|
+
}
|
|
795
|
+
async clear() {
|
|
796
|
+
await this.clearStoredRecord();
|
|
797
|
+
this._keyPair = null;
|
|
798
|
+
this._keyInfo = null;
|
|
799
|
+
this._idToken = null;
|
|
800
|
+
}
|
|
801
|
+
// Auth2 doesn't use key rotation; minimal no-op implementations.
|
|
802
|
+
async rotateKeyPair() {
|
|
803
|
+
return this.init();
|
|
804
|
+
}
|
|
805
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
806
|
+
async commitRotation(authenticatorId) {
|
|
807
|
+
if (this._keyInfo) {
|
|
808
|
+
this._keyInfo.authenticatorId = authenticatorId;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
async rollbackRotation() {
|
|
812
|
+
}
|
|
813
|
+
async generateAndStore() {
|
|
814
|
+
const keyPair = await crypto.subtle.generateKey(
|
|
815
|
+
{ name: "ECDSA", namedCurve: "P-256" },
|
|
816
|
+
true,
|
|
817
|
+
// extractable — needed to export PKCS#8 for SecureStore
|
|
818
|
+
["sign", "verify"]
|
|
819
|
+
);
|
|
820
|
+
const rawPublicKey = new Uint8Array(await crypto.subtle.exportKey("raw", keyPair.publicKey));
|
|
821
|
+
const publicKeyBase58 = import_bs58.default.encode(rawPublicKey);
|
|
822
|
+
const keyIdBuffer = await crypto.subtle.digest("SHA-256", rawPublicKey.buffer);
|
|
823
|
+
const keyId = (0, import_base64url.base64urlEncode)(new Uint8Array(keyIdBuffer)).substring(0, 16);
|
|
824
|
+
this._keyPair = keyPair;
|
|
825
|
+
this._keyInfo = { keyId, publicKey: publicKeyBase58, createdAt: Date.now() };
|
|
826
|
+
const pkcs8Buffer = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
|
|
827
|
+
const privateKeyPkcs8 = import_buffer.Buffer.from(pkcs8Buffer).toString("base64");
|
|
828
|
+
await this.storeRecord({ privateKeyPkcs8, keyInfo: this._keyInfo });
|
|
829
|
+
return this._keyInfo;
|
|
830
|
+
}
|
|
831
|
+
async importPublicKeyFromBase58(base58PublicKey) {
|
|
832
|
+
const rawBytes = import_bs58.default.decode(base58PublicKey);
|
|
833
|
+
return crypto.subtle.importKey(
|
|
834
|
+
"raw",
|
|
835
|
+
rawBytes.buffer.slice(rawBytes.byteOffset, rawBytes.byteOffset + rawBytes.byteLength),
|
|
836
|
+
{ name: "ECDSA", namedCurve: "P-256" },
|
|
837
|
+
true,
|
|
838
|
+
// extractable so createAuth2RequestJar can export it as JWK
|
|
839
|
+
["verify"]
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
async importPrivateKey(pkcs8Base64) {
|
|
843
|
+
const pkcs8Bytes = import_buffer.Buffer.from(pkcs8Base64, "base64");
|
|
844
|
+
return crypto.subtle.importKey(
|
|
845
|
+
"pkcs8",
|
|
846
|
+
pkcs8Bytes,
|
|
847
|
+
{ name: "ECDSA", namedCurve: "P-256" },
|
|
848
|
+
false,
|
|
849
|
+
// non-extractable once loaded into memory
|
|
850
|
+
["sign"]
|
|
851
|
+
);
|
|
852
|
+
}
|
|
853
|
+
async loadRecord() {
|
|
854
|
+
try {
|
|
855
|
+
const raw = await SecureStore2.getItemAsync(this.storageKey);
|
|
856
|
+
return raw ? JSON.parse(raw) : null;
|
|
857
|
+
} catch {
|
|
858
|
+
return null;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
async storeRecord(record) {
|
|
862
|
+
await SecureStore2.setItemAsync(this.storageKey, JSON.stringify(record), { requireAuthentication: false });
|
|
863
|
+
}
|
|
864
|
+
async clearStoredRecord() {
|
|
865
|
+
try {
|
|
866
|
+
await SecureStore2.deleteItemAsync(this.storageKey);
|
|
867
|
+
} catch {
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
};
|
|
871
|
+
|
|
635
872
|
// src/providers/embedded/url-params.ts
|
|
636
873
|
var import_react_native6 = require("react-native");
|
|
637
874
|
var ExpoURLParamsAccessor = class {
|
|
@@ -701,11 +938,11 @@ var ExpoURLParamsAccessor = class {
|
|
|
701
938
|
};
|
|
702
939
|
|
|
703
940
|
// src/providers/embedded/stamper.ts
|
|
704
|
-
var
|
|
941
|
+
var SecureStore3 = __toESM(require("expo-secure-store"));
|
|
705
942
|
var import_api_key_stamper = require("@phantom/api-key-stamper");
|
|
706
943
|
var import_constants2 = require("@phantom/constants");
|
|
707
944
|
var import_crypto = require("@phantom/crypto");
|
|
708
|
-
var
|
|
945
|
+
var import_base64url2 = require("@phantom/base64url");
|
|
709
946
|
var ReactNativeStamper = class {
|
|
710
947
|
// Optional for PKI, required for OIDC
|
|
711
948
|
constructor(config = {}) {
|
|
@@ -761,11 +998,11 @@ var ReactNativeStamper = class {
|
|
|
761
998
|
const activeKey = this.getActiveKeyName();
|
|
762
999
|
const pendingKey = this.getPendingKeyName();
|
|
763
1000
|
try {
|
|
764
|
-
await
|
|
1001
|
+
await SecureStore3.deleteItemAsync(activeKey);
|
|
765
1002
|
} catch (error) {
|
|
766
1003
|
}
|
|
767
1004
|
try {
|
|
768
|
-
await
|
|
1005
|
+
await SecureStore3.deleteItemAsync(pendingKey);
|
|
769
1006
|
} catch (error) {
|
|
770
1007
|
}
|
|
771
1008
|
this.activeKeyRecord = null;
|
|
@@ -787,7 +1024,7 @@ var ReactNativeStamper = class {
|
|
|
787
1024
|
}
|
|
788
1025
|
if (this.activeKeyRecord) {
|
|
789
1026
|
try {
|
|
790
|
-
await
|
|
1027
|
+
await SecureStore3.deleteItemAsync(this.getActiveKeyName());
|
|
791
1028
|
} catch (error) {
|
|
792
1029
|
}
|
|
793
1030
|
}
|
|
@@ -798,7 +1035,7 @@ var ReactNativeStamper = class {
|
|
|
798
1035
|
this.pendingKeyRecord = null;
|
|
799
1036
|
await this.storeKeyRecord(this.activeKeyRecord, "active");
|
|
800
1037
|
try {
|
|
801
|
-
await
|
|
1038
|
+
await SecureStore3.deleteItemAsync(this.getPendingKeyName());
|
|
802
1039
|
} catch (error) {
|
|
803
1040
|
}
|
|
804
1041
|
}
|
|
@@ -810,7 +1047,7 @@ var ReactNativeStamper = class {
|
|
|
810
1047
|
return;
|
|
811
1048
|
}
|
|
812
1049
|
try {
|
|
813
|
-
await
|
|
1050
|
+
await SecureStore3.deleteItemAsync(this.getPendingKeyName());
|
|
814
1051
|
} catch (error) {
|
|
815
1052
|
}
|
|
816
1053
|
this.pendingKeyRecord = null;
|
|
@@ -836,18 +1073,18 @@ var ReactNativeStamper = class {
|
|
|
836
1073
|
return record;
|
|
837
1074
|
}
|
|
838
1075
|
createKeyId(publicKey) {
|
|
839
|
-
return (0,
|
|
1076
|
+
return (0, import_base64url2.base64urlEncode)(new TextEncoder().encode(publicKey)).substring(0, 16);
|
|
840
1077
|
}
|
|
841
1078
|
async storeKeyRecord(record, type) {
|
|
842
1079
|
const keyName = type === "active" ? this.getActiveKeyName() : this.getPendingKeyName();
|
|
843
|
-
await
|
|
1080
|
+
await SecureStore3.setItemAsync(keyName, JSON.stringify(record), {
|
|
844
1081
|
requireAuthentication: false
|
|
845
1082
|
});
|
|
846
1083
|
}
|
|
847
1084
|
async loadActiveKeyRecord() {
|
|
848
1085
|
try {
|
|
849
1086
|
const activeKey = this.getActiveKeyName();
|
|
850
|
-
const storedRecord = await
|
|
1087
|
+
const storedRecord = await SecureStore3.getItemAsync(activeKey);
|
|
851
1088
|
if (storedRecord) {
|
|
852
1089
|
return JSON.parse(storedRecord);
|
|
853
1090
|
}
|
|
@@ -858,7 +1095,7 @@ var ReactNativeStamper = class {
|
|
|
858
1095
|
async loadPendingKeyRecord() {
|
|
859
1096
|
try {
|
|
860
1097
|
const pendingKey = this.getPendingKeyName();
|
|
861
|
-
const storedRecord = await
|
|
1098
|
+
const storedRecord = await SecureStore3.getItemAsync(pendingKey);
|
|
862
1099
|
if (storedRecord) {
|
|
863
1100
|
return JSON.parse(storedRecord);
|
|
864
1101
|
}
|
|
@@ -940,13 +1177,25 @@ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appNam
|
|
|
940
1177
|
}, [config]);
|
|
941
1178
|
const sdk = (0, import_react8.useMemo)(() => {
|
|
942
1179
|
const storage = new ExpoSecureStorage();
|
|
943
|
-
const authProvider = new ExpoAuthProvider();
|
|
944
1180
|
const urlParamsAccessor = new ExpoURLParamsAccessor();
|
|
945
1181
|
const logger = new ExpoLogger(debugConfig?.enabled || false);
|
|
946
|
-
const stamper = new ReactNativeStamper({
|
|
1182
|
+
const stamper = config.unstable__auth2Options ? new ExpoAuth2Stamper(`phantom-auth2-${memoizedConfig.appId}`) : new ReactNativeStamper({
|
|
947
1183
|
keyPrefix: `phantom-rn-${memoizedConfig.appId}`,
|
|
948
1184
|
appId: memoizedConfig.appId
|
|
949
1185
|
});
|
|
1186
|
+
const authProvider = config.unstable__auth2Options && config.authOptions?.authUrl && config.authOptions?.redirectUrl && config.apiBaseUrl && stamper instanceof ExpoAuth2Stamper ? new ExpoAuth2AuthProvider(
|
|
1187
|
+
stamper,
|
|
1188
|
+
{
|
|
1189
|
+
redirectUri: config.authOptions.redirectUrl,
|
|
1190
|
+
connectLoginUrl: config.authOptions.authUrl,
|
|
1191
|
+
clientId: config.unstable__auth2Options.clientId,
|
|
1192
|
+
authApiBaseUrl: config.unstable__auth2Options.authApiBaseUrl
|
|
1193
|
+
},
|
|
1194
|
+
{
|
|
1195
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
1196
|
+
appId: config.appId
|
|
1197
|
+
}
|
|
1198
|
+
) : new ExpoAuthProvider();
|
|
950
1199
|
const platformName = `${import_react_native7.Platform.OS}-${import_react_native7.Platform.Version}`;
|
|
951
1200
|
const platform = {
|
|
952
1201
|
storage,
|
|
@@ -957,16 +1206,17 @@ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appNam
|
|
|
957
1206
|
name: platformName,
|
|
958
1207
|
analyticsHeaders: {
|
|
959
1208
|
[import_constants3.ANALYTICS_HEADERS.SDK_TYPE]: "react-native",
|
|
960
|
-
[import_constants3.ANALYTICS_HEADERS.PLATFORM]:
|
|
1209
|
+
[import_constants3.ANALYTICS_HEADERS.PLATFORM]: "ext-sdk",
|
|
961
1210
|
[import_constants3.ANALYTICS_HEADERS.PLATFORM_VERSION]: `${import_react_native7.Platform.Version}`,
|
|
1211
|
+
[import_constants3.ANALYTICS_HEADERS.CLIENT]: import_react_native7.Platform.OS,
|
|
962
1212
|
[import_constants3.ANALYTICS_HEADERS.APP_ID]: config.appId,
|
|
963
1213
|
[import_constants3.ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
|
|
964
|
-
[import_constants3.ANALYTICS_HEADERS.SDK_VERSION]: "1.0.
|
|
1214
|
+
[import_constants3.ANALYTICS_HEADERS.SDK_VERSION]: "1.0.5"
|
|
965
1215
|
// Replaced at build time
|
|
966
1216
|
}
|
|
967
1217
|
};
|
|
968
1218
|
return new import_embedded_provider_core.EmbeddedProvider(memoizedConfig, platform, logger);
|
|
969
|
-
}, [memoizedConfig, debugConfig, config
|
|
1219
|
+
}, [memoizedConfig, debugConfig, config]);
|
|
970
1220
|
(0, import_react8.useEffect)(() => {
|
|
971
1221
|
const handleConnectStart = () => {
|
|
972
1222
|
setIsConnecting(true);
|
package/dist/index.mjs
CHANGED
|
@@ -515,7 +515,7 @@ var ExpoAuthProvider = class {
|
|
|
515
515
|
// OAuth session management - defaults to allow refresh unless explicitly clearing after logout
|
|
516
516
|
clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
|
|
517
517
|
allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
|
|
518
|
-
sdk_version: "1.0.
|
|
518
|
+
sdk_version: "1.0.5",
|
|
519
519
|
sdk_type: "react-native",
|
|
520
520
|
platform: Platform.OS
|
|
521
521
|
});
|
|
@@ -590,6 +590,248 @@ var ExpoAuthProvider = class {
|
|
|
590
590
|
}
|
|
591
591
|
};
|
|
592
592
|
|
|
593
|
+
// src/providers/embedded/ExpoAuth2AuthProvider.ts
|
|
594
|
+
import * as WebBrowser2 from "expo-web-browser";
|
|
595
|
+
import {
|
|
596
|
+
createCodeVerifier,
|
|
597
|
+
exchangeAuthCode,
|
|
598
|
+
Auth2KmsRpcClient,
|
|
599
|
+
createConnectStartUrl
|
|
600
|
+
} from "@phantom/auth2";
|
|
601
|
+
var ExpoAuth2AuthProvider = class {
|
|
602
|
+
constructor(stamper, auth2ProviderOptions, kmsClientOptions) {
|
|
603
|
+
this.stamper = stamper;
|
|
604
|
+
this.auth2ProviderOptions = auth2ProviderOptions;
|
|
605
|
+
this.kms = new Auth2KmsRpcClient(stamper, kmsClientOptions);
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Runs the full PKCE Auth2 flow inline using expo-web-browser.
|
|
609
|
+
*
|
|
610
|
+
* Unlike the browser flow (which requires a page redirect and resumeAuthFromRedirect),
|
|
611
|
+
* expo-web-browser intercepts the OAuth callback URL and returns it synchronously,
|
|
612
|
+
* so the token exchange and KMS calls all happen here before returning AuthResult.
|
|
613
|
+
*/
|
|
614
|
+
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,
|
|
628
|
+
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: ""
|
|
633
|
+
});
|
|
634
|
+
await WebBrowser2.warmUpAsync();
|
|
635
|
+
let result;
|
|
636
|
+
try {
|
|
637
|
+
result = await WebBrowser2.openAuthSessionAsync(url, this.auth2ProviderOptions.redirectUri);
|
|
638
|
+
} finally {
|
|
639
|
+
await WebBrowser2.coolDownAsync();
|
|
640
|
+
}
|
|
641
|
+
if (!result.url) {
|
|
642
|
+
throw new Error("Authentication failed");
|
|
643
|
+
}
|
|
644
|
+
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 } = await exchangeAuthCode({
|
|
659
|
+
authApiBaseUrl: this.auth2ProviderOptions.authApiBaseUrl,
|
|
660
|
+
clientId: this.auth2ProviderOptions.clientId,
|
|
661
|
+
redirectUri: this.auth2ProviderOptions.redirectUri,
|
|
662
|
+
code,
|
|
663
|
+
codeVerifier
|
|
664
|
+
});
|
|
665
|
+
await this.stamper.setIdToken(idToken);
|
|
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
|
+
}
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
// src/providers/embedded/ExpoAuth2Stamper.ts
|
|
681
|
+
import * as SecureStore2 from "expo-secure-store";
|
|
682
|
+
import bs58 from "bs58";
|
|
683
|
+
import { Buffer } from "buffer";
|
|
684
|
+
import { base64urlEncode } from "@phantom/base64url";
|
|
685
|
+
import { Algorithm } from "@phantom/sdk-types";
|
|
686
|
+
var ExpoAuth2Stamper = class {
|
|
687
|
+
/**
|
|
688
|
+
* @param storageKey - expo-secure-store key used to persist the P-256 private key.
|
|
689
|
+
* Use a unique key per app, e.g. `phantom-auth2-<appId>`.
|
|
690
|
+
*/
|
|
691
|
+
constructor(storageKey) {
|
|
692
|
+
this.storageKey = storageKey;
|
|
693
|
+
this._keyPair = null;
|
|
694
|
+
this._keyInfo = null;
|
|
695
|
+
this._idToken = null;
|
|
696
|
+
this.algorithm = Algorithm.secp256r1;
|
|
697
|
+
this.type = "OIDC";
|
|
698
|
+
}
|
|
699
|
+
async init() {
|
|
700
|
+
const stored = await this.loadRecord();
|
|
701
|
+
if (stored) {
|
|
702
|
+
this._keyPair = {
|
|
703
|
+
privateKey: await this.importPrivateKey(stored.privateKeyPkcs8),
|
|
704
|
+
publicKey: await this.importPublicKeyFromBase58(stored.keyInfo.publicKey)
|
|
705
|
+
};
|
|
706
|
+
this._keyInfo = stored.keyInfo;
|
|
707
|
+
if (stored.idToken) {
|
|
708
|
+
this._idToken = stored.idToken;
|
|
709
|
+
}
|
|
710
|
+
return this._keyInfo;
|
|
711
|
+
}
|
|
712
|
+
return this.generateAndStore();
|
|
713
|
+
}
|
|
714
|
+
getKeyInfo() {
|
|
715
|
+
return this._keyInfo;
|
|
716
|
+
}
|
|
717
|
+
getCryptoKeyPair() {
|
|
718
|
+
return this._keyPair;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Arms the stamper with the OIDC id token for subsequent KMS stamp() calls.
|
|
722
|
+
*
|
|
723
|
+
* Persists the token to SecureStore alongside the key pair so that
|
|
724
|
+
* auto-connect can restore it on the next app launch without a new login.
|
|
725
|
+
*/
|
|
726
|
+
async setIdToken(idToken) {
|
|
727
|
+
this._idToken = idToken;
|
|
728
|
+
const existing = await this.loadRecord();
|
|
729
|
+
if (existing) {
|
|
730
|
+
await this.storeRecord({ ...existing, idToken });
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
async stamp(params) {
|
|
734
|
+
if (!this._keyPair || !this._keyInfo || this._idToken === null) {
|
|
735
|
+
throw new Error("ExpoAuth2Stamper not initialized. Call init() first.");
|
|
736
|
+
}
|
|
737
|
+
const signatureRaw = await crypto.subtle.sign(
|
|
738
|
+
{ name: "ECDSA", hash: "SHA-256" },
|
|
739
|
+
this._keyPair.privateKey,
|
|
740
|
+
new Uint8Array(params.data)
|
|
741
|
+
);
|
|
742
|
+
const rawPublicKey = bs58.decode(this._keyInfo.publicKey);
|
|
743
|
+
const stampData = {
|
|
744
|
+
kind: this.type,
|
|
745
|
+
idToken: this._idToken,
|
|
746
|
+
publicKey: base64urlEncode(rawPublicKey),
|
|
747
|
+
algorithm: this.algorithm,
|
|
748
|
+
// The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
|
|
749
|
+
salt: "",
|
|
750
|
+
signature: base64urlEncode(new Uint8Array(signatureRaw))
|
|
751
|
+
};
|
|
752
|
+
return base64urlEncode(new TextEncoder().encode(JSON.stringify(stampData)));
|
|
753
|
+
}
|
|
754
|
+
async resetKeyPair() {
|
|
755
|
+
await this.clear();
|
|
756
|
+
return this.generateAndStore();
|
|
757
|
+
}
|
|
758
|
+
async clear() {
|
|
759
|
+
await this.clearStoredRecord();
|
|
760
|
+
this._keyPair = null;
|
|
761
|
+
this._keyInfo = null;
|
|
762
|
+
this._idToken = null;
|
|
763
|
+
}
|
|
764
|
+
// Auth2 doesn't use key rotation; minimal no-op implementations.
|
|
765
|
+
async rotateKeyPair() {
|
|
766
|
+
return this.init();
|
|
767
|
+
}
|
|
768
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
769
|
+
async commitRotation(authenticatorId) {
|
|
770
|
+
if (this._keyInfo) {
|
|
771
|
+
this._keyInfo.authenticatorId = authenticatorId;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
async rollbackRotation() {
|
|
775
|
+
}
|
|
776
|
+
async generateAndStore() {
|
|
777
|
+
const keyPair = await crypto.subtle.generateKey(
|
|
778
|
+
{ name: "ECDSA", namedCurve: "P-256" },
|
|
779
|
+
true,
|
|
780
|
+
// extractable — needed to export PKCS#8 for SecureStore
|
|
781
|
+
["sign", "verify"]
|
|
782
|
+
);
|
|
783
|
+
const rawPublicKey = new Uint8Array(await crypto.subtle.exportKey("raw", keyPair.publicKey));
|
|
784
|
+
const publicKeyBase58 = bs58.encode(rawPublicKey);
|
|
785
|
+
const keyIdBuffer = await crypto.subtle.digest("SHA-256", rawPublicKey.buffer);
|
|
786
|
+
const keyId = base64urlEncode(new Uint8Array(keyIdBuffer)).substring(0, 16);
|
|
787
|
+
this._keyPair = keyPair;
|
|
788
|
+
this._keyInfo = { keyId, publicKey: publicKeyBase58, createdAt: Date.now() };
|
|
789
|
+
const pkcs8Buffer = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
|
|
790
|
+
const privateKeyPkcs8 = Buffer.from(pkcs8Buffer).toString("base64");
|
|
791
|
+
await this.storeRecord({ privateKeyPkcs8, keyInfo: this._keyInfo });
|
|
792
|
+
return this._keyInfo;
|
|
793
|
+
}
|
|
794
|
+
async importPublicKeyFromBase58(base58PublicKey) {
|
|
795
|
+
const rawBytes = bs58.decode(base58PublicKey);
|
|
796
|
+
return crypto.subtle.importKey(
|
|
797
|
+
"raw",
|
|
798
|
+
rawBytes.buffer.slice(rawBytes.byteOffset, rawBytes.byteOffset + rawBytes.byteLength),
|
|
799
|
+
{ name: "ECDSA", namedCurve: "P-256" },
|
|
800
|
+
true,
|
|
801
|
+
// extractable so createAuth2RequestJar can export it as JWK
|
|
802
|
+
["verify"]
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
async importPrivateKey(pkcs8Base64) {
|
|
806
|
+
const pkcs8Bytes = Buffer.from(pkcs8Base64, "base64");
|
|
807
|
+
return crypto.subtle.importKey(
|
|
808
|
+
"pkcs8",
|
|
809
|
+
pkcs8Bytes,
|
|
810
|
+
{ name: "ECDSA", namedCurve: "P-256" },
|
|
811
|
+
false,
|
|
812
|
+
// non-extractable once loaded into memory
|
|
813
|
+
["sign"]
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
async loadRecord() {
|
|
817
|
+
try {
|
|
818
|
+
const raw = await SecureStore2.getItemAsync(this.storageKey);
|
|
819
|
+
return raw ? JSON.parse(raw) : null;
|
|
820
|
+
} catch {
|
|
821
|
+
return null;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
async storeRecord(record) {
|
|
825
|
+
await SecureStore2.setItemAsync(this.storageKey, JSON.stringify(record), { requireAuthentication: false });
|
|
826
|
+
}
|
|
827
|
+
async clearStoredRecord() {
|
|
828
|
+
try {
|
|
829
|
+
await SecureStore2.deleteItemAsync(this.storageKey);
|
|
830
|
+
} catch {
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
|
|
593
835
|
// src/providers/embedded/url-params.ts
|
|
594
836
|
import { Linking } from "react-native";
|
|
595
837
|
var ExpoURLParamsAccessor = class {
|
|
@@ -659,11 +901,11 @@ var ExpoURLParamsAccessor = class {
|
|
|
659
901
|
};
|
|
660
902
|
|
|
661
903
|
// src/providers/embedded/stamper.ts
|
|
662
|
-
import * as
|
|
904
|
+
import * as SecureStore3 from "expo-secure-store";
|
|
663
905
|
import { ApiKeyStamper } from "@phantom/api-key-stamper";
|
|
664
906
|
import { DEFAULT_AUTHENTICATOR_ALGORITHM } from "@phantom/constants";
|
|
665
907
|
import { generateKeyPair } from "@phantom/crypto";
|
|
666
|
-
import { base64urlEncode } from "@phantom/base64url";
|
|
908
|
+
import { base64urlEncode as base64urlEncode2 } from "@phantom/base64url";
|
|
667
909
|
var ReactNativeStamper = class {
|
|
668
910
|
// Optional for PKI, required for OIDC
|
|
669
911
|
constructor(config = {}) {
|
|
@@ -719,11 +961,11 @@ var ReactNativeStamper = class {
|
|
|
719
961
|
const activeKey = this.getActiveKeyName();
|
|
720
962
|
const pendingKey = this.getPendingKeyName();
|
|
721
963
|
try {
|
|
722
|
-
await
|
|
964
|
+
await SecureStore3.deleteItemAsync(activeKey);
|
|
723
965
|
} catch (error) {
|
|
724
966
|
}
|
|
725
967
|
try {
|
|
726
|
-
await
|
|
968
|
+
await SecureStore3.deleteItemAsync(pendingKey);
|
|
727
969
|
} catch (error) {
|
|
728
970
|
}
|
|
729
971
|
this.activeKeyRecord = null;
|
|
@@ -745,7 +987,7 @@ var ReactNativeStamper = class {
|
|
|
745
987
|
}
|
|
746
988
|
if (this.activeKeyRecord) {
|
|
747
989
|
try {
|
|
748
|
-
await
|
|
990
|
+
await SecureStore3.deleteItemAsync(this.getActiveKeyName());
|
|
749
991
|
} catch (error) {
|
|
750
992
|
}
|
|
751
993
|
}
|
|
@@ -756,7 +998,7 @@ var ReactNativeStamper = class {
|
|
|
756
998
|
this.pendingKeyRecord = null;
|
|
757
999
|
await this.storeKeyRecord(this.activeKeyRecord, "active");
|
|
758
1000
|
try {
|
|
759
|
-
await
|
|
1001
|
+
await SecureStore3.deleteItemAsync(this.getPendingKeyName());
|
|
760
1002
|
} catch (error) {
|
|
761
1003
|
}
|
|
762
1004
|
}
|
|
@@ -768,7 +1010,7 @@ var ReactNativeStamper = class {
|
|
|
768
1010
|
return;
|
|
769
1011
|
}
|
|
770
1012
|
try {
|
|
771
|
-
await
|
|
1013
|
+
await SecureStore3.deleteItemAsync(this.getPendingKeyName());
|
|
772
1014
|
} catch (error) {
|
|
773
1015
|
}
|
|
774
1016
|
this.pendingKeyRecord = null;
|
|
@@ -794,18 +1036,18 @@ var ReactNativeStamper = class {
|
|
|
794
1036
|
return record;
|
|
795
1037
|
}
|
|
796
1038
|
createKeyId(publicKey) {
|
|
797
|
-
return
|
|
1039
|
+
return base64urlEncode2(new TextEncoder().encode(publicKey)).substring(0, 16);
|
|
798
1040
|
}
|
|
799
1041
|
async storeKeyRecord(record, type) {
|
|
800
1042
|
const keyName = type === "active" ? this.getActiveKeyName() : this.getPendingKeyName();
|
|
801
|
-
await
|
|
1043
|
+
await SecureStore3.setItemAsync(keyName, JSON.stringify(record), {
|
|
802
1044
|
requireAuthentication: false
|
|
803
1045
|
});
|
|
804
1046
|
}
|
|
805
1047
|
async loadActiveKeyRecord() {
|
|
806
1048
|
try {
|
|
807
1049
|
const activeKey = this.getActiveKeyName();
|
|
808
|
-
const storedRecord = await
|
|
1050
|
+
const storedRecord = await SecureStore3.getItemAsync(activeKey);
|
|
809
1051
|
if (storedRecord) {
|
|
810
1052
|
return JSON.parse(storedRecord);
|
|
811
1053
|
}
|
|
@@ -816,7 +1058,7 @@ var ReactNativeStamper = class {
|
|
|
816
1058
|
async loadPendingKeyRecord() {
|
|
817
1059
|
try {
|
|
818
1060
|
const pendingKey = this.getPendingKeyName();
|
|
819
|
-
const storedRecord = await
|
|
1061
|
+
const storedRecord = await SecureStore3.getItemAsync(pendingKey);
|
|
820
1062
|
if (storedRecord) {
|
|
821
1063
|
return JSON.parse(storedRecord);
|
|
822
1064
|
}
|
|
@@ -898,13 +1140,25 @@ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appNam
|
|
|
898
1140
|
}, [config]);
|
|
899
1141
|
const sdk = useMemo2(() => {
|
|
900
1142
|
const storage = new ExpoSecureStorage();
|
|
901
|
-
const authProvider = new ExpoAuthProvider();
|
|
902
1143
|
const urlParamsAccessor = new ExpoURLParamsAccessor();
|
|
903
1144
|
const logger = new ExpoLogger(debugConfig?.enabled || false);
|
|
904
|
-
const stamper = new ReactNativeStamper({
|
|
1145
|
+
const stamper = config.unstable__auth2Options ? new ExpoAuth2Stamper(`phantom-auth2-${memoizedConfig.appId}`) : new ReactNativeStamper({
|
|
905
1146
|
keyPrefix: `phantom-rn-${memoizedConfig.appId}`,
|
|
906
1147
|
appId: memoizedConfig.appId
|
|
907
1148
|
});
|
|
1149
|
+
const authProvider = config.unstable__auth2Options && config.authOptions?.authUrl && config.authOptions?.redirectUrl && config.apiBaseUrl && stamper instanceof ExpoAuth2Stamper ? new ExpoAuth2AuthProvider(
|
|
1150
|
+
stamper,
|
|
1151
|
+
{
|
|
1152
|
+
redirectUri: config.authOptions.redirectUrl,
|
|
1153
|
+
connectLoginUrl: config.authOptions.authUrl,
|
|
1154
|
+
clientId: config.unstable__auth2Options.clientId,
|
|
1155
|
+
authApiBaseUrl: config.unstable__auth2Options.authApiBaseUrl
|
|
1156
|
+
},
|
|
1157
|
+
{
|
|
1158
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
1159
|
+
appId: config.appId
|
|
1160
|
+
}
|
|
1161
|
+
) : new ExpoAuthProvider();
|
|
908
1162
|
const platformName = `${Platform2.OS}-${Platform2.Version}`;
|
|
909
1163
|
const platform = {
|
|
910
1164
|
storage,
|
|
@@ -915,16 +1169,17 @@ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appNam
|
|
|
915
1169
|
name: platformName,
|
|
916
1170
|
analyticsHeaders: {
|
|
917
1171
|
[ANALYTICS_HEADERS.SDK_TYPE]: "react-native",
|
|
918
|
-
[ANALYTICS_HEADERS.PLATFORM]:
|
|
1172
|
+
[ANALYTICS_HEADERS.PLATFORM]: "ext-sdk",
|
|
919
1173
|
[ANALYTICS_HEADERS.PLATFORM_VERSION]: `${Platform2.Version}`,
|
|
1174
|
+
[ANALYTICS_HEADERS.CLIENT]: Platform2.OS,
|
|
920
1175
|
[ANALYTICS_HEADERS.APP_ID]: config.appId,
|
|
921
1176
|
[ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
|
|
922
|
-
[ANALYTICS_HEADERS.SDK_VERSION]: "1.0.
|
|
1177
|
+
[ANALYTICS_HEADERS.SDK_VERSION]: "1.0.5"
|
|
923
1178
|
// Replaced at build time
|
|
924
1179
|
}
|
|
925
1180
|
};
|
|
926
1181
|
return new EmbeddedProvider(memoizedConfig, platform, logger);
|
|
927
|
-
}, [memoizedConfig, debugConfig, config
|
|
1182
|
+
}, [memoizedConfig, debugConfig, config]);
|
|
928
1183
|
useEffect2(() => {
|
|
929
1184
|
const handleConnectStart = () => {
|
|
930
1185
|
setIsConnecting(true);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phantom/react-native-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Phantom Wallet SDK for React Native and Expo applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -45,15 +45,16 @@
|
|
|
45
45
|
"directory": "packages/react-native-sdk"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@phantom/api-key-stamper": "^1.0.
|
|
49
|
-
"@phantom/
|
|
50
|
-
"@phantom/
|
|
51
|
-
"@phantom/
|
|
52
|
-
"@phantom/
|
|
53
|
-
"@phantom/
|
|
54
|
-
"@phantom/
|
|
55
|
-
"@phantom/
|
|
56
|
-
"@phantom/
|
|
48
|
+
"@phantom/api-key-stamper": "^1.0.5",
|
|
49
|
+
"@phantom/auth2": "^1.0.1",
|
|
50
|
+
"@phantom/base64url": "^1.0.5",
|
|
51
|
+
"@phantom/chain-interfaces": "^1.0.5",
|
|
52
|
+
"@phantom/client": "^1.0.5",
|
|
53
|
+
"@phantom/constants": "^1.0.5",
|
|
54
|
+
"@phantom/crypto": "^1.0.5",
|
|
55
|
+
"@phantom/embedded-provider-core": "^1.0.5",
|
|
56
|
+
"@phantom/sdk-types": "^1.0.5",
|
|
57
|
+
"@phantom/wallet-sdk-ui": "^1.0.5",
|
|
57
58
|
"@types/bs58": "^5.0.0",
|
|
58
59
|
"bs58": "^6.0.0",
|
|
59
60
|
"buffer": "^6.0.3"
|