@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 +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +179 -60
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +162 -44
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/src/managed-wallet.tsx +26 -4
- package/src/storage.ts +1 -0
- package/src/utils/device.ts +13 -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
|
@@ -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
|
|
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
|
|
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
|
|
1170
|
+
var import_react_native4 = require("react-native");
|
|
1033
1171
|
|
|
1034
1172
|
// src/ui/theme.ts
|
|
1035
|
-
var
|
|
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,
|
|
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)(
|
|
1090
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
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
|
-
|
|
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)(
|
|
1098
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1099
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
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)(
|
|
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
|
-
|
|
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)(
|
|
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
|
-
|
|
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)(
|
|
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)(
|
|
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 =
|
|
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
|
|
1249
|
-
|
|
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,
|