@dubsdotapp/expo 0.2.22 → 0.2.24
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.mts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +176 -69
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +160 -53
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/managed-wallet.tsx +24 -6
- package/src/storage.ts +1 -0
- package/src/wallet/phantom-deeplink/deeplink-handler.ts +10 -1
- package/src/wallet/phantom-deeplink/phantom-deeplink-adapter.ts +115 -0
package/dist/index.d.mts
CHANGED
|
@@ -430,6 +430,7 @@ declare const STORAGE_KEYS: {
|
|
|
430
430
|
readonly MWA_AUTH_TOKEN: "dubs_mwa_auth_token";
|
|
431
431
|
readonly JWT_TOKEN: "dubs_jwt_token";
|
|
432
432
|
readonly PHANTOM_SESSION: "dubs_phantom_session";
|
|
433
|
+
readonly PHANTOM_CONNECT_IN_FLIGHT: "dubs_phantom_connect_in_flight";
|
|
433
434
|
};
|
|
434
435
|
/**
|
|
435
436
|
* Creates a TokenStorage backed by expo-secure-store.
|
|
@@ -622,6 +623,8 @@ interface PhantomDeeplinkAdapterConfig {
|
|
|
622
623
|
timeout?: number;
|
|
623
624
|
/** Called when the Phantom session changes (save/clear for persistence) */
|
|
624
625
|
onSessionChange?: (session: PhantomSession | null) => void;
|
|
626
|
+
/** Storage for persisting in-flight connect state (cold-start recovery on Android) */
|
|
627
|
+
storage?: TokenStorage;
|
|
625
628
|
}
|
|
626
629
|
/**
|
|
627
630
|
* Phantom wallet adapter using deeplinks (works on iOS and Android).
|
|
@@ -649,6 +652,17 @@ declare class PhantomDeeplinkAdapter implements WalletAdapter {
|
|
|
649
652
|
getSession(): PhantomSession | null;
|
|
650
653
|
connect(): Promise<void>;
|
|
651
654
|
disconnect(): void;
|
|
655
|
+
/**
|
|
656
|
+
* Return and clear the stashed cold-start URL from the deeplink handler.
|
|
657
|
+
* Used by ManagedWalletProvider to detect an in-flight connect on cold start.
|
|
658
|
+
*/
|
|
659
|
+
consumeColdStartUrl(): string | null;
|
|
660
|
+
/**
|
|
661
|
+
* Complete a connect flow that was interrupted by the OS killing the app (Android).
|
|
662
|
+
* Loads the saved in-flight keypair from storage, decrypts the Phantom response
|
|
663
|
+
* from the cold-start URL, and restores the session.
|
|
664
|
+
*/
|
|
665
|
+
completeConnectFromColdStart(url: string): Promise<void>;
|
|
652
666
|
signTransaction(transaction: Transaction): Promise<Transaction>;
|
|
653
667
|
signMessage(message: Uint8Array): Promise<Uint8Array>;
|
|
654
668
|
/** Remove the Linking event listener. Call when the adapter is no longer needed. */
|
package/dist/index.d.ts
CHANGED
|
@@ -430,6 +430,7 @@ declare const STORAGE_KEYS: {
|
|
|
430
430
|
readonly MWA_AUTH_TOKEN: "dubs_mwa_auth_token";
|
|
431
431
|
readonly JWT_TOKEN: "dubs_jwt_token";
|
|
432
432
|
readonly PHANTOM_SESSION: "dubs_phantom_session";
|
|
433
|
+
readonly PHANTOM_CONNECT_IN_FLIGHT: "dubs_phantom_connect_in_flight";
|
|
433
434
|
};
|
|
434
435
|
/**
|
|
435
436
|
* Creates a TokenStorage backed by expo-secure-store.
|
|
@@ -622,6 +623,8 @@ interface PhantomDeeplinkAdapterConfig {
|
|
|
622
623
|
timeout?: number;
|
|
623
624
|
/** Called when the Phantom session changes (save/clear for persistence) */
|
|
624
625
|
onSessionChange?: (session: PhantomSession | null) => void;
|
|
626
|
+
/** Storage for persisting in-flight connect state (cold-start recovery on Android) */
|
|
627
|
+
storage?: TokenStorage;
|
|
625
628
|
}
|
|
626
629
|
/**
|
|
627
630
|
* Phantom wallet adapter using deeplinks (works on iOS and Android).
|
|
@@ -649,6 +652,17 @@ declare class PhantomDeeplinkAdapter implements WalletAdapter {
|
|
|
649
652
|
getSession(): PhantomSession | null;
|
|
650
653
|
connect(): Promise<void>;
|
|
651
654
|
disconnect(): void;
|
|
655
|
+
/**
|
|
656
|
+
* Return and clear the stashed cold-start URL from the deeplink handler.
|
|
657
|
+
* Used by ManagedWalletProvider to detect an in-flight connect on cold start.
|
|
658
|
+
*/
|
|
659
|
+
consumeColdStartUrl(): string | null;
|
|
660
|
+
/**
|
|
661
|
+
* Complete a connect flow that was interrupted by the OS killing the app (Android).
|
|
662
|
+
* Loads the saved in-flight keypair from storage, decrypts the Phantom response
|
|
663
|
+
* from the cold-start URL, and restores the session.
|
|
664
|
+
*/
|
|
665
|
+
completeConnectFromColdStart(url: string): Promise<void>;
|
|
652
666
|
signTransaction(transaction: Transaction): Promise<Transaction>;
|
|
653
667
|
signMessage(message: Uint8Array): Promise<Uint8Array>;
|
|
654
668
|
/** Remove the Linking event listener. Call when the adapter is no longer needed. */
|
package/dist/index.js
CHANGED
|
@@ -522,7 +522,8 @@ var DubsClient = class {
|
|
|
522
522
|
var STORAGE_KEYS = {
|
|
523
523
|
MWA_AUTH_TOKEN: "dubs_mwa_auth_token",
|
|
524
524
|
JWT_TOKEN: "dubs_jwt_token",
|
|
525
|
-
PHANTOM_SESSION: "dubs_phantom_session"
|
|
525
|
+
PHANTOM_SESSION: "dubs_phantom_session",
|
|
526
|
+
PHANTOM_CONNECT_IN_FLIGHT: "dubs_phantom_connect_in_flight"
|
|
526
527
|
};
|
|
527
528
|
function createSecureStoreStorage() {
|
|
528
529
|
let SecureStore = null;
|
|
@@ -560,7 +561,7 @@ var import_web34 = require("@solana/web3.js");
|
|
|
560
561
|
|
|
561
562
|
// src/managed-wallet.tsx
|
|
562
563
|
var import_react = require("react");
|
|
563
|
-
var
|
|
564
|
+
var import_react_native4 = require("react-native");
|
|
564
565
|
|
|
565
566
|
// src/wallet/mwa-adapter.ts
|
|
566
567
|
var import_web3 = require("@solana/web3.js");
|
|
@@ -699,6 +700,7 @@ var DeeplinkHandler = class {
|
|
|
699
700
|
constructor(redirectBase) {
|
|
700
701
|
this.pending = /* @__PURE__ */ new Map();
|
|
701
702
|
this.subscription = null;
|
|
703
|
+
this._coldStartUrl = null;
|
|
702
704
|
this.redirectBase = redirectBase.replace(/\/+$/, "");
|
|
703
705
|
console.log(TAG, "Created with redirectBase:", this.redirectBase);
|
|
704
706
|
}
|
|
@@ -808,9 +810,16 @@ var DeeplinkHandler = class {
|
|
|
808
810
|
this.pending.delete(id);
|
|
809
811
|
req.resolve({ params });
|
|
810
812
|
} else {
|
|
811
|
-
console.log(TAG, "No pending requests to resolve \u2014
|
|
813
|
+
console.log(TAG, "No pending requests to resolve \u2014 stashing as cold-start URL");
|
|
814
|
+
this._coldStartUrl = url;
|
|
812
815
|
}
|
|
813
816
|
}
|
|
817
|
+
/** Return and clear the stashed cold-start URL (if any). */
|
|
818
|
+
getColdStartUrl() {
|
|
819
|
+
const url = this._coldStartUrl;
|
|
820
|
+
this._coldStartUrl = null;
|
|
821
|
+
return url;
|
|
822
|
+
}
|
|
814
823
|
/** Extract the request ID from the dubs_rid query parameter. */
|
|
815
824
|
extractRequestId(url) {
|
|
816
825
|
try {
|
|
@@ -902,6 +911,18 @@ var PhantomDeeplinkAdapter = class {
|
|
|
902
911
|
redirect_link: redirectLink,
|
|
903
912
|
app_url: appUrl
|
|
904
913
|
});
|
|
914
|
+
if (this.config.storage) {
|
|
915
|
+
console.log(TAG2, "Saving in-flight connect state to storage");
|
|
916
|
+
await this.config.storage.setItem(
|
|
917
|
+
STORAGE_KEYS.PHANTOM_CONNECT_IN_FLIGHT,
|
|
918
|
+
JSON.stringify({
|
|
919
|
+
dappPublicKey: dappPubBase58,
|
|
920
|
+
dappSecretKey: import_bs582.default.encode(this._dappKeyPair.secretKey),
|
|
921
|
+
requestId,
|
|
922
|
+
createdAt: Date.now()
|
|
923
|
+
})
|
|
924
|
+
);
|
|
925
|
+
}
|
|
905
926
|
const url = `https://phantom.app/ul/v1/connect?${params.toString()}`;
|
|
906
927
|
console.log(TAG2, "Opening Phantom connect deeplink...");
|
|
907
928
|
const response = await this.handler.send(url, requestId, this.timeout);
|
|
@@ -929,6 +950,8 @@ var PhantomDeeplinkAdapter = class {
|
|
|
929
950
|
this._connected = true;
|
|
930
951
|
console.log(TAG2, "Connected! Wallet:", this._publicKey.toBase58());
|
|
931
952
|
this.config.onSessionChange?.(this.getSession());
|
|
953
|
+
this.config.storage?.deleteItem(STORAGE_KEYS.PHANTOM_CONNECT_IN_FLIGHT).catch(() => {
|
|
954
|
+
});
|
|
932
955
|
}
|
|
933
956
|
disconnect() {
|
|
934
957
|
console.log(TAG2, "disconnect() \u2014 clearing state, was connected:", this._connected, "wallet:", this._publicKey?.toBase58());
|
|
@@ -941,6 +964,73 @@ var PhantomDeeplinkAdapter = class {
|
|
|
941
964
|
this.config.onSessionChange?.(null);
|
|
942
965
|
console.log(TAG2, "Disconnected");
|
|
943
966
|
}
|
|
967
|
+
/**
|
|
968
|
+
* Return and clear the stashed cold-start URL from the deeplink handler.
|
|
969
|
+
* Used by ManagedWalletProvider to detect an in-flight connect on cold start.
|
|
970
|
+
*/
|
|
971
|
+
consumeColdStartUrl() {
|
|
972
|
+
return this.handler.getColdStartUrl();
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* Complete a connect flow that was interrupted by the OS killing the app (Android).
|
|
976
|
+
* Loads the saved in-flight keypair from storage, decrypts the Phantom response
|
|
977
|
+
* from the cold-start URL, and restores the session.
|
|
978
|
+
*/
|
|
979
|
+
async completeConnectFromColdStart(url) {
|
|
980
|
+
if (!this.config.storage) {
|
|
981
|
+
throw new Error("Cannot recover cold-start connect: no storage configured");
|
|
982
|
+
}
|
|
983
|
+
console.log(TAG2, "completeConnectFromColdStart() \u2014 attempting recovery");
|
|
984
|
+
const raw = await this.config.storage.getItem(STORAGE_KEYS.PHANTOM_CONNECT_IN_FLIGHT);
|
|
985
|
+
if (!raw) {
|
|
986
|
+
throw new Error("No in-flight connect state found in storage");
|
|
987
|
+
}
|
|
988
|
+
const inFlight = JSON.parse(raw);
|
|
989
|
+
const age = Date.now() - inFlight.createdAt;
|
|
990
|
+
if (age > 12e4) {
|
|
991
|
+
console.log(TAG2, `In-flight state expired (${Math.round(age / 1e3)}s old), clearing`);
|
|
992
|
+
await this.config.storage.deleteItem(STORAGE_KEYS.PHANTOM_CONNECT_IN_FLIGHT).catch(() => {
|
|
993
|
+
});
|
|
994
|
+
throw new Error("In-flight connect state expired");
|
|
995
|
+
}
|
|
996
|
+
console.log(TAG2, `In-flight state age: ${Math.round(age / 1e3)}s, requestId: ${inFlight.requestId}`);
|
|
997
|
+
this._dappKeyPair = {
|
|
998
|
+
publicKey: import_bs582.default.decode(inFlight.dappPublicKey),
|
|
999
|
+
secretKey: import_bs582.default.decode(inFlight.dappSecretKey)
|
|
1000
|
+
};
|
|
1001
|
+
const parsed = new URL(url);
|
|
1002
|
+
const params = {};
|
|
1003
|
+
parsed.searchParams.forEach((value, key) => {
|
|
1004
|
+
params[key] = value;
|
|
1005
|
+
});
|
|
1006
|
+
if (params.errorCode) {
|
|
1007
|
+
const errorMessage = params.errorMessage ? decodeURIComponent(params.errorMessage) : `Phantom error code: ${params.errorCode}`;
|
|
1008
|
+
await this.config.storage.deleteItem(STORAGE_KEYS.PHANTOM_CONNECT_IN_FLIGHT).catch(() => {
|
|
1009
|
+
});
|
|
1010
|
+
throw new Error(errorMessage);
|
|
1011
|
+
}
|
|
1012
|
+
const phantomPubBase58 = params.phantom_encryption_public_key;
|
|
1013
|
+
if (!phantomPubBase58) {
|
|
1014
|
+
await this.config.storage.deleteItem(STORAGE_KEYS.PHANTOM_CONNECT_IN_FLIGHT).catch(() => {
|
|
1015
|
+
});
|
|
1016
|
+
throw new Error("Phantom did not return an encryption public key");
|
|
1017
|
+
}
|
|
1018
|
+
console.log(TAG2, "Cold-start: Phantom public key:", phantomPubBase58);
|
|
1019
|
+
this._phantomPublicKey = import_bs582.default.decode(phantomPubBase58);
|
|
1020
|
+
this._sharedSecret = deriveSharedSecret(
|
|
1021
|
+
this._dappKeyPair.secretKey,
|
|
1022
|
+
this._phantomPublicKey
|
|
1023
|
+
);
|
|
1024
|
+
const data = decryptPayload(params.data, params.nonce, this._sharedSecret);
|
|
1025
|
+
console.log(TAG2, "Cold-start: Decrypted connect data \u2014 public_key:", data.public_key);
|
|
1026
|
+
this._sessionToken = data.session;
|
|
1027
|
+
this._publicKey = new import_web32.PublicKey(data.public_key);
|
|
1028
|
+
this._connected = true;
|
|
1029
|
+
console.log(TAG2, "Cold-start recovery complete! Wallet:", this._publicKey.toBase58());
|
|
1030
|
+
this.config.onSessionChange?.(this.getSession());
|
|
1031
|
+
await this.config.storage.deleteItem(STORAGE_KEYS.PHANTOM_CONNECT_IN_FLIGHT).catch(() => {
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
944
1034
|
async signTransaction(transaction) {
|
|
945
1035
|
this.assertConnected();
|
|
946
1036
|
console.log(TAG2, "signTransaction() \u2014 serializing transaction");
|
|
@@ -1029,58 +1119,11 @@ var PhantomDeeplinkAdapter = class {
|
|
|
1029
1119
|
}
|
|
1030
1120
|
};
|
|
1031
1121
|
|
|
1032
|
-
// src/utils/device.ts
|
|
1033
|
-
var import_react_native2 = require("react-native");
|
|
1034
|
-
function isSolanaSeeker() {
|
|
1035
|
-
try {
|
|
1036
|
-
const Device = require("expo-device");
|
|
1037
|
-
return Device.brand?.toLowerCase() === "solanamobile";
|
|
1038
|
-
} catch {
|
|
1039
|
-
return false;
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
async function getDeviceInfo() {
|
|
1043
|
-
try {
|
|
1044
|
-
const Device = require("expo-device");
|
|
1045
|
-
return {
|
|
1046
|
-
platform: import_react_native2.Platform.OS,
|
|
1047
|
-
modelName: Device.modelName,
|
|
1048
|
-
brand: Device.brand,
|
|
1049
|
-
manufacturer: Device.manufacturer,
|
|
1050
|
-
osName: Device.osName,
|
|
1051
|
-
osVersion: Device.osVersion,
|
|
1052
|
-
deviceType: Device.deviceType,
|
|
1053
|
-
deviceName: Device.deviceName,
|
|
1054
|
-
totalMemory: Device.totalMemory,
|
|
1055
|
-
modelId: Device.modelId,
|
|
1056
|
-
designName: Device.designName,
|
|
1057
|
-
productName: Device.productName,
|
|
1058
|
-
isDevice: Device.isDevice
|
|
1059
|
-
};
|
|
1060
|
-
} catch {
|
|
1061
|
-
return {
|
|
1062
|
-
platform: import_react_native2.Platform.OS,
|
|
1063
|
-
modelName: null,
|
|
1064
|
-
brand: null,
|
|
1065
|
-
manufacturer: null,
|
|
1066
|
-
osName: null,
|
|
1067
|
-
osVersion: null,
|
|
1068
|
-
deviceType: null,
|
|
1069
|
-
deviceName: null,
|
|
1070
|
-
totalMemory: null,
|
|
1071
|
-
modelId: null,
|
|
1072
|
-
designName: null,
|
|
1073
|
-
productName: null,
|
|
1074
|
-
isDevice: null
|
|
1075
|
-
};
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
1122
|
// src/ui/ConnectWalletScreen.tsx
|
|
1080
|
-
var
|
|
1123
|
+
var import_react_native3 = require("react-native");
|
|
1081
1124
|
|
|
1082
1125
|
// src/ui/theme.ts
|
|
1083
|
-
var
|
|
1126
|
+
var import_react_native2 = require("react-native");
|
|
1084
1127
|
var dark = {
|
|
1085
1128
|
background: "#08080D",
|
|
1086
1129
|
surface: "#111118",
|
|
@@ -1114,7 +1157,7 @@ var light = {
|
|
|
1114
1157
|
errorBorder: "#FECACA"
|
|
1115
1158
|
};
|
|
1116
1159
|
function useDubsTheme() {
|
|
1117
|
-
const scheme = (0,
|
|
1160
|
+
const scheme = (0, import_react_native2.useColorScheme)();
|
|
1118
1161
|
return scheme === "light" ? light : dark;
|
|
1119
1162
|
}
|
|
1120
1163
|
function mergeTheme(base, overrides) {
|
|
@@ -1134,44 +1177,44 @@ function ConnectWalletScreen({
|
|
|
1134
1177
|
}) {
|
|
1135
1178
|
const t = useDubsTheme();
|
|
1136
1179
|
const accent = accentColor || t.accent;
|
|
1137
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1138
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1180
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.View, { style: [styles.container, { backgroundColor: t.background }], children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native3.View, { style: styles.content, children: [
|
|
1181
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native3.View, { style: styles.brandingSection, children: [
|
|
1139
1182
|
appIcon ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1140
|
-
|
|
1183
|
+
import_react_native3.Image,
|
|
1141
1184
|
{
|
|
1142
1185
|
source: { uri: appIcon },
|
|
1143
1186
|
style: styles.logoImage
|
|
1144
1187
|
}
|
|
1145
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1146
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1147
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1188
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.View, { style: [styles.logoCircle, { backgroundColor: accent }], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.Text, { style: styles.logoText, children: appName.charAt(0).toUpperCase() }) }),
|
|
1189
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.Text, { style: [styles.appName, { color: t.text }], children: appName }),
|
|
1190
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.Text, { style: [styles.subtitle, { color: t.textMuted }], children: tagline || "Connect your Solana wallet to get started" })
|
|
1148
1191
|
] }),
|
|
1149
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1192
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native3.View, { style: styles.actionSection, children: [
|
|
1150
1193
|
error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1151
|
-
|
|
1194
|
+
import_react_native3.View,
|
|
1152
1195
|
{
|
|
1153
1196
|
style: [
|
|
1154
1197
|
styles.errorBox,
|
|
1155
1198
|
{ backgroundColor: t.errorBg, borderColor: t.errorBorder }
|
|
1156
1199
|
],
|
|
1157
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1200
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.Text, { style: [styles.errorText, { color: t.errorText }], children: error })
|
|
1158
1201
|
}
|
|
1159
1202
|
) : null,
|
|
1160
1203
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1161
|
-
|
|
1204
|
+
import_react_native3.TouchableOpacity,
|
|
1162
1205
|
{
|
|
1163
1206
|
style: [styles.connectButton, { backgroundColor: accent }],
|
|
1164
1207
|
onPress: onConnect,
|
|
1165
1208
|
disabled: connecting,
|
|
1166
1209
|
activeOpacity: 0.8,
|
|
1167
|
-
children: connecting ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1210
|
+
children: connecting ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.ActivityIndicator, { color: "#FFFFFF", size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.Text, { style: styles.connectButtonText, children: "Connect Wallet" })
|
|
1168
1211
|
}
|
|
1169
1212
|
),
|
|
1170
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1213
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.Text, { style: [styles.hint, { color: t.textDim }], children: "Phantom, Solflare, or any Solana wallet" })
|
|
1171
1214
|
] })
|
|
1172
1215
|
] }) });
|
|
1173
1216
|
}
|
|
1174
|
-
var styles =
|
|
1217
|
+
var styles = import_react_native3.StyleSheet.create({
|
|
1175
1218
|
container: {
|
|
1176
1219
|
flex: 1,
|
|
1177
1220
|
justifyContent: "center"
|
|
@@ -1256,6 +1299,7 @@ function getOrCreatePhantomAdapter(config) {
|
|
|
1256
1299
|
redirectUri: config.redirectUri,
|
|
1257
1300
|
appUrl: config.appUrl,
|
|
1258
1301
|
cluster: config.cluster,
|
|
1302
|
+
storage: config.storage,
|
|
1259
1303
|
onSessionChange: (session) => {
|
|
1260
1304
|
if (session) {
|
|
1261
1305
|
console.log(TAG3, "Phantom session changed \u2014 saving to storage, wallet:", session.walletPublicKey);
|
|
@@ -1293,9 +1337,8 @@ function ManagedWalletProvider({
|
|
|
1293
1337
|
const [connecting, setConnecting] = (0, import_react.useState)(false);
|
|
1294
1338
|
const [isReady, setIsReady] = (0, import_react.useState)(false);
|
|
1295
1339
|
const [error, setError] = (0, import_react.useState)(null);
|
|
1296
|
-
const
|
|
1297
|
-
|
|
1298
|
-
console.log(TAG3, `Platform: ${import_react_native5.Platform.OS}, seeker: ${seeker}, redirectUri: ${redirectUri ? "provided" : "not set"}, usePhantom: ${usePhantom}`);
|
|
1340
|
+
const usePhantom = import_react_native4.Platform.OS === "ios" && !!redirectUri;
|
|
1341
|
+
console.log(TAG3, `Platform: ${import_react_native4.Platform.OS}, redirectUri: ${redirectUri ? "provided" : "not set"}, usePhantom: ${usePhantom}`);
|
|
1299
1342
|
const adapterRef = (0, import_react.useRef)(null);
|
|
1300
1343
|
const transactRef = (0, import_react.useRef)(null);
|
|
1301
1344
|
if (!adapterRef.current) {
|
|
@@ -1345,6 +1388,21 @@ function ManagedWalletProvider({
|
|
|
1345
1388
|
}
|
|
1346
1389
|
return;
|
|
1347
1390
|
}
|
|
1391
|
+
const coldStartUrl = phantom.consumeColdStartUrl();
|
|
1392
|
+
if (coldStartUrl) {
|
|
1393
|
+
try {
|
|
1394
|
+
console.log(TAG3, "Cold-start URL detected, attempting recovery");
|
|
1395
|
+
await phantom.completeConnectFromColdStart(coldStartUrl);
|
|
1396
|
+
if (!cancelled) {
|
|
1397
|
+
console.log(TAG3, "Cold-start recovery succeeded");
|
|
1398
|
+
setConnected(true);
|
|
1399
|
+
setIsReady(true);
|
|
1400
|
+
}
|
|
1401
|
+
return;
|
|
1402
|
+
} catch (err) {
|
|
1403
|
+
console.log(TAG3, "Cold-start recovery failed:", err instanceof Error ? err.message : err);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1348
1406
|
try {
|
|
1349
1407
|
const savedSession = await storage.getItem(STORAGE_KEYS.PHANTOM_SESSION);
|
|
1350
1408
|
if (savedSession && !cancelled) {
|
|
@@ -1436,6 +1494,8 @@ function ManagedWalletProvider({
|
|
|
1436
1494
|
});
|
|
1437
1495
|
await storage.deleteItem(STORAGE_KEYS.JWT_TOKEN).catch(() => {
|
|
1438
1496
|
});
|
|
1497
|
+
await storage.deleteItem(STORAGE_KEYS.PHANTOM_CONNECT_IN_FLIGHT).catch(() => {
|
|
1498
|
+
});
|
|
1439
1499
|
setConnected(false);
|
|
1440
1500
|
console.log(TAG3, "disconnect() \u2014 done");
|
|
1441
1501
|
}, [adapter, storage, usePhantom]);
|
|
@@ -1838,6 +1898,53 @@ var import_bs583 = __toESM(require("bs58"));
|
|
|
1838
1898
|
var import_react10 = require("react");
|
|
1839
1899
|
var AuthContext = (0, import_react10.createContext)(null);
|
|
1840
1900
|
|
|
1901
|
+
// src/utils/device.ts
|
|
1902
|
+
var import_react_native5 = require("react-native");
|
|
1903
|
+
function isSolanaSeeker() {
|
|
1904
|
+
try {
|
|
1905
|
+
const Device = require("expo-device");
|
|
1906
|
+
return Device.brand?.toLowerCase() === "solanamobile";
|
|
1907
|
+
} catch {
|
|
1908
|
+
return false;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
async function getDeviceInfo() {
|
|
1912
|
+
try {
|
|
1913
|
+
const Device = require("expo-device");
|
|
1914
|
+
return {
|
|
1915
|
+
platform: import_react_native5.Platform.OS,
|
|
1916
|
+
modelName: Device.modelName,
|
|
1917
|
+
brand: Device.brand,
|
|
1918
|
+
manufacturer: Device.manufacturer,
|
|
1919
|
+
osName: Device.osName,
|
|
1920
|
+
osVersion: Device.osVersion,
|
|
1921
|
+
deviceType: Device.deviceType,
|
|
1922
|
+
deviceName: Device.deviceName,
|
|
1923
|
+
totalMemory: Device.totalMemory,
|
|
1924
|
+
modelId: Device.modelId,
|
|
1925
|
+
designName: Device.designName,
|
|
1926
|
+
productName: Device.productName,
|
|
1927
|
+
isDevice: Device.isDevice
|
|
1928
|
+
};
|
|
1929
|
+
} catch {
|
|
1930
|
+
return {
|
|
1931
|
+
platform: import_react_native5.Platform.OS,
|
|
1932
|
+
modelName: null,
|
|
1933
|
+
brand: null,
|
|
1934
|
+
manufacturer: null,
|
|
1935
|
+
osName: null,
|
|
1936
|
+
osVersion: null,
|
|
1937
|
+
deviceType: null,
|
|
1938
|
+
deviceName: null,
|
|
1939
|
+
totalMemory: null,
|
|
1940
|
+
modelId: null,
|
|
1941
|
+
designName: null,
|
|
1942
|
+
productName: null,
|
|
1943
|
+
isDevice: null
|
|
1944
|
+
};
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1841
1948
|
// src/hooks/useAuth.ts
|
|
1842
1949
|
function useAuth() {
|
|
1843
1950
|
const sharedAuth = (0, import_react11.useContext)(AuthContext);
|