@turnkey/react-wallet-kit 1.0.0-beta.4 → 1.0.0-beta.6

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 (114) hide show
  1. package/dist/_virtual/_rollupPluginBabelHelpers.mjs +49 -1
  2. package/dist/_virtual/_rollupPluginBabelHelpers.mjs.map +1 -1
  3. package/dist/components/auth/OTP.d.ts +1 -0
  4. package/dist/components/auth/OTP.d.ts.map +1 -1
  5. package/dist/components/auth/OTP.js +5 -1
  6. package/dist/components/auth/OTP.js.map +1 -1
  7. package/dist/components/auth/OTP.mjs +6 -3
  8. package/dist/components/auth/OTP.mjs.map +1 -1
  9. package/dist/components/auth/Wallet.d.ts +4 -4
  10. package/dist/components/auth/Wallet.d.ts.map +1 -1
  11. package/dist/components/auth/Wallet.js +70 -49
  12. package/dist/components/auth/Wallet.js.map +1 -1
  13. package/dist/components/auth/Wallet.mjs +74 -54
  14. package/dist/components/auth/Wallet.mjs.map +1 -1
  15. package/dist/components/auth/index.d.ts +5 -1
  16. package/dist/components/auth/index.d.ts.map +1 -1
  17. package/dist/components/auth/index.js +98 -12
  18. package/dist/components/auth/index.js.map +1 -1
  19. package/dist/components/auth/index.mjs +185 -82
  20. package/dist/components/auth/index.mjs.map +1 -1
  21. package/dist/components/export/Export.d.ts +2 -1
  22. package/dist/components/export/Export.d.ts.map +1 -1
  23. package/dist/components/export/Export.js +7 -6
  24. package/dist/components/export/Export.js.map +1 -1
  25. package/dist/components/export/Export.mjs +7 -6
  26. package/dist/components/export/Export.mjs.map +1 -1
  27. package/dist/components/export/ExportWarning.d.ts +2 -1
  28. package/dist/components/export/ExportWarning.d.ts.map +1 -1
  29. package/dist/components/export/ExportWarning.js +3 -2
  30. package/dist/components/export/ExportWarning.js.map +1 -1
  31. package/dist/components/export/ExportWarning.mjs +3 -2
  32. package/dist/components/export/ExportWarning.mjs.map +1 -1
  33. package/dist/components/import/Import.d.ts +7 -3
  34. package/dist/components/import/Import.d.ts.map +1 -1
  35. package/dist/components/import/Import.js +74 -37
  36. package/dist/components/import/Import.js.map +1 -1
  37. package/dist/components/import/Import.mjs +116 -54
  38. package/dist/components/import/Import.mjs.map +1 -1
  39. package/dist/components/user/ConnectWallet.d.ts +8 -0
  40. package/dist/components/user/ConnectWallet.d.ts.map +1 -0
  41. package/dist/components/user/{LinkWallet.js → ConnectWallet.js} +18 -18
  42. package/dist/components/user/ConnectWallet.js.map +1 -0
  43. package/dist/components/user/{LinkWallet.mjs → ConnectWallet.mjs} +25 -25
  44. package/dist/components/user/ConnectWallet.mjs.map +1 -0
  45. package/dist/index.d.ts +2 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +13 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/index.mjs +2 -1
  50. package/dist/index.mjs.map +1 -1
  51. package/dist/providers/TurnkeyProvider.d.ts +1 -0
  52. package/dist/providers/TurnkeyProvider.d.ts.map +1 -1
  53. package/dist/providers/TurnkeyProvider.js +1 -0
  54. package/dist/providers/TurnkeyProvider.js.map +1 -1
  55. package/dist/providers/TurnkeyProvider.mjs +1 -0
  56. package/dist/providers/TurnkeyProvider.mjs.map +1 -1
  57. package/dist/providers/client/Hook.d.ts +1 -0
  58. package/dist/providers/client/Hook.d.ts.map +1 -1
  59. package/dist/providers/client/Hook.js +1 -0
  60. package/dist/providers/client/Hook.js.map +1 -1
  61. package/dist/providers/client/Hook.mjs +1 -0
  62. package/dist/providers/client/Hook.mjs.map +1 -1
  63. package/dist/providers/client/Provider.d.ts +3 -3
  64. package/dist/providers/client/Provider.d.ts.map +1 -1
  65. package/dist/providers/client/Provider.js +1019 -388
  66. package/dist/providers/client/Provider.js.map +1 -1
  67. package/dist/providers/client/Provider.mjs +2901 -2162
  68. package/dist/providers/client/Provider.mjs.map +1 -1
  69. package/dist/providers/client/Types.d.ts +164 -50
  70. package/dist/providers/client/Types.d.ts.map +1 -1
  71. package/dist/providers/client/Types.js +1 -0
  72. package/dist/providers/client/Types.js.map +1 -1
  73. package/dist/providers/client/Types.mjs +1 -0
  74. package/dist/providers/client/Types.mjs.map +1 -1
  75. package/dist/providers/modal/Hook.d.ts +1 -0
  76. package/dist/providers/modal/Hook.d.ts.map +1 -1
  77. package/dist/providers/modal/Hook.js +1 -0
  78. package/dist/providers/modal/Hook.js.map +1 -1
  79. package/dist/providers/modal/Hook.mjs +1 -0
  80. package/dist/providers/modal/Hook.mjs.map +1 -1
  81. package/dist/providers/modal/Provider.js +1 -1
  82. package/dist/providers/modal/Provider.mjs +1 -1
  83. package/dist/providers/modal/Root.d.ts.map +1 -1
  84. package/dist/providers/modal/Root.js +18 -14
  85. package/dist/providers/modal/Root.js.map +1 -1
  86. package/dist/providers/modal/Root.mjs +19 -17
  87. package/dist/providers/modal/Root.mjs.map +1 -1
  88. package/dist/styles.css +1 -1
  89. package/dist/types/base.d.ts +81 -2
  90. package/dist/types/base.d.ts.map +1 -1
  91. package/dist/types/base.js +35 -0
  92. package/dist/types/base.js.map +1 -1
  93. package/dist/types/base.mjs +31 -1
  94. package/dist/types/base.mjs.map +1 -1
  95. package/dist/utils/timers.d.ts +73 -0
  96. package/dist/utils/timers.d.ts.map +1 -0
  97. package/dist/utils/timers.js +133 -0
  98. package/dist/utils/timers.js.map +1 -0
  99. package/dist/utils/timers.mjs +142 -0
  100. package/dist/utils/timers.mjs.map +1 -0
  101. package/dist/{utils.d.ts → utils/utils.d.ts} +9 -3
  102. package/dist/utils/utils.d.ts.map +1 -0
  103. package/dist/{utils.js → utils/utils.js} +43 -5
  104. package/dist/utils/utils.js.map +1 -0
  105. package/dist/{utils.mjs → utils/utils.mjs} +48 -7
  106. package/dist/utils/utils.mjs.map +1 -0
  107. package/package.json +4 -4
  108. package/dist/components/user/LinkWallet.d.ts +0 -8
  109. package/dist/components/user/LinkWallet.d.ts.map +0 -1
  110. package/dist/components/user/LinkWallet.js.map +0 -1
  111. package/dist/components/user/LinkWallet.mjs.map +0 -1
  112. package/dist/utils.d.ts.map +0 -1
  113. package/dist/utils.js.map +0 -1
  114. package/dist/utils.mjs.map +0 -1
@@ -4,7 +4,8 @@
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var sha2 = require('@noble/hashes/sha2');
6
6
  var utils$1 = require('@noble/hashes/utils');
7
- var utils = require('../../utils.js');
7
+ var utils = require('../../utils/utils.js');
8
+ var timers = require('../../utils/timers.js');
8
9
  var core = require('@turnkey/core');
9
10
  var react = require('react');
10
11
  var sdkTypes = require('@turnkey/sdk-types');
@@ -23,7 +24,7 @@ var UpdatePhoneNumber = require('../../components/user/UpdatePhoneNumber.js');
23
24
  var UpdateUserName = require('../../components/user/UpdateUserName.js');
24
25
  var RemoveOAuthProvider = require('../../components/user/RemoveOAuthProvider.js');
25
26
  var RemovePasskey = require('../../components/user/RemovePasskey.js');
26
- var LinkWallet = require('../../components/user/LinkWallet.js');
27
+ var ConnectWallet = require('../../components/user/ConnectWallet.js');
27
28
  var Types = require('./Types.js');
28
29
  var OTP = require('../../components/auth/OTP.js');
29
30
  var RemoveEmail = require('../../components/user/RemoveEmail.js');
@@ -32,7 +33,7 @@ var RemovePhoneNumber = require('../../components/user/RemovePhoneNumber.js');
32
33
  /**
33
34
  * Provides Turnkey client authentication, session management, wallet operations, and user profile management
34
35
  * for the React Wallet Kit SDK. This context provider encapsulates all core authentication flows (Passkey, Wallet, OTP, OAuth),
35
- * session lifecycle (creation, expiration, refresh), wallet linking/import/export, and user profile updates (email, phone, name).
36
+ * session lifecycle (creation, expiration, refresh), wallet connecting/import/export, and user profile updates (email, phone, name).
36
37
  *
37
38
  * The provider automatically initializes the Turnkey client, fetches configuration (including proxy auth config if needed),
38
39
  * and synchronizes session and authentication state. It exposes a comprehensive set of methods for authentication flows,
@@ -41,9 +42,9 @@ var RemovePhoneNumber = require('../../components/user/RemovePhoneNumber.js');
41
42
  * Features:
42
43
  * - Passkey, Wallet, OTP (Email/SMS), and OAuth (Google, Apple, Facebook) authentication and sign-up flows.
43
44
  * - Session management: creation, expiration scheduling, refresh, and clearing.
44
- * - Wallet management: fetch, link, import, export, account management.
45
+ * - Wallet management: fetch, connect, import, export, account management.
45
46
  * - User profile management: email, phone, name, OAuth provider, and passkey linking/removal.
46
- * - Modal-driven UI flows for authentication, wallet linking, and profile updates.
47
+ * - Modal-driven UI flows for authentication, wallet connecting, and profile updates.
47
48
  * - Error handling and callback integration for custom error and event responses.
48
49
  *
49
50
  * Usage:
@@ -67,6 +68,9 @@ const ClientProvider = ({
67
68
  const [user, setUser] = react.useState(undefined);
68
69
  const [clientState, setClientState] = react.useState();
69
70
  const [authState, setAuthState] = react.useState(base.AuthState.Unauthenticated);
71
+ // we use this custom hook to only update the state if the value is different
72
+ // this is so our useEffect that calls `initializeWalletProviderListeners()` only runs when it needs to
73
+ const [walletProviders, setWalletProviders] = utils.useWalletProviderState();
70
74
  const expiryTimeoutsRef = react.useRef({});
71
75
  const proxyAuthConfigRef = react.useRef(null);
72
76
  const [allSessions, setAllSessions] = react.useState(undefined);
@@ -74,78 +78,6 @@ const ClientProvider = ({
74
78
  pushPage,
75
79
  closeModal
76
80
  } = Hook.useModal();
77
- react.useEffect(() => {
78
- if (proxyAuthConfigRef.current) return;
79
- // Only fetch the proxy auth config once. Use that to build the master config.
80
- const fetchProxyAuthConfig = async () => {
81
- try {
82
- let proxyAuthConfig;
83
- if (config.authProxyConfigId) {
84
- // Only fetch the proxy auth config if we have an authProxyId. This is a way for devs to explicitly disable the proxy auth.
85
- proxyAuthConfig = await core.getAuthProxyConfig(config.authProxyConfigId, config.authProxyUrl);
86
- proxyAuthConfigRef.current = proxyAuthConfig;
87
- }
88
- setMasterConfig(buildConfig(proxyAuthConfig));
89
- } catch {
90
- setClientState(base.ClientState.Error);
91
- }
92
- };
93
- fetchProxyAuthConfig();
94
- }, []);
95
- react.useEffect(() => {
96
- // Start the client initialization process once we have the master config.
97
- if (!masterConfig) return;
98
- initializeClient();
99
- }, [masterConfig]);
100
- react.useEffect(() => {
101
- // Handle changes to the passed in config prop -- update the master config
102
- // If the proxyAuthConfigRef is already set, we don't need to fetch it again. Rebuild the master config with the updated config and stored proxyAuthConfig
103
- if (!proxyAuthConfigRef.current && config.authProxyConfigId) return;
104
- setMasterConfig(buildConfig(proxyAuthConfigRef.current ?? undefined));
105
- }, [config, proxyAuthConfigRef.current]);
106
- /**
107
- * @internal
108
- * we use debouncedFetchWallets() to prevent multiple rapid wallet events
109
- * like accountsChanged and disconnect from triggering fetchWallets repeatedly
110
- * it must be defined outside the useEffect so all event listeners share the same
111
- * debounced function instance - otherwise, a new one would be created on every render
112
- */
113
- const debouncedFetchWallets = utils.useDebouncedCallback(fetchWallets, 100);
114
- react.useEffect(() => {
115
- if (!client) return;
116
- let cleanup = () => {};
117
- initializeProviders(getWalletProviders, debouncedFetchWallets).then(fn => {
118
- cleanup = fn;
119
- }).catch(err => {
120
- console.error("Failed to init providers:", err);
121
- });
122
- return () => {
123
- cleanup();
124
- };
125
- }, [client, debouncedFetchWallets]);
126
- react.useEffect(() => {
127
- // authState must be consistent with session state. We found during testing that there are cases where the session and authState can be out of sync in very rare edge cases.
128
- // This will ensure that they are always in sync and remove the need to setAuthState manually in other places.
129
- if (session && utils.isValidSession(session)) {
130
- setAuthState(base.AuthState.Authenticated);
131
- } else {
132
- setAuthState(base.AuthState.Unauthenticated);
133
- }
134
- }, [session]);
135
- react.useEffect(() => {
136
- // This will handle any redirect based oAuth. It then initializes the session. This is the last step before client is considered "ready"
137
- if (!client || !masterConfig) return;
138
- completeRedirectOauth().finally(() => {
139
- clearSessionTimeouts();
140
- initializeSessions().finally(() => {
141
- // Set the client state to ready only after all initializations are done.
142
- setClientState(base.ClientState.Ready);
143
- });
144
- });
145
- return () => {
146
- clearSessionTimeouts();
147
- };
148
- }, [client]);
149
81
  const completeRedirectOauth = async () => {
150
82
  // Check for either hash or search parameters that could indicate an OAuth redirect
151
83
  if (window.location.hash || window.location.search) {
@@ -161,6 +93,7 @@ const ClientProvider = ({
161
93
  const flow = stateParams.get("flow");
162
94
  const publicKey = stateParams.get("publicKey");
163
95
  const openModal = stateParams.get("openModal");
96
+ const sessionKey = stateParams.get("sessionKey");
164
97
  if (provider === "facebook" && flow === "redirect" && publicKey) {
165
98
  // We have all the required parameters for a Facebook PKCE flow
166
99
  const clientId = masterConfig?.auth?.oauthConfig?.facebookClientId;
@@ -185,8 +118,13 @@ const ClientProvider = ({
185
118
  await completeOauth({
186
119
  oidcToken,
187
120
  publicKey,
188
- providerName: "facebook"
121
+ providerName: "facebook",
122
+ ...(sessionKey && {
123
+ sessionKey
124
+ })
189
125
  });
126
+ // Clean up the URL after processing
127
+ window.history.replaceState(null, document.title, window.location.pathname);
190
128
  resolve();
191
129
  } catch (err) {
192
130
  reject(err);
@@ -209,6 +147,114 @@ const ClientProvider = ({
209
147
  });
210
148
  }
211
149
  }
150
+ if (provider === "discord" && flow === "redirect" && publicKey) {
151
+ const clientId = masterConfig?.auth?.oauthConfig?.discordClientId;
152
+ const redirectURI = masterConfig?.auth?.oauthConfig?.oauthRedirectUri;
153
+ const verifier = sessionStorage.getItem("discord_verifier");
154
+ const nonce = stateParams.get("nonce");
155
+ const sessionKey = stateParams.get("sessionKey");
156
+ if (clientId && redirectURI && verifier && nonce) {
157
+ await new Promise((resolve, reject) => {
158
+ pushPage({
159
+ key: `Discord OAuth`,
160
+ content: jsxRuntime.jsx(Action.ActionPage, {
161
+ title: `Authenticating with Discord...`,
162
+ action: async () => {
163
+ try {
164
+ const resp = await client?.httpClient.proxyOAuth2Authenticate({
165
+ provider: "OAUTH2_PROVIDER_DISCORD",
166
+ authCode: code,
167
+ redirectUri: redirectURI,
168
+ codeVerifier: verifier,
169
+ nonce: nonce
170
+ });
171
+ sessionStorage.removeItem("discord_verifier");
172
+ const oidcToken = resp?.oidcToken;
173
+ if (!oidcToken) {
174
+ throw new sdkTypes.TurnkeyError("Missing OIDC token", sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
175
+ }
176
+ await completeOauth({
177
+ oidcToken,
178
+ publicKey,
179
+ providerName: "discord",
180
+ ...(sessionKey && {
181
+ sessionKey
182
+ })
183
+ });
184
+ // Clean up the URL after processing
185
+ window.history.replaceState(null, document.title, window.location.pathname);
186
+ resolve(null);
187
+ } catch (err) {
188
+ reject(err);
189
+ if (callbacks?.onError) {
190
+ callbacks.onError(err instanceof sdkTypes.TurnkeyError ? err : new sdkTypes.TurnkeyError("Discord authentication failed", sdkTypes.TurnkeyErrorCodes.OAUTH_SIGNUP_ERROR, err));
191
+ }
192
+ }
193
+ },
194
+ icon: jsxRuntime.jsx(reactFontawesome.FontAwesomeIcon, {
195
+ size: "3x",
196
+ icon: freeBrandsSvgIcons.faDiscord
197
+ })
198
+ }),
199
+ showTitle: false
200
+ });
201
+ });
202
+ }
203
+ }
204
+ if (provider === "twitter" && flow === "redirect" && publicKey) {
205
+ const clientId = masterConfig?.auth?.oauthConfig?.xClientId;
206
+ const redirectURI = masterConfig?.auth?.oauthConfig?.oauthRedirectUri;
207
+ const verifier = sessionStorage.getItem("twitter_verifier");
208
+ const nonce = stateParams.get("nonce");
209
+ const sessionKey = stateParams.get("sessionKey");
210
+ if (clientId && redirectURI && verifier && nonce) {
211
+ await new Promise((resolve, reject) => {
212
+ pushPage({
213
+ key: `Twitter OAuth`,
214
+ content: jsxRuntime.jsx(Action.ActionPage, {
215
+ title: `Authenticating with Twitter...`,
216
+ action: async () => {
217
+ try {
218
+ const resp = await client?.httpClient.proxyOAuth2Authenticate({
219
+ provider: "OAUTH2_PROVIDER_X",
220
+ authCode: code,
221
+ redirectUri: redirectURI,
222
+ codeVerifier: verifier,
223
+ nonce: nonce
224
+ });
225
+ sessionStorage.removeItem("twitter_verifier");
226
+ const oidcToken = resp?.oidcToken;
227
+ if (!oidcToken) {
228
+ throw new sdkTypes.TurnkeyError("Missing OIDC token", sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
229
+ }
230
+ await completeOauth({
231
+ oidcToken,
232
+ publicKey,
233
+ providerName: "twitter",
234
+ ...(sessionKey && {
235
+ sessionKey
236
+ })
237
+ });
238
+ // Clean up the URL after processing
239
+ window.history.replaceState(null, document.title, window.location.pathname);
240
+ resolve(null);
241
+ } catch (err) {
242
+ reject(err);
243
+ if (callbacks?.onError) {
244
+ callbacks.onError(err instanceof sdkTypes.TurnkeyError ? err : new sdkTypes.TurnkeyError("Twitter authentication failed", sdkTypes.TurnkeyErrorCodes.OAUTH_SIGNUP_ERROR, err));
245
+ }
246
+ }
247
+ },
248
+ icon: jsxRuntime.jsx(reactFontawesome.FontAwesomeIcon, {
249
+ size: "3x",
250
+ icon: freeBrandsSvgIcons.faXTwitter
251
+ })
252
+ }),
253
+ showTitle: false
254
+ });
255
+ });
256
+ }
257
+ }
212
258
  }
213
259
  }
214
260
  // Handle Google/Apple redirects (uses hash with id_token)
@@ -220,7 +266,8 @@ const ClientProvider = ({
220
266
  provider,
221
267
  flow,
222
268
  publicKey,
223
- openModal
269
+ openModal,
270
+ sessionKey
224
271
  } = utils.parseOAuthRedirect(hash);
225
272
  if (idToken && flow === "redirect" && publicKey) {
226
273
  if (openModal === "true") {
@@ -252,7 +299,10 @@ const ClientProvider = ({
252
299
  publicKey,
253
300
  ...(provider ? {
254
301
  providerName: provider
255
- } : {})
302
+ } : {}),
303
+ ...(sessionKey && {
304
+ sessionKey
305
+ })
256
306
  });
257
307
  resolve(null);
258
308
  } catch (err) {
@@ -292,11 +342,13 @@ const ClientProvider = ({
292
342
  passkeyAuthEnabled: config.auth?.methods?.passkeyAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("passkey"),
293
343
  walletAuthEnabled: config.auth?.methods?.walletAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("wallet"),
294
344
  googleOauthEnabled: config.auth?.methods?.googleOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("google"),
345
+ xOauthEnabled: config.auth?.methods?.xOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("x"),
346
+ discordOauthEnabled: config.auth?.methods?.discordOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("discord"),
295
347
  appleOauthEnabled: config.auth?.methods?.appleOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("apple"),
296
348
  facebookOauthEnabled: config.auth?.methods?.facebookOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("facebook")
297
349
  };
298
350
  // Set a default ordering for the oAuth methods
299
- const oauthOrder = config.auth?.oauthOrder ?? ["google", "apple", "facebook"].filter(provider => resolvedMethods[`${provider}OauthEnabled`]);
351
+ const oauthOrder = config.auth?.oauthOrder ?? ["google", "apple", "x", "discord", "facebook"].filter(provider => resolvedMethods[`${provider}OauthEnabled`]);
300
352
  // Set a default ordering for the overall auth methods
301
353
  const methodOrder = config.auth?.methodOrder ?? [oauthOrder.length > 0 ? "socials" : null, resolvedMethods.emailOtpAuthEnabled ? "email" : null, resolvedMethods.smsOtpAuthEnabled ? "sms" : null, resolvedMethods.passkeyAuthEnabled ? "passkey" : null, resolvedMethods.walletAuthEnabled ? "wallet" : null].filter(Boolean);
302
354
  return {
@@ -438,14 +490,86 @@ const ClientProvider = ({
438
490
  };
439
491
  /**
440
492
  * @internal
441
- * Schedules a session expiration and warning timeout for the given session key.
493
+ * Attach listeners for connected wallet providers so we can refresh state on changes.
494
+ *
495
+ * - Ethereum: listens for disconnect and chain switches to trigger a refresh.
496
+ * - Solana: listens for disconnect via Wallet Standard `change` events to trigger a refresh.
497
+ * - WalletConnect: listens for disconnect via our custom wrapper’s `change` event to trigger a refresh.
442
498
  *
443
- * - This function sets up two timeouts: one for warning before the session expires and another to expire the session.
444
- * - The warning timeout is set to trigger before the session expires, allowing for actions like refreshing the session.
445
- * - The expiration timeout clears the session and triggers any necessary callbacks.
499
+ * Notes:
500
+ * - Only providers that are connected are bound.
501
+ * - WalletConnect is excluded from the “native” paths and handled via its unified `change` event.
502
+ *
503
+ * @param walletProviders - Discovered providers; only connected ones are bound.
504
+ * @param onWalletsChanged - Invoked when a relevant provider event occurs.
505
+ * @returns Cleanup function that removes all listeners registered by this call.
506
+ */
507
+ async function initializeWalletProviderListeners(walletProviders, onWalletsChanged) {
508
+ if (walletProviders.length === 0) return () => {};
509
+ const cleanups = [];
510
+ // we only want to initialize these listeners for connected walletProviders
511
+ const nativeOnly = provider => provider.interfaceType !== core.WalletInterfaceType.WalletConnect;
512
+ const ethProviders = masterConfig?.walletConfig?.chains.ethereum?.native ? walletProviders.filter(provider => provider.chainInfo.namespace === core.Chain.Ethereum && nativeOnly(provider) && provider.connectedAddresses.length > 0) : [];
513
+ const solProviders = masterConfig?.walletConfig?.chains.solana?.native ? walletProviders.filter(provider => provider.chainInfo.namespace === core.Chain.Solana && nativeOnly(provider) && provider.connectedAddresses.length > 0) : [];
514
+ // we exclude WalletConnect from the native event wiring
515
+ // this is because WC is handled separately with a custom wrapper’s
516
+ // `change` event
517
+ const wcProviders = walletProviders.filter(p => p.interfaceType === core.WalletInterfaceType.WalletConnect && p.connectedAddresses.length > 0);
518
+ function attachEthereumListeners(provider, onWalletsChanged) {
519
+ if (typeof provider.on !== "function") return;
520
+ const handleChainChanged = _chainId => onWalletsChanged();
521
+ const handleAccountsChanged = accounts => {
522
+ if (accounts.length === 0) onWalletsChanged();
523
+ };
524
+ const handleDisconnect = () => onWalletsChanged();
525
+ provider.on("chainChanged", handleChainChanged);
526
+ provider.on("accountsChanged", handleAccountsChanged);
527
+ provider.on("disconnect", handleDisconnect);
528
+ return () => {
529
+ provider.removeListener("chainChanged", handleChainChanged);
530
+ provider.removeListener("accountsChanged", handleAccountsChanged);
531
+ provider.removeListener("disconnect", handleDisconnect);
532
+ };
533
+ }
534
+ function attachSolanaListeners(provider, onWalletsChanged) {
535
+ const cleanups = [];
536
+ const walletEvents = provider?.features?.["standard:events"];
537
+ if (walletEvents?.on) {
538
+ const offChange = walletEvents.on("change", _evt => {
539
+ onWalletsChanged();
540
+ });
541
+ cleanups.push(offChange);
542
+ }
543
+ return () => cleanups.forEach(fn => fn());
544
+ }
545
+ ethProviders.forEach(p => {
546
+ const cleanup = attachEthereumListeners(p.provider, onWalletsChanged);
547
+ if (cleanup) cleanups.push(cleanup);
548
+ });
549
+ solProviders.forEach(p => {
550
+ const cleanup = attachSolanaListeners(p.provider, onWalletsChanged);
551
+ if (cleanup) cleanups.push(cleanup);
552
+ });
553
+ wcProviders.forEach(p => {
554
+ const standardEvents = p.provider?.features?.["standard:events"];
555
+ if (standardEvents?.on) {
556
+ const unsubscribe = standardEvents.on("change", onWalletsChanged);
557
+ cleanups.push(unsubscribe);
558
+ }
559
+ });
560
+ return () => {
561
+ cleanups.forEach(remove => remove());
562
+ };
563
+ }
564
+ /**
565
+ * @internal
566
+ * Schedules a session expiration and warning timer for the given session key.
567
+ *
568
+ * - Sets up two timers: one for warning before expiry and one for actual expiry.
569
+ * - Uses capped timeouts under the hood so delays > 24.8 days are safe (see utils/timers.ts).
446
570
  *
447
571
  * @param params.sessionKey - The key of the session to schedule expiration for.
448
- * @param params.expiry - The expiration time in seconds for the session.
572
+ * @param params.expiry - The expiration time in seconds since epoch.
449
573
  * @throws {TurnkeyError} If an error occurs while scheduling the session expiration.
450
574
  */
451
575
  async function scheduleSessionExpiration(params) {
@@ -454,21 +578,18 @@ const ClientProvider = ({
454
578
  expiry
455
579
  } = params;
456
580
  try {
457
- // Clear any existing timeout for this session key
458
- if (expiryTimeoutsRef.current[sessionKey]) {
459
- clearTimeout(expiryTimeoutsRef.current[sessionKey]);
460
- delete expiryTimeoutsRef.current[sessionKey];
461
- }
462
- if (expiryTimeoutsRef.current[`${sessionKey}-warning`]) {
463
- clearTimeout(expiryTimeoutsRef.current[`${sessionKey}-warning`]);
464
- delete expiryTimeoutsRef.current[`${sessionKey}-warning`];
465
- }
466
- const timeUntilExpiry = expiry * 1000 - Date.now();
581
+ const warnKey = `${sessionKey}-warning`;
582
+ // Replace any prior timers for this session
583
+ timers.clearKey(expiryTimeoutsRef.current, sessionKey);
584
+ timers.clearKey(expiryTimeoutsRef.current, warnKey);
585
+ const now = Date.now();
586
+ const expiryMs = expiry * 1000;
587
+ const timeUntilExpiry = expiryMs - now;
467
588
  const beforeExpiry = async () => {
468
589
  const activeSession = await getSession();
469
- if (!activeSession && expiryTimeoutsRef.current[sessionKey]) {
470
- clearTimeout(expiryTimeoutsRef.current[`${sessionKey}-warning`]);
471
- expiryTimeoutsRef.current[`${sessionKey}-warning`] = setTimeout(beforeExpiry, 10000);
590
+ if (!activeSession && expiryTimeoutsRef.current[warnKey]) {
591
+ // Keep nudging until session materializes (short 10s timer is fine)
592
+ timers.setTimeoutInMap(expiryTimeoutsRef.current, warnKey, beforeExpiry, 10_000);
472
593
  return;
473
594
  }
474
595
  const session = await getSession({
@@ -496,27 +617,36 @@ const ClientProvider = ({
496
617
  if ((await getActiveSessionKey()) === sessionKey) {
497
618
  setSession(undefined);
498
619
  }
499
- setAllSessions(prevSessions => {
500
- if (!prevSessions) return prevSessions;
501
- const newSessions = {
502
- ...prevSessions
620
+ setAllSessions(prev => {
621
+ if (!prev) return prev;
622
+ const next = {
623
+ ...prev
503
624
  };
504
- delete newSessions[sessionKey];
505
- return newSessions;
625
+ delete next[sessionKey];
626
+ return next;
506
627
  });
507
628
  await clearSession({
508
629
  sessionKey
509
630
  });
510
- delete expiryTimeoutsRef.current[sessionKey];
511
- delete expiryTimeoutsRef.current[`${sessionKey}-warning`];
631
+ // Remove timers for this session
632
+ timers.clearKey(expiryTimeoutsRef.current, sessionKey);
633
+ timers.clearKey(expiryTimeoutsRef.current, warnKey);
512
634
  await logout();
513
635
  };
514
- if (timeUntilExpiry <= utils.SESSION_WARNING_THRESHOLD_MS) {
515
- beforeExpiry();
636
+ // Already expired → expire immediately
637
+ if (timeUntilExpiry <= 0) {
638
+ await expireSession();
639
+ return;
640
+ }
641
+ // Warning timer (if threshold is in the future)
642
+ const warnAt = expiryMs - utils.SESSION_WARNING_THRESHOLD_MS;
643
+ if (warnAt <= now) {
644
+ void beforeExpiry(); // fire-and-forget is fine
516
645
  } else {
517
- expiryTimeoutsRef.current[`${sessionKey}-warning`] = setTimeout(beforeExpiry, timeUntilExpiry - utils.SESSION_WARNING_THRESHOLD_MS);
646
+ timers.setCappedTimeoutInMap(expiryTimeoutsRef.current, warnKey, beforeExpiry, warnAt - now);
518
647
  }
519
- expiryTimeoutsRef.current[sessionKey] = setTimeout(expireSession, timeUntilExpiry);
648
+ // Actual expiry timer (safe for long delays)
649
+ timers.setCappedTimeoutInMap(expiryTimeoutsRef.current, sessionKey, expireSession, timeUntilExpiry);
520
650
  } catch (error) {
521
651
  if (error instanceof sdkTypes.TurnkeyError || error instanceof sdkTypes.TurnkeyNetworkError) {
522
652
  callbacks?.onError?.(error);
@@ -526,25 +656,25 @@ const ClientProvider = ({
526
656
  }
527
657
  }
528
658
  /**
529
- * Clears all scheduled session expiration and warning timeouts for the client.
659
+ * Clears all scheduled session timers (warning + expiry).
530
660
  *
531
- * - This function removes all active session expiration and warning timeouts managed by the provider.
532
- * - It is called automatically when sessions are re-initialized or on logout to prevent memory leaks and ensure no stale timeouts remain.
533
- * - All timeouts stored in `expiryTimeoutsRef` are cleared and the reference is reset.
661
+ * - Removes all active timers managed by this client.
662
+ * - Useful on re-init or logout to avoid stale timers.
534
663
  *
535
- * @throws {TurnkeyError} If an error occurs while clearing the timeouts.
664
+ * @throws {TurnkeyError} If an error occurs while clearing the timers.
536
665
  */
537
- function clearSessionTimeouts() {
666
+ function clearSessionTimeouts(sessionKeys) {
538
667
  try {
539
- Object.values(expiryTimeoutsRef.current).forEach(timeout => {
540
- clearTimeout(timeout);
541
- });
542
- expiryTimeoutsRef.current = {};
668
+ if (sessionKeys) {
669
+ timers.clearKeys(expiryTimeoutsRef.current, sessionKeys);
670
+ } else {
671
+ timers.clearAll(expiryTimeoutsRef.current); // clears & deletes everything
672
+ }
543
673
  } catch (error) {
544
674
  if (error instanceof sdkTypes.TurnkeyError || error instanceof sdkTypes.TurnkeyNetworkError) {
545
675
  callbacks?.onError?.(error);
546
676
  } else {
547
- callbacks?.onError?.(new sdkTypes.TurnkeyError(`Failed to clear session timeouts`, sdkTypes.TurnkeyErrorCodes.CLEAR_SESSION_TIMEOUTS_ERROR, error));
677
+ callbacks?.onError?.(new sdkTypes.TurnkeyError("Failed to clear session timeouts", sdkTypes.TurnkeyErrorCodes.CLEAR_SESSION_TIMEOUTS_ERROR, error));
548
678
  }
549
679
  }
550
680
  }
@@ -560,7 +690,12 @@ const ClientProvider = ({
560
690
  * @returns A void promise.
561
691
  * @throws {TurnkeyError} If the client is not initialized or if there is an error during the process.
562
692
  */
563
- const handlePostAuth = async () => {
693
+ const handlePostAuth = async params => {
694
+ const {
695
+ method,
696
+ action,
697
+ identifier
698
+ } = params;
564
699
  try {
565
700
  const sessionKey = await getActiveSessionKey();
566
701
  const session = await getSession({
@@ -577,6 +712,12 @@ const ClientProvider = ({
577
712
  setAllSessions(allSessions);
578
713
  await refreshWallets();
579
714
  await refreshUser();
715
+ callbacks?.onAuthenticationSuccess?.({
716
+ session,
717
+ method,
718
+ action,
719
+ identifier
720
+ });
580
721
  } catch (error) {
581
722
  if (error instanceof sdkTypes.TurnkeyError || error instanceof sdkTypes.TurnkeyNetworkError) {
582
723
  callbacks?.onError?.(error);
@@ -590,42 +731,58 @@ const ClientProvider = ({
590
731
  * Handles the post-logout flow.
591
732
  *
592
733
  * - This function is called after a successful logout or session clear.
593
- * - It clears all scheduled session expiration and warning timeouts to prevent memory leaks.
594
- * - It resets the session state, removes all session and user data from memory, and clears the wallets list.
734
+ * - It clears all scheduled session expiration and warning timeouts associated to the session key to prevent memory leaks.
735
+ * - It resets the session state, removes user data from memory, the logged out session from all sessions state, and clears the wallets list.
595
736
  * - This ensures that all sensitive information is removed from the provider state after logout.
596
737
  * - Called internally after logout or when all sessions are cleared.
597
738
  *
598
739
  * @returns void
599
740
  * @throws {TurnkeyError} If there is an error during the post-logout process.
600
741
  */
601
- const handlePostLogout = () => {
742
+ const handlePostLogout = sessionKey => {
602
743
  try {
603
- clearSessionTimeouts();
744
+ clearSessionTimeouts(sessionKey ? [sessionKey, `${sessionKey}-warning`] : undefined);
745
+ setAllSessions(prev => {
746
+ if (!prev) return prev;
747
+ if (sessionKey) {
748
+ const next = {
749
+ ...prev
750
+ };
751
+ delete next[sessionKey];
752
+ return next;
753
+ }
754
+ return {};
755
+ });
604
756
  setSession(undefined);
605
- setAllSessions(undefined);
606
757
  setUser(undefined);
607
758
  setWallets([]);
608
759
  } catch (error) {
609
760
  callbacks?.onError?.(new sdkTypes.TurnkeyError(`Failed to initialize sessions`, sdkTypes.TurnkeyErrorCodes.HANDLE_POST_LOGOUT_ERROR, error));
610
761
  }
611
762
  };
612
- async function createPasskey(params) {
763
+ const createPasskey = react.useCallback(async params => {
613
764
  if (!client) {
614
765
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
615
766
  }
616
767
  return utils.withTurnkeyErrorHandling(() => client.createPasskey({
617
768
  ...params
618
769
  }), callbacks, "Failed to create passkey");
619
- }
620
- async function logout(params) {
770
+ }, [client, callbacks]);
771
+ const logout = react.useCallback(async params => {
621
772
  if (!client) {
622
773
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
623
774
  }
624
- utils.withTurnkeyErrorHandling(() => client.logout(params), callbacks, "Failed to logout");
625
- handlePostLogout();
775
+ await utils.withTurnkeyErrorHandling(async () => {
776
+ // If no sessionKey is provided, we try to get the active one.
777
+ let sessionKey = params?.sessionKey;
778
+ if (!sessionKey) sessionKey = await getActiveSessionKey();
779
+ await client.logout(params);
780
+ // We only handle post logout if the sessionKey is defined since that means we actually logged out of a session.
781
+ if (sessionKey) handlePostLogout(sessionKey);
782
+ }, callbacks, "Failed to logout");
626
783
  return;
627
- }
628
- async function loginWithPasskey(params) {
784
+ }, [client, callbacks]);
785
+ const loginWithPasskey = react.useCallback(async params => {
629
786
  if (!client) {
630
787
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
631
788
  }
@@ -635,11 +792,15 @@ const ClientProvider = ({
635
792
  expirationSeconds
636
793
  }), callbacks, "Failed to login with passkey");
637
794
  if (res) {
638
- await handlePostAuth();
795
+ await handlePostAuth({
796
+ method: base.AuthMethod.Passkey,
797
+ action: sdkTypes.AuthAction.LOGIN,
798
+ identifier: res.credentialId
799
+ });
639
800
  }
640
801
  return res;
641
- }
642
- async function signUpWithPasskey(params) {
802
+ }, [client, callbacks]);
803
+ const signUpWithPasskey = react.useCallback(async params => {
643
804
  if (!client) {
644
805
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
645
806
  }
@@ -670,95 +831,61 @@ const ClientProvider = ({
670
831
  expirationSeconds
671
832
  }), callbacks, "Failed to sign up with passkey");
672
833
  if (res) {
673
- await handlePostAuth();
834
+ await handlePostAuth({
835
+ method: base.AuthMethod.Passkey,
836
+ action: sdkTypes.AuthAction.SIGNUP,
837
+ identifier: res.credentialId
838
+ });
674
839
  }
675
840
  return res;
676
- }
677
- async function getWalletProviders(chain) {
841
+ }, [client, callbacks]);
842
+ const getWalletProviders = react.useCallback(async chain => {
678
843
  if (!client) {
679
844
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
680
845
  }
681
- return await client.getWalletProviders(chain);
682
- }
683
- async function connectWalletAccount(walletProvider) {
846
+ const newProviders = await client.getWalletProviders(chain);
847
+ // we update state with the latest providers
848
+ // we keep this state so that initializeWalletProviderListeners() re-runs
849
+ // whenever the list of connected providers changes
850
+ // this ensures we attach disconnect listeners for each connected provider
851
+ setWalletProviders(newProviders);
852
+ return newProviders;
853
+ }, [client, callbacks]);
854
+ const connectWalletAccount = react.useCallback(async walletProvider => {
684
855
  if (!client) {
685
856
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
686
857
  }
687
858
  await client.connectWalletAccount(walletProvider);
859
+ // this will update our walletProvider state
688
860
  await refreshWallets();
689
- }
690
- async function disconnectWalletAccount(walletProvider) {
861
+ }, [client, callbacks]);
862
+ const disconnectWalletAccount = react.useCallback(async walletProvider => {
691
863
  if (!client) {
692
864
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
693
865
  }
694
866
  await client.disconnectWalletAccount(walletProvider);
695
- // we only refresh the wallets if there is an active session
696
- // this is needed because for WalletConnect you can unlink a wallet before
697
- // actually being logged in
698
- if (session) {
867
+ // we only refresh the wallets if:
868
+ // 1. there is an active session. This is needed because for WalletConnect
869
+ // you can disconnect a wallet before actually being logged in
870
+ //
871
+ // 2. it was a WalletConnect provider that we just disconnected. Since
872
+ // native providers emit a disconnect event which will already refresh
873
+ // the wallets. This event is triggered in `initializeWalletProviderListeners()`
874
+ if (session && walletProvider.interfaceType === core.WalletInterfaceType.WalletConnect) {
875
+ // this will update our walletProvider state
699
876
  await refreshWallets();
700
877
  }
701
- }
702
- async function switchWalletProviderChain(walletProvider, chainOrId) {
878
+ }, [client, callbacks]);
879
+ const switchWalletAccountChain = react.useCallback(async params => {
703
880
  if (!client) {
704
881
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
705
882
  }
706
- await client.switchWalletProviderChain(walletProvider, chainOrId);
707
- }
708
- // TODO: MOE PLEASE COMMENT THESE
709
- async function initializeProviders(getWalletProviders, onWalletsChanged) {
710
- const cleanups = [];
711
- // we only want to initialize these listeners for native wallet providers
712
- // and not WalletConnect
713
- const nativeOnly = p => p.interfaceType !== core.WalletInterfaceType.WalletConnect;
714
- const ethProviders = masterConfig?.walletConfig?.chains.ethereum?.native ? (await getWalletProviders(core.Chain.Ethereum)).filter(nativeOnly) : [];
715
- const solProviders = masterConfig?.walletConfig?.chains.solana?.native ? (await getWalletProviders(core.Chain.Solana)).filter(nativeOnly) : [];
716
- function attachEthereumListeners(provider, onWalletsChanged) {
717
- if (typeof provider.on !== "function") return;
718
- const handleAccountsChanged = accounts => {
719
- if (accounts.length === 0) onWalletsChanged();
720
- };
721
- const handleDisconnect = () => onWalletsChanged();
722
- provider.on("accountsChanged", handleAccountsChanged);
723
- provider.on("disconnect", handleDisconnect);
724
- return () => {
725
- provider.removeListener("accountsChanged", handleAccountsChanged);
726
- provider.removeListener("disconnect", handleDisconnect);
727
- };
728
- }
729
- function attachSolanaListeners(provider, onWalletsChanged) {
730
- const cleanups = [];
731
- const walletEvents = provider.features?.["standard:events"];
732
- if (walletEvents?.on) {
733
- const handleChange = event => {
734
- if (event.type === "change" || event.type === "accountsChanged") {
735
- onWalletsChanged();
736
- }
737
- };
738
- walletEvents.on("change", handleChange);
739
- walletEvents.on("accountsChanged", handleChange);
740
- cleanups.push(() => {
741
- walletEvents.off?.("change", handleChange);
742
- walletEvents.off?.("accountsChanged", handleChange);
743
- });
744
- }
745
- return cleanups.length > 0 ? () => {
746
- cleanups.forEach(fn => fn());
747
- } : () => {};
748
- }
749
- ethProviders.forEach(p => {
750
- const cleanup = attachEthereumListeners(p.provider, onWalletsChanged);
751
- if (cleanup) cleanups.push(cleanup);
752
- });
753
- solProviders.forEach(p => {
754
- const cleanup = attachSolanaListeners(p.provider, onWalletsChanged);
755
- if (cleanup) cleanups.push(cleanup);
883
+ await client.switchWalletAccountChain({
884
+ ...params,
885
+ walletProviders
756
886
  });
757
- return () => {
758
- cleanups.forEach(remove => remove());
759
- };
760
- }
761
- async function loginWithWallet(params) {
887
+ }, [client, callbacks]);
888
+ const loginWithWallet = react.useCallback(async params => {
762
889
  if (!client) {
763
890
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
764
891
  }
@@ -768,11 +895,15 @@ const ClientProvider = ({
768
895
  expirationSeconds
769
896
  }), callbacks, "Failed to login with wallet");
770
897
  if (res) {
771
- await handlePostAuth();
898
+ await handlePostAuth({
899
+ method: base.AuthMethod.Wallet,
900
+ action: sdkTypes.AuthAction.LOGIN,
901
+ identifier: res.address
902
+ });
772
903
  }
773
904
  return res;
774
- }
775
- async function signUpWithWallet(params) {
905
+ }, [client, callbacks]);
906
+ const signUpWithWallet = react.useCallback(async params => {
776
907
  if (!client) {
777
908
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
778
909
  }
@@ -793,11 +924,15 @@ const ClientProvider = ({
793
924
  expirationSeconds
794
925
  }), callbacks, "Failed to sign up with wallet");
795
926
  if (res) {
796
- await handlePostAuth();
927
+ await handlePostAuth({
928
+ method: base.AuthMethod.Wallet,
929
+ action: sdkTypes.AuthAction.SIGNUP,
930
+ identifier: res.address
931
+ });
797
932
  }
798
933
  return res;
799
- }
800
- async function loginOrSignupWithWallet(params) {
934
+ }, [client, callbacks, masterConfig]);
935
+ const loginOrSignupWithWallet = react.useCallback(async params => {
801
936
  if (!client) {
802
937
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
803
938
  }
@@ -818,33 +953,41 @@ const ClientProvider = ({
818
953
  expirationSeconds
819
954
  }), callbacks, "Failed to login or sign up with wallet");
820
955
  if (res) {
821
- await handlePostAuth();
956
+ await handlePostAuth({
957
+ method: base.AuthMethod.Wallet,
958
+ action: res.action,
959
+ identifier: res.address
960
+ });
822
961
  }
823
962
  return res;
824
- }
825
- async function initOtp(params) {
963
+ }, [client, callbacks, masterConfig]);
964
+ const initOtp = react.useCallback(async params => {
826
965
  if (!client) {
827
966
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
828
967
  }
829
968
  return utils.withTurnkeyErrorHandling(() => client.initOtp(params), callbacks, "Failed to initialize OTP");
830
- }
831
- async function verifyOtp(params) {
969
+ }, [client, callbacks]);
970
+ const verifyOtp = react.useCallback(async params => {
832
971
  if (!client) {
833
972
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
834
973
  }
835
974
  return utils.withTurnkeyErrorHandling(() => client.verifyOtp(params), callbacks, "Failed to verify OTP");
836
- }
837
- async function loginWithOtp(params) {
975
+ }, [client, callbacks]);
976
+ const loginWithOtp = react.useCallback(async params => {
838
977
  if (!client) {
839
978
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
840
979
  }
841
980
  const res = await utils.withTurnkeyErrorHandling(() => client.loginWithOtp(params), callbacks, "Failed to login with OTP");
842
981
  if (res) {
843
- await handlePostAuth();
982
+ await handlePostAuth({
983
+ method: base.AuthMethod.Otp,
984
+ action: sdkTypes.AuthAction.LOGIN,
985
+ identifier: params.verificationToken
986
+ });
844
987
  }
845
988
  return res;
846
- }
847
- async function signUpWithOtp(params) {
989
+ }, [client, callbacks]);
990
+ const signUpWithOtp = react.useCallback(async params => {
848
991
  if (!client) {
849
992
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
850
993
  }
@@ -868,11 +1011,15 @@ const ClientProvider = ({
868
1011
  };
869
1012
  const res = await utils.withTurnkeyErrorHandling(() => client.signUpWithOtp(params), callbacks, "Failed to sign up with OTP");
870
1013
  if (res) {
871
- await handlePostAuth();
1014
+ await handlePostAuth({
1015
+ method: base.AuthMethod.Otp,
1016
+ action: sdkTypes.AuthAction.SIGNUP,
1017
+ identifier: params.verificationToken
1018
+ });
872
1019
  }
873
1020
  return res;
874
- }
875
- async function completeOtp(params) {
1021
+ }, [client, callbacks, masterConfig]);
1022
+ const completeOtp = react.useCallback(async params => {
876
1023
  if (!client) {
877
1024
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
878
1025
  }
@@ -896,11 +1043,29 @@ const ClientProvider = ({
896
1043
  };
897
1044
  const res = await utils.withTurnkeyErrorHandling(() => client.completeOtp(params), callbacks, "Failed to complete OTP");
898
1045
  if (res) {
899
- await handlePostAuth();
1046
+ await handlePostAuth({
1047
+ method: base.AuthMethod.Otp,
1048
+ action: res.action,
1049
+ identifier: res.verificationToken
1050
+ });
900
1051
  }
901
1052
  return res;
902
- }
903
- async function completeOauth(params) {
1053
+ }, [client, callbacks, masterConfig]);
1054
+ const loginWithOauth = react.useCallback(async params => {
1055
+ if (!client) {
1056
+ throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1057
+ }
1058
+ const res = await utils.withTurnkeyErrorHandling(() => client.loginWithOauth(params), callbacks, "Failed to login with OAuth");
1059
+ if (res) {
1060
+ await handlePostAuth({
1061
+ method: base.AuthMethod.Oauth,
1062
+ action: sdkTypes.AuthAction.LOGIN,
1063
+ identifier: params.oidcToken
1064
+ });
1065
+ }
1066
+ return res;
1067
+ }, [client, callbacks]);
1068
+ const signUpWithOauth = react.useCallback(async params => {
904
1069
  if (!client) {
905
1070
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
906
1071
  }
@@ -908,30 +1073,24 @@ const ClientProvider = ({
908
1073
  throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
909
1074
  }
910
1075
  // If createSubOrgParams is not provided, use the default from masterConfig
911
- const createSubOrgParams = params.createSubOrgParams ?? masterConfig.auth?.createSuborgParams?.oauth;
1076
+ let createSubOrgParams = params.createSubOrgParams ?? masterConfig.auth?.createSuborgParams?.oauth;
912
1077
  params = createSubOrgParams !== undefined ? {
913
1078
  ...params,
914
1079
  createSubOrgParams
915
1080
  } : {
916
1081
  ...params
917
1082
  };
918
- const res = await utils.withTurnkeyErrorHandling(() => client.completeOauth(params), callbacks, "Failed to complete OAuth");
1083
+ const res = await utils.withTurnkeyErrorHandling(() => client.signUpWithOauth(params), callbacks, "Failed to sign up with OAuth");
919
1084
  if (res) {
920
- await handlePostAuth();
1085
+ await handlePostAuth({
1086
+ method: base.AuthMethod.Oauth,
1087
+ action: sdkTypes.AuthAction.SIGNUP,
1088
+ identifier: params.oidcToken
1089
+ });
921
1090
  }
922
1091
  return res;
923
- }
924
- async function loginWithOauth(params) {
925
- if (!client) {
926
- throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
927
- }
928
- const res = await utils.withTurnkeyErrorHandling(() => client.loginWithOauth(params), callbacks, "Failed to login with OAuth");
929
- if (res) {
930
- await handlePostAuth();
931
- }
932
- return res;
933
- }
934
- async function signUpWithOauth(params) {
1092
+ }, [client, callbacks, masterConfig]);
1093
+ const completeOauth = react.useCallback(async params => {
935
1094
  if (!client) {
936
1095
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
937
1096
  }
@@ -939,43 +1098,53 @@ const ClientProvider = ({
939
1098
  throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
940
1099
  }
941
1100
  // If createSubOrgParams is not provided, use the default from masterConfig
942
- let createSubOrgParams = params.createSubOrgParams ?? masterConfig.auth?.createSuborgParams?.oauth;
1101
+ const createSubOrgParams = params.createSubOrgParams ?? masterConfig.auth?.createSuborgParams?.oauth;
943
1102
  params = createSubOrgParams !== undefined ? {
944
1103
  ...params,
945
1104
  createSubOrgParams
946
1105
  } : {
947
1106
  ...params
948
1107
  };
949
- const res = await utils.withTurnkeyErrorHandling(() => client.signUpWithOauth(params), callbacks, "Failed to sign up with OAuth");
1108
+ const res = await utils.withTurnkeyErrorHandling(() => client.completeOauth(params), callbacks, "Failed to complete OAuth");
950
1109
  if (res) {
951
- await handlePostAuth();
1110
+ await handlePostAuth({
1111
+ method: base.AuthMethod.Oauth,
1112
+ action: res.action,
1113
+ identifier: params.oidcToken
1114
+ });
952
1115
  }
953
1116
  return res;
954
- }
955
- async function fetchWallets(params) {
1117
+ }, [client, callbacks, masterConfig]);
1118
+ const fetchWallets = react.useCallback(async params => {
956
1119
  if (!client) {
957
1120
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
958
1121
  }
959
1122
  return utils.withTurnkeyErrorHandling(() => client.fetchWallets(params), callbacks, "Failed to fetch wallets");
960
- }
961
- async function fetchWalletAccounts(params) {
1123
+ }, [client, callbacks]);
1124
+ const fetchWalletAccounts = react.useCallback(async params => {
962
1125
  if (!client) {
963
1126
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
964
1127
  }
965
1128
  return utils.withTurnkeyErrorHandling(() => client.fetchWalletAccounts(params), callbacks, "Failed to fetch wallet accounts");
966
- }
967
- async function signMessage(params) {
1129
+ }, [client, callbacks]);
1130
+ const fetchPrivateKeys = react.useCallback(async params => {
1131
+ if (!client) {
1132
+ throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1133
+ }
1134
+ return utils.withTurnkeyErrorHandling(() => client.fetchPrivateKeys(params), callbacks, "Failed to fetch private keys");
1135
+ }, [client, callbacks]);
1136
+ const signMessage = react.useCallback(async params => {
968
1137
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
969
1138
  return utils.withTurnkeyErrorHandling(() => client.signMessage(params), callbacks, "Failed to sign message");
970
- }
971
- async function handleSignMessage(params) {
1139
+ }, [client, callbacks]);
1140
+ const handleSignMessage = react.useCallback(async params => {
972
1141
  const {
973
1142
  successPageDuration = 2000
974
1143
  } = params;
975
1144
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
976
1145
  return new Promise((resolve, reject) => {
977
1146
  pushPage({
978
- key: "Sign Message",
1147
+ key: "Sign message",
979
1148
  content: jsxRuntime.jsx(Message.SignMessageModal, {
980
1149
  message: params.message,
981
1150
  subText: params?.subText,
@@ -1000,146 +1169,146 @@ const ClientProvider = ({
1000
1169
  })
1001
1170
  });
1002
1171
  });
1003
- }
1004
- async function signTransaction(params) {
1172
+ }, [client, callbacks]);
1173
+ const signTransaction = react.useCallback(async params => {
1005
1174
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1006
1175
  return utils.withTurnkeyErrorHandling(() => client.signTransaction(params), callbacks, "Failed to sign transaction");
1007
- }
1008
- async function signAndSendTransaction(params) {
1176
+ }, [client, callbacks]);
1177
+ const signAndSendTransaction = react.useCallback(async params => {
1009
1178
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1010
1179
  return utils.withTurnkeyErrorHandling(() => client.signAndSendTransaction(params), callbacks, "Failed to sign transaction");
1011
- }
1012
- async function fetchUser(params) {
1180
+ }, [client, callbacks]);
1181
+ const fetchUser = react.useCallback(async params => {
1013
1182
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1014
1183
  return utils.withTurnkeyErrorHandling(() => client.fetchUser(params), callbacks, "Failed to fetch user");
1015
- }
1016
- async function updateUserEmail(params) {
1184
+ }, [client, callbacks]);
1185
+ const updateUserEmail = react.useCallback(async params => {
1017
1186
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1018
1187
  const res = await utils.withTurnkeyErrorHandling(() => client.updateUserEmail(params), callbacks, "Failed to update user email");
1019
1188
  if (res) await refreshUser({
1020
1189
  stampWith: params?.stampWith
1021
1190
  });
1022
1191
  return res;
1023
- }
1024
- async function removeUserEmail(params) {
1192
+ }, [client, callbacks]);
1193
+ const removeUserEmail = react.useCallback(async params => {
1025
1194
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1026
1195
  const res = await utils.withTurnkeyErrorHandling(() => client.removeUserEmail(params), callbacks, "Failed to remove user email");
1027
1196
  if (res) await refreshUser({
1028
1197
  stampWith: params?.stampWith
1029
1198
  });
1030
1199
  return res;
1031
- }
1032
- async function updateUserPhoneNumber(params) {
1200
+ }, [client, callbacks]);
1201
+ const updateUserPhoneNumber = react.useCallback(async params => {
1033
1202
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1034
1203
  const res = await utils.withTurnkeyErrorHandling(() => client.updateUserPhoneNumber(params), callbacks, "Failed to update user phone number");
1035
1204
  if (res) await refreshUser({
1036
1205
  stampWith: params?.stampWith
1037
1206
  });
1038
1207
  return res;
1039
- }
1040
- async function removeUserPhoneNumber(params) {
1208
+ }, [client, callbacks]);
1209
+ const removeUserPhoneNumber = react.useCallback(async params => {
1041
1210
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1042
1211
  const res = await utils.withTurnkeyErrorHandling(() => client.removeUserPhoneNumber(params), callbacks, "Failed to remove user phone number");
1043
1212
  if (res) await refreshUser({
1044
1213
  stampWith: params?.stampWith
1045
1214
  });
1046
1215
  return res;
1047
- }
1048
- async function updateUserName(params) {
1216
+ }, [client, callbacks]);
1217
+ const updateUserName = react.useCallback(async params => {
1049
1218
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1050
1219
  const res = await utils.withTurnkeyErrorHandling(() => client.updateUserName(params), callbacks, "Failed to update user name");
1051
1220
  if (res) await refreshUser({
1052
1221
  stampWith: params?.stampWith
1053
1222
  });
1054
1223
  return res;
1055
- }
1056
- async function addOauthProvider(params) {
1224
+ }, [client, callbacks]);
1225
+ const addOauthProvider = react.useCallback(async params => {
1057
1226
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1058
1227
  const res = await utils.withTurnkeyErrorHandling(() => client.addOauthProvider(params), callbacks, "Failed to add OAuth provider");
1059
1228
  if (res) await refreshUser({
1060
1229
  stampWith: params?.stampWith
1061
1230
  });
1062
1231
  return res;
1063
- }
1064
- async function removeOauthProviders(params) {
1232
+ }, [client, callbacks]);
1233
+ const removeOauthProviders = react.useCallback(async params => {
1065
1234
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1066
1235
  const res = await utils.withTurnkeyErrorHandling(() => client.removeOauthProviders(params), callbacks, "Failed to remove OAuth providers");
1067
1236
  if (res) await refreshUser({
1068
1237
  stampWith: params?.stampWith
1069
1238
  });
1070
1239
  return res;
1071
- }
1072
- async function addPasskey(params) {
1240
+ }, [client, callbacks]);
1241
+ const addPasskey = react.useCallback(async params => {
1073
1242
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1074
1243
  const res = await utils.withTurnkeyErrorHandling(() => client.addPasskey(params), callbacks, "Failed to add passkey");
1075
1244
  if (res) await refreshUser({
1076
1245
  stampWith: params?.stampWith
1077
1246
  });
1078
1247
  return res;
1079
- }
1080
- async function removePasskeys(params) {
1248
+ }, [client, callbacks]);
1249
+ const removePasskeys = react.useCallback(async params => {
1081
1250
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1082
1251
  const res = await utils.withTurnkeyErrorHandling(() => client.removePasskeys(params), callbacks, "Failed to remove passkeys");
1083
1252
  if (res) await refreshUser({
1084
1253
  stampWith: params?.stampWith
1085
1254
  });
1086
1255
  return res;
1087
- }
1088
- async function createWallet(params) {
1256
+ }, [client, callbacks]);
1257
+ const createWallet = react.useCallback(async params => {
1089
1258
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1090
1259
  const res = await utils.withTurnkeyErrorHandling(() => client.createWallet(params), callbacks, "Failed to create wallet");
1091
1260
  if (res) await refreshWallets({
1092
1261
  stampWith: params?.stampWith
1093
1262
  });
1094
1263
  return res;
1095
- }
1096
- async function createWalletAccounts(params) {
1264
+ }, [client, callbacks]);
1265
+ const createWalletAccounts = react.useCallback(async params => {
1097
1266
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1098
1267
  const res = await utils.withTurnkeyErrorHandling(() => client.createWalletAccounts(params), callbacks, "Failed to create wallet accounts");
1099
1268
  if (res) await refreshWallets({
1100
1269
  stampWith: params?.stampWith
1101
1270
  });
1102
1271
  return res;
1103
- }
1104
- async function exportWallet(params) {
1272
+ }, [client, callbacks]);
1273
+ const exportWallet = react.useCallback(async params => {
1105
1274
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1106
1275
  const res = await utils.withTurnkeyErrorHandling(() => client.exportWallet(params), callbacks, "Failed to export wallet");
1107
1276
  if (res) await refreshWallets({
1108
1277
  stampWith: params?.stampWith
1109
1278
  });
1110
1279
  return res;
1111
- }
1112
- async function exportPrivateKey(params) {
1280
+ }, [client, callbacks]);
1281
+ const exportPrivateKey = react.useCallback(async params => {
1113
1282
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1114
1283
  const res = await utils.withTurnkeyErrorHandling(() => client.exportPrivateKey(params), callbacks, "Failed to export private key");
1115
1284
  return res;
1116
- }
1117
- async function exportWalletAccount(params) {
1285
+ }, [client, callbacks]);
1286
+ const exportWalletAccount = react.useCallback(async params => {
1118
1287
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1119
1288
  const res = await utils.withTurnkeyErrorHandling(() => client.exportWalletAccount(params), callbacks, "Failed to export wallet accounts");
1120
1289
  if (res) await refreshWallets({
1121
1290
  stampWith: params?.stampWith
1122
1291
  });
1123
1292
  return res;
1124
- }
1125
- async function importWallet(params) {
1293
+ }, [client, callbacks]);
1294
+ const importWallet = react.useCallback(async params => {
1126
1295
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1127
1296
  const res = await utils.withTurnkeyErrorHandling(() => client.importWallet(params), callbacks, "Failed to import wallet");
1128
1297
  if (res) await refreshWallets({
1129
1298
  stampWith: params?.stampWith
1130
1299
  });
1131
1300
  return res;
1132
- }
1133
- async function importPrivateKey(params) {
1301
+ }, [client, callbacks]);
1302
+ const importPrivateKey = react.useCallback(async params => {
1134
1303
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1135
1304
  const res = await utils.withTurnkeyErrorHandling(() => client.importPrivateKey(params), callbacks, "Failed to import private key");
1136
1305
  return res;
1137
- }
1138
- async function deleteSubOrganization(params) {
1306
+ }, [client, callbacks]);
1307
+ const deleteSubOrganization = react.useCallback(async params => {
1139
1308
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1140
1309
  return utils.withTurnkeyErrorHandling(() => client.deleteSubOrganization(params), callbacks, "Failed to delete sub-organization");
1141
- }
1142
- async function storeSession(params) {
1310
+ }, [client, callbacks]);
1311
+ const storeSession = react.useCallback(async params => {
1143
1312
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1144
1313
  await utils.withTurnkeyErrorHandling(() => client.storeSession(params), callbacks, "Failed to store session");
1145
1314
  const sessionKey = await getActiveSessionKey();
@@ -1156,8 +1325,8 @@ const ClientProvider = ({
1156
1325
  setSession(session);
1157
1326
  setAllSessions(allSessions);
1158
1327
  return;
1159
- }
1160
- async function clearSession(params) {
1328
+ }, [client, callbacks]);
1329
+ const clearSession = react.useCallback(async params => {
1161
1330
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1162
1331
  await utils.withTurnkeyErrorHandling(() => client.clearSession(params), callbacks, "Failed to clear session");
1163
1332
  const session = await getSession();
@@ -1165,14 +1334,14 @@ const ClientProvider = ({
1165
1334
  setSession(session);
1166
1335
  setAllSessions(allSessions);
1167
1336
  return;
1168
- }
1169
- async function clearAllSessions() {
1337
+ }, [client, callbacks]);
1338
+ const clearAllSessions = react.useCallback(async () => {
1170
1339
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1171
1340
  setSession(undefined);
1172
1341
  setAllSessions(undefined);
1173
1342
  return await utils.withTurnkeyErrorHandling(() => client.clearAllSessions(), callbacks, "Failed to clear all sessions");
1174
- }
1175
- async function refreshSession(params) {
1343
+ }, [client, callbacks]);
1344
+ const refreshSession = react.useCallback(async params => {
1176
1345
  if (!client) {
1177
1346
  throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1178
1347
  }
@@ -1200,16 +1369,16 @@ const ClientProvider = ({
1200
1369
  setSession(session);
1201
1370
  setAllSessions(allSessions);
1202
1371
  return res;
1203
- }
1204
- async function getSession(params) {
1372
+ }, [client, callbacks]);
1373
+ const getSession = react.useCallback(async params => {
1205
1374
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1206
1375
  return utils.withTurnkeyErrorHandling(() => client.getSession(params), callbacks, "Failed to get session");
1207
- }
1208
- async function getAllSessions() {
1376
+ }, [client, callbacks]);
1377
+ const getAllSessions = react.useCallback(async () => {
1209
1378
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1210
1379
  return utils.withTurnkeyErrorHandling(() => client.getAllSessions(), callbacks, "Failed to get all sessions");
1211
- }
1212
- async function setActiveSession(params) {
1380
+ }, [client, callbacks]);
1381
+ const setActiveSession = react.useCallback(async params => {
1213
1382
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1214
1383
  const session = await utils.withTurnkeyErrorHandling(() => client.getSession({
1215
1384
  sessionKey: params.sessionKey
@@ -1219,25 +1388,29 @@ const ClientProvider = ({
1219
1388
  }
1220
1389
  await utils.withTurnkeyErrorHandling(() => client.setActiveSession(params), callbacks, "Failed to set active session");
1221
1390
  setSession(session);
1391
+ await utils.withTurnkeyErrorHandling(async () => {
1392
+ await refreshWallets();
1393
+ await refreshUser();
1394
+ }, callbacks, "Failed to refresh data after setting active session");
1222
1395
  return;
1223
- }
1224
- async function getActiveSessionKey() {
1396
+ }, [client, callbacks]);
1397
+ const getActiveSessionKey = react.useCallback(async () => {
1225
1398
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1226
1399
  return utils.withTurnkeyErrorHandling(() => client.getActiveSessionKey(), callbacks, "Failed to get active session key");
1227
- }
1228
- async function clearUnusedKeyPairs() {
1400
+ }, [client, callbacks]);
1401
+ const clearUnusedKeyPairs = react.useCallback(async () => {
1229
1402
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1230
1403
  return utils.withTurnkeyErrorHandling(() => client.clearUnusedKeyPairs(), callbacks, "Failed to clear unused key pairs");
1231
- }
1232
- async function createApiKeyPair(params) {
1404
+ }, [client, callbacks]);
1405
+ const createApiKeyPair = react.useCallback(async params => {
1233
1406
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1234
1407
  return utils.withTurnkeyErrorHandling(() => client.createApiKeyPair(params), callbacks, "Failed to create API key pair");
1235
- }
1236
- async function getProxyAuthConfig() {
1408
+ }, [client, callbacks]);
1409
+ const getProxyAuthConfig = react.useCallback(async () => {
1237
1410
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1238
1411
  return utils.withTurnkeyErrorHandling(() => client.getProxyAuthConfig(), callbacks, "Failed to get proxy auth config");
1239
- }
1240
- async function refreshUser(params) {
1412
+ }, [client, callbacks]);
1413
+ const refreshUser = react.useCallback(async params => {
1241
1414
  const {
1242
1415
  stampWith
1243
1416
  } = params || {};
@@ -1248,20 +1421,298 @@ const ClientProvider = ({
1248
1421
  if (user) {
1249
1422
  setUser(user);
1250
1423
  }
1251
- }
1252
- async function refreshWallets(params) {
1424
+ }, [client, callbacks]);
1425
+ const refreshWallets = react.useCallback(async params => {
1426
+ const {
1427
+ stampWith
1428
+ } = params || {};
1429
+ if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1430
+ const walletProviders = await utils.withTurnkeyErrorHandling(() => getWalletProviders(), callbacks, "Failed to refresh wallets");
1431
+ const wallets = await utils.withTurnkeyErrorHandling(() => fetchWallets({
1432
+ stampWith,
1433
+ walletProviders
1434
+ }), callbacks, "Failed to refresh wallets");
1435
+ if (wallets) {
1436
+ setWallets(wallets);
1437
+ }
1438
+ }, [client, callbacks, getWalletProviders, fetchWallets]);
1439
+ const handleDiscordOauth = react.useCallback(async params => {
1440
+ const {
1441
+ clientId = masterConfig?.auth?.oauthConfig?.discordClientId,
1442
+ openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
1443
+ additionalState: additionalParameters
1444
+ } = params || {};
1445
+ try {
1446
+ if (!masterConfig) {
1447
+ throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1448
+ }
1449
+ if (!clientId) {
1450
+ throw new sdkTypes.TurnkeyError("Discord Client ID is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1451
+ }
1452
+ if (!masterConfig.auth?.oauthConfig?.oauthRedirectUri) {
1453
+ throw new sdkTypes.TurnkeyError("OAuth Redirect URI is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1454
+ }
1455
+ const flow = openInPage ? "redirect" : "popup";
1456
+ const redirectURI = masterConfig.auth?.oauthConfig.oauthRedirectUri;
1457
+ // Create key pair and generate nonce
1458
+ const publicKey = await createApiKeyPair();
1459
+ if (!publicKey) {
1460
+ throw new Error("Failed to create public key for OAuth.");
1461
+ }
1462
+ const nonce = utils$1.bytesToHex(sha2.sha256(publicKey));
1463
+ // Generate PKCE challenge pair
1464
+ const {
1465
+ verifier,
1466
+ codeChallenge
1467
+ } = await utils.generateChallengePair();
1468
+ sessionStorage.setItem("discord_verifier", verifier);
1469
+ // Construct Discord Auth URL
1470
+ const discordAuthUrl = new URL(utils.DISCORD_AUTH_URL);
1471
+ discordAuthUrl.searchParams.set("client_id", clientId);
1472
+ discordAuthUrl.searchParams.set("redirect_uri", redirectURI);
1473
+ discordAuthUrl.searchParams.set("response_type", "code");
1474
+ discordAuthUrl.searchParams.set("code_challenge", codeChallenge);
1475
+ discordAuthUrl.searchParams.set("code_challenge_method", "S256");
1476
+ discordAuthUrl.searchParams.set("scope", "identify email");
1477
+ discordAuthUrl.searchParams.set("state", `provider=discord&flow=${flow}&publicKey=${encodeURIComponent(publicKey)}&nonce=${nonce}`);
1478
+ if (additionalParameters) {
1479
+ const extra = Object.entries(additionalParameters).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
1480
+ if (extra) {
1481
+ discordAuthUrl.searchParams.set("state", discordAuthUrl.searchParams.get("state") + `&${extra}`);
1482
+ }
1483
+ }
1484
+ if (openInPage) {
1485
+ window.location.href = discordAuthUrl.toString();
1486
+ return new Promise((_, reject) => {
1487
+ const timeout = setTimeout(() => {
1488
+ reject(new Error("Authentication timed out."));
1489
+ }, 300000);
1490
+ window.addEventListener("beforeunload", () => clearTimeout(timeout));
1491
+ });
1492
+ } else {
1493
+ const width = utils.popupWidth;
1494
+ const height = utils.popupHeight;
1495
+ const left = window.screenX + (window.innerWidth - width) / 2;
1496
+ const top = window.screenY + (window.innerHeight - height) / 2;
1497
+ const authWindow = window.open("about:blank", "_blank", `width=${width},height=${height},top=${top},left=${left},scrollbars=yes,resizable=yes`);
1498
+ if (!authWindow) {
1499
+ throw new Error("Failed to open Discord login window.");
1500
+ }
1501
+ authWindow.location.href = discordAuthUrl.toString();
1502
+ return new Promise((resolve, reject) => {
1503
+ const interval = setInterval(() => {
1504
+ try {
1505
+ if (authWindow.closed) {
1506
+ clearInterval(interval);
1507
+ reject(new Error("Authentication window was closed."));
1508
+ return;
1509
+ }
1510
+ const url = authWindow.location.href || "";
1511
+ if (url.startsWith(window.location.origin)) {
1512
+ const urlParams = new URLSearchParams(new URL(url).search);
1513
+ const authCode = urlParams.get("code");
1514
+ const stateParam = urlParams.get("state");
1515
+ const sessionKey = stateParam?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
1516
+ if (authCode && stateParam && stateParam.includes("provider=discord")) {
1517
+ authWindow.close();
1518
+ clearInterval(interval);
1519
+ const verifier = sessionStorage.getItem("discord_verifier");
1520
+ if (!verifier) {
1521
+ reject(new Error("Missing PKCE verifier"));
1522
+ return;
1523
+ }
1524
+ client?.httpClient.proxyOAuth2Authenticate({
1525
+ provider: "OAUTH2_PROVIDER_DISCORD",
1526
+ authCode,
1527
+ redirectUri: redirectURI,
1528
+ codeVerifier: verifier,
1529
+ nonce: nonce
1530
+ }).then(resp => {
1531
+ sessionStorage.removeItem("discord_verifier");
1532
+ const oidcToken = resp.oidcToken;
1533
+ if (params?.onOauthSuccess) {
1534
+ params.onOauthSuccess({
1535
+ oidcToken,
1536
+ providerName: "discord",
1537
+ ...(sessionKey && {
1538
+ sessionKey
1539
+ })
1540
+ });
1541
+ } else if (callbacks?.onOauthRedirect) {
1542
+ callbacks.onOauthRedirect({
1543
+ idToken: oidcToken,
1544
+ publicKey,
1545
+ ...(sessionKey && {
1546
+ sessionKey
1547
+ })
1548
+ });
1549
+ } else {
1550
+ completeOauth({
1551
+ oidcToken,
1552
+ publicKey,
1553
+ providerName: "discord",
1554
+ ...(sessionKey && {
1555
+ sessionKey
1556
+ })
1557
+ }).then(() => resolve()).catch(reject);
1558
+ return;
1559
+ }
1560
+ resolve();
1561
+ }).catch(reject);
1562
+ }
1563
+ }
1564
+ } catch {
1565
+ // ignore cross-origin
1566
+ }
1567
+ }, 500);
1568
+ if (authWindow.closed) {
1569
+ clearInterval(interval);
1570
+ }
1571
+ });
1572
+ }
1573
+ } catch (error) {
1574
+ throw error;
1575
+ }
1576
+ }, [client, callbacks]);
1577
+ const handleXOauth = react.useCallback(async params => {
1253
1578
  const {
1254
- stampWith
1579
+ clientId = masterConfig?.auth?.oauthConfig?.xClientId,
1580
+ openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
1581
+ additionalState: additionalParameters
1255
1582
  } = params || {};
1256
- if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
1257
- const wallets = await utils.withTurnkeyErrorHandling(() => fetchWallets({
1258
- stampWith
1259
- }), callbacks, "Failed to refresh wallets");
1260
- if (wallets) {
1261
- setWallets(wallets);
1583
+ try {
1584
+ if (!masterConfig) {
1585
+ throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1586
+ }
1587
+ if (!clientId) {
1588
+ throw new sdkTypes.TurnkeyError("Twitter Client ID is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1589
+ }
1590
+ if (!masterConfig.auth?.oauthConfig?.oauthRedirectUri) {
1591
+ throw new sdkTypes.TurnkeyError("OAuth Redirect URI is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
1592
+ }
1593
+ const flow = openInPage ? "redirect" : "popup";
1594
+ const redirectURI = masterConfig.auth?.oauthConfig.oauthRedirectUri;
1595
+ // Create key pair and generate nonce
1596
+ const publicKey = await createApiKeyPair();
1597
+ if (!publicKey) {
1598
+ throw new Error("Failed to create public key for OAuth.");
1599
+ }
1600
+ const nonce = utils$1.bytesToHex(sha2.sha256(publicKey));
1601
+ // Generate PKCE challenge pair
1602
+ const {
1603
+ verifier,
1604
+ codeChallenge
1605
+ } = await utils.generateChallengePair();
1606
+ sessionStorage.setItem("twitter_verifier", verifier);
1607
+ // Construct Twitter Auth URL
1608
+ const twitterAuthUrl = new URL(utils.X_AUTH_URL);
1609
+ twitterAuthUrl.searchParams.set("client_id", clientId);
1610
+ twitterAuthUrl.searchParams.set("redirect_uri", redirectURI);
1611
+ twitterAuthUrl.searchParams.set("response_type", "code");
1612
+ twitterAuthUrl.searchParams.set("code_challenge", codeChallenge);
1613
+ twitterAuthUrl.searchParams.set("code_challenge_method", "S256");
1614
+ twitterAuthUrl.searchParams.set("scope", "tweet.read users.read");
1615
+ twitterAuthUrl.searchParams.set("state", `provider=twitter&flow=${flow}&publicKey=${encodeURIComponent(publicKey)}&nonce=${nonce}`);
1616
+ if (additionalParameters) {
1617
+ const extra = Object.entries(additionalParameters).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
1618
+ if (extra) {
1619
+ twitterAuthUrl.searchParams.set("state", twitterAuthUrl.searchParams.get("state") + `&${extra}`);
1620
+ }
1621
+ }
1622
+ if (openInPage) {
1623
+ window.location.href = twitterAuthUrl.toString();
1624
+ return new Promise((_, reject) => {
1625
+ const timeout = setTimeout(() => {
1626
+ reject(new Error("Authentication timed out."));
1627
+ }, 300000);
1628
+ window.addEventListener("beforeunload", () => clearTimeout(timeout));
1629
+ });
1630
+ } else {
1631
+ const width = utils.popupWidth;
1632
+ const height = utils.popupHeight;
1633
+ const left = window.screenX + (window.innerWidth - width) / 2;
1634
+ const top = window.screenY + (window.innerHeight - height) / 2;
1635
+ const authWindow = window.open("about:blank", "_blank", `width=${width},height=${height},top=${top},left=${left},scrollbars=yes,resizable=yes`);
1636
+ if (!authWindow) {
1637
+ throw new Error("Failed to open Twitter login window.");
1638
+ }
1639
+ authWindow.location.href = twitterAuthUrl.toString();
1640
+ return new Promise((resolve, reject) => {
1641
+ const interval = setInterval(() => {
1642
+ try {
1643
+ if (authWindow.closed) {
1644
+ clearInterval(interval);
1645
+ reject(new Error("Authentication window was closed."));
1646
+ return;
1647
+ }
1648
+ const url = authWindow.location.href || "";
1649
+ if (url.startsWith(window.location.origin)) {
1650
+ const urlParams = new URLSearchParams(new URL(url).search);
1651
+ const authCode = urlParams.get("code");
1652
+ const stateParam = urlParams.get("state");
1653
+ const sessionKey = stateParam?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
1654
+ if (authCode && stateParam && stateParam.includes("provider=twitter")) {
1655
+ authWindow.close();
1656
+ clearInterval(interval);
1657
+ const verifier = sessionStorage.getItem("twitter_verifier");
1658
+ if (!verifier) {
1659
+ reject(new Error("Missing PKCE verifier"));
1660
+ return;
1661
+ }
1662
+ client?.httpClient.proxyOAuth2Authenticate({
1663
+ provider: "OAUTH2_PROVIDER_X",
1664
+ authCode,
1665
+ redirectUri: redirectURI,
1666
+ codeVerifier: verifier,
1667
+ nonce
1668
+ }).then(resp => {
1669
+ sessionStorage.removeItem("twitter_verifier");
1670
+ const oidcToken = resp.oidcToken;
1671
+ if (params?.onOauthSuccess) {
1672
+ params.onOauthSuccess({
1673
+ oidcToken,
1674
+ providerName: "twitter",
1675
+ ...(sessionKey && {
1676
+ sessionKey
1677
+ })
1678
+ });
1679
+ } else if (callbacks?.onOauthRedirect) {
1680
+ callbacks.onOauthRedirect({
1681
+ idToken: oidcToken,
1682
+ publicKey,
1683
+ ...(sessionKey && {
1684
+ sessionKey
1685
+ })
1686
+ });
1687
+ } else {
1688
+ completeOauth({
1689
+ oidcToken,
1690
+ publicKey,
1691
+ providerName: "twitter",
1692
+ ...(sessionKey && {
1693
+ sessionKey
1694
+ })
1695
+ }).then(() => resolve()).catch(reject);
1696
+ return;
1697
+ }
1698
+ resolve();
1699
+ }).catch(reject);
1700
+ }
1701
+ }
1702
+ } catch {
1703
+ // ignore cross-origin
1704
+ }
1705
+ }, 500);
1706
+ if (authWindow.closed) {
1707
+ clearInterval(interval);
1708
+ }
1709
+ });
1710
+ }
1711
+ } catch (error) {
1712
+ throw error;
1262
1713
  }
1263
- }
1264
- async function handleGoogleOauth(params) {
1714
+ }, [client, callbacks]);
1715
+ const handleGoogleOauth = react.useCallback(async params => {
1265
1716
  const {
1266
1717
  clientId = masterConfig?.auth?.oauthConfig?.googleClientId,
1267
1718
  openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
@@ -1341,24 +1792,35 @@ const ClientProvider = ({
1341
1792
  if (url.startsWith(window.location.origin)) {
1342
1793
  const hashParams = new URLSearchParams(url.split("#")[1]);
1343
1794
  const idToken = hashParams.get("id_token");
1795
+ const stateParams = hashParams.get("state");
1796
+ const sessionKey = stateParams?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
1344
1797
  if (idToken) {
1345
1798
  authWindow.close();
1346
1799
  clearInterval(interval);
1347
1800
  if (params?.onOauthSuccess) {
1348
1801
  params.onOauthSuccess({
1349
1802
  oidcToken: idToken,
1350
- providerName: "google"
1803
+ providerName: "google",
1804
+ ...(sessionKey && {
1805
+ sessionKey
1806
+ })
1351
1807
  });
1352
1808
  } else if (callbacks?.onOauthRedirect) {
1353
1809
  callbacks.onOauthRedirect({
1354
1810
  idToken,
1355
- publicKey
1811
+ publicKey,
1812
+ ...(sessionKey && {
1813
+ sessionKey
1814
+ })
1356
1815
  });
1357
1816
  } else {
1358
1817
  completeOauth({
1359
1818
  oidcToken: idToken,
1360
1819
  publicKey,
1361
- providerName: "google"
1820
+ providerName: "google",
1821
+ ...(sessionKey && {
1822
+ sessionKey
1823
+ })
1362
1824
  }).then(() => resolve()).catch(reject);
1363
1825
  return;
1364
1826
  }
@@ -1377,8 +1839,8 @@ const ClientProvider = ({
1377
1839
  } catch (error) {
1378
1840
  throw error;
1379
1841
  }
1380
- }
1381
- async function handleAppleOauth(params) {
1842
+ }, [client, callbacks]);
1843
+ const handleAppleOauth = react.useCallback(async params => {
1382
1844
  const {
1383
1845
  clientId = masterConfig?.auth?.oauthConfig?.appleClientId,
1384
1846
  openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
@@ -1454,24 +1916,35 @@ const ClientProvider = ({
1454
1916
  if (url.startsWith(window.location.origin)) {
1455
1917
  const hashParams = new URLSearchParams(url.split("#")[1]);
1456
1918
  const idToken = hashParams.get("id_token");
1919
+ const stateParams = hashParams.get("state");
1920
+ const sessionKey = stateParams?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
1457
1921
  if (idToken) {
1458
1922
  authWindow.close();
1459
1923
  clearInterval(interval);
1460
1924
  if (params?.onOauthSuccess) {
1461
1925
  params.onOauthSuccess({
1462
1926
  oidcToken: idToken,
1463
- providerName: "apple"
1927
+ providerName: "apple",
1928
+ ...(sessionKey && {
1929
+ sessionKey
1930
+ })
1464
1931
  });
1465
1932
  } else if (callbacks?.onOauthRedirect) {
1466
1933
  callbacks.onOauthRedirect({
1467
1934
  idToken,
1468
- publicKey
1935
+ publicKey,
1936
+ ...(sessionKey && {
1937
+ sessionKey
1938
+ })
1469
1939
  });
1470
1940
  } else {
1471
1941
  completeOauth({
1472
1942
  oidcToken: idToken,
1473
1943
  publicKey,
1474
- providerName: "apple"
1944
+ providerName: "apple",
1945
+ ...(sessionKey && {
1946
+ sessionKey
1947
+ })
1475
1948
  }).then(() => resolve()).catch(reject);
1476
1949
  return;
1477
1950
  }
@@ -1490,8 +1963,8 @@ const ClientProvider = ({
1490
1963
  } catch (error) {
1491
1964
  throw error;
1492
1965
  }
1493
- }
1494
- async function handleFacebookOauth(params) {
1966
+ }, [client, callbacks]);
1967
+ const handleFacebookOauth = react.useCallback(async params => {
1495
1968
  const {
1496
1969
  clientId = masterConfig?.auth?.oauthConfig?.facebookClientId,
1497
1970
  openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
@@ -1578,6 +2051,7 @@ const ClientProvider = ({
1578
2051
  const authCode = urlParams.get("code");
1579
2052
  const stateParam = urlParams.get("state");
1580
2053
  if (authCode && stateParam && stateParam.includes("provider=facebook")) {
2054
+ const sessionKey = stateParam?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
1581
2055
  authWindow.close();
1582
2056
  clearInterval(interval);
1583
2057
  // Exchange code for token
@@ -1591,18 +2065,27 @@ const ClientProvider = ({
1591
2065
  if (params?.onOauthSuccess) {
1592
2066
  params.onOauthSuccess({
1593
2067
  oidcToken: tokenData.id_token,
1594
- providerName: "apple"
2068
+ providerName: "facebook",
2069
+ ...(sessionKey && {
2070
+ sessionKey
2071
+ })
1595
2072
  });
1596
2073
  } else if (callbacks?.onOauthRedirect) {
1597
2074
  callbacks.onOauthRedirect({
1598
2075
  idToken: tokenData.id_token,
1599
- publicKey
2076
+ publicKey,
2077
+ ...(sessionKey && {
2078
+ sessionKey
2079
+ })
1600
2080
  });
1601
2081
  } else {
1602
2082
  completeOauth({
1603
2083
  oidcToken: tokenData.id_token,
1604
2084
  publicKey,
1605
- providerName: "facebook"
2085
+ providerName: "facebook",
2086
+ ...(sessionKey && {
2087
+ sessionKey
2088
+ })
1606
2089
  }).then(() => resolve()).catch(reject);
1607
2090
  return;
1608
2091
  }
@@ -1622,21 +2105,23 @@ const ClientProvider = ({
1622
2105
  } catch (error) {
1623
2106
  throw error;
1624
2107
  }
1625
- }
1626
- const handleLogin = async () => {
2108
+ }, [client, callbacks]);
2109
+ const handleLogin = react.useCallback(async params => {
1627
2110
  pushPage({
1628
2111
  key: "Log in or sign up",
1629
- content: jsxRuntime.jsx(index.AuthComponent, {})
2112
+ content: jsxRuntime.jsx(index.AuthComponent, {
2113
+ sessionKey: params?.sessionKey
2114
+ })
1630
2115
  });
1631
- };
1632
- const handleExportWallet = async params => {
2116
+ }, [pushPage]);
2117
+ const handleExportWallet = react.useCallback(async params => {
1633
2118
  const {
1634
2119
  walletId,
1635
2120
  targetPublicKey,
1636
2121
  stampWith
1637
2122
  } = params;
1638
2123
  pushPage({
1639
- key: "Export Wallet",
2124
+ key: "Export wallet",
1640
2125
  content: jsxRuntime.jsx(Export.ExportComponent, {
1641
2126
  target: walletId,
1642
2127
  exportType: base.ExportType.Wallet,
@@ -1648,18 +2133,22 @@ const ClientProvider = ({
1648
2133
  } : {})
1649
2134
  })
1650
2135
  });
1651
- };
1652
- const handleExportPrivateKey = async params => {
2136
+ }, [pushPage]);
2137
+ const handleExportPrivateKey = react.useCallback(async params => {
1653
2138
  const {
1654
2139
  privateKeyId,
1655
2140
  targetPublicKey,
2141
+ keyFormat,
1656
2142
  stampWith
1657
2143
  } = params;
1658
2144
  pushPage({
1659
- key: "Export Private Key",
2145
+ key: "Export private key",
1660
2146
  content: jsxRuntime.jsx(Export.ExportComponent, {
1661
2147
  target: privateKeyId,
1662
2148
  exportType: base.ExportType.PrivateKey,
2149
+ ...(keyFormat !== undefined ? {
2150
+ keyFormat
2151
+ } : {}),
1663
2152
  ...(targetPublicKey !== undefined ? {
1664
2153
  targetPublicKey
1665
2154
  } : {}),
@@ -1668,18 +2157,22 @@ const ClientProvider = ({
1668
2157
  } : {})
1669
2158
  })
1670
2159
  });
1671
- };
1672
- const handleExportWalletAccount = async params => {
2160
+ }, [pushPage]);
2161
+ const handleExportWalletAccount = react.useCallback(async params => {
1673
2162
  const {
1674
2163
  address,
1675
2164
  targetPublicKey,
2165
+ keyFormat,
1676
2166
  stampWith
1677
2167
  } = params;
1678
2168
  pushPage({
1679
- key: "Export Wallet Account",
2169
+ key: "Export wallet account",
1680
2170
  content: jsxRuntime.jsx(Export.ExportComponent, {
1681
2171
  target: address,
1682
2172
  exportType: base.ExportType.WalletAccount,
2173
+ ...(keyFormat !== undefined ? {
2174
+ keyFormat
2175
+ } : {}),
1683
2176
  ...(targetPublicKey !== undefined ? {
1684
2177
  targetPublicKey
1685
2178
  } : {}),
@@ -1688,8 +2181,8 @@ const ClientProvider = ({
1688
2181
  } : {})
1689
2182
  })
1690
2183
  });
1691
- };
1692
- const handleImportWallet = async params => {
2184
+ }, [pushPage]);
2185
+ const handleImportWallet = react.useCallback(async params => {
1693
2186
  const {
1694
2187
  defaultWalletAccounts,
1695
2188
  successPageDuration = 2000,
@@ -1697,8 +2190,9 @@ const ClientProvider = ({
1697
2190
  } = params || {};
1698
2191
  try {
1699
2192
  return utils.withTurnkeyErrorHandling(() => new Promise((resolve, reject) => pushPage({
1700
- key: "Import Wallet",
2193
+ key: "Import wallet",
1701
2194
  content: jsxRuntime.jsx(Import.ImportComponent, {
2195
+ importType: base.ImportType.Wallet,
1702
2196
  onError: error => {
1703
2197
  reject(error);
1704
2198
  },
@@ -1720,8 +2214,41 @@ const ClientProvider = ({
1720
2214
  }
1721
2215
  throw new sdkTypes.TurnkeyError("Failed to import wallet.", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR, error);
1722
2216
  }
1723
- };
1724
- const handleUpdateUserName = async params => {
2217
+ }, [pushPage]);
2218
+ const handleImportPrivateKey = react.useCallback(async params => {
2219
+ const {
2220
+ curve,
2221
+ addressFormats,
2222
+ successPageDuration = 2000,
2223
+ stampWith
2224
+ } = params || {};
2225
+ try {
2226
+ return utils.withTurnkeyErrorHandling(() => new Promise((resolve, reject) => pushPage({
2227
+ key: "Import private key",
2228
+ content: jsxRuntime.jsx(Import.ImportComponent, {
2229
+ importType: base.ImportType.PrivateKey,
2230
+ curve: curve,
2231
+ addressFormats: addressFormats,
2232
+ onError: error => {
2233
+ reject(error);
2234
+ },
2235
+ onSuccess: privateKeyId => resolve(privateKeyId),
2236
+ ...(successPageDuration !== undefined && {
2237
+ successPageDuration
2238
+ }),
2239
+ ...(stampWith !== undefined && {
2240
+ stampWith
2241
+ })
2242
+ })
2243
+ })));
2244
+ } catch (error) {
2245
+ if (error instanceof sdkTypes.TurnkeyError) {
2246
+ throw error;
2247
+ }
2248
+ throw new sdkTypes.TurnkeyError("Failed to import private key.", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR, error);
2249
+ }
2250
+ }, [pushPage]);
2251
+ const handleUpdateUserName = react.useCallback(async params => {
1725
2252
  const {
1726
2253
  successPageDuration = 2000,
1727
2254
  subTitle,
@@ -1786,8 +2313,8 @@ const ClientProvider = ({
1786
2313
  }
1787
2314
  throw new sdkTypes.TurnkeyError("Failed to update user name.", sdkTypes.TurnkeyErrorCodes.UPDATE_USER_NAME_ERROR, error);
1788
2315
  }
1789
- };
1790
- const handleUpdateUserPhoneNumber = async params => {
2316
+ }, [pushPage]);
2317
+ const handleUpdateUserPhoneNumber = react.useCallback(async params => {
1791
2318
  const {
1792
2319
  successPageDuration = 2000,
1793
2320
  subTitle,
@@ -1890,8 +2417,8 @@ const ClientProvider = ({
1890
2417
  }
1891
2418
  throw new sdkTypes.TurnkeyError("Failed to initialize OTP for sms verification.", sdkTypes.TurnkeyErrorCodes.INIT_OTP_ERROR, error);
1892
2419
  }
1893
- };
1894
- const handleUpdateUserEmail = async params => {
2420
+ }, [pushPage]);
2421
+ const handleUpdateUserEmail = react.useCallback(async params => {
1895
2422
  const {
1896
2423
  successPageDuration = 2000,
1897
2424
  subTitle,
@@ -1987,8 +2514,8 @@ const ClientProvider = ({
1987
2514
  }
1988
2515
  throw new sdkTypes.TurnkeyError("Failed to initialize OTP for email verification.", sdkTypes.TurnkeyErrorCodes.INIT_OTP_ERROR, error);
1989
2516
  }
1990
- };
1991
- const handleAddEmail = async params => {
2517
+ }, [pushPage]);
2518
+ const handleAddEmail = react.useCallback(async params => {
1992
2519
  const {
1993
2520
  successPageDuration = 2000,
1994
2521
  subTitle,
@@ -2084,8 +2611,8 @@ const ClientProvider = ({
2084
2611
  }
2085
2612
  throw new sdkTypes.TurnkeyError("Failed to initialize OTP for email verification.", sdkTypes.TurnkeyErrorCodes.INIT_OTP_ERROR, error);
2086
2613
  }
2087
- };
2088
- const handleAddPhoneNumber = async params => {
2614
+ }, [pushPage]);
2615
+ const handleAddPhoneNumber = react.useCallback(async params => {
2089
2616
  const {
2090
2617
  successPageDuration = 2000,
2091
2618
  subTitle,
@@ -2192,8 +2719,8 @@ const ClientProvider = ({
2192
2719
  }
2193
2720
  throw new sdkTypes.TurnkeyError("Failed to initialize OTP for sms verification.", sdkTypes.TurnkeyErrorCodes.INIT_OTP_ERROR, error);
2194
2721
  }
2195
- };
2196
- const handleRemovePasskey = async params => {
2722
+ }, [pushPage]);
2723
+ const handleRemovePasskey = react.useCallback(async params => {
2197
2724
  const {
2198
2725
  authenticatorId,
2199
2726
  successPageDuration = 2000,
@@ -2233,8 +2760,8 @@ const ClientProvider = ({
2233
2760
  preventBack: true
2234
2761
  });
2235
2762
  }), callbacks, "Failed to remove passkey");
2236
- };
2237
- const handleAddPasskey = async params => {
2763
+ }, [pushPage]);
2764
+ const handleAddPasskey = react.useCallback(async params => {
2238
2765
  const {
2239
2766
  name,
2240
2767
  displayName,
@@ -2278,8 +2805,8 @@ const ClientProvider = ({
2278
2805
  }
2279
2806
  throw new sdkTypes.TurnkeyError("Failed to add passkey in handler.", sdkTypes.TurnkeyErrorCodes.ADD_PASSKEY_ERROR, error);
2280
2807
  }
2281
- };
2282
- const handleRemoveOauthProvider = async params => {
2808
+ }, [pushPage]);
2809
+ const handleRemoveOauthProvider = react.useCallback(async params => {
2283
2810
  const {
2284
2811
  providerId,
2285
2812
  successPageDuration = 2000,
@@ -2322,8 +2849,8 @@ const ClientProvider = ({
2322
2849
  }
2323
2850
  throw new sdkTypes.TurnkeyError("Failed to remove OAuth provider in handler.", sdkTypes.TurnkeyErrorCodes.REMOVE_OAUTH_PROVIDER_ERROR, error);
2324
2851
  }
2325
- };
2326
- const handleAddOauthProvider = async params => {
2852
+ }, [pushPage]);
2853
+ const handleAddOauthProvider = react.useCallback(async params => {
2327
2854
  if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
2328
2855
  if (!session) {
2329
2856
  throw new sdkTypes.TurnkeyError("No active session found.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
@@ -2352,6 +2879,22 @@ const ClientProvider = ({
2352
2879
  });
2353
2880
  };
2354
2881
  switch (providerName) {
2882
+ case sdkTypes.OAuthProviders.DISCORD:
2883
+ {
2884
+ await handleDiscordOauth({
2885
+ openInPage: false,
2886
+ onOauthSuccess
2887
+ });
2888
+ break;
2889
+ }
2890
+ case sdkTypes.OAuthProviders.X:
2891
+ {
2892
+ await handleXOauth({
2893
+ openInPage: false,
2894
+ onOauthSuccess
2895
+ });
2896
+ break;
2897
+ }
2355
2898
  case sdkTypes.OAuthProviders.GOOGLE:
2356
2899
  {
2357
2900
  await handleGoogleOauth({
@@ -2381,8 +2924,8 @@ const ClientProvider = ({
2381
2924
  throw new sdkTypes.TurnkeyError(`Unsupported OAuth provider: ${providerName}`, sdkTypes.TurnkeyErrorCodes.NOT_FOUND);
2382
2925
  }
2383
2926
  }
2384
- };
2385
- const handleLinkExternalWallet = async params => {
2927
+ }, [pushPage]);
2928
+ const handleConnectExternalWallet = react.useCallback(async params => {
2386
2929
  const {
2387
2930
  successPageDuration = 2000
2388
2931
  } = params || {};
@@ -2395,14 +2938,14 @@ const ClientProvider = ({
2395
2938
  }
2396
2939
  const providers = await getWalletProviders();
2397
2940
  pushPage({
2398
- key: "Link Wallet",
2399
- content: jsxRuntime.jsx(LinkWallet.LinkWalletModal, {
2941
+ key: "Connect wallet",
2942
+ content: jsxRuntime.jsx(ConnectWallet.ConnectWalletModal, {
2400
2943
  providers: providers,
2401
2944
  successPageDuration: successPageDuration
2402
2945
  })
2403
2946
  });
2404
- };
2405
- const handleRemoveUserEmail = async params => {
2947
+ }, [pushPage]);
2948
+ const handleRemoveUserEmail = react.useCallback(async params => {
2406
2949
  const {
2407
2950
  successPageDuration = 2000,
2408
2951
  stampWith,
@@ -2440,8 +2983,8 @@ const ClientProvider = ({
2440
2983
  }
2441
2984
  throw new sdkTypes.TurnkeyError("Failed to remove user email.", sdkTypes.TurnkeyErrorCodes.UPDATE_USER_EMAIL_ERROR, error);
2442
2985
  }
2443
- };
2444
- const handleRemoveUserPhoneNumber = async params => {
2986
+ }, [pushPage]);
2987
+ const handleRemoveUserPhoneNumber = react.useCallback(async params => {
2445
2988
  const {
2446
2989
  successPageDuration = 2000,
2447
2990
  stampWith,
@@ -2479,7 +3022,91 @@ const ClientProvider = ({
2479
3022
  }
2480
3023
  throw new sdkTypes.TurnkeyError("Failed to remove user phone number.", sdkTypes.TurnkeyErrorCodes.UPDATE_USER_PHONE_NUMBER_ERROR, error);
2481
3024
  }
2482
- };
3025
+ }, [pushPage]);
3026
+ react.useEffect(() => {
3027
+ if (proxyAuthConfigRef.current) return;
3028
+ // Only fetch the proxy auth config once. Use that to build the master config.
3029
+ const fetchProxyAuthConfig = async () => {
3030
+ try {
3031
+ let proxyAuthConfig;
3032
+ if (config.authProxyConfigId) {
3033
+ // Only fetch the proxy auth config if we have an authProxyId. This is a way for devs to explicitly disable the proxy auth.
3034
+ proxyAuthConfig = await core.getAuthProxyConfig(config.authProxyConfigId, config.authProxyUrl);
3035
+ proxyAuthConfigRef.current = proxyAuthConfig;
3036
+ }
3037
+ setMasterConfig(buildConfig(proxyAuthConfig));
3038
+ } catch {
3039
+ setClientState(base.ClientState.Error);
3040
+ }
3041
+ };
3042
+ fetchProxyAuthConfig();
3043
+ }, []);
3044
+ react.useEffect(() => {
3045
+ // Start the client initialization process once we have the master config.
3046
+ if (!masterConfig) return;
3047
+ initializeClient();
3048
+ }, [masterConfig]);
3049
+ react.useEffect(() => {
3050
+ // Handle changes to the passed in config prop -- update the master config
3051
+ // If the proxyAuthConfigRef is already set, we don't need to fetch it again. Rebuild the master config with the updated config and stored proxyAuthConfig
3052
+ if (!proxyAuthConfigRef.current && config.authProxyConfigId) return;
3053
+ setMasterConfig(buildConfig(proxyAuthConfigRef.current ?? undefined));
3054
+ }, [config, proxyAuthConfigRef.current]);
3055
+ /**
3056
+ * @internal
3057
+ * We create `debouncedRefreshWallets()` so that multiple rapid wallet events
3058
+ * (for example, on Solana a single disconnect can emit several events we listen for)
3059
+ * only trigger `refreshWallets()` once.
3060
+ *
3061
+ * Defining the debounced function outside of the `useEffect` ensures all event
3062
+ * listeners in `initializeProviders` share the same instance, instead of creating
3063
+ * a new one on every render.
3064
+ */
3065
+ const debouncedRefreshWallets = utils.useDebouncedCallback(refreshWallets, 100);
3066
+ react.useEffect(() => {
3067
+ if (!client) return;
3068
+ const handleRefreshWallets = async () => {
3069
+ // we only refresh the wallets if there is an active session
3070
+ // this is needed because a disconnect event can occur
3071
+ // while the user is unauthenticated
3072
+ if (session) {
3073
+ // this will update our walletProvider state
3074
+ await debouncedRefreshWallets();
3075
+ }
3076
+ };
3077
+ let cleanup = () => {};
3078
+ initializeWalletProviderListeners(walletProviders, handleRefreshWallets).then(fn => {
3079
+ cleanup = fn;
3080
+ }).catch(err => {
3081
+ console.error("Failed to init providers:", err);
3082
+ });
3083
+ return () => {
3084
+ cleanup();
3085
+ };
3086
+ }, [client, walletProviders]);
3087
+ react.useEffect(() => {
3088
+ // authState must be consistent with session state. We found during testing that there are cases where the session and authState can be out of sync in very rare edge cases.
3089
+ // This will ensure that they are always in sync and remove the need to setAuthState manually in other places.
3090
+ if (session && utils.isValidSession(session)) {
3091
+ setAuthState(base.AuthState.Authenticated);
3092
+ } else {
3093
+ setAuthState(base.AuthState.Unauthenticated);
3094
+ }
3095
+ }, [session]);
3096
+ react.useEffect(() => {
3097
+ // This will handle any redirect based oAuth. It then initializes the session. This is the last step before client is considered "ready"
3098
+ if (!client || !masterConfig) return;
3099
+ completeRedirectOauth().finally(() => {
3100
+ clearSessionTimeouts();
3101
+ initializeSessions().finally(() => {
3102
+ // Set the client state to ready only after all initializations are done.
3103
+ setClientState(base.ClientState.Ready);
3104
+ });
3105
+ });
3106
+ return () => {
3107
+ clearSessionTimeouts();
3108
+ };
3109
+ }, [client]);
2483
3110
  return jsxRuntime.jsx(Types.ClientContext.Provider, {
2484
3111
  value: {
2485
3112
  session,
@@ -2497,7 +3124,7 @@ const ClientProvider = ({
2497
3124
  getWalletProviders,
2498
3125
  connectWalletAccount,
2499
3126
  disconnectWalletAccount,
2500
- switchWalletProviderChain,
3127
+ switchWalletAccountChain,
2501
3128
  loginWithWallet,
2502
3129
  signUpWithWallet,
2503
3130
  loginOrSignupWithWallet,
@@ -2511,6 +3138,7 @@ const ClientProvider = ({
2511
3138
  completeOauth,
2512
3139
  fetchWallets,
2513
3140
  fetchWalletAccounts,
3141
+ fetchPrivateKeys,
2514
3142
  refreshWallets,
2515
3143
  signMessage,
2516
3144
  signTransaction,
@@ -2547,12 +3175,15 @@ const ClientProvider = ({
2547
3175
  getProxyAuthConfig,
2548
3176
  handleLogin,
2549
3177
  handleGoogleOauth,
3178
+ handleXOauth,
3179
+ handleDiscordOauth,
2550
3180
  handleAppleOauth,
2551
3181
  handleFacebookOauth,
2552
3182
  handleExportWallet,
2553
3183
  handleExportPrivateKey,
2554
3184
  handleExportWalletAccount,
2555
3185
  handleImportWallet,
3186
+ handleImportPrivateKey,
2556
3187
  handleUpdateUserEmail,
2557
3188
  handleUpdateUserPhoneNumber,
2558
3189
  handleUpdateUserName,
@@ -2563,7 +3194,7 @@ const ClientProvider = ({
2563
3194
  handleAddEmail,
2564
3195
  handleAddPhoneNumber,
2565
3196
  handleSignMessage,
2566
- handleLinkExternalWallet,
3197
+ handleConnectExternalWallet,
2567
3198
  handleRemoveUserEmail,
2568
3199
  handleRemoveUserPhoneNumber
2569
3200
  },