@dynamic-labs/sdk-react-core 4.78.0 → 4.79.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/package.cjs +4 -4
  3. package/package.js +4 -4
  4. package/package.json +15 -15
  5. package/src/index.cjs +9 -1
  6. package/src/index.d.ts +2 -1
  7. package/src/index.js +4 -1
  8. package/src/lib/context/ErrorContext/ErrorContext.cjs +2 -2
  9. package/src/lib/context/ErrorContext/ErrorContext.d.ts +1 -1
  10. package/src/lib/context/ErrorContext/ErrorContext.js +2 -2
  11. package/src/lib/context/PhantomRedirectContext/useResponseHandlers.cjs +3 -1
  12. package/src/lib/context/PhantomRedirectContext/useResponseHandlers.js +3 -1
  13. package/src/lib/context/SocialRedirectContext/SocialRedirectContext.cjs +7 -6
  14. package/src/lib/context/SocialRedirectContext/SocialRedirectContext.js +7 -6
  15. package/src/lib/shared/utils/functions/index.d.ts +1 -0
  16. package/src/lib/shared/utils/functions/instrumentAuthLoginFailed/index.d.ts +2 -0
  17. package/src/lib/shared/utils/functions/instrumentAuthLoginFailed/instrumentAuthLoginFailed.cjs +17 -0
  18. package/src/lib/shared/utils/functions/instrumentAuthLoginFailed/instrumentAuthLoginFailed.d.ts +7 -0
  19. package/src/lib/shared/utils/functions/instrumentAuthLoginFailed/instrumentAuthLoginFailed.js +13 -0
  20. package/src/lib/utils/hooks/authenticationHooks/useConnectAndSign/useConnectAndSign.cjs +10 -1
  21. package/src/lib/utils/hooks/authenticationHooks/useConnectAndSign/useConnectAndSign.js +10 -1
  22. package/src/lib/utils/hooks/authenticationHooks/useConnectAndSignSplitSteps/useConnectAndSignSplitSteps.cjs +5 -1
  23. package/src/lib/utils/hooks/authenticationHooks/useConnectAndSignSplitSteps/useConnectAndSignSplitSteps.js +5 -1
  24. package/src/lib/utils/hooks/authenticationHooks/useSignConnectOnlyUser/useSignConnectOnlyUser.cjs +5 -1
  25. package/src/lib/utils/hooks/authenticationHooks/useSignConnectOnlyUser/useSignConnectOnlyUser.js +5 -1
  26. package/src/lib/utils/hooks/index.d.ts +3 -2
  27. package/src/lib/utils/hooks/useGoogleDriveBackupReadiness/index.d.ts +1 -0
  28. package/src/lib/utils/hooks/useGoogleDriveBackupReadiness/useGoogleDriveBackupReadiness.cjs +222 -0
  29. package/src/lib/utils/hooks/useGoogleDriveBackupReadiness/useGoogleDriveBackupReadiness.d.ts +85 -0
  30. package/src/lib/utils/hooks/useGoogleDriveBackupReadiness/useGoogleDriveBackupReadiness.js +218 -0
  31. package/src/lib/utils/hooks/useSocialAuth/useSocialAuth.cjs +6 -2
  32. package/src/lib/utils/hooks/useSocialAuth/useSocialAuth.js +6 -2
  33. package/src/lib/utils/hooks/useUserAuth/useUserAuth.cjs +7 -1
  34. package/src/lib/utils/hooks/useUserAuth/useUserAuth.js +7 -1
  35. package/src/lib/utils/hooks/useVerifyWallet/useVerifyWallet.cjs +6 -2
  36. package/src/lib/utils/hooks/useVerifyWallet/useVerifyWallet.js +6 -2
  37. package/src/lib/utils/hooks/useWalletBackup/googleDriveBackupErrors.cjs +33 -0
  38. package/src/lib/utils/hooks/useWalletBackup/googleDriveBackupErrors.d.ts +8 -0
  39. package/src/lib/utils/hooks/useWalletBackup/googleDriveBackupErrors.js +29 -0
  40. package/src/lib/utils/hooks/useWalletBackup/googleDriveScopes.cjs +22 -0
  41. package/src/lib/utils/hooks/useWalletBackup/googleDriveScopes.d.ts +10 -0
  42. package/src/lib/utils/hooks/useWalletBackup/googleDriveScopes.js +16 -0
  43. package/src/lib/utils/hooks/useWalletBackup/index.d.ts +2 -0
  44. package/src/lib/utils/hooks/useWalletBackup/useWalletBackup.cjs +8 -0
  45. package/src/lib/utils/hooks/useWalletBackup/useWalletBackup.d.ts +2 -0
  46. package/src/lib/utils/hooks/useWalletBackup/useWalletBackup.js +9 -1
  47. package/src/lib/utils/hooks/useWalletItemActions/useHandleWalletItem/useHandleWalletItem.cjs +1 -1
  48. package/src/lib/utils/hooks/useWalletItemActions/useHandleWalletItem/useHandleWalletItem.js +1 -1
  49. package/src/lib/views/EmailVerification/EmailVerification.cjs +3 -1
  50. package/src/lib/views/EmailVerification/EmailVerification.js +3 -1
@@ -0,0 +1 @@
1
+ export { useGoogleDriveBackupReadiness, type GoogleDriveBackupReadiness, type GoogleDriveBackupReadinessStatus, type UseGoogleDriveBackupReadinessReturn, } from './useGoogleDriveBackupReadiness';
@@ -0,0 +1,222 @@
1
+ 'use client'
2
+ 'use strict';
3
+
4
+ Object.defineProperty(exports, '__esModule', { value: true });
5
+
6
+ var _tslib = require('../../../../../_virtual/_tslib.cjs');
7
+ var React = require('react');
8
+ var sdkApiCore = require('@dynamic-labs/sdk-api-core');
9
+ require('@dynamic-labs-sdk/client/core');
10
+ require('eventemitter3');
11
+ require('@dynamic-labs/utils');
12
+ require('@dynamic-labs-sdk/client');
13
+ require('../../../config/ApiEndpoint.cjs');
14
+ require('@dynamic-labs/iconic');
15
+ require('@dynamic-labs/wallet-connector-core');
16
+ require('react/jsx-runtime');
17
+ require('../../../context/ViewContext/ViewContext.cjs');
18
+ require('../../../shared/logger.cjs');
19
+ require('@dynamic-labs/wallet-book');
20
+ require('../../constants/colors.cjs');
21
+ require('../../constants/values.cjs');
22
+ require('../../../shared/consts/index.cjs');
23
+ require('@dynamic-labs/multi-wallet');
24
+ require('react-international-phone');
25
+ require('../../../store/state/nonce/nonce.cjs');
26
+ var api = require('../../../data/api/api.cjs');
27
+ require('@dynamic-labs/locale');
28
+ var dynamicContextProps = require('../../../store/state/dynamicContextProps/dynamicContextProps.cjs');
29
+ require('../../../store/state/primaryWalletId/primaryWalletId.cjs');
30
+ require('../../../store/state/connectedWalletsInfo/connectedWalletsInfo.cjs');
31
+ require('../../functions/getWaasAddressTypeLabel/getWaasAddressTypeLabel.cjs');
32
+ require('../../../events/dynamicEvents.cjs');
33
+ var useUser = require('../../../client/extension/user/useUser/useUser.cjs');
34
+ var useRefreshAuth = require('../useRefreshAuth/useRefreshAuth.cjs');
35
+ var useSocialAccounts = require('../useSocialAccounts/useSocialAccounts.cjs');
36
+ var googleDriveScopes = require('../useWalletBackup/googleDriveScopes.cjs');
37
+
38
+ const IDLE = {
39
+ isChecking: false,
40
+ missingScopes: [],
41
+ status: 'idle',
42
+ };
43
+ // Returns the UUID of the user_oauth_accounts row backing the linked Google
44
+ // credential. The path param of /sdk/.../oauthAccounts/:id/accessToken expects
45
+ // this UUID — *not* the third-party `oauthAccountId` (Google's numeric profile
46
+ // ID), which is what JwtVerifiedCredential.oauthAccountId stores.
47
+ const findGoogleOauthAccountId = (credentials) => {
48
+ var _a, _b;
49
+ return (_b = (_a = credentials === null || credentials === void 0 ? void 0 : credentials.find((cred) => cred.format === 'oauth' && cred.oauthProvider === sdkApiCore.ProviderEnum.Google)) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : undefined;
50
+ };
51
+ /**
52
+ * Pre-flight readiness check for backing up MPC keyshares to Google Drive.
53
+ *
54
+ * Recommended pattern: call `check()` when the user opens the "Backup to
55
+ * Google Drive" CTA. If `status` is `'needs-access'`, prompt the user and
56
+ * call `requestAccess()` to re-prompt the Google consent screen. Then
57
+ * proceed with `useWalletBackup().backupWallet(...)`.
58
+ *
59
+ * Status values:
60
+ * - `'idle'`: never checked / after `reset()`.
61
+ * - `'ready'`: stored scopes include both required Drive scopes; backup
62
+ * should succeed (barring transient errors).
63
+ * - `'needs-access'`: backup likely won't succeed without user intervention.
64
+ * Covers (a) missing scopes — `missingScopes` lists the gap, (b) no Google
65
+ * account linked — `missingScopes` is populated with the full required set
66
+ * so callers can render copy directly, and (c) legacy tokens captured
67
+ * before scope tracking shipped (`scopes: []` from the API) — indicated
68
+ * by an empty `missingScopes` array. Call `requestAccess()` to recover.
69
+ * - `'error'`: the underlying fetch or relink rejected. `error` holds
70
+ * the cause.
71
+ *
72
+ * `isChecking` is true while a `check()` or `requestAccess()` is in flight.
73
+ *
74
+ * `missingScopes` lists the scopes the user still needs to grant. When no
75
+ * Google account is linked, this is the full required set; when scopes are
76
+ * known to be partially missing, only the missing ones; for legacy
77
+ * "unknown scopes" tokens, the array is empty.
78
+ *
79
+ * @example
80
+ * ```tsx
81
+ * function BackupButton({ wallet }) {
82
+ * const { status, missingScopes, isChecking, check, requestAccess } =
83
+ * useGoogleDriveBackupReadiness();
84
+ * const { backupWallet, lastBackupError, clearBackupError } = useWalletBackup();
85
+ *
86
+ * // Layer 2: post-flight fallback — needed for legacy users where the stored
87
+ * // `scopes` is empty so pre-flight returned 'unknown'. Because `lastBackupError`
88
+ * // is React state it won't update until the next render; read it in a
89
+ * // useEffect rather than synchronously after `await backupWallet(...)`.
90
+ * useEffect(() => {
91
+ * if (lastBackupError && isInsufficientGoogleDriveScopesError(lastBackupError)) {
92
+ * clearBackupError();
93
+ * requestAccess(); // re-prompt consent, then caller can retry backupWallet
94
+ * }
95
+ * }, [lastBackupError]);
96
+ *
97
+ * const onBackup = async () => {
98
+ * // Layer 1: pre-flight readiness — saves an MPC reshare ceremony when
99
+ * // we can already tell the upload will fail.
100
+ * let r = await check();
101
+ * if (r.status === 'needs-access') {
102
+ * r = await requestAccess(); // popup re-consent
103
+ * if (r.status !== 'ready') return; // user cancelled or relink failed
104
+ * }
105
+ *
106
+ * await backupWallet(wallet, 'GoogleDrive');
107
+ * // If the backup failed due to missing scopes, the useEffect above will
108
+ * // fire on the next render with the updated lastBackupError.
109
+ * };
110
+ *
111
+ * return (
112
+ * <>
113
+ * {status === 'needs-access' && (
114
+ * <p>We need access to: {missingScopes.join(', ')}</p>
115
+ * )}
116
+ * <button onClick={onBackup} disabled={isChecking}>Backup to Drive</button>
117
+ * </>
118
+ * );
119
+ * }
120
+ * ```
121
+ */
122
+ const useGoogleDriveBackupReadiness = () => {
123
+ const user = useUser.useUser();
124
+ const environmentId = dynamicContextProps.useEnvironmentId();
125
+ const refresh = useRefreshAuth.useRefreshAuth();
126
+ const { linkSocialAccount } = useSocialAccounts.useSocialAccounts();
127
+ const [readiness, setReadiness] = React.useState(IDLE);
128
+ const performCheck = React.useCallback((verifiedCredentials) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
129
+ const oauthAccountId = findGoogleOauthAccountId(verifiedCredentials);
130
+ if (!oauthAccountId) {
131
+ // No Google credential linked — every required scope is effectively
132
+ // missing, so populate the full set so callers can render
133
+ // "we need access to ..." UX without checking the empty case.
134
+ const result = {
135
+ isChecking: false,
136
+ missingScopes: googleDriveScopes.GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES,
137
+ status: 'needs-access',
138
+ };
139
+ setReadiness(result);
140
+ return result;
141
+ }
142
+ try {
143
+ const { accessToken, scopes } = yield api.sdkApi().getEndUserOauthAccessToken({
144
+ environmentId,
145
+ oauthAccountId,
146
+ });
147
+ // Empty scopes = legacy token captured before scope tracking; we
148
+ // can't confirm the upload will work, so surface as needs-access
149
+ // (with empty missingScopes to differentiate from the explicit
150
+ // missing case). requestAccess() forces a fresh token so the
151
+ // column populates.
152
+ if (scopes.length === 0) {
153
+ const result = {
154
+ accessToken,
155
+ isChecking: false,
156
+ missingScopes: [],
157
+ status: 'needs-access',
158
+ };
159
+ setReadiness(result);
160
+ return result;
161
+ }
162
+ const missing = googleDriveScopes.findMissingGoogleDriveBackupScopes(scopes);
163
+ const result = missing.length === 0
164
+ ? {
165
+ accessToken,
166
+ isChecking: false,
167
+ missingScopes: [],
168
+ status: 'ready',
169
+ }
170
+ : {
171
+ accessToken,
172
+ isChecking: false,
173
+ missingScopes: missing,
174
+ status: 'needs-access',
175
+ };
176
+ setReadiness(result);
177
+ return result;
178
+ }
179
+ catch (error) {
180
+ const result = {
181
+ error,
182
+ isChecking: false,
183
+ missingScopes: [],
184
+ status: 'error',
185
+ };
186
+ setReadiness(result);
187
+ return result;
188
+ }
189
+ }), [environmentId]);
190
+ const check = React.useCallback(() => _tslib.__awaiter(void 0, void 0, void 0, function* () {
191
+ setReadiness((prev) => (Object.assign(Object.assign({}, prev), { isChecking: true })));
192
+ return performCheck(user === null || user === void 0 ? void 0 : user.verifiedCredentials);
193
+ }), [performCheck, user]);
194
+ const requestAccess = React.useCallback(() => _tslib.__awaiter(void 0, void 0, void 0, function* () {
195
+ var _a;
196
+ setReadiness((prev) => (Object.assign(Object.assign({}, prev), { isChecking: true })));
197
+ try {
198
+ yield linkSocialAccount(sdkApiCore.ProviderEnum.Google, {
199
+ forcePopup: true,
200
+ showWidgetAfterConnection: false,
201
+ });
202
+ const updatedUser = yield refresh();
203
+ return performCheck((_a = updatedUser === null || updatedUser === void 0 ? void 0 : updatedUser.verifiedCredentials) !== null && _a !== void 0 ? _a : user === null || user === void 0 ? void 0 : user.verifiedCredentials);
204
+ }
205
+ catch (error) {
206
+ const result = {
207
+ error,
208
+ isChecking: false,
209
+ missingScopes: [],
210
+ status: 'error',
211
+ };
212
+ setReadiness(result);
213
+ return result;
214
+ }
215
+ }), [linkSocialAccount, performCheck, refresh, user]);
216
+ const reset = React.useCallback(() => setReadiness(IDLE), []);
217
+ return Object.assign(Object.assign({}, readiness), { check,
218
+ requestAccess,
219
+ reset });
220
+ };
221
+
222
+ exports.useGoogleDriveBackupReadiness = useGoogleDriveBackupReadiness;
@@ -0,0 +1,85 @@
1
+ export type GoogleDriveBackupReadinessStatus = 'idle' | 'ready' | 'needs-access' | 'error';
2
+ export type GoogleDriveBackupReadiness = {
3
+ status: GoogleDriveBackupReadinessStatus;
4
+ isChecking: boolean;
5
+ missingScopes: readonly string[];
6
+ accessToken?: string;
7
+ error?: unknown;
8
+ };
9
+ export type UseGoogleDriveBackupReadinessReturn = GoogleDriveBackupReadiness & {
10
+ check: () => Promise<GoogleDriveBackupReadiness>;
11
+ requestAccess: () => Promise<GoogleDriveBackupReadiness>;
12
+ reset: () => void;
13
+ };
14
+ /**
15
+ * Pre-flight readiness check for backing up MPC keyshares to Google Drive.
16
+ *
17
+ * Recommended pattern: call `check()` when the user opens the "Backup to
18
+ * Google Drive" CTA. If `status` is `'needs-access'`, prompt the user and
19
+ * call `requestAccess()` to re-prompt the Google consent screen. Then
20
+ * proceed with `useWalletBackup().backupWallet(...)`.
21
+ *
22
+ * Status values:
23
+ * - `'idle'`: never checked / after `reset()`.
24
+ * - `'ready'`: stored scopes include both required Drive scopes; backup
25
+ * should succeed (barring transient errors).
26
+ * - `'needs-access'`: backup likely won't succeed without user intervention.
27
+ * Covers (a) missing scopes — `missingScopes` lists the gap, (b) no Google
28
+ * account linked — `missingScopes` is populated with the full required set
29
+ * so callers can render copy directly, and (c) legacy tokens captured
30
+ * before scope tracking shipped (`scopes: []` from the API) — indicated
31
+ * by an empty `missingScopes` array. Call `requestAccess()` to recover.
32
+ * - `'error'`: the underlying fetch or relink rejected. `error` holds
33
+ * the cause.
34
+ *
35
+ * `isChecking` is true while a `check()` or `requestAccess()` is in flight.
36
+ *
37
+ * `missingScopes` lists the scopes the user still needs to grant. When no
38
+ * Google account is linked, this is the full required set; when scopes are
39
+ * known to be partially missing, only the missing ones; for legacy
40
+ * "unknown scopes" tokens, the array is empty.
41
+ *
42
+ * @example
43
+ * ```tsx
44
+ * function BackupButton({ wallet }) {
45
+ * const { status, missingScopes, isChecking, check, requestAccess } =
46
+ * useGoogleDriveBackupReadiness();
47
+ * const { backupWallet, lastBackupError, clearBackupError } = useWalletBackup();
48
+ *
49
+ * // Layer 2: post-flight fallback — needed for legacy users where the stored
50
+ * // `scopes` is empty so pre-flight returned 'unknown'. Because `lastBackupError`
51
+ * // is React state it won't update until the next render; read it in a
52
+ * // useEffect rather than synchronously after `await backupWallet(...)`.
53
+ * useEffect(() => {
54
+ * if (lastBackupError && isInsufficientGoogleDriveScopesError(lastBackupError)) {
55
+ * clearBackupError();
56
+ * requestAccess(); // re-prompt consent, then caller can retry backupWallet
57
+ * }
58
+ * }, [lastBackupError]);
59
+ *
60
+ * const onBackup = async () => {
61
+ * // Layer 1: pre-flight readiness — saves an MPC reshare ceremony when
62
+ * // we can already tell the upload will fail.
63
+ * let r = await check();
64
+ * if (r.status === 'needs-access') {
65
+ * r = await requestAccess(); // popup re-consent
66
+ * if (r.status !== 'ready') return; // user cancelled or relink failed
67
+ * }
68
+ *
69
+ * await backupWallet(wallet, 'GoogleDrive');
70
+ * // If the backup failed due to missing scopes, the useEffect above will
71
+ * // fire on the next render with the updated lastBackupError.
72
+ * };
73
+ *
74
+ * return (
75
+ * <>
76
+ * {status === 'needs-access' && (
77
+ * <p>We need access to: {missingScopes.join(', ')}</p>
78
+ * )}
79
+ * <button onClick={onBackup} disabled={isChecking}>Backup to Drive</button>
80
+ * </>
81
+ * );
82
+ * }
83
+ * ```
84
+ */
85
+ export declare const useGoogleDriveBackupReadiness: () => UseGoogleDriveBackupReadinessReturn;
@@ -0,0 +1,218 @@
1
+ 'use client'
2
+ import { __awaiter } from '../../../../../_virtual/_tslib.js';
3
+ import { useState, useCallback } from 'react';
4
+ import { ProviderEnum } from '@dynamic-labs/sdk-api-core';
5
+ import '@dynamic-labs-sdk/client/core';
6
+ import 'eventemitter3';
7
+ import '@dynamic-labs/utils';
8
+ import '@dynamic-labs-sdk/client';
9
+ import '../../../config/ApiEndpoint.js';
10
+ import '@dynamic-labs/iconic';
11
+ import '@dynamic-labs/wallet-connector-core';
12
+ import 'react/jsx-runtime';
13
+ import '../../../context/ViewContext/ViewContext.js';
14
+ import '../../../shared/logger.js';
15
+ import '@dynamic-labs/wallet-book';
16
+ import '../../constants/colors.js';
17
+ import '../../constants/values.js';
18
+ import '../../../shared/consts/index.js';
19
+ import '@dynamic-labs/multi-wallet';
20
+ import 'react-international-phone';
21
+ import '../../../store/state/nonce/nonce.js';
22
+ import { sdkApi } from '../../../data/api/api.js';
23
+ import '@dynamic-labs/locale';
24
+ import { useEnvironmentId } from '../../../store/state/dynamicContextProps/dynamicContextProps.js';
25
+ import '../../../store/state/primaryWalletId/primaryWalletId.js';
26
+ import '../../../store/state/connectedWalletsInfo/connectedWalletsInfo.js';
27
+ import '../../functions/getWaasAddressTypeLabel/getWaasAddressTypeLabel.js';
28
+ import '../../../events/dynamicEvents.js';
29
+ import { useUser } from '../../../client/extension/user/useUser/useUser.js';
30
+ import { useRefreshAuth } from '../useRefreshAuth/useRefreshAuth.js';
31
+ import { useSocialAccounts } from '../useSocialAccounts/useSocialAccounts.js';
32
+ import { findMissingGoogleDriveBackupScopes, GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES } from '../useWalletBackup/googleDriveScopes.js';
33
+
34
+ const IDLE = {
35
+ isChecking: false,
36
+ missingScopes: [],
37
+ status: 'idle',
38
+ };
39
+ // Returns the UUID of the user_oauth_accounts row backing the linked Google
40
+ // credential. The path param of /sdk/.../oauthAccounts/:id/accessToken expects
41
+ // this UUID — *not* the third-party `oauthAccountId` (Google's numeric profile
42
+ // ID), which is what JwtVerifiedCredential.oauthAccountId stores.
43
+ const findGoogleOauthAccountId = (credentials) => {
44
+ var _a, _b;
45
+ return (_b = (_a = credentials === null || credentials === void 0 ? void 0 : credentials.find((cred) => cred.format === 'oauth' && cred.oauthProvider === ProviderEnum.Google)) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : undefined;
46
+ };
47
+ /**
48
+ * Pre-flight readiness check for backing up MPC keyshares to Google Drive.
49
+ *
50
+ * Recommended pattern: call `check()` when the user opens the "Backup to
51
+ * Google Drive" CTA. If `status` is `'needs-access'`, prompt the user and
52
+ * call `requestAccess()` to re-prompt the Google consent screen. Then
53
+ * proceed with `useWalletBackup().backupWallet(...)`.
54
+ *
55
+ * Status values:
56
+ * - `'idle'`: never checked / after `reset()`.
57
+ * - `'ready'`: stored scopes include both required Drive scopes; backup
58
+ * should succeed (barring transient errors).
59
+ * - `'needs-access'`: backup likely won't succeed without user intervention.
60
+ * Covers (a) missing scopes — `missingScopes` lists the gap, (b) no Google
61
+ * account linked — `missingScopes` is populated with the full required set
62
+ * so callers can render copy directly, and (c) legacy tokens captured
63
+ * before scope tracking shipped (`scopes: []` from the API) — indicated
64
+ * by an empty `missingScopes` array. Call `requestAccess()` to recover.
65
+ * - `'error'`: the underlying fetch or relink rejected. `error` holds
66
+ * the cause.
67
+ *
68
+ * `isChecking` is true while a `check()` or `requestAccess()` is in flight.
69
+ *
70
+ * `missingScopes` lists the scopes the user still needs to grant. When no
71
+ * Google account is linked, this is the full required set; when scopes are
72
+ * known to be partially missing, only the missing ones; for legacy
73
+ * "unknown scopes" tokens, the array is empty.
74
+ *
75
+ * @example
76
+ * ```tsx
77
+ * function BackupButton({ wallet }) {
78
+ * const { status, missingScopes, isChecking, check, requestAccess } =
79
+ * useGoogleDriveBackupReadiness();
80
+ * const { backupWallet, lastBackupError, clearBackupError } = useWalletBackup();
81
+ *
82
+ * // Layer 2: post-flight fallback — needed for legacy users where the stored
83
+ * // `scopes` is empty so pre-flight returned 'unknown'. Because `lastBackupError`
84
+ * // is React state it won't update until the next render; read it in a
85
+ * // useEffect rather than synchronously after `await backupWallet(...)`.
86
+ * useEffect(() => {
87
+ * if (lastBackupError && isInsufficientGoogleDriveScopesError(lastBackupError)) {
88
+ * clearBackupError();
89
+ * requestAccess(); // re-prompt consent, then caller can retry backupWallet
90
+ * }
91
+ * }, [lastBackupError]);
92
+ *
93
+ * const onBackup = async () => {
94
+ * // Layer 1: pre-flight readiness — saves an MPC reshare ceremony when
95
+ * // we can already tell the upload will fail.
96
+ * let r = await check();
97
+ * if (r.status === 'needs-access') {
98
+ * r = await requestAccess(); // popup re-consent
99
+ * if (r.status !== 'ready') return; // user cancelled or relink failed
100
+ * }
101
+ *
102
+ * await backupWallet(wallet, 'GoogleDrive');
103
+ * // If the backup failed due to missing scopes, the useEffect above will
104
+ * // fire on the next render with the updated lastBackupError.
105
+ * };
106
+ *
107
+ * return (
108
+ * <>
109
+ * {status === 'needs-access' && (
110
+ * <p>We need access to: {missingScopes.join(', ')}</p>
111
+ * )}
112
+ * <button onClick={onBackup} disabled={isChecking}>Backup to Drive</button>
113
+ * </>
114
+ * );
115
+ * }
116
+ * ```
117
+ */
118
+ const useGoogleDriveBackupReadiness = () => {
119
+ const user = useUser();
120
+ const environmentId = useEnvironmentId();
121
+ const refresh = useRefreshAuth();
122
+ const { linkSocialAccount } = useSocialAccounts();
123
+ const [readiness, setReadiness] = useState(IDLE);
124
+ const performCheck = useCallback((verifiedCredentials) => __awaiter(void 0, void 0, void 0, function* () {
125
+ const oauthAccountId = findGoogleOauthAccountId(verifiedCredentials);
126
+ if (!oauthAccountId) {
127
+ // No Google credential linked — every required scope is effectively
128
+ // missing, so populate the full set so callers can render
129
+ // "we need access to ..." UX without checking the empty case.
130
+ const result = {
131
+ isChecking: false,
132
+ missingScopes: GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES,
133
+ status: 'needs-access',
134
+ };
135
+ setReadiness(result);
136
+ return result;
137
+ }
138
+ try {
139
+ const { accessToken, scopes } = yield sdkApi().getEndUserOauthAccessToken({
140
+ environmentId,
141
+ oauthAccountId,
142
+ });
143
+ // Empty scopes = legacy token captured before scope tracking; we
144
+ // can't confirm the upload will work, so surface as needs-access
145
+ // (with empty missingScopes to differentiate from the explicit
146
+ // missing case). requestAccess() forces a fresh token so the
147
+ // column populates.
148
+ if (scopes.length === 0) {
149
+ const result = {
150
+ accessToken,
151
+ isChecking: false,
152
+ missingScopes: [],
153
+ status: 'needs-access',
154
+ };
155
+ setReadiness(result);
156
+ return result;
157
+ }
158
+ const missing = findMissingGoogleDriveBackupScopes(scopes);
159
+ const result = missing.length === 0
160
+ ? {
161
+ accessToken,
162
+ isChecking: false,
163
+ missingScopes: [],
164
+ status: 'ready',
165
+ }
166
+ : {
167
+ accessToken,
168
+ isChecking: false,
169
+ missingScopes: missing,
170
+ status: 'needs-access',
171
+ };
172
+ setReadiness(result);
173
+ return result;
174
+ }
175
+ catch (error) {
176
+ const result = {
177
+ error,
178
+ isChecking: false,
179
+ missingScopes: [],
180
+ status: 'error',
181
+ };
182
+ setReadiness(result);
183
+ return result;
184
+ }
185
+ }), [environmentId]);
186
+ const check = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
187
+ setReadiness((prev) => (Object.assign(Object.assign({}, prev), { isChecking: true })));
188
+ return performCheck(user === null || user === void 0 ? void 0 : user.verifiedCredentials);
189
+ }), [performCheck, user]);
190
+ const requestAccess = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
191
+ var _a;
192
+ setReadiness((prev) => (Object.assign(Object.assign({}, prev), { isChecking: true })));
193
+ try {
194
+ yield linkSocialAccount(ProviderEnum.Google, {
195
+ forcePopup: true,
196
+ showWidgetAfterConnection: false,
197
+ });
198
+ const updatedUser = yield refresh();
199
+ return performCheck((_a = updatedUser === null || updatedUser === void 0 ? void 0 : updatedUser.verifiedCredentials) !== null && _a !== void 0 ? _a : user === null || user === void 0 ? void 0 : user.verifiedCredentials);
200
+ }
201
+ catch (error) {
202
+ const result = {
203
+ error,
204
+ isChecking: false,
205
+ missingScopes: [],
206
+ status: 'error',
207
+ };
208
+ setReadiness(result);
209
+ return result;
210
+ }
211
+ }), [linkSocialAccount, performCheck, refresh, user]);
212
+ const reset = useCallback(() => setReadiness(IDLE), []);
213
+ return Object.assign(Object.assign({}, readiness), { check,
214
+ requestAccess,
215
+ reset });
216
+ };
217
+
218
+ export { useGoogleDriveBackupReadiness };
@@ -20,6 +20,7 @@ var logger = require('../../../shared/logger.cjs');
20
20
  require('@dynamic-labs/wallet-book');
21
21
  require('../../constants/colors.cjs');
22
22
  require('../../constants/values.cjs');
23
+ var instrumentAuthLoginFailed = require('../../../shared/utils/functions/instrumentAuthLoginFailed/instrumentAuthLoginFailed.cjs');
23
24
  require('../../../shared/consts/index.cjs');
24
25
  var dynamicEvents = require('../../../events/dynamicEvents.cjs');
25
26
  var ErrorContext = require('../../../context/ErrorContext/ErrorContext.cjs');
@@ -178,9 +179,12 @@ const useSocialAuth = ({ onSettled, onError, onFarcasterUrl, }) => {
178
179
  }, [onError, onSettled]);
179
180
  const handleError = React.useCallback((provider, code, message, options) => {
180
181
  const error = { code, message };
181
- logger.logger.error(message);
182
+ instrumentAuthLoginFailed.instrumentAuthLoginFailed(error, {
183
+ authOrigin: 'social-auth',
184
+ provider,
185
+ });
182
186
  setError(error);
183
- setErrorMessage(code);
187
+ setErrorMessage(code, error, 'social-auth');
184
188
  onFailed(provider, { error }, options);
185
189
  }, [onFailed, setErrorMessage]);
186
190
  const checkValidProvider = React.useCallback((provider, authMode) => {
@@ -16,6 +16,7 @@ import { logger } from '../../../shared/logger.js';
16
16
  import '@dynamic-labs/wallet-book';
17
17
  import '../../constants/colors.js';
18
18
  import '../../constants/values.js';
19
+ import { instrumentAuthLoginFailed } from '../../../shared/utils/functions/instrumentAuthLoginFailed/instrumentAuthLoginFailed.js';
19
20
  import '../../../shared/consts/index.js';
20
21
  import { dynamicEvents } from '../../../events/dynamicEvents.js';
21
22
  import { useErrorContext } from '../../../context/ErrorContext/ErrorContext.js';
@@ -174,9 +175,12 @@ const useSocialAuth = ({ onSettled, onError, onFarcasterUrl, }) => {
174
175
  }, [onError, onSettled]);
175
176
  const handleError = useCallback((provider, code, message, options) => {
176
177
  const error = { code, message };
177
- logger.error(message);
178
+ instrumentAuthLoginFailed(error, {
179
+ authOrigin: 'social-auth',
180
+ provider,
181
+ });
178
182
  setError(error);
179
- setErrorMessage(code);
183
+ setErrorMessage(code, error, 'social-auth');
180
184
  onFailed(provider, { error }, options);
181
185
  }, [onFailed, setErrorMessage]);
182
186
  const checkValidProvider = useCallback((provider, authMode) => {
@@ -20,6 +20,7 @@ require('@dynamic-labs/wallet-book');
20
20
  require('../../constants/colors.cjs');
21
21
  require('../../constants/values.cjs');
22
22
  var hasPendingMfaAction = require('../../../shared/utils/functions/hasPendingMfaAction/hasPendingMfaAction.cjs');
23
+ var instrumentAuthLoginFailed = require('../../../shared/utils/functions/instrumentAuthLoginFailed/instrumentAuthLoginFailed.cjs');
23
24
  require('../../../shared/consts/index.cjs');
24
25
  require('@dynamic-labs/multi-wallet');
25
26
  require('react-international-phone');
@@ -134,6 +135,10 @@ const useUserAuth = ({ authMethod, }) => {
134
135
  }), [authMethod, clearStackAndPushInitialView, handleLogOut]);
135
136
  const handleAuthError = React.useCallback((error, { options = {}, onError, }) => {
136
137
  var _a, _b;
138
+ instrumentAuthLoginFailed.instrumentAuthLoginFailed(error, {
139
+ authOrigin: 'user-auth',
140
+ provider: authMethod,
141
+ });
137
142
  if (error instanceof client.MfaInvalidOtpError ||
138
143
  error instanceof client.MfaRateLimitedError) {
139
144
  throw error;
@@ -196,12 +201,13 @@ const useUserAuth = ({ authMethod, }) => {
196
201
  return;
197
202
  }
198
203
  if (error.code) {
199
- setErrorMessage(error.code);
204
+ setErrorMessage(error.code, error, 'user-auth');
200
205
  }
201
206
  else {
202
207
  setError(error.message);
203
208
  }
204
209
  }, [
210
+ authMethod,
205
211
  pushView,
206
212
  setDeniedOauthProvider,
207
213
  setDeniedOauthUsername,
@@ -16,6 +16,7 @@ import '@dynamic-labs/wallet-book';
16
16
  import '../../constants/colors.js';
17
17
  import '../../constants/values.js';
18
18
  import { hasPendingMfaAction } from '../../../shared/utils/functions/hasPendingMfaAction/hasPendingMfaAction.js';
19
+ import { instrumentAuthLoginFailed } from '../../../shared/utils/functions/instrumentAuthLoginFailed/instrumentAuthLoginFailed.js';
19
20
  import '../../../shared/consts/index.js';
20
21
  import '@dynamic-labs/multi-wallet';
21
22
  import 'react-international-phone';
@@ -130,6 +131,10 @@ const useUserAuth = ({ authMethod, }) => {
130
131
  }), [authMethod, clearStackAndPushInitialView, handleLogOut]);
131
132
  const handleAuthError = useCallback((error, { options = {}, onError, }) => {
132
133
  var _a, _b;
134
+ instrumentAuthLoginFailed(error, {
135
+ authOrigin: 'user-auth',
136
+ provider: authMethod,
137
+ });
133
138
  if (error instanceof MfaInvalidOtpError ||
134
139
  error instanceof MfaRateLimitedError) {
135
140
  throw error;
@@ -192,12 +197,13 @@ const useUserAuth = ({ authMethod, }) => {
192
197
  return;
193
198
  }
194
199
  if (error.code) {
195
- setErrorMessage(error.code);
200
+ setErrorMessage(error.code, error, 'user-auth');
196
201
  }
197
202
  else {
198
203
  setError(error.message);
199
204
  }
200
205
  }, [
206
+ authMethod,
201
207
  pushView,
202
208
  setDeniedOauthProvider,
203
209
  setDeniedOauthUsername,
@@ -19,6 +19,7 @@ var localStorage = require('../../constants/localStorage.cjs');
19
19
  require('../../constants/colors.cjs');
20
20
  require('../../constants/values.cjs');
21
21
  require('@dynamic-labs/sdk-api-core');
22
+ var instrumentAuthLoginFailed = require('../../../shared/utils/functions/instrumentAuthLoginFailed/instrumentAuthLoginFailed.cjs');
22
23
  require('../../../shared/consts/index.cjs');
23
24
  var authMode = require('../../../store/state/authMode/authMode.cjs');
24
25
  var useInternalDynamicContext = require('../../../context/DynamicContext/useDynamicContext/useInternalDynamicContext/useInternalDynamicContext.cjs');
@@ -290,12 +291,15 @@ const useVerifyWallet = ({ displaySiweStatement, environmentId, projectSettings,
290
291
  handleDisconnectWallet({ walletConnector });
291
292
  clearStackAndPushInitialView();
292
293
  }
293
- logger.logger.error(e);
294
+ instrumentAuthLoginFailed.instrumentAuthLoginFailed(e, {
295
+ authOrigin: 'wallet-verify',
296
+ provider: walletConnector === null || walletConnector === void 0 ? void 0 : walletConnector.key,
297
+ });
294
298
  if (debugError) {
295
299
  setError(`${e.message}\n ${e.stack}`);
296
300
  }
297
301
  else {
298
- setErrorMessage(e.code);
302
+ setErrorMessage(e.code, e, 'wallet-verify');
299
303
  }
300
304
  };
301
305
  return (_a) => _tslib.__awaiter(void 0, [_a], void 0, function* ({ walletConnector, getAddressOpts, publicWalletAddress, captchaToken, oauth, signedMessageOverride, messageToSignOverride, requestedScopes, }) {