@dubsdotapp/expo 0.2.21 → 0.2.23

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 CHANGED
@@ -17,6 +17,11 @@ interface DeviceInfo {
17
17
  productName: string | null;
18
18
  isDevice: boolean | null;
19
19
  }
20
+ /**
21
+ * Detect Solana Seeker device (synchronous — safe to call during render).
22
+ * Checks brand from expo-device; returns false if expo-device isn't installed.
23
+ */
24
+ declare function isSolanaSeeker(): boolean;
20
25
  declare function getDeviceInfo(): Promise<DeviceInfo>;
21
26
 
22
27
  interface Opponent {
@@ -425,6 +430,7 @@ declare const STORAGE_KEYS: {
425
430
  readonly MWA_AUTH_TOKEN: "dubs_mwa_auth_token";
426
431
  readonly JWT_TOKEN: "dubs_jwt_token";
427
432
  readonly PHANTOM_SESSION: "dubs_phantom_session";
433
+ readonly PHANTOM_CONNECT_IN_FLIGHT: "dubs_phantom_connect_in_flight";
428
434
  };
429
435
  /**
430
436
  * Creates a TokenStorage backed by expo-secure-store.
@@ -617,6 +623,8 @@ interface PhantomDeeplinkAdapterConfig {
617
623
  timeout?: number;
618
624
  /** Called when the Phantom session changes (save/clear for persistence) */
619
625
  onSessionChange?: (session: PhantomSession | null) => void;
626
+ /** Storage for persisting in-flight connect state (cold-start recovery on Android) */
627
+ storage?: TokenStorage;
620
628
  }
621
629
  /**
622
630
  * Phantom wallet adapter using deeplinks (works on iOS and Android).
@@ -644,6 +652,17 @@ declare class PhantomDeeplinkAdapter implements WalletAdapter {
644
652
  getSession(): PhantomSession | null;
645
653
  connect(): Promise<void>;
646
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>;
647
666
  signTransaction(transaction: Transaction): Promise<Transaction>;
648
667
  signMessage(message: Uint8Array): Promise<Uint8Array>;
649
668
  /** Remove the Linking event listener. Call when the adapter is no longer needed. */
@@ -889,4 +908,4 @@ declare function CreateCustomGameSheet({ visible, onDismiss, title, maxPlayers,
889
908
  */
890
909
  declare function signAndSendBase64Transaction(base64Tx: string, wallet: WalletAdapter, connection: Connection): Promise<string>;
891
910
 
892
- export { AuthGate, type AuthGateProps, type AuthStatus, type AuthenticateParams, type AuthenticateResult, type Bettor, type BuildClaimParams, type BuildClaimResult, type CheckUsernameResult, type ClaimMutationResult, type ConfirmGameParams, type ConfirmGameResult, ConnectWalletScreen, type ConnectWalletScreenProps, type CreateCustomGameMutationResult, type CreateCustomGameParams, type CreateCustomGameResult, CreateCustomGameSheet, type CreateCustomGameSheetProps, type CreateGameMutationResult, type CreateGameParams, type CreateGameResult, DEFAULT_BASE_URL, DEFAULT_RPC_URL, type DeviceInfo, DubsApiError, type DubsAppUser, DubsClient, type DubsClientConfig, type DubsContextValue, type DubsNetwork, DubsProvider, type DubsProviderProps, type DubsPublicUser, type DubsTheme, type DubsUser, type EsportsMatchDetail, type EsportsMatchOpponent, type EsportsMatchResult, type EventMedia, type EventMeta, type EventStream, type GameDetail, type GameListItem, type GameListOpponent, type GameMedia, GamePoster, type GamePosterProps, type GetGamesParams, type GetNetworkGamesParams, type GetUpcomingEventsParams, JoinGameButton, type JoinGameButtonProps, type JoinGameMutationResult, type JoinGameParams, type JoinGameResult, LivePoolsCard, type LivePoolsCardProps, type LiveScore, type LiveScoreCompetitor, type MutationResult, type MutationStatus, type MwaAdapterConfig, type MwaTransactFn, MwaWalletAdapter, NETWORK_CONFIG, type NonceResult, type Opponent, type Pagination, type ParsedError, PhantomDeeplinkAdapter, type PhantomDeeplinkAdapterConfig, type PhantomSession, PickWinnerCard, type PickWinnerCardProps, PlayersCard, type PlayersCardProps, type QueryResult, type RegisterParams, type RegisterResult, type RegistrationScreenProps, SOLANA_PROGRAM_ERRORS, STORAGE_KEYS, SettingsSheet, type SettingsSheetProps, type SolanaErrorCode, type TokenStorage, type UiConfig, type UnifiedEvent, type UseAuthResult, UserProfileCard, type UserProfileCardProps, type ValidateEventResult, type WalletAdapter, createSecureStoreStorage, getDeviceInfo, mergeTheme, parseSolanaError, signAndSendBase64Transaction, useAppConfig, useAuth, useClaim, useCreateCustomGame, useCreateGame, useDubs, useDubsTheme, useEvents, useGame, useGames, useJoinGame, useNetworkGames };
911
+ export { AuthGate, type AuthGateProps, type AuthStatus, type AuthenticateParams, type AuthenticateResult, type Bettor, type BuildClaimParams, type BuildClaimResult, type CheckUsernameResult, type ClaimMutationResult, type ConfirmGameParams, type ConfirmGameResult, ConnectWalletScreen, type ConnectWalletScreenProps, type CreateCustomGameMutationResult, type CreateCustomGameParams, type CreateCustomGameResult, CreateCustomGameSheet, type CreateCustomGameSheetProps, type CreateGameMutationResult, type CreateGameParams, type CreateGameResult, DEFAULT_BASE_URL, DEFAULT_RPC_URL, type DeviceInfo, DubsApiError, type DubsAppUser, DubsClient, type DubsClientConfig, type DubsContextValue, type DubsNetwork, DubsProvider, type DubsProviderProps, type DubsPublicUser, type DubsTheme, type DubsUser, type EsportsMatchDetail, type EsportsMatchOpponent, type EsportsMatchResult, type EventMedia, type EventMeta, type EventStream, type GameDetail, type GameListItem, type GameListOpponent, type GameMedia, GamePoster, type GamePosterProps, type GetGamesParams, type GetNetworkGamesParams, type GetUpcomingEventsParams, JoinGameButton, type JoinGameButtonProps, type JoinGameMutationResult, type JoinGameParams, type JoinGameResult, LivePoolsCard, type LivePoolsCardProps, type LiveScore, type LiveScoreCompetitor, type MutationResult, type MutationStatus, type MwaAdapterConfig, type MwaTransactFn, MwaWalletAdapter, NETWORK_CONFIG, type NonceResult, type Opponent, type Pagination, type ParsedError, PhantomDeeplinkAdapter, type PhantomDeeplinkAdapterConfig, type PhantomSession, PickWinnerCard, type PickWinnerCardProps, PlayersCard, type PlayersCardProps, type QueryResult, type RegisterParams, type RegisterResult, type RegistrationScreenProps, SOLANA_PROGRAM_ERRORS, STORAGE_KEYS, SettingsSheet, type SettingsSheetProps, type SolanaErrorCode, type TokenStorage, type UiConfig, type UnifiedEvent, type UseAuthResult, UserProfileCard, type UserProfileCardProps, type ValidateEventResult, type WalletAdapter, createSecureStoreStorage, getDeviceInfo, isSolanaSeeker, mergeTheme, parseSolanaError, signAndSendBase64Transaction, useAppConfig, useAuth, useClaim, useCreateCustomGame, useCreateGame, useDubs, useDubsTheme, useEvents, useGame, useGames, useJoinGame, useNetworkGames };
package/dist/index.d.ts CHANGED
@@ -17,6 +17,11 @@ interface DeviceInfo {
17
17
  productName: string | null;
18
18
  isDevice: boolean | null;
19
19
  }
20
+ /**
21
+ * Detect Solana Seeker device (synchronous — safe to call during render).
22
+ * Checks brand from expo-device; returns false if expo-device isn't installed.
23
+ */
24
+ declare function isSolanaSeeker(): boolean;
20
25
  declare function getDeviceInfo(): Promise<DeviceInfo>;
21
26
 
22
27
  interface Opponent {
@@ -425,6 +430,7 @@ declare const STORAGE_KEYS: {
425
430
  readonly MWA_AUTH_TOKEN: "dubs_mwa_auth_token";
426
431
  readonly JWT_TOKEN: "dubs_jwt_token";
427
432
  readonly PHANTOM_SESSION: "dubs_phantom_session";
433
+ readonly PHANTOM_CONNECT_IN_FLIGHT: "dubs_phantom_connect_in_flight";
428
434
  };
429
435
  /**
430
436
  * Creates a TokenStorage backed by expo-secure-store.
@@ -617,6 +623,8 @@ interface PhantomDeeplinkAdapterConfig {
617
623
  timeout?: number;
618
624
  /** Called when the Phantom session changes (save/clear for persistence) */
619
625
  onSessionChange?: (session: PhantomSession | null) => void;
626
+ /** Storage for persisting in-flight connect state (cold-start recovery on Android) */
627
+ storage?: TokenStorage;
620
628
  }
621
629
  /**
622
630
  * Phantom wallet adapter using deeplinks (works on iOS and Android).
@@ -644,6 +652,17 @@ declare class PhantomDeeplinkAdapter implements WalletAdapter {
644
652
  getSession(): PhantomSession | null;
645
653
  connect(): Promise<void>;
646
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>;
647
666
  signTransaction(transaction: Transaction): Promise<Transaction>;
648
667
  signMessage(message: Uint8Array): Promise<Uint8Array>;
649
668
  /** Remove the Linking event listener. Call when the adapter is no longer needed. */
@@ -889,4 +908,4 @@ declare function CreateCustomGameSheet({ visible, onDismiss, title, maxPlayers,
889
908
  */
890
909
  declare function signAndSendBase64Transaction(base64Tx: string, wallet: WalletAdapter, connection: Connection): Promise<string>;
891
910
 
892
- export { AuthGate, type AuthGateProps, type AuthStatus, type AuthenticateParams, type AuthenticateResult, type Bettor, type BuildClaimParams, type BuildClaimResult, type CheckUsernameResult, type ClaimMutationResult, type ConfirmGameParams, type ConfirmGameResult, ConnectWalletScreen, type ConnectWalletScreenProps, type CreateCustomGameMutationResult, type CreateCustomGameParams, type CreateCustomGameResult, CreateCustomGameSheet, type CreateCustomGameSheetProps, type CreateGameMutationResult, type CreateGameParams, type CreateGameResult, DEFAULT_BASE_URL, DEFAULT_RPC_URL, type DeviceInfo, DubsApiError, type DubsAppUser, DubsClient, type DubsClientConfig, type DubsContextValue, type DubsNetwork, DubsProvider, type DubsProviderProps, type DubsPublicUser, type DubsTheme, type DubsUser, type EsportsMatchDetail, type EsportsMatchOpponent, type EsportsMatchResult, type EventMedia, type EventMeta, type EventStream, type GameDetail, type GameListItem, type GameListOpponent, type GameMedia, GamePoster, type GamePosterProps, type GetGamesParams, type GetNetworkGamesParams, type GetUpcomingEventsParams, JoinGameButton, type JoinGameButtonProps, type JoinGameMutationResult, type JoinGameParams, type JoinGameResult, LivePoolsCard, type LivePoolsCardProps, type LiveScore, type LiveScoreCompetitor, type MutationResult, type MutationStatus, type MwaAdapterConfig, type MwaTransactFn, MwaWalletAdapter, NETWORK_CONFIG, type NonceResult, type Opponent, type Pagination, type ParsedError, PhantomDeeplinkAdapter, type PhantomDeeplinkAdapterConfig, type PhantomSession, PickWinnerCard, type PickWinnerCardProps, PlayersCard, type PlayersCardProps, type QueryResult, type RegisterParams, type RegisterResult, type RegistrationScreenProps, SOLANA_PROGRAM_ERRORS, STORAGE_KEYS, SettingsSheet, type SettingsSheetProps, type SolanaErrorCode, type TokenStorage, type UiConfig, type UnifiedEvent, type UseAuthResult, UserProfileCard, type UserProfileCardProps, type ValidateEventResult, type WalletAdapter, createSecureStoreStorage, getDeviceInfo, mergeTheme, parseSolanaError, signAndSendBase64Transaction, useAppConfig, useAuth, useClaim, useCreateCustomGame, useCreateGame, useDubs, useDubsTheme, useEvents, useGame, useGames, useJoinGame, useNetworkGames };
911
+ export { AuthGate, type AuthGateProps, type AuthStatus, type AuthenticateParams, type AuthenticateResult, type Bettor, type BuildClaimParams, type BuildClaimResult, type CheckUsernameResult, type ClaimMutationResult, type ConfirmGameParams, type ConfirmGameResult, ConnectWalletScreen, type ConnectWalletScreenProps, type CreateCustomGameMutationResult, type CreateCustomGameParams, type CreateCustomGameResult, CreateCustomGameSheet, type CreateCustomGameSheetProps, type CreateGameMutationResult, type CreateGameParams, type CreateGameResult, DEFAULT_BASE_URL, DEFAULT_RPC_URL, type DeviceInfo, DubsApiError, type DubsAppUser, DubsClient, type DubsClientConfig, type DubsContextValue, type DubsNetwork, DubsProvider, type DubsProviderProps, type DubsPublicUser, type DubsTheme, type DubsUser, type EsportsMatchDetail, type EsportsMatchOpponent, type EsportsMatchResult, type EventMedia, type EventMeta, type EventStream, type GameDetail, type GameListItem, type GameListOpponent, type GameMedia, GamePoster, type GamePosterProps, type GetGamesParams, type GetNetworkGamesParams, type GetUpcomingEventsParams, JoinGameButton, type JoinGameButtonProps, type JoinGameMutationResult, type JoinGameParams, type JoinGameResult, LivePoolsCard, type LivePoolsCardProps, type LiveScore, type LiveScoreCompetitor, type MutationResult, type MutationStatus, type MwaAdapterConfig, type MwaTransactFn, MwaWalletAdapter, NETWORK_CONFIG, type NonceResult, type Opponent, type Pagination, type ParsedError, PhantomDeeplinkAdapter, type PhantomDeeplinkAdapterConfig, type PhantomSession, PickWinnerCard, type PickWinnerCardProps, PlayersCard, type PlayersCardProps, type QueryResult, type RegisterParams, type RegisterResult, type RegistrationScreenProps, SOLANA_PROGRAM_ERRORS, STORAGE_KEYS, SettingsSheet, type SettingsSheetProps, type SolanaErrorCode, type TokenStorage, type UiConfig, type UnifiedEvent, type UseAuthResult, UserProfileCard, type UserProfileCardProps, type ValidateEventResult, type WalletAdapter, createSecureStoreStorage, getDeviceInfo, isSolanaSeeker, mergeTheme, parseSolanaError, signAndSendBase64Transaction, useAppConfig, useAuth, useClaim, useCreateCustomGame, useCreateGame, useDubs, useDubsTheme, useEvents, useGame, useGames, useJoinGame, useNetworkGames };
package/dist/index.js CHANGED
@@ -52,6 +52,7 @@ __export(index_exports, {
52
52
  UserProfileCard: () => UserProfileCard,
53
53
  createSecureStoreStorage: () => createSecureStoreStorage,
54
54
  getDeviceInfo: () => getDeviceInfo,
55
+ isSolanaSeeker: () => isSolanaSeeker,
55
56
  mergeTheme: () => mergeTheme,
56
57
  parseSolanaError: () => parseSolanaError,
57
58
  signAndSendBase64Transaction: () => signAndSendBase64Transaction,
@@ -521,7 +522,8 @@ var DubsClient = class {
521
522
  var STORAGE_KEYS = {
522
523
  MWA_AUTH_TOKEN: "dubs_mwa_auth_token",
523
524
  JWT_TOKEN: "dubs_jwt_token",
524
- PHANTOM_SESSION: "dubs_phantom_session"
525
+ PHANTOM_SESSION: "dubs_phantom_session",
526
+ PHANTOM_CONNECT_IN_FLIGHT: "dubs_phantom_connect_in_flight"
525
527
  };
526
528
  function createSecureStoreStorage() {
527
529
  let SecureStore = null;
@@ -559,7 +561,7 @@ var import_web34 = require("@solana/web3.js");
559
561
 
560
562
  // src/managed-wallet.tsx
561
563
  var import_react = require("react");
562
- var import_react_native4 = require("react-native");
564
+ var import_react_native5 = require("react-native");
563
565
 
564
566
  // src/wallet/mwa-adapter.ts
565
567
  var import_web3 = require("@solana/web3.js");
@@ -698,6 +700,7 @@ var DeeplinkHandler = class {
698
700
  constructor(redirectBase) {
699
701
  this.pending = /* @__PURE__ */ new Map();
700
702
  this.subscription = null;
703
+ this._coldStartUrl = null;
701
704
  this.redirectBase = redirectBase.replace(/\/+$/, "");
702
705
  console.log(TAG, "Created with redirectBase:", this.redirectBase);
703
706
  }
@@ -807,9 +810,16 @@ var DeeplinkHandler = class {
807
810
  this.pending.delete(id);
808
811
  req.resolve({ params });
809
812
  } else {
810
- console.log(TAG, "No pending requests to resolve \u2014 redirect may have arrived late");
813
+ console.log(TAG, "No pending requests to resolve \u2014 stashing as cold-start URL");
814
+ this._coldStartUrl = url;
811
815
  }
812
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
+ }
813
823
  /** Extract the request ID from the dubs_rid query parameter. */
814
824
  extractRequestId(url) {
815
825
  try {
@@ -901,6 +911,18 @@ var PhantomDeeplinkAdapter = class {
901
911
  redirect_link: redirectLink,
902
912
  app_url: appUrl
903
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
+ }
904
926
  const url = `https://phantom.app/ul/v1/connect?${params.toString()}`;
905
927
  console.log(TAG2, "Opening Phantom connect deeplink...");
906
928
  const response = await this.handler.send(url, requestId, this.timeout);
@@ -928,6 +950,8 @@ var PhantomDeeplinkAdapter = class {
928
950
  this._connected = true;
929
951
  console.log(TAG2, "Connected! Wallet:", this._publicKey.toBase58());
930
952
  this.config.onSessionChange?.(this.getSession());
953
+ this.config.storage?.deleteItem(STORAGE_KEYS.PHANTOM_CONNECT_IN_FLIGHT).catch(() => {
954
+ });
931
955
  }
932
956
  disconnect() {
933
957
  console.log(TAG2, "disconnect() \u2014 clearing state, was connected:", this._connected, "wallet:", this._publicKey?.toBase58());
@@ -940,6 +964,73 @@ var PhantomDeeplinkAdapter = class {
940
964
  this.config.onSessionChange?.(null);
941
965
  console.log(TAG2, "Disconnected");
942
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
+ }
943
1034
  async signTransaction(transaction) {
944
1035
  this.assertConnected();
945
1036
  console.log(TAG2, "signTransaction() \u2014 serializing transaction");
@@ -1028,11 +1119,58 @@ var PhantomDeeplinkAdapter = class {
1028
1119
  }
1029
1120
  };
1030
1121
 
1122
+ // src/utils/device.ts
1123
+ var import_react_native2 = require("react-native");
1124
+ function isSolanaSeeker() {
1125
+ try {
1126
+ const Device = require("expo-device");
1127
+ return Device.brand?.toLowerCase() === "solanamobile";
1128
+ } catch {
1129
+ return false;
1130
+ }
1131
+ }
1132
+ async function getDeviceInfo() {
1133
+ try {
1134
+ const Device = require("expo-device");
1135
+ return {
1136
+ platform: import_react_native2.Platform.OS,
1137
+ modelName: Device.modelName,
1138
+ brand: Device.brand,
1139
+ manufacturer: Device.manufacturer,
1140
+ osName: Device.osName,
1141
+ osVersion: Device.osVersion,
1142
+ deviceType: Device.deviceType,
1143
+ deviceName: Device.deviceName,
1144
+ totalMemory: Device.totalMemory,
1145
+ modelId: Device.modelId,
1146
+ designName: Device.designName,
1147
+ productName: Device.productName,
1148
+ isDevice: Device.isDevice
1149
+ };
1150
+ } catch {
1151
+ return {
1152
+ platform: import_react_native2.Platform.OS,
1153
+ modelName: null,
1154
+ brand: null,
1155
+ manufacturer: null,
1156
+ osName: null,
1157
+ osVersion: null,
1158
+ deviceType: null,
1159
+ deviceName: null,
1160
+ totalMemory: null,
1161
+ modelId: null,
1162
+ designName: null,
1163
+ productName: null,
1164
+ isDevice: null
1165
+ };
1166
+ }
1167
+ }
1168
+
1031
1169
  // src/ui/ConnectWalletScreen.tsx
1032
- var import_react_native3 = require("react-native");
1170
+ var import_react_native4 = require("react-native");
1033
1171
 
1034
1172
  // src/ui/theme.ts
1035
- var import_react_native2 = require("react-native");
1173
+ var import_react_native3 = require("react-native");
1036
1174
  var dark = {
1037
1175
  background: "#08080D",
1038
1176
  surface: "#111118",
@@ -1066,7 +1204,7 @@ var light = {
1066
1204
  errorBorder: "#FECACA"
1067
1205
  };
1068
1206
  function useDubsTheme() {
1069
- const scheme = (0, import_react_native2.useColorScheme)();
1207
+ const scheme = (0, import_react_native3.useColorScheme)();
1070
1208
  return scheme === "light" ? light : dark;
1071
1209
  }
1072
1210
  function mergeTheme(base, overrides) {
@@ -1086,44 +1224,44 @@ function ConnectWalletScreen({
1086
1224
  }) {
1087
1225
  const t = useDubsTheme();
1088
1226
  const accent = accentColor || t.accent;
1089
- 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: [
1090
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native3.View, { style: styles.brandingSection, children: [
1227
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native4.View, { style: [styles.container, { backgroundColor: t.background }], children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native4.View, { style: styles.content, children: [
1228
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native4.View, { style: styles.brandingSection, children: [
1091
1229
  appIcon ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1092
- import_react_native3.Image,
1230
+ import_react_native4.Image,
1093
1231
  {
1094
1232
  source: { uri: appIcon },
1095
1233
  style: styles.logoImage
1096
1234
  }
1097
- ) : /* @__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() }) }),
1098
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.Text, { style: [styles.appName, { color: t.text }], children: appName }),
1099
- /* @__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" })
1235
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native4.View, { style: [styles.logoCircle, { backgroundColor: accent }], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native4.Text, { style: styles.logoText, children: appName.charAt(0).toUpperCase() }) }),
1236
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native4.Text, { style: [styles.appName, { color: t.text }], children: appName }),
1237
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native4.Text, { style: [styles.subtitle, { color: t.textMuted }], children: tagline || "Connect your Solana wallet to get started" })
1100
1238
  ] }),
1101
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native3.View, { style: styles.actionSection, children: [
1239
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native4.View, { style: styles.actionSection, children: [
1102
1240
  error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1103
- import_react_native3.View,
1241
+ import_react_native4.View,
1104
1242
  {
1105
1243
  style: [
1106
1244
  styles.errorBox,
1107
1245
  { backgroundColor: t.errorBg, borderColor: t.errorBorder }
1108
1246
  ],
1109
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.Text, { style: [styles.errorText, { color: t.errorText }], children: error })
1247
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native4.Text, { style: [styles.errorText, { color: t.errorText }], children: error })
1110
1248
  }
1111
1249
  ) : null,
1112
1250
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1113
- import_react_native3.TouchableOpacity,
1251
+ import_react_native4.TouchableOpacity,
1114
1252
  {
1115
1253
  style: [styles.connectButton, { backgroundColor: accent }],
1116
1254
  onPress: onConnect,
1117
1255
  disabled: connecting,
1118
1256
  activeOpacity: 0.8,
1119
- 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" })
1257
+ children: connecting ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native4.ActivityIndicator, { color: "#FFFFFF", size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native4.Text, { style: styles.connectButtonText, children: "Connect Wallet" })
1120
1258
  }
1121
1259
  ),
1122
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native3.Text, { style: [styles.hint, { color: t.textDim }], children: "Phantom, Solflare, or any Solana wallet" })
1260
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native4.Text, { style: [styles.hint, { color: t.textDim }], children: "Phantom, Solflare, or any Solana wallet" })
1123
1261
  ] })
1124
1262
  ] }) });
1125
1263
  }
1126
- var styles = import_react_native3.StyleSheet.create({
1264
+ var styles = import_react_native4.StyleSheet.create({
1127
1265
  container: {
1128
1266
  flex: 1,
1129
1267
  justifyContent: "center"
@@ -1208,6 +1346,7 @@ function getOrCreatePhantomAdapter(config) {
1208
1346
  redirectUri: config.redirectUri,
1209
1347
  appUrl: config.appUrl,
1210
1348
  cluster: config.cluster,
1349
+ storage: config.storage,
1211
1350
  onSessionChange: (session) => {
1212
1351
  if (session) {
1213
1352
  console.log(TAG3, "Phantom session changed \u2014 saving to storage, wallet:", session.walletPublicKey);
@@ -1245,8 +1384,9 @@ function ManagedWalletProvider({
1245
1384
  const [connecting, setConnecting] = (0, import_react.useState)(false);
1246
1385
  const [isReady, setIsReady] = (0, import_react.useState)(false);
1247
1386
  const [error, setError] = (0, import_react.useState)(null);
1248
- const usePhantom = import_react_native4.Platform.OS === "ios" && !!redirectUri;
1249
- console.log(TAG3, `Platform: ${import_react_native4.Platform.OS}, redirectUri: ${redirectUri ? "provided" : "not set"}, usePhantom: ${usePhantom}`);
1387
+ const seeker = import_react_native5.Platform.OS === "android" && isSolanaSeeker();
1388
+ const usePhantom = !seeker && !!redirectUri;
1389
+ console.log(TAG3, `Platform: ${import_react_native5.Platform.OS}, seeker: ${seeker}, redirectUri: ${redirectUri ? "provided" : "not set"}, usePhantom: ${usePhantom}`);
1250
1390
  const adapterRef = (0, import_react.useRef)(null);
1251
1391
  const transactRef = (0, import_react.useRef)(null);
1252
1392
  if (!adapterRef.current) {
@@ -1296,6 +1436,21 @@ function ManagedWalletProvider({
1296
1436
  }
1297
1437
  return;
1298
1438
  }
1439
+ const coldStartUrl = phantom.consumeColdStartUrl();
1440
+ if (coldStartUrl) {
1441
+ try {
1442
+ console.log(TAG3, "Cold-start URL detected, attempting recovery");
1443
+ await phantom.completeConnectFromColdStart(coldStartUrl);
1444
+ if (!cancelled) {
1445
+ console.log(TAG3, "Cold-start recovery succeeded");
1446
+ setConnected(true);
1447
+ setIsReady(true);
1448
+ }
1449
+ return;
1450
+ } catch (err) {
1451
+ console.log(TAG3, "Cold-start recovery failed:", err instanceof Error ? err.message : err);
1452
+ }
1453
+ }
1299
1454
  try {
1300
1455
  const savedSession = await storage.getItem(STORAGE_KEYS.PHANTOM_SESSION);
1301
1456
  if (savedSession && !cancelled) {
@@ -1387,6 +1542,8 @@ function ManagedWalletProvider({
1387
1542
  });
1388
1543
  await storage.deleteItem(STORAGE_KEYS.JWT_TOKEN).catch(() => {
1389
1544
  });
1545
+ await storage.deleteItem(STORAGE_KEYS.PHANTOM_CONNECT_IN_FLIGHT).catch(() => {
1546
+ });
1390
1547
  setConnected(false);
1391
1548
  console.log(TAG3, "disconnect() \u2014 done");
1392
1549
  }, [adapter, storage, usePhantom]);
@@ -1789,45 +1946,6 @@ var import_bs583 = __toESM(require("bs58"));
1789
1946
  var import_react10 = require("react");
1790
1947
  var AuthContext = (0, import_react10.createContext)(null);
1791
1948
 
1792
- // src/utils/device.ts
1793
- var import_react_native5 = require("react-native");
1794
- async function getDeviceInfo() {
1795
- try {
1796
- const Device = require("expo-device");
1797
- return {
1798
- platform: import_react_native5.Platform.OS,
1799
- modelName: Device.modelName,
1800
- brand: Device.brand,
1801
- manufacturer: Device.manufacturer,
1802
- osName: Device.osName,
1803
- osVersion: Device.osVersion,
1804
- deviceType: Device.deviceType,
1805
- deviceName: Device.deviceName,
1806
- totalMemory: Device.totalMemory,
1807
- modelId: Device.modelId,
1808
- designName: Device.designName,
1809
- productName: Device.productName,
1810
- isDevice: Device.isDevice
1811
- };
1812
- } catch {
1813
- return {
1814
- platform: import_react_native5.Platform.OS,
1815
- modelName: null,
1816
- brand: null,
1817
- manufacturer: null,
1818
- osName: null,
1819
- osVersion: null,
1820
- deviceType: null,
1821
- deviceName: null,
1822
- totalMemory: null,
1823
- modelId: null,
1824
- designName: null,
1825
- productName: null,
1826
- isDevice: null
1827
- };
1828
- }
1829
- }
1830
-
1831
1949
  // src/hooks/useAuth.ts
1832
1950
  function useAuth() {
1833
1951
  const sharedAuth = (0, import_react11.useContext)(AuthContext);
@@ -3694,6 +3812,7 @@ var styles9 = import_react_native14.StyleSheet.create({
3694
3812
  UserProfileCard,
3695
3813
  createSecureStoreStorage,
3696
3814
  getDeviceInfo,
3815
+ isSolanaSeeker,
3697
3816
  mergeTheme,
3698
3817
  parseSolanaError,
3699
3818
  signAndSendBase64Transaction,