@web3auth/no-modal 11.0.0-beta.1 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/lib.cjs/account-linking/errors.js +111 -0
  2. package/dist/lib.cjs/account-linking/index.js +4 -0
  3. package/dist/lib.cjs/account-linking/rest.js +6 -6
  4. package/dist/lib.cjs/account-linking/vue.js +0 -1
  5. package/dist/lib.cjs/base/connector/constants.js +2 -0
  6. package/dist/lib.cjs/base/errors/index.js +21 -50
  7. package/dist/lib.cjs/base/utils.js +1 -1
  8. package/dist/lib.cjs/connectors/auth-connector/authConnector.js +125 -58
  9. package/dist/lib.cjs/connectors/metamask-connector/metamaskConnector.js +40 -31
  10. package/dist/lib.cjs/index.js +24 -19
  11. package/dist/lib.cjs/noModal.js +60 -26
  12. package/dist/lib.cjs/providers/account-abstraction-provider/providers/AccountAbstractionProvider.js +8 -4
  13. package/dist/lib.cjs/providers/account-abstraction-provider/providers/utils.js +0 -17
  14. package/dist/lib.cjs/providers/account-abstraction-provider/rpc/ethRpcMiddlewares.js +15 -0
  15. package/dist/lib.cjs/react/context/useWeb3AuthInnerContextValue.js +18 -2
  16. package/dist/lib.cjs/react/solana/provider.js +6 -2
  17. package/dist/lib.cjs/react/wagmi/index.js +6 -0
  18. package/dist/lib.cjs/react/wagmi/provider.js +74 -23
  19. package/dist/lib.cjs/types/account-linking/errors.d.ts +17 -0
  20. package/dist/lib.cjs/types/account-linking/index.d.ts +1 -0
  21. package/dist/lib.cjs/types/base/connector/constants.d.ts +1 -0
  22. package/dist/lib.cjs/types/base/connector/interfaces.d.ts +1 -1
  23. package/dist/lib.cjs/types/base/errors/index.d.ts +2 -13
  24. package/dist/lib.cjs/types/connectors/auth-connector/authConnector.d.ts +8 -2
  25. package/dist/lib.cjs/types/connectors/auth-connector/interface.d.ts +1 -1
  26. package/dist/lib.cjs/types/noModal.d.ts +1 -1
  27. package/dist/lib.cjs/types/providers/account-abstraction-provider/rpc/ethRpcMiddlewares.d.ts +1 -0
  28. package/dist/lib.cjs/types/react/wagmi/constants.d.ts +2 -0
  29. package/dist/lib.cjs/types/react/wagmi/provider.d.ts +7 -0
  30. package/dist/lib.cjs/types/vue/wagmi/constants.d.ts +2 -0
  31. package/dist/lib.cjs/types/vue/wagmi/provider.d.ts +7 -1
  32. package/dist/lib.cjs/vue/solana/provider.js +50 -26
  33. package/dist/lib.cjs/vue/useWeb3AuthInnerContextValue.js +13 -6
  34. package/dist/lib.cjs/vue/wagmi/index.js +6 -0
  35. package/dist/lib.cjs/vue/wagmi/provider.js +78 -35
  36. package/dist/lib.esm/account-linking/errors.js +92 -0
  37. package/dist/lib.esm/account-linking/index.js +1 -0
  38. package/dist/lib.esm/account-linking/rest.js +3 -3
  39. package/dist/lib.esm/account-linking/vue.js +0 -1
  40. package/dist/lib.esm/base/connector/constants.js +2 -1
  41. package/dist/lib.esm/base/errors/index.js +21 -50
  42. package/dist/lib.esm/base/utils.js +1 -1
  43. package/dist/lib.esm/connectors/auth-connector/authConnector.js +109 -41
  44. package/dist/lib.esm/connectors/base-solana-connector/baseSolanaConnector.js +1 -1
  45. package/dist/lib.esm/connectors/metamask-connector/metamaskConnector.js +42 -33
  46. package/dist/lib.esm/index.js +3 -2
  47. package/dist/lib.esm/noModal.js +56 -20
  48. package/dist/lib.esm/providers/account-abstraction-provider/providers/AccountAbstractionProvider.js +10 -5
  49. package/dist/lib.esm/providers/account-abstraction-provider/providers/utils.js +0 -3
  50. package/dist/lib.esm/providers/account-abstraction-provider/rpc/ethRpcMiddlewares.js +17 -3
  51. package/dist/lib.esm/react/context/useWeb3AuthInnerContextValue.js +18 -2
  52. package/dist/lib.esm/react/solana/provider.js +6 -2
  53. package/dist/lib.esm/react/wagmi/index.js +1 -1
  54. package/dist/lib.esm/react/wagmi/provider.js +74 -25
  55. package/dist/lib.esm/vue/solana/provider.js +51 -28
  56. package/dist/lib.esm/vue/useWeb3AuthInnerContextValue.js +11 -4
  57. package/dist/lib.esm/vue/wagmi/index.js +1 -1
  58. package/dist/lib.esm/vue/wagmi/provider.js +73 -34
  59. package/package.json +20 -20
@@ -3,11 +3,11 @@ import _objectSpread from '@babel/runtime/helpers/objectSpread2';
3
3
  import _defineProperty from '@babel/runtime/helpers/defineProperty';
4
4
  import { CHAIN_NAMESPACES, BUTTON_POSITION, CONFIRMATION_STRATEGY } from '@toruslabs/base-controllers';
5
5
  import { SMART_ACCOUNT_EIP_STANDARD, EIP7702_SUPPORTED_SMART_ACCOUNT_TYPES } from '@toruslabs/ethereum-controllers';
6
- import { SafeEventEmitter, serializeError, UX_MODE, cloneDeep, CookieStorage, LocalStorageAdapter, MemoryStorage } from '@web3auth/auth';
6
+ import { SafeEventEmitter, BUILD_ENV, serializeError, UX_MODE, cloneDeep, CookieStorage, LocalStorageAdapter, MemoryStorage } from '@web3auth/auth';
7
7
  import deepmerge from 'deepmerge';
8
8
  import { deserialize } from './base/deserialize.js';
9
- import { WalletInitializationError, WalletLoginError, AccountLinkingError } from './base/errors/index.js';
10
9
  import { LOGIN_MODE, SMART_ACCOUNT_WALLET_SCOPE, WEB3AUTH_STATE_STORAGE_KEY } from './base/constants.js';
10
+ import { WalletInitializationError, WalletLoginError } from './base/errors/index.js';
11
11
  import { log } from './base/loglevel.js';
12
12
  import { CONNECTOR_STATUS, CONNECTOR_INITIAL_AUTHENTICATION_MODE, CONNECTOR_EVENTS } from './base/connector/constants.js';
13
13
  import { Analytics, ANALYTICS_INTEGRATION_TYPE, ANALYTICS_SDK_TYPE, ANALYTICS_EVENTS } from './base/analytics.js';
@@ -16,6 +16,7 @@ import { WALLET_CONNECTORS } from './base/wallet/index.js';
16
16
  import { CONNECTOR_NAMESPACES } from './base/chain/IChainInterface.js';
17
17
  import { CONNECTED_STATUSES, CAN_LOGOUT_STATUSES, CAN_AUTHORIZE_STATUSES } from './base/connector/connectorStatus.js';
18
18
  import { assertAuthConnector, authConnector, isAuthConnector } from './connectors/auth-connector/authConnector.js';
19
+ import { AccountLinkingError } from './account-linking/errors.js';
19
20
  import { CommonJRPCProvider } from './providers/base-provider/CommonJRPCProvider.js';
20
21
  import { walletServicesPlugin } from './plugins/wallet-services-plugin/plugin.js';
21
22
  import { metaMaskConnector } from './connectors/metamask-connector/metamaskConnector.js';
@@ -54,7 +55,9 @@ class Web3AuthNoModal extends SafeEventEmitter {
54
55
  if (!options.clientId) throw WalletInitializationError.invalidParams("Please provide a valid clientId in constructor");
55
56
  if (options.enableLogging) log.enableAll();else log.setLevel("error");
56
57
  if (!options.initialAuthenticationMode) options.initialAuthenticationMode = CONNECTOR_INITIAL_AUTHENTICATION_MODE.CONNECT_AND_SIGN;
57
- this.coreOptions = options;
58
+ this.coreOptions = _objectSpread(_objectSpread({}, options), {}, {
59
+ authBuildEnv: options.authBuildEnv || BUILD_ENV.PRODUCTION
60
+ });
58
61
  this.storage = this.getStorageMethod();
59
62
  this.analytics = new Analytics();
60
63
  if (options.disableAnalytics) {
@@ -925,7 +928,6 @@ class Web3AuthNoModal extends SafeEventEmitter {
925
928
  }));
926
929
  this.setActiveWalletConnectorKey();
927
930
  this.connectionReconnected = data.reconnected;
928
- const connectedChainId = ethereumProvider === null || ethereumProvider === void 0 ? void 0 : ethereumProvider.chainId;
929
931
 
930
932
  // when ssr is enabled, we need to get the idToken from the connector.
931
933
  if (this.coreOptions.ssr) {
@@ -950,14 +952,16 @@ class Web3AuthNoModal extends SafeEventEmitter {
950
952
  // The following block only hits during rehydration
951
953
 
952
954
  const {
953
- activeAccount
955
+ activeAccount,
956
+ currentChainId
954
957
  } = this.state;
955
- // if the active account is not the primary account, i.e. not `null`, create an isolated connector and connect to the chain
958
+ let rehydrateWithLinkedAccount = false;
959
+ // for rehydration, if the active account is not the primary account, i.e. not `null`, create an isolated connector and connect to the chain
956
960
  if (activeAccount && !activeAccount.isPrimary && activeAccount.connector !== WALLET_CONNECTORS.AUTH) {
957
961
  var _ref3, _walletConnector$prov, _linkedAccountConnect, _ref4, _walletConnector$sola, _linkedAccountConnect2;
958
962
  const accountLinkingConnector = isAuthConnector(connector) ? connector : this.getConnector(WALLET_CONNECTORS.AUTH);
959
963
  assertAuthConnector(accountLinkingConnector, "Account switching requires the AUTH connector to be available.");
960
- const targetChainId = accountLinkingConnector.getChainIdForLinkedAccount(activeAccount, connectedChainId);
964
+ const targetChainId = accountLinkingConnector.getChainIdForLinkedAccount(activeAccount, currentChainId);
961
965
  const walletConnector = await this.createIsolatedWalletConnector(activeAccount.connector, targetChainId);
962
966
  let linkedAccountConnection = null;
963
967
  if (!this.hasUsableConnectedSwitchConnector(walletConnector)) {
@@ -977,6 +981,7 @@ class Web3AuthNoModal extends SafeEventEmitter {
977
981
  });
978
982
  this.setConnectedWalletConnectorState(connectedWalletState, activeAccount);
979
983
  this.setActiveWalletConnectorKey(activeAccount);
984
+ rehydrateWithLinkedAccount = true;
980
985
  }
981
986
  if (ethereumProvider) {
982
987
  await this.bindPrimaryEthereumSigningProxy(ethereumProvider, data.connectorName);
@@ -989,24 +994,44 @@ class Web3AuthNoModal extends SafeEventEmitter {
989
994
  });
990
995
  this.setConnectedWalletConnectorState(primaryConnectedWalletState);
991
996
  await this.setState({
992
- primaryConnectorName: data.connectorName,
993
- currentChainId: connectedChainId
997
+ primaryConnectorName: data.connectorName
994
998
  });
995
999
  this.cacheWallet(data.connectorName);
996
1000
  const isConnectAndSign = this.coreOptions.initialAuthenticationMode === CONNECTOR_INITIAL_AUTHENTICATION_MODE.CONNECT_AND_SIGN;
997
- if (this.consentRequired && !isConnectAndSign && !this.state.hasUserConsent) {
1001
+ const pendingUserConsent = this.consentRequired && !this.state.hasUserConsent;
1002
+ if (pendingUserConsent && !isConnectAndSign) {
998
1003
  this.status = CONNECTOR_STATUS.CONSENT_REQUIRING;
999
1004
  this.emit(CONNECTOR_EVENTS.CONSENT_REQUIRING);
1000
1005
  log.debug("consent_requiring", this.status, this.primaryConnectorName);
1001
1006
  } else {
1002
- this.status = CONNECTOR_STATUS.CONNECTED;
1007
+ // In CONNECT_AND_SIGN mode the AUTHORIZED handler can run before this point (e.g. when `ssr=true`
1008
+ // this handler `await`s `connector.getAuthTokenInfo()` which fires AUTHORIZED mid-execution).
1009
+ // Don't downgrade an already-advanced status (CONSENT_REQUIRING or AUTHORIZED) back to CONNECTED;
1010
+ // otherwise `acceptConsent` would throw "Cannot accept consent: not in consent_requiring state".
1011
+ if (this.status !== CONNECTOR_STATUS.CONSENT_REQUIRING && this.status !== CONNECTOR_STATUS.AUTHORIZED) {
1012
+ this.status = CONNECTOR_STATUS.CONNECTED;
1013
+ }
1003
1014
  log.debug("connected", this.status, this.primaryConnectorName);
1004
- this.connectToPlugins(_objectSpread(_objectSpread({}, data), {}, {
1005
- connector: data.connectorName
1006
- }));
1015
+ // Defer plugin connection until consent is accepted; otherwise plugins would start before the consent step completes.
1016
+ // `completeConsentAcceptance` connects the plugins once the user accepts the consent.
1017
+ if (!pendingUserConsent) {
1018
+ this.connectToPlugins(_objectSpread(_objectSpread({}, data), {}, {
1019
+ connector: data.connectorName
1020
+ }));
1021
+ }
1022
+
1023
+ // `pendingUserConsent` signals listeners (LoginModal, React/Vue contexts) to skip processing this CONNECTED event,
1024
+ // so the upcoming AUTHORIZED -> CONSENT_REQUIRING transition is not overridden by a late CONNECTED handler in CONNECT_AND_SIGN mode.
1007
1025
  this.emit(CONNECTOR_EVENTS.CONNECTED, _objectSpread(_objectSpread({}, data), {}, {
1008
- loginMode: this.loginMode
1026
+ loginMode: this.loginMode,
1027
+ pendingUserConsent
1009
1028
  }));
1029
+
1030
+ // if we're rehydrating with a linked account, we need to emit a CONNECTION_UPDATED event
1031
+ // so that upstream listeners and context are updated with the linked connection.
1032
+ if (rehydrateWithLinkedAccount) {
1033
+ this.emit(CONNECTOR_EVENTS.CONNECTION_UPDATED, this.connection);
1034
+ }
1010
1035
  }
1011
1036
  });
1012
1037
  connector.on(CONNECTOR_EVENTS.DISCONNECTED, async data => {
@@ -1226,7 +1251,15 @@ class Web3AuthNoModal extends SafeEventEmitter {
1226
1251
  return activeChainId;
1227
1252
  }
1228
1253
  async createLinkingWalletConnector(connectorName, chainId, config) {
1229
- return this.createIsolatedWalletConnector(connectorName, chainId, config);
1254
+ try {
1255
+ const linkingConnector = await this.createIsolatedWalletConnector(connectorName, chainId, config);
1256
+ return linkingConnector;
1257
+ } catch (error) {
1258
+ if (error instanceof AccountLinkingError && error.code === 5405) {
1259
+ throw error;
1260
+ }
1261
+ throw AccountLinkingError.walletProofFailed(error instanceof Error ? error.message : String(error), error);
1262
+ }
1230
1263
  }
1231
1264
  async createSwitchingWalletConnector(connectorName, chainId, config) {
1232
1265
  return this.createIsolatedWalletConnector(connectorName, chainId, config);
@@ -1381,12 +1414,12 @@ class Web3AuthNoModal extends SafeEventEmitter {
1381
1414
  account
1382
1415
  });
1383
1416
  }
1384
- async linkAccountWithConnector(connectorName, chainId, walletConnector) {
1417
+ async linkAccountWithConnector(connectorName, chainId, connectorToLink) {
1385
1418
  const authConnector = this.getMainAuthConnector();
1386
1419
  const result = await authConnector.linkAccount({
1387
1420
  connectorName,
1388
1421
  chainId,
1389
- walletConnector,
1422
+ connectorToLink,
1390
1423
  authSessionTokens: {
1391
1424
  accessToken: this.accessToken,
1392
1425
  idToken: this.idToken
@@ -1395,7 +1428,7 @@ class Web3AuthNoModal extends SafeEventEmitter {
1395
1428
  await this.setState({
1396
1429
  idToken: result.idToken
1397
1430
  });
1398
- await this.cacheConnectedLinkedWalletConnector(authConnector, walletConnector);
1431
+ await this.cacheConnectedLinkedWalletConnector(authConnector, connectorToLink);
1399
1432
  return result;
1400
1433
  }
1401
1434
  getMainAuthConnector() {
@@ -1575,7 +1608,10 @@ class Web3AuthNoModal extends SafeEventEmitter {
1575
1608
  });
1576
1609
  }
1577
1610
  async setCurrentChain(chainId) {
1578
- if (chainId === this.currentChainId) return;
1611
+ const {
1612
+ currentChainId
1613
+ } = this.state;
1614
+ if (chainId === currentChainId) return;
1579
1615
  const newChain = this.coreOptions.chains.find(chain => chain.chainId === chainId);
1580
1616
  if (!newChain) throw WalletInitializationError.invalidParams(`Invalid chainId: ${chainId}`);
1581
1617
  await this.setState({
@@ -5,7 +5,7 @@ export { SMART_ACCOUNT } from '@toruslabs/ethereum-controllers';
5
5
  import { JRPCEngineV2, providerFromEngineV2, providerErrors } from '@web3auth/auth';
6
6
  import { defineChain, createPublicClient, http, createWalletClient, custom } from 'viem';
7
7
  import { createPaymasterClient, createBundlerClient } from 'viem/account-abstraction';
8
- import { createEoaMiddleware, providerAsMiddleware, createAaMiddleware } from '../rpc/ethRpcMiddlewares.js';
8
+ import { createEoaMiddleware, providerAsMiddleware, createAaMiddleware, createEip7702And5792MiddlewareForAaProvider } from '../rpc/ethRpcMiddlewares.js';
9
9
  import { getProviderHandlers } from './utils.js';
10
10
  import { BaseProvider } from '../../base-provider/baseProvider.js';
11
11
  import { CHAIN_NAMESPACES } from '@toruslabs/base-controllers';
@@ -55,9 +55,10 @@ class AccountAbstractionProvider extends BaseProvider {
55
55
  }
56
56
  async setupProvider(eoaProvider) {
57
57
  var _bundlerConfig$transp;
58
- const {
59
- currentChain
60
- } = this;
58
+ const currentChain = this.currentChain;
59
+ if (!currentChain) {
60
+ throw WalletInitializationError.invalidProviderConfigError(`AA chain config not found for chain ${this.chainId}`);
61
+ }
61
62
  const {
62
63
  chainNamespace
63
64
  } = currentChain;
@@ -134,9 +135,13 @@ class AccountAbstractionProvider extends BaseProvider {
134
135
  eoaProvider,
135
136
  handlers: providerHandlers
136
137
  });
138
+
139
+ // middleware to handle EIP-7702 and EIP-5792 methods,
140
+ // currently, we do not support EIP-7702 and EIP-5792 methods for account abstraction provider
141
+ const eip7702And5792Middleware = await createEip7702And5792MiddlewareForAaProvider();
137
142
  const eoaMiddleware = providerAsMiddleware(eoaProvider);
138
143
  const engine = JRPCEngineV2.create({
139
- middleware: [aaMiddleware, eoaMiddleware]
144
+ middleware: [aaMiddleware, eip7702And5792Middleware, eoaMiddleware]
140
145
  });
141
146
  const provider = providerFromEngineV2(engine);
142
147
  this.updateProviderEngineProxy(provider);
@@ -2,7 +2,6 @@ import _objectSpread from '@babel/runtime/helpers/objectSpread2';
2
2
  import { isHexString, add0x } from '@toruslabs/metadata-helpers';
3
3
  import { providerErrors } from '@web3auth/auth';
4
4
  import { createWalletClient, http } from 'viem';
5
- import { log } from '../../../base/loglevel.js';
6
5
 
7
6
  function getProviderHandlers({
8
7
  bundlerClient,
@@ -20,8 +19,6 @@ function getProviderHandlers({
20
19
  const [smartAccounts, eoaAccounts] = await Promise.all([smartAccount.getAddress(), eoaProvider.request({
21
20
  method: "eth_accounts"
22
21
  })]);
23
- log.info("smartAccounts", smartAccounts);
24
- log.info("eoaAccounts", eoaAccounts);
25
22
  return [smartAccounts, ...eoaAccounts];
26
23
  },
27
24
  getPrivateKey: async _ => {
@@ -1,6 +1,6 @@
1
1
  import _objectSpread from '@babel/runtime/helpers/objectSpread2';
2
- import { METHOD_TYPES } from '@toruslabs/ethereum-controllers';
3
- import { createScaffoldMiddlewareV2, rpcErrors } from '@web3auth/auth';
2
+ import { METHOD_TYPES, EIP_5792_METHODS, EIP_7702_METHODS } from '@toruslabs/ethereum-controllers';
3
+ import { createScaffoldMiddlewareV2, providerErrors, rpcErrors } from '@web3auth/auth';
4
4
 
5
5
  async function createAaMiddleware({
6
6
  eoaProvider,
@@ -181,6 +181,20 @@ async function createEoaMiddleware({
181
181
  eth_requestAccounts: requestAccounts
182
182
  });
183
183
  }
184
+ async function createEip7702And5792MiddlewareForAaProvider() {
185
+ const eip5792Methods = Object.values(EIP_5792_METHODS);
186
+ const eip7702Methods = Object.values(EIP_7702_METHODS);
187
+ const eip7702And5792Methods = [...eip5792Methods, ...eip7702Methods];
188
+ return async ({
189
+ request,
190
+ next
191
+ }) => {
192
+ if (eip7702And5792Methods.includes(request.method)) {
193
+ throw providerErrors.unsupportedMethod(`${request.method} is not supported for account abstraction provider`);
194
+ }
195
+ return next(request);
196
+ };
197
+ }
184
198
  function providerAsMiddleware(provider) {
185
199
  return async ({
186
200
  request
@@ -192,4 +206,4 @@ function providerAsMiddleware(provider) {
192
206
  };
193
207
  }
194
208
 
195
- export { createAaMiddleware, createEoaMiddleware, providerAsMiddleware };
209
+ export { createAaMiddleware, createEip7702And5792MiddlewareForAaProvider, createEoaMiddleware, providerAsMiddleware };
@@ -127,19 +127,26 @@ function useWeb3AuthInnerContextValue({
127
127
  const authorizedListener = () => {
128
128
  setStatus(web3Auth.status);
129
129
  if (web3Auth.status === CONNECTOR_STATUS.AUTHORIZED) {
130
+ var _web3Auth$currentChai7, _web3Auth$currentChai8;
131
+ setIsInitialized(true);
130
132
  setIsConnected(true);
133
+ // on rehydration, `AUTHORIZED` event can be fired first in `CONNECT_AND_SIGN` mode, before `CONNECTED` event.
134
+ // Update the connection state here, so that clients can use the connection state immediately.
135
+ setConnection(web3Auth.connection);
136
+ setChainId(web3Auth.currentChainId);
137
+ setChainNamespace((_web3Auth$currentChai7 = (_web3Auth$currentChai8 = web3Auth.currentChain) === null || _web3Auth$currentChai8 === void 0 ? void 0 : _web3Auth$currentChai8.chainNamespace) !== null && _web3Auth$currentChai7 !== void 0 ? _web3Auth$currentChai7 : null);
131
138
  setIsAuthorized(true);
132
139
  }
133
140
  };
134
141
  const consentAcceptedListener = () => {
135
142
  setStatus(web3Auth.status);
136
143
  if (web3Auth.status === CONNECTOR_STATUS.CONNECTED || web3Auth.status === CONNECTOR_STATUS.AUTHORIZED) {
137
- var _web3Auth$currentChai7, _web3Auth$currentChai8;
144
+ var _web3Auth$currentChai9, _web3Auth$currentChai0;
138
145
  setIsInitialized(true);
139
146
  setIsConnected(true);
140
147
  setConnection(web3Auth.connection);
141
148
  setChainId(web3Auth.currentChainId);
142
- setChainNamespace((_web3Auth$currentChai7 = (_web3Auth$currentChai8 = web3Auth.currentChain) === null || _web3Auth$currentChai8 === void 0 ? void 0 : _web3Auth$currentChai8.chainNamespace) !== null && _web3Auth$currentChai7 !== void 0 ? _web3Auth$currentChai7 : null);
149
+ setChainNamespace((_web3Auth$currentChai9 = (_web3Auth$currentChai0 = web3Auth.currentChain) === null || _web3Auth$currentChai0 === void 0 ? void 0 : _web3Auth$currentChai0.chainNamespace) !== null && _web3Auth$currentChai9 !== void 0 ? _web3Auth$currentChai9 : null);
143
150
  if (web3Auth.status === CONNECTOR_STATUS.AUTHORIZED) {
144
151
  setIsAuthorized(true);
145
152
  }
@@ -148,6 +155,13 @@ function useWeb3AuthInnerContextValue({
148
155
  const mfaEnabledListener = nextIsMFAEnabled => {
149
156
  if (typeof nextIsMFAEnabled === "boolean") setIsMFAEnabled(nextIsMFAEnabled);
150
157
  };
158
+ const connectionUpdatedListener = () => {
159
+ var _web3Auth$currentChai1, _web3Auth$currentChai10;
160
+ setStatus(web3Auth.status);
161
+ setConnection(web3Auth.connection);
162
+ setChainId(web3Auth.currentChainId);
163
+ setChainNamespace((_web3Auth$currentChai1 = (_web3Auth$currentChai10 = web3Auth.currentChain) === null || _web3Auth$currentChai10 === void 0 ? void 0 : _web3Auth$currentChai10.chainNamespace) !== null && _web3Auth$currentChai1 !== void 0 ? _web3Auth$currentChai1 : null);
164
+ };
151
165
  if (web3Auth) {
152
166
  web3Auth.on(CONNECTOR_EVENTS.NOT_READY, notReadyListener);
153
167
  web3Auth.on(CONNECTOR_EVENTS.READY, readyListener);
@@ -158,6 +172,7 @@ function useWeb3AuthInnerContextValue({
158
172
  web3Auth.on(CONNECTOR_EVENTS.ERRORED, errorListener);
159
173
  web3Auth.on(CONNECTOR_EVENTS.REHYDRATION_ERROR, rehydrationErrorListener);
160
174
  web3Auth.on(CONNECTOR_EVENTS.MFA_ENABLED, mfaEnabledListener);
175
+ web3Auth.on(CONNECTOR_EVENTS.CONNECTION_UPDATED, connectionUpdatedListener);
161
176
  if (web3Auth.loginMode === LOGIN_MODE.MODAL) {
162
177
  web3Auth.on(CONNECTOR_EVENTS.CONSENT_ACCEPTED, consentAcceptedListener);
163
178
  }
@@ -173,6 +188,7 @@ function useWeb3AuthInnerContextValue({
173
188
  web3Auth.removeListener(CONNECTOR_EVENTS.REHYDRATION_ERROR, rehydrationErrorListener);
174
189
  web3Auth.removeListener(CONNECTOR_EVENTS.MFA_ENABLED, mfaEnabledListener);
175
190
  web3Auth.removeListener(CONNECTOR_EVENTS.AUTHORIZED, authorizedListener);
191
+ web3Auth.removeListener(CONNECTOR_EVENTS.CONNECTION_UPDATED, connectionUpdatedListener);
176
192
  if (web3Auth.loginMode === LOGIN_MODE.MODAL) {
177
193
  web3Auth.removeListener(CONNECTOR_EVENTS.CONSENT_ACCEPTED, consentAcceptedListener);
178
194
  }
@@ -52,6 +52,7 @@ function useFrameworkKitSolanaClient() {
52
52
  isInitialized
53
53
  } = useWeb3Auth();
54
54
  const {
55
+ chainId,
55
56
  chainNamespace
56
57
  } = useChain();
57
58
  const ref = useRef(null);
@@ -102,7 +103,10 @@ function useFrameworkKitSolanaClient() {
102
103
  }
103
104
 
104
105
  // only reconnect for the primary connector
105
- if (conn.connectorName !== (web3Auth === null || web3Auth === void 0 ? void 0 : web3Auth.primaryConnectorName)) return;
106
+ if (conn.connectorName !== (web3Auth === null || web3Auth === void 0 ? void 0 : web3Auth.primaryConnectorName)) {
107
+ adopt(makePlaceholder(rpc));
108
+ return;
109
+ }
106
110
  try {
107
111
  const solanaWalletId = "wallet-standard:" + conn.connectorName;
108
112
  const connector = createWalletStandardConnector(solanaWallet, {
@@ -134,7 +138,7 @@ function useFrameworkKitSolanaClient() {
134
138
  return () => {
135
139
  stale = true;
136
140
  };
137
- }, [isConnected, connection === null || connection === void 0 ? void 0 : connection.solanaWallet, chainNamespace, web3Auth, isInitialized, connection === null || connection === void 0 ? void 0 : connection.connectorName]);
141
+ }, [isConnected, connection === null || connection === void 0 ? void 0 : connection.solanaWallet, chainId, chainNamespace, web3Auth, isInitialized, connection === null || connection === void 0 ? void 0 : connection.connectorName]);
138
142
  return client;
139
143
  }
140
144
  function SolanaProvider(_ref) {
@@ -1 +1 @@
1
- export { WagmiProvider } from './provider.js';
1
+ export { WagmiProvider, connectWeb3AuthWithWagmi, createWeb3AuthConnectorForWagmi, disconnectWeb3AuthFromWagmi, getWeb3authConnector, resetConnectorState, setupConnector } from './provider.js';
@@ -1,42 +1,61 @@
1
1
  import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties';
2
2
  import _objectSpread from '@babel/runtime/helpers/objectSpread2';
3
- import { useMemo, createElement, useEffect, Fragment } from 'react';
4
- import { defineChain } from 'viem';
5
- import { createConfig, WagmiProvider as WagmiProvider$1, webSocket, http, fallback, useConfig, useReconnect, useConnectionEffect } from 'wagmi';
6
- import { injected } from 'wagmi/connectors';
3
+ import { useMemo, createElement, useRef, useEffect, Fragment } from 'react';
4
+ import { defineChain, isAddress } from 'viem';
5
+ import { createConfig, WagmiProvider as WagmiProvider$1, injected, webSocket, http, fallback, useConfig, useReconnect, useConnectionEffect } from 'wagmi';
7
6
  import { defaultWagmiConfig } from './constants.js';
8
7
  import { useWeb3Auth } from '../hooks/useWeb3Auth.js';
9
8
  import { CHAIN_NAMESPACES } from '@toruslabs/base-controllers';
10
9
  import { WalletInitializationError } from '../../base/errors/index.js';
11
- import { useWeb3AuthDisconnect } from '../hooks/useWeb3AuthDisconnect.js';
10
+ import { WEB3AUTH_CONNECTOR_ID } from '../../base/connector/constants.js';
12
11
  import { log } from '../../base/loglevel.js';
12
+ import { useWeb3AuthDisconnect } from '../hooks/useWeb3AuthDisconnect.js';
13
13
 
14
14
  const _excluded = ["children"];
15
- const WEB3AUTH_CONNECTOR_ID = "web3auth";
16
15
  function getWeb3authConnector(config) {
17
16
  return config.connectors.find(c => c.id === WEB3AUTH_CONNECTOR_ID);
18
17
  }
19
18
 
20
- // Helper to initialize connectors for the given wallets
19
+ // Helper to create a Web3Auth connector to connect with wagmi
21
20
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
- async function setupConnector(provider, config) {
23
- let connector = getWeb3authConnector(config);
24
- if (connector) return connector;
25
-
26
- // Create new connector if not already existing
27
- connector = injected({
21
+ function createWeb3AuthConnectorForWagmi(provider) {
22
+ const baseConnector = injected({
28
23
  target: {
29
24
  provider: provider,
30
25
  id: WEB3AUTH_CONNECTOR_ID,
31
26
  name: "Web3Auth"
32
27
  }
33
28
  });
29
+ return config => {
30
+ const connector = baseConnector(config);
31
+ const baseOnAccountsChanged = connector.onAccountsChanged.bind(connector);
32
+
33
+ // we need to handle the `accountsChanged` event emitted on the cross-namespace chain switch.
34
+ // on evm -> solana, the accountsChanged event is emitted with the solana address, which is not valid for evm.
35
+ // that causes the `invalid account address` error in wagmi. So, here, we're filtering out the solana addresses.
36
+ connector.onAccountsChanged = accounts => {
37
+ if (accounts.length > 0 && !accounts.every(account => typeof account === "string" && isAddress(account))) {
38
+ log.warn("onAccountsChanged::accountsChanged event received on non-EVM address");
39
+ return;
40
+ }
41
+ baseOnAccountsChanged(accounts);
42
+ };
43
+ return connector;
44
+ };
45
+ }
46
+
47
+ // Helper to initialize connectors for the given wallets
48
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
+ function setupConnector(provider, config) {
50
+ let connector = getWeb3authConnector(config);
51
+ if (connector) return connector;
52
+
53
+ // Create new connector if not already existing
54
+ connector = createWeb3AuthConnectorForWagmi(provider);
34
55
  const result = config._internal.connectors.setup(connector);
35
56
  config._internal.connectors.setState(current => [...current, result]);
36
57
  return result;
37
58
  }
38
-
39
- // Helper to connect a wallet and update wagmi state
40
59
  async function connectWeb3AuthWithWagmi(connector, config) {
41
60
  var _config$storage, _config$storage2;
42
61
  await Promise.all([(_config$storage = config.storage) === null || _config$storage === void 0 ? void 0 : _config$storage.removeItem(`${connector.id}.disconnected`), (_config$storage2 = config.storage) === null || _config$storage2 === void 0 ? void 0 : _config$storage2.setItem("recentConnectorId", connector.id)]);
@@ -76,9 +95,11 @@ async function disconnectWeb3AuthFromWagmi(config) {
76
95
  function Web3AuthWagmiProvider({
77
96
  children
78
97
  }) {
98
+ var _connection$ethereumP, _connection$connector;
79
99
  const {
80
100
  isConnected,
81
- connection
101
+ connection,
102
+ chainNamespace
82
103
  } = useWeb3Auth();
83
104
  const {
84
105
  disconnect
@@ -87,34 +108,62 @@ function Web3AuthWagmiProvider({
87
108
  const {
88
109
  mutate: reconnect
89
110
  } = useReconnect();
111
+ const suppressWagmiDisconnect = useRef(false);
112
+ const lastSyncedProvider = useRef((_connection$ethereumP = connection === null || connection === void 0 ? void 0 : connection.ethereumProvider) !== null && _connection$ethereumP !== void 0 ? _connection$ethereumP : null);
113
+ const lastSyncedConnectorName = useRef((_connection$connector = connection === null || connection === void 0 ? void 0 : connection.connectorName) !== null && _connection$connector !== void 0 ? _connection$connector : null);
90
114
  useConnectionEffect({
91
115
  onDisconnect: async () => {
92
116
  log.info("Disconnected from wagmi");
93
- if (isConnected) await disconnect();
94
- const connector = getWeb3authConnector(wagmiConfig);
117
+ const isSuppressed = suppressWagmiDisconnect.current;
118
+ suppressWagmiDisconnect.current = false;
119
+ if (!isSuppressed && isConnected) await disconnect();
120
+
95
121
  // reset wagmi connector state if the provider handles disconnection because of the accountsChanged event
96
122
  // from the connected provider
97
- if (connector) {
123
+ if (getWeb3authConnector(wagmiConfig)) {
98
124
  resetConnectorState(wagmiConfig);
99
125
  }
100
126
  }
101
127
  });
102
128
  useEffect(() => {
103
- (async () => {
104
- if (isConnected && connection !== null && connection !== void 0 && connection.ethereumProvider) {
105
- const connector = await setupConnector(connection.ethereumProvider, wagmiConfig);
129
+ (async _connection$ethereumP2 => {
130
+ const newConnection = connection !== null && connection !== void 0 ? connection : null;
131
+ const newEth = (_connection$ethereumP2 = connection === null || connection === void 0 ? void 0 : connection.ethereumProvider) !== null && _connection$ethereumP2 !== void 0 ? _connection$ethereumP2 : null;
132
+ const w3aWagmiConnector = getWeb3authConnector(wagmiConfig);
133
+ const shouldBindToWagmi = isConnected && chainNamespace === CHAIN_NAMESPACES.EIP155 && Boolean(newConnection && newEth);
134
+ if (shouldBindToWagmi) {
135
+ const hasSameBinding = lastSyncedProvider.current === newEth && lastSyncedConnectorName.current === newConnection.connectorName && wagmiConfig.state.status === "connected";
136
+ if (hasSameBinding) {
137
+ // rehydration: already connected to the same provider, so no need to reconnect
138
+ return;
139
+ }
140
+
141
+ // `ethereumProvider` is a stable proxy (`commonJRPCProvider`) across account switches,
142
+ // so key wagmi resyncs off the Web3Auth connection object instead of provider identity.
143
+ if (w3aWagmiConnector) {
144
+ resetConnectorState(wagmiConfig);
145
+ }
146
+ lastSyncedProvider.current = newEth;
147
+ lastSyncedConnectorName.current = newConnection.connectorName;
148
+ const connector = setupConnector(newEth, wagmiConfig);
106
149
  if (!connector) {
150
+ log.error("Failed to setup react wagmi connector");
107
151
  throw new Error("Failed to setup connector");
108
152
  }
109
153
  await connectWeb3AuthWithWagmi(connector, wagmiConfig);
110
154
  reconnect();
111
- } else if (!isConnected) {
155
+ } else {
156
+ lastSyncedProvider.current = null;
157
+ lastSyncedConnectorName.current = null;
112
158
  if (wagmiConfig.state.status === "connected") {
159
+ suppressWagmiDisconnect.current = true;
113
160
  await disconnectWeb3AuthFromWagmi(wagmiConfig);
161
+ } else if (w3aWagmiConnector) {
162
+ resetConnectorState(wagmiConfig);
114
163
  }
115
164
  }
116
165
  })();
117
- }, [isConnected, wagmiConfig, connection, reconnect]);
166
+ }, [chainNamespace, connection, isConnected, reconnect, wagmiConfig]);
118
167
  return createElement(Fragment, null, children);
119
168
  }
120
169
  function WagmiProvider(_ref) {
@@ -208,4 +257,4 @@ function WagmiProvider(_ref) {
208
257
  }), createElement(Web3AuthWagmiProvider, null, children));
209
258
  }
210
259
 
211
- export { WagmiProvider };
260
+ export { WagmiProvider, connectWeb3AuthWithWagmi, createWeb3AuthConnectorForWagmi, disconnectWeb3AuthFromWagmi, getWeb3authConnector, resetConnectorState, setupConnector };
@@ -3,6 +3,7 @@ import { defineComponent, ref, provide, watch, h, Fragment } from 'vue';
3
3
  import { CHAIN_NAMESPACES } from '@toruslabs/base-controllers';
4
4
  import { SOLANA_CLIENT_KEY } from './constants.js';
5
5
  import { useWeb3Auth } from '../composables/useWeb3Auth.js';
6
+ import { useChain } from '../composables/useChain.js';
6
7
  import { log } from '../../base/loglevel.js';
7
8
 
8
9
  const disposeClient = async client => {
@@ -29,63 +30,85 @@ const SolanaProvider = defineComponent({
29
30
  connection,
30
31
  web3Auth
31
32
  } = useWeb3Auth();
33
+ const {
34
+ chainId
35
+ } = useChain();
32
36
  const clientRef = ref(null);
37
+ // Holds the token for the newest requested sync run. Older async runs compare against it
38
+ // before publishing results so a slower reconnect cannot overwrite a newer chain/account update.
39
+ let activeSyncToken = null;
33
40
 
34
41
  // provide the client to the app
35
42
  provide(SOLANA_CLIENT_KEY, clientRef);
36
-
37
- // watch for changes in the connection and chain namespace
38
- watch([isConnected, connection], async ([newIsConnected, newConnection]) => {
39
- var _web3Auth$value;
40
- if (!newIsConnected || !(newConnection !== null && newConnection !== void 0 && newConnection.solanaWallet)) {
41
- if (clientRef.value) {
42
- await disposeClient(clientRef.value);
43
- clientRef.value = null;
43
+ const syncClient = async () => {
44
+ var _web3Auth$value, _web3Auth$value2;
45
+ // Only the latest async, `syncing` run should be allowed to attach its client.
46
+ // A fresh Symbol gives each run a unique identity without relying on counters.
47
+ const syncToken = Symbol("solana-client-sync");
48
+ activeSyncToken = syncToken;
49
+ const newIsConnected = isConnected.value;
50
+ const newConnection = connection.value;
51
+ const currentChain = (_web3Auth$value = web3Auth.value) === null || _web3Auth$value === void 0 ? void 0 : _web3Auth$value.currentChain;
52
+ if (!newIsConnected || !(newConnection !== null && newConnection !== void 0 && newConnection.solanaWallet) || (currentChain === null || currentChain === void 0 ? void 0 : currentChain.chainNamespace) !== CHAIN_NAMESPACES.SOLANA ||
53
+ // only reconnect for the primary connector
54
+ newConnection.connectorName !== ((_web3Auth$value2 = web3Auth.value) === null || _web3Auth$value2 === void 0 ? void 0 : _web3Auth$value2.primaryConnectorName)) {
55
+ const prevClient = clientRef.value;
56
+ clientRef.value = null;
57
+ if (prevClient) {
58
+ await disposeClient(prevClient);
44
59
  }
45
60
  return;
46
61
  }
47
- const currentChain = web3Auth.value.currentChain;
48
- let chainConfig;
49
- if ((currentChain === null || currentChain === void 0 ? void 0 : currentChain.chainNamespace) === CHAIN_NAMESPACES.SOLANA) {
50
- chainConfig = currentChain;
51
- } else {
52
- // use the 1st Solana chain if current chain is not solana
53
- chainConfig = web3Auth.value.coreOptions.chains.find(c => c.chainNamespace === CHAIN_NAMESPACES.SOLANA);
54
- if (!chainConfig) return;
55
- }
56
-
57
- // only reconnect for the primary connector
58
- if (newConnection.connectorName !== ((_web3Auth$value = web3Auth.value) === null || _web3Auth$value === void 0 ? void 0 : _web3Auth$value.primaryConnectorName)) return;
59
62
  const prevClient = clientRef.value;
63
+ clientRef.value = null;
64
+ if (prevClient) {
65
+ await disposeClient(prevClient);
66
+ }
67
+ let client = null;
60
68
  try {
61
69
  // create a wallet standard connector from connected wallet
62
- const solanaWalletId = "wallet-standard:" + connection.value.connectorName;
63
- const connector = createWalletStandardConnector(connection.value.solanaWallet, {
70
+ const solanaWalletId = "wallet-standard:" + newConnection.connectorName;
71
+ const connector = createWalletStandardConnector(newConnection.solanaWallet, {
64
72
  id: solanaWalletId,
65
- name: connection.value.connectorName
73
+ name: newConnection.connectorName
66
74
  });
67
75
 
68
76
  // create a solana client
69
77
  const {
70
78
  rpcTarget,
71
79
  wsTarget
72
- } = chainConfig;
73
- const client = createClient({
80
+ } = currentChain;
81
+ client = createClient({
74
82
  endpoint: rpcTarget,
75
83
  websocketEndpoint: wsTarget,
76
84
  walletConnectors: [connector]
77
85
  });
78
- clientRef.value = client;
79
- if (prevClient) await disposeClient(prevClient);
80
86
 
81
87
  // connect the client to the wallet
82
88
  await client.actions.connectWallet(solanaWalletId, {
83
89
  autoConnect: true
84
90
  });
91
+ // If another sync started while connectWallet was in flight, this client is stale.
92
+ if (activeSyncToken !== syncToken) {
93
+ await disposeClient(client);
94
+ return;
95
+ }
96
+ clientRef.value = client;
85
97
  } catch (err) {
98
+ if (client) {
99
+ await disposeClient(client);
100
+ }
86
101
  log.error("Failed to create or connect Solana client", err);
87
- clientRef.value = null;
102
+ // Only clear the shared ref when this failing run is still the newest one.
103
+ if (activeSyncToken === syncToken) {
104
+ clientRef.value = null;
105
+ }
88
106
  }
107
+ };
108
+
109
+ // watch for changes in the connection and active chain
110
+ watch([isConnected, connection, chainId], () => {
111
+ void syncClient();
89
112
  }, {
90
113
  immediate: true
91
114
  });