UNPKG

@web3auth/no-modal

Version:
937 lines (935 loc) 51 kB
'use strict'; var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties'); var _objectSpread = require('@babel/runtime/helpers/objectSpread2'); var _defineProperty = require('@babel/runtime/helpers/defineProperty'); var baseControllers = require('@toruslabs/base-controllers'); var auth = require('@web3auth/auth'); var deepmerge = require('deepmerge'); var analytics = require('./base/analytics.js'); var IChainInterface = require('./base/chain/IChainInterface.js'); var index = require('./base/errors/index.js'); var index$1 = require('./base/wallet/index.js'); var connectorStatus = require('./base/connector/connectorStatus.js'); var constants = require('./base/connector/constants.js'); var utils$1 = require('./base/connector/utils.js'); var constants$1 = require('./base/constants.js'); var cookie = require('./base/cookie.js'); var loglevel = require('./base/loglevel.js'); require('./base/plugin/errors.js'); var IPlugin = require('./base/plugin/IPlugin.js'); var utils = require('./base/utils.js'); var deserialize = require('./base/deserialize.js'); var authConnector = require('./connectors/auth-connector/authConnector.js'); var metamaskConnector = require('./connectors/metamask-connector/metamaskConnector.js'); var plugin = require('./plugins/wallet-services-plugin/plugin.js'); require('./providers/base-provider/utils.js'); var CommonJRPCProvider = require('./providers/base-provider/CommonJRPCProvider.js'); require('./providers/base-provider/commonPrivateKeyProvider.js'); const _excluded = ["walletScope"]; class Web3AuthNoModal extends auth.SafeEventEmitter { constructor(options, initialState) { super(); _defineProperty(this, "coreOptions", void 0); _defineProperty(this, "status", constants.CONNECTOR_STATUS.NOT_READY); _defineProperty(this, "aaProvider", null); _defineProperty(this, "connectors", []); _defineProperty(this, "commonJRPCProvider", null); _defineProperty(this, "analytics", void 0); _defineProperty(this, "plugins", {}); _defineProperty(this, "storage", void 0); _defineProperty(this, "state", { connectedConnectorName: null, cachedConnector: null, currentChainId: null, idToken: null }); _defineProperty(this, "loginMode", constants$1.LOGIN_MODE.NO_MODAL); if (!options.clientId) throw index.WalletInitializationError.invalidParams("Please provide a valid clientId in constructor"); if (options.enableLogging) loglevel.log.enableAll();else loglevel.log.setLevel("error"); if (!options.storageType) options.storageType = "local"; this.coreOptions = options; this.storage = this.getStorageMethod(); this.analytics = new analytics.Analytics(); if (options.disableAnalytics) { this.analytics.disable(); } this.analytics.setGlobalProperties({ integration_type: analytics.ANALYTICS_INTEGRATION_TYPE.NATIVE_SDK }); this.loadState(initialState); if (this.state.idToken && this.coreOptions.ssr) { // connect-only is the default authentication mode, so we need to set the status to connected if the idToken is present and ssr is enabled this.status = this.coreOptions.initialAuthenticationMode === constants.CONNECTOR_INITIAL_AUTHENTICATION_MODE.CONNECT_AND_SIGN ? constants.CONNECTOR_STATUS.AUTHORIZED : constants.CONNECTOR_STATUS.CONNECTED; } } get currentChain() { var _this$coreOptions$cha; if (!this.currentChainId) return undefined; return (_this$coreOptions$cha = this.coreOptions.chains) === null || _this$coreOptions$cha === void 0 ? void 0 : _this$coreOptions$cha.find(chain => chain.chainId === this.currentChainId); } get connected() { return Boolean(this.connectedConnector); } get provider() { if (this.status !== constants.CONNECTOR_STATUS.NOT_READY && this.commonJRPCProvider) { return this.commonJRPCProvider; } return null; } get connectedConnectorName() { return this.state.connectedConnectorName; } get cachedConnector() { return this.state.cachedConnector; } get currentChainId() { var _this$coreOptions$cha2; return this.state.currentChainId || this.coreOptions.defaultChainId || ((_this$coreOptions$cha2 = this.coreOptions.chains) === null || _this$coreOptions$cha2 === void 0 || (_this$coreOptions$cha2 = _this$coreOptions$cha2[0]) === null || _this$coreOptions$cha2 === void 0 ? void 0 : _this$coreOptions$cha2.chainId) || null; } get connectedConnector() { var _this$currentChain; return this.getConnector(this.connectedConnectorName, (_this$currentChain = this.currentChain) === null || _this$currentChain === void 0 ? void 0 : _this$currentChain.chainNamespace); } get accountAbstractionProvider() { return this.aaProvider; } get idToken() { return this.state.idToken || null; } set provider(_) { throw new Error("Not implemented"); } async init(options) { // init analytics const startTime = Date.now(); this.analytics.init(); this.analytics.identify(this.coreOptions.clientId, { web3auth_client_id: this.coreOptions.clientId, web3auth_network: this.coreOptions.web3AuthNetwork }); this.analytics.setGlobalProperties({ dapp_url: window.location.origin, sdk_name: analytics.ANALYTICS_SDK_TYPE.WEB_NO_MODAL, sdk_version: utils.sdkVersion, // Required for organization analytics web3auth_client_id: this.coreOptions.clientId, web3auth_network: this.coreOptions.web3AuthNetwork }); let trackData = {}; try { var _authConnector$authIn, _this$coreOptions$uiC; const { signal } = options || {}; // get project config let projectConfig; try { var _this$coreOptions$acc; projectConfig = await utils.fetchProjectConfig({ clientId: this.coreOptions.clientId, web3AuthNetwork: this.coreOptions.web3AuthNetwork, aaProvider: (_this$coreOptions$acc = this.coreOptions.accountAbstractionConfig) === null || _this$coreOptions$acc === void 0 ? void 0 : _this$coreOptions$acc.smartAccountType, authBuildEnv: this.coreOptions.authBuildEnv }); } catch (e) { const error = await auth.serializeError(e); loglevel.log.error("Failed to fetch project configurations", error); throw index.WalletInitializationError.notReady("failed to fetch project configurations", error); } // init config this.initAccountAbstractionConfig(projectConfig); this.initChainsConfig(projectConfig); this.initCachedConnectorAndChainId(); this.initUIConfig(projectConfig); this.initWalletServicesConfig(projectConfig); this.initSessionTimeConfig(projectConfig); this.analytics.setGlobalProperties({ team_id: projectConfig.teamId }); trackData = this.getInitializationTrackData(); // setup common JRPC provider await utils.withAbort(() => this.setupCommonJRPCProvider(), signal); // initialize connectors this.on(constants.CONNECTOR_EVENTS.CONNECTORS_UPDATED, async ({ connectors: newConnectors }) => { const onAbortHandler = () => { var _this$connectors; if (((_this$connectors = this.connectors) === null || _this$connectors === void 0 ? void 0 : _this$connectors.length) > 0) { this.cleanup(); } }; await utils.withAbort(() => Promise.all(newConnectors.map(this.setupConnector.bind(this))), signal, onAbortHandler); // emit connector ready event if (this.status === constants.CONNECTOR_STATUS.NOT_READY) { this.status = constants.CONNECTOR_STATUS.READY; this.emit(constants.CONNECTOR_EVENTS.READY); } }); await utils.withAbort(() => this.loadConnectors({ projectConfig }), signal); await utils.withAbort(() => this.initPlugins(), signal); // track completion event const authConnector = this.getConnector(index$1.WALLET_CONNECTORS.AUTH); trackData = _objectSpread(_objectSpread({}, trackData), {}, { connectors: this.connectors.map(connector => connector.name), plugins: Object.keys(this.plugins), auth_ux_mode: (authConnector === null || authConnector === void 0 || (_authConnector$authIn = authConnector.authInstance) === null || _authConnector$authIn === void 0 || (_authConnector$authIn = _authConnector$authIn.options) === null || _authConnector$authIn === void 0 ? void 0 : _authConnector$authIn.uxMode) || ((_this$coreOptions$uiC = this.coreOptions.uiConfig) === null || _this$coreOptions$uiC === void 0 ? void 0 : _this$coreOptions$uiC.uxMode) }); this.analytics.track(analytics.ANALYTICS_EVENTS.SDK_INITIALIZATION_COMPLETED, _objectSpread(_objectSpread({}, trackData), {}, { duration: Date.now() - startTime })); } catch (error) { if (error instanceof DOMException && error.name === "AbortError") return; // track failure event this.analytics.track(analytics.ANALYTICS_EVENTS.SDK_INITIALIZATION_FAILED, _objectSpread(_objectSpread(_objectSpread({}, trackData), utils.getErrorAnalyticsProperties(error)), {}, { duration: Date.now() - startTime })); loglevel.log.error("Failed to initialize modal", error); throw error; } } // we need to take into account the chainNamespace as for external connectors, same connector name can be used for multiple chain namespaces getConnector(connectorName, chainNamespace) { return this.connectors.find(connector => { if (connector.name !== connectorName) return false; if (chainNamespace) { if (connector.connectorNamespace === IChainInterface.CONNECTOR_NAMESPACES.MULTICHAIN) return true; return connector.connectorNamespace === chainNamespace; } return true; }) || null; } clearCache() { this.setState({ connectedConnectorName: null, cachedConnector: null, currentChainId: null, idToken: null }); } async cleanup() { for (const connector of this.connectors) { if (connector.cleanup) await connector.cleanup(); } } async switchChain(params) { var _this$currentChain2; if (params.chainId === ((_this$currentChain2 = this.currentChain) === null || _this$currentChain2 === void 0 ? void 0 : _this$currentChain2.chainId)) return; const newChainConfig = this.coreOptions.chains.find(x => x.chainId === params.chainId); if (!newChainConfig) throw index.WalletInitializationError.invalidParams("Invalid chainId"); if (connectorStatus.CONNECTED_STATUSES.includes(this.status) && this.connectedConnector) { await this.connectedConnector.switchChain(params); return; } if (this.commonJRPCProvider) { await this.commonJRPCProvider.switchChain(params); return; } throw index.WalletInitializationError.notReady(`No wallet is ready`); } /** * Connect to a specific wallet connector * @param connectorName - Key of the wallet connector to use. */ async connectTo(connectorName, loginParams, loginMode) { this.loginMode = loginMode || "no-modal"; const connector = this.getConnector(connectorName, loginParams === null || loginParams === void 0 ? void 0 : loginParams.chainNamespace); if (!connector || !this.commonJRPCProvider) throw index.WalletInitializationError.notFound(`Please add wallet connector for ${connectorName} wallet, before connecting`); const initialChain = this.getInitialChainIdForConnector(connector); const finalLoginParams = _objectSpread(_objectSpread({}, loginParams), {}, { chainId: initialChain.chainId, getIdentityToken: this.coreOptions.initialAuthenticationMode === constants.CONNECTOR_INITIAL_AUTHENTICATION_MODE.CONNECT_AND_SIGN }); // track connection started event const startTime = Date.now(); let eventData; if (connectorName === index$1.WALLET_CONNECTORS.AUTH) { var _connector$authInstan; const authLoginParams = loginParams; const authConnectionConfig = connector.getOAuthProviderConfig({ authConnection: authLoginParams.authConnection, authConnectionId: authLoginParams.authConnectionId, groupedAuthConnectionId: authLoginParams.groupedAuthConnectionId }); eventData = { connector: connectorName, connector_type: connector.type, chain_id: utils.getCaipChainId(initialChain), chain_name: initialChain.displayName, chain_namespace: initialChain.chainNamespace, auth_connection: authLoginParams.authConnection, auth_connection_id: authLoginParams.authConnectionId, group_auth_connection_id: authLoginParams.groupedAuthConnectionId, mfa_level: authLoginParams.mfaLevel, wallet_key_enabled: authLoginParams.getWalletKey, extra_login_options_enabled: Boolean(authLoginParams.extraLoginOptions), dapp_share_enabled: Boolean(authLoginParams.dappShare), curve: authLoginParams.curve, auth_dapp_url: authLoginParams.dappUrl, is_sfa: Boolean(authLoginParams.idToken), is_default_auth_connection: authConnectionConfig === null || authConnectionConfig === void 0 ? void 0 : authConnectionConfig.isDefault, auth_ux_mode: (_connector$authInstan = connector.authInstance) === null || _connector$authInstan === void 0 || (_connector$authInstan = _connector$authInstan.options) === null || _connector$authInstan === void 0 ? void 0 : _connector$authInstan.uxMode }; } else { eventData = { connector: connectorName, connector_type: connector.type, is_injected: connector.isInjected, chain_id: utils.getCaipChainId(initialChain), chain_name: initialChain.displayName, chain_namespace: initialChain.chainNamespace }; } // track connection started event this.analytics.track(analytics.ANALYTICS_EVENTS.CONNECTION_STARTED, eventData); return new Promise((resolve, reject) => { let connectedEventCompleted = false; let authorizedEventReceived = false; const cleanup = () => { this.removeListener(constants.CONNECTOR_EVENTS.CONNECTED, onConnected); this.removeListener(constants.CONNECTOR_EVENTS.ERRORED, onErrored); this.removeListener(constants.CONNECTOR_EVENTS.AUTHORIZED, onAuthorized); }; const checkCompletion = async () => { // In CONNECT_AND_SIGN mode, wait for both connected event and authorized event if (finalLoginParams.getIdentityToken) { if (connectedEventCompleted && authorizedEventReceived) { await completeConnection(); } } else { // In CONNECT_ONLY mode, just wait for connected event if (connectedEventCompleted) { await completeConnection(); } } }; const completeConnection = async () => { try { // track connection completed event const userInfo = await connector.getUserInfo(); this.analytics.track(analytics.ANALYTICS_EVENTS.CONNECTION_COMPLETED, _objectSpread(_objectSpread({}, eventData), {}, { is_mfa_enabled: userInfo === null || userInfo === void 0 ? void 0 : userInfo.isMfaEnabled, duration: Date.now() - startTime })); cleanup(); resolve(this.provider); } catch (error) { cleanup(); reject(error); } }; const onConnected = async () => { connectedEventCompleted = true; await checkCompletion(); }; const onAuthorized = async () => { authorizedEventReceived = true; await checkCompletion(); }; const onErrored = async err => { // track connection failed event this.analytics.track(analytics.ANALYTICS_EVENTS.CONNECTION_FAILED, _objectSpread(_objectSpread(_objectSpread({}, eventData), utils.getErrorAnalyticsProperties(err)), {}, { duration: Date.now() - startTime })); cleanup(); reject(err); }; this.once(constants.CONNECTOR_EVENTS.CONNECTED, onConnected); if (finalLoginParams.getIdentityToken) { this.once(constants.CONNECTOR_EVENTS.AUTHORIZED, onAuthorized); } this.once(constants.CONNECTOR_EVENTS.ERRORED, onErrored); connector.connect(finalLoginParams); this.setCurrentChain(initialChain.chainId); }); } async logout(options = { cleanup: false }) { if (!connectorStatus.CONNECTED_STATUSES.includes(this.status) || !this.connectedConnector) throw index.WalletLoginError.notConnectedError(`No wallet is connected`); if (this.connectedConnector.status === constants.CONNECTOR_STATUS.DISCONNECTING) return; await this.connectedConnector.disconnect(options); } async getUserInfo() { var _this$connectedConnec; loglevel.log.debug("Getting user info", this.status, (_this$connectedConnec = this.connectedConnector) === null || _this$connectedConnec === void 0 ? void 0 : _this$connectedConnec.name); if (!connectorStatus.CAN_AUTHORIZE_STATUSES.includes(this.status) || !this.connectedConnector) throw index.WalletLoginError.notConnectedError(`No wallet is connected`); return this.connectedConnector.getUserInfo(); } async enableMFA(loginParams) { var _authConnector$authIn2; if (!connectorStatus.CONNECTED_STATUSES.includes(this.status) || !this.connectedConnector) throw index.WalletLoginError.notConnectedError(`No wallet is connected`); if (this.connectedConnector.name !== index$1.WALLET_CONNECTORS.AUTH) throw index.WalletLoginError.unsupportedOperation(`EnableMFA is not supported for this connector.`); const authConnector = this.connectedConnector; const trackData = { connector: this.connectedConnector.name, auth_ux_mode: (_authConnector$authIn2 = authConnector.authInstance) === null || _authConnector$authIn2 === void 0 || (_authConnector$authIn2 = _authConnector$authIn2.options) === null || _authConnector$authIn2 === void 0 ? void 0 : _authConnector$authIn2.uxMode }; try { this.analytics.track(analytics.ANALYTICS_EVENTS.MFA_ENABLEMENT_STARTED, trackData); await this.connectedConnector.enableMFA(loginParams); } catch (error) { this.analytics.track(analytics.ANALYTICS_EVENTS.MFA_ENABLEMENT_FAILED, _objectSpread(_objectSpread({}, trackData), utils.getErrorAnalyticsProperties(error))); throw error; } } async manageMFA(loginParams) { var _authConnector$authIn3; if (!connectorStatus.CONNECTED_STATUSES.includes(this.status) || !this.connectedConnector) throw index.WalletLoginError.notConnectedError(`No wallet is connected`); if (this.connectedConnector.name !== index$1.WALLET_CONNECTORS.AUTH) throw index.WalletLoginError.unsupportedOperation(`ManageMFA is not supported for this connector.`); const authConnector = this.connectedConnector; const trackData = { connector: this.connectedConnector.name, auth_ux_mode: (_authConnector$authIn3 = authConnector.authInstance) === null || _authConnector$authIn3 === void 0 || (_authConnector$authIn3 = _authConnector$authIn3.options) === null || _authConnector$authIn3 === void 0 ? void 0 : _authConnector$authIn3.uxMode }; try { this.analytics.track(analytics.ANALYTICS_EVENTS.MFA_MANAGEMENT_SELECTED, trackData); await this.connectedConnector.manageMFA(loginParams); } catch (error) { this.analytics.track(analytics.ANALYTICS_EVENTS.MFA_MANAGEMENT_FAILED, _objectSpread(_objectSpread({}, trackData), utils.getErrorAnalyticsProperties(error))); throw error; } } async getIdentityToken() { if (!connectorStatus.CAN_AUTHORIZE_STATUSES.includes(this.status) || !this.connectedConnector) throw index.WalletLoginError.notConnectedError(`No wallet is connected`); const trackData = { connector: this.connectedConnector.name }; try { this.analytics.track(analytics.ANALYTICS_EVENTS.IDENTITY_TOKEN_STARTED, trackData); const identityToken = await this.connectedConnector.getIdentityToken(); this.analytics.track(analytics.ANALYTICS_EVENTS.IDENTITY_TOKEN_COMPLETED, trackData); return identityToken; } catch (error) { this.analytics.track(analytics.ANALYTICS_EVENTS.IDENTITY_TOKEN_FAILED, _objectSpread(_objectSpread({}, trackData), utils.getErrorAnalyticsProperties(error))); throw error; } } getPlugin(name) { return this.plugins[name] || null; } setAnalyticsProperties(properties) { this.analytics.setGlobalProperties(properties); } initChainsConfig(projectConfig) { // merge chains from project config with core options, core options chains will take precedence over project config chains const chainMap = new Map(); const allChains = [...(projectConfig.chains || []), ...(this.coreOptions.chains || [])]; for (const chain of allChains) { const existingChain = chainMap.get(chain.chainId); if (!existingChain) chainMap.set(chain.chainId, chain);else chainMap.set(chain.chainId, _objectSpread(_objectSpread({}, existingChain), chain)); } this.coreOptions.chains = Array.from(chainMap.values()); // validate chains and namespaces if (this.coreOptions.chains.length === 0) { loglevel.log.error("chain info not found. Please configure chains on dashboard at https://dashboard.web3auth.io"); throw index.WalletInitializationError.invalidParams("Please configure chains on dashboard at https://dashboard.web3auth.io"); } const validChainNamespaces = new Set(Object.values(baseControllers.CHAIN_NAMESPACES)); for (const chain of this.coreOptions.chains) { if (!chain.chainNamespace || !validChainNamespaces.has(chain.chainNamespace)) { loglevel.log.error(`Please provide a valid chainNamespace in chains for chain ${chain.chainId}`); throw index.WalletInitializationError.invalidParams(`Please provide a valid chainNamespace in chains for chain ${chain.chainId}`); } if (chain.chainNamespace !== baseControllers.CHAIN_NAMESPACES.OTHER && !utils.isHexStrict(chain.chainId)) { loglevel.log.error(`Please provide a valid chainId in chains for chain ${chain.chainId}`); throw index.WalletInitializationError.invalidParams(`Please provide a valid chainId as hex string in chains for chain ${chain.chainId}`); } if (chain.chainNamespace !== baseControllers.CHAIN_NAMESPACES.OTHER) { try { new URL(chain.rpcTarget); } catch (error) { // TODO: add support for chain.wsTarget loglevel.log.error(`Please provide a valid rpcTarget in chains for chain ${chain.chainId}`, error); throw index.WalletInitializationError.invalidParams(`Please provide a valid rpcTarget in chains for chain ${chain.chainId}`); } } } // if AA is enabled, filter out chains that are not AA-supported if (this.coreOptions.accountAbstractionConfig) { // write a for loop over accountAbstractionConfig.chains and check if the chainId is valid if (this.coreOptions.accountAbstractionConfig.chains.length === 0) { loglevel.log.error("Please configure chains for smart accounts on dashboard at https://dashboard.web3auth.io"); throw index.WalletInitializationError.invalidParams("Please configure chains for smart accounts on dashboard at https://dashboard.web3auth.io"); } for (const chain of this.coreOptions.accountAbstractionConfig.chains) { if (!utils.isHexStrict(chain.chainId)) { loglevel.log.error(`Please provide a valid chainId in accountAbstractionConfig.chains for chain ${chain.chainId}`); throw index.WalletInitializationError.invalidParams(`Please provide a valid chainId in accountAbstractionConfig.chains for chain ${chain.chainId}`); } try { var _chain$bundlerConfig; new URL((_chain$bundlerConfig = chain.bundlerConfig) === null || _chain$bundlerConfig === void 0 ? void 0 : _chain$bundlerConfig.url); } catch (error) { loglevel.log.error(`Please provide a valid bundlerConfig.url in accountAbstractionConfig.chains for chain ${chain.chainId}`, error); throw index.WalletInitializationError.invalidParams(`Please provide a valid bundlerConfig.url in accountAbstractionConfig.chains for chain ${chain.chainId}`); } if (!chainMap.has(chain.chainId)) { loglevel.log.error(`Please provide chain config for AA chain in accountAbstractionConfig.chains for chain ${chain.chainId}`); throw index.WalletInitializationError.invalidParams(`Please provide chain config for AA chain in accountAbstractionConfig.chains for chain ${chain.chainId}`); } } // const aaSupportedChainIds = new Set( // this.coreOptions.accountAbstractionConfig?.chains // ?.filter((chain) => chain.chainId && chain.bundlerConfig?.url) // .map((chain) => chain.chainId) || [] // ); // this.coreOptions.chains = this.coreOptions.chains.filter( // (chain) => chain.chainNamespace !== CHAIN_NAMESPACES.EIP155 || aaSupportedChainIds.has(chain.chainId) // ); // if (this.coreOptions.chains.length === 0) { // log.error("Account Abstraction is enabled but no supported chains found"); // throw WalletInitializationError.invalidParams("Account Abstraction is enabled but no supported chains found"); // } } } initAccountAbstractionConfig(projectConfig) { var _this$coreOptions$acc2; const isAAEnabled = Boolean(this.coreOptions.accountAbstractionConfig || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.smartAccounts)); if (!isAAEnabled) return; // merge smart account config from project config with core options, core options will take precedence over project config const _ref = (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.smartAccounts) || {}, { walletScope } = _ref, configWithoutWalletScope = _objectWithoutProperties(_ref, _excluded); const aaChainMap = new Map(); const allAaChains = [...((configWithoutWalletScope === null || configWithoutWalletScope === void 0 ? void 0 : configWithoutWalletScope.chains) || []), ...(((_this$coreOptions$acc2 = this.coreOptions.accountAbstractionConfig) === null || _this$coreOptions$acc2 === void 0 ? void 0 : _this$coreOptions$acc2.chains) || [])]; for (const chain of allAaChains) { const existingChain = aaChainMap.get(chain.chainId); if (!existingChain) aaChainMap.set(chain.chainId, chain);else aaChainMap.set(chain.chainId, _objectSpread(_objectSpread({}, existingChain), chain)); } this.coreOptions.accountAbstractionConfig = _objectSpread(_objectSpread({}, deepmerge(configWithoutWalletScope || {}, this.coreOptions.accountAbstractionConfig || {})), {}, { chains: Array.from(aaChainMap.values()) }); // determine if we should use AA with external wallet if (this.coreOptions.useAAWithExternalWallet === undefined) { this.coreOptions.useAAWithExternalWallet = walletScope === constants$1.SMART_ACCOUNT_WALLET_SCOPE.ALL; } } initUIConfig(projectConfig) { this.coreOptions.uiConfig = deepmerge.all([{ mode: "light", uxMode: auth.UX_MODE.POPUP }, auth.cloneDeep(projectConfig.whitelabel || {}), this.coreOptions.uiConfig || {}]); } initSessionTimeConfig(projectConfig) { if (this.coreOptions.sessionTime) return; if (projectConfig.sessionTime) this.coreOptions.sessionTime = projectConfig.sessionTime; } initCachedConnectorAndChainId() { // init chainId using cached chainId if it exists and is valid, otherwise use the defaultChainId or the first chain const cachedChainId = this.state.currentChainId; const isCachedChainIdValid = cachedChainId && this.coreOptions.chains.some(chain => chain.chainId === cachedChainId); if (this.coreOptions.defaultChainId && !utils.isHexStrict(this.coreOptions.defaultChainId)) throw index.WalletInitializationError.invalidParams("Please provide a valid defaultChainId in constructor"); const currentChainId = isCachedChainIdValid ? cachedChainId : this.coreOptions.defaultChainId || this.coreOptions.chains[0].chainId; this.setState({ currentChainId }); } initWalletServicesConfig(projectConfig) { var _this$coreOptions$wal, _this$coreOptions$wal2, _this$coreOptions$wal3, _ref2, _this$coreOptions$wal4, _this$coreOptions$wal5; const { enableKeyExport, walletUi } = projectConfig; const { enablePortfolioWidget = false, enableTokenDisplay = true, enableNftDisplay = true, enableWalletConnect = true, enableBuyButton = true, enableSendButton = true, enableSwapButton = true, enableReceiveButton = true, enableShowAllTokensButton = true, enableConfirmationModal = false, enableDefiPositionsDisplay = true, portfolioWidgetPosition = baseControllers.BUTTON_POSITION.BOTTOM_LEFT, defaultPortfolio = "token" } = walletUi || {}; const projectConfigWhiteLabel = { showWidgetButton: enablePortfolioWidget, hideNftDisplay: !enableNftDisplay, hideTokenDisplay: !enableTokenDisplay, hideTransfers: !enableSendButton, hideTopup: !enableBuyButton, hideReceive: !enableReceiveButton, hideSwap: !enableSwapButton, hideShowAllTokens: !enableShowAllTokensButton, hideWalletConnect: !enableWalletConnect, hideDefiPositionsDisplay: !enableDefiPositionsDisplay, buttonPosition: portfolioWidgetPosition, defaultPortfolio }; const whiteLabel = deepmerge.all([projectConfigWhiteLabel, ((_this$coreOptions$wal = this.coreOptions.walletServicesConfig) === null || _this$coreOptions$wal === void 0 ? void 0 : _this$coreOptions$wal.whiteLabel) || {}]); const confirmationStrategy = (_this$coreOptions$wal2 = (_this$coreOptions$wal3 = this.coreOptions.walletServicesConfig) === null || _this$coreOptions$wal3 === void 0 ? void 0 : _this$coreOptions$wal3.confirmationStrategy) !== null && _this$coreOptions$wal2 !== void 0 ? _this$coreOptions$wal2 : enableConfirmationModal ? baseControllers.CONFIRMATION_STRATEGY.MODAL : baseControllers.CONFIRMATION_STRATEGY.AUTO_APPROVE; const isKeyExportEnabled = (_ref2 = (_this$coreOptions$wal4 = (_this$coreOptions$wal5 = this.coreOptions.walletServicesConfig) === null || _this$coreOptions$wal5 === void 0 ? void 0 : _this$coreOptions$wal5.enableKeyExport) !== null && _this$coreOptions$wal4 !== void 0 ? _this$coreOptions$wal4 : enableKeyExport) !== null && _ref2 !== void 0 ? _ref2 : true; this.coreOptions.walletServicesConfig = _objectSpread(_objectSpread({}, this.coreOptions.walletServicesConfig), {}, { confirmationStrategy, whiteLabel, enableKeyExport: isKeyExportEnabled }); } getInitializationTrackData() { try { var _this$coreOptions$cha3, _this$coreOptions$cha4, _this$coreOptions$cha5, _this$coreOptions$cha6, _this$coreOptions$uiC2; const defaultChain = (_this$coreOptions$cha3 = this.coreOptions.chains) === null || _this$coreOptions$cha3 === void 0 ? void 0 : _this$coreOptions$cha3.find(chain => chain.chainId === this.coreOptions.defaultChainId); const rpcHostnames = Array.from(new Set((_this$coreOptions$cha4 = this.coreOptions.chains) === null || _this$coreOptions$cha4 === void 0 ? void 0 : _this$coreOptions$cha4.map(chain => utils.getHostname(chain.rpcTarget)))).filter(Boolean); return _objectSpread(_objectSpread(_objectSpread({ chain_ids: (_this$coreOptions$cha5 = this.coreOptions.chains) === null || _this$coreOptions$cha5 === void 0 ? void 0 : _this$coreOptions$cha5.map(chain => utils.getCaipChainId(chain)), chain_names: (_this$coreOptions$cha6 = this.coreOptions.chains) === null || _this$coreOptions$cha6 === void 0 ? void 0 : _this$coreOptions$cha6.map(chain => chain.displayName), chain_rpc_targets: rpcHostnames, default_chain_id: defaultChain ? utils.getCaipChainId(defaultChain) : undefined, default_chain_name: defaultChain === null || defaultChain === void 0 ? void 0 : defaultChain.displayName, logging_enabled: this.coreOptions.enableLogging, storage_type: this.coreOptions.storageType, session_time: this.coreOptions.sessionTime, sfa_key_enabled: this.coreOptions.useSFAKey, mipd_enabled: this.coreOptions.multiInjectedProviderDiscovery, private_key_provider_enabled: Boolean(this.coreOptions.privateKeyProvider), ssr_enabled: this.coreOptions.ssr, auth_build_env: this.coreOptions.authBuildEnv, auth_ux_mode: (_this$coreOptions$uiC2 = this.coreOptions.uiConfig) === null || _this$coreOptions$uiC2 === void 0 ? void 0 : _this$coreOptions$uiC2.uxMode, auth_mfa_level: this.coreOptions.mfaLevel, auth_mfa_settings: Object.keys(this.coreOptions.mfaSettings || {}), aa_enabled_for_external_wallets: this.coreOptions.accountAbstractionConfig ? this.coreOptions.useAAWithExternalWallet : undefined }, utils.getWhitelabelAnalyticsProperties(this.coreOptions.uiConfig)), utils.getAaAnalyticsProperties(this.coreOptions.accountAbstractionConfig)), utils.getWalletServicesAnalyticsProperties(this.coreOptions.walletServicesConfig)); } catch (error) { loglevel.log.error("Failed to get initialization track data", error); return {}; } } async setupCommonJRPCProvider() { this.commonJRPCProvider = await CommonJRPCProvider.CommonJRPCProvider.getProviderInstance({ chain: this.currentChain, chains: this.coreOptions.chains }); // sync chainId this.commonJRPCProvider.on("chainChanged", chainId => this.setCurrentChain(chainId)); } async setupConnector(connector) { this.subscribeToConnectorEvents(connector); try { const initialChain = this.getInitialChainIdForConnector(connector); const autoConnect = this.checkIfAutoConnect(connector); await connector.init({ autoConnect, chainId: initialChain.chainId, getIdentityToken: this.coreOptions.initialAuthenticationMode === constants.CONNECTOR_INITIAL_AUTHENTICATION_MODE.CONNECT_AND_SIGN }); } catch (e) { loglevel.log.error(e, connector.name); } } async loadConnectors({ projectConfig, modalMode }) { var _this$coreOptions$mul; // always add auth connector const connectorFns = [...(this.coreOptions.connectors || []), authConnector.authConnector()]; const config = { projectConfig, coreOptions: this.coreOptions, analytics: this.analytics }; // add injected connectors const isExternalWalletEnabled = Boolean(projectConfig.externalWalletAuth); const isMipdEnabled = isExternalWalletEnabled && ((_this$coreOptions$mul = this.coreOptions.multiInjectedProviderDiscovery) !== null && _this$coreOptions$mul !== void 0 ? _this$coreOptions$mul : true); const chainNamespaces = new Set(this.coreOptions.chains.map(chain => chain.chainNamespace)); // prioritize using MM connector over injected connector for EVM chains if (utils.isBrowser() && chainNamespaces.has(baseControllers.CHAIN_NAMESPACES.EIP155)) { // only set headless to true if modal SDK is used, otherwise just use the modal from native Metamask SDK connectorFns.push(metamaskConnector.metaMaskConnector(modalMode ? { headless: true } : undefined)); } if (isMipdEnabled && utils.isBrowser()) { // Solana chains if (chainNamespaces.has(baseControllers.CHAIN_NAMESPACES.SOLANA)) { const { createSolanaMipd, hasSolanaWalletStandardFeatures, walletStandardConnector } = await Promise.resolve().then(function () { return require('./connectors/injected-solana-connector/index.js'); }); const solanaMipd = createSolanaMipd(); // subscribe to new injected connectors solanaMipd.on("register", async (...wallets) => { const newConnectors = wallets.filter(hasSolanaWalletStandardFeatures).map(wallet => walletStandardConnector(wallet)(config)); this.setConnectors(newConnectors); }); connectorFns.push(...solanaMipd.get().filter(wallet => hasSolanaWalletStandardFeatures(wallet)).map(walletStandardConnector)); } // EVM chains if (chainNamespaces.has(baseControllers.CHAIN_NAMESPACES.EIP155)) { const { createMipd, injectedEvmConnector } = await Promise.resolve().then(function () { return require('./connectors/injected-evm-connector/index.js'); }); const evmMipd = createMipd(); // subscribe to new injected connectors evmMipd.subscribe(providerDetails => { const newConnectors = providerDetails.map(providerDetail => injectedEvmConnector(providerDetail)(config)); this.setConnectors(newConnectors); }); connectorFns.push(...evmMipd.getProviders().map(injectedEvmConnector)); } } // add WalletConnectV2 connector if external wallets are enabled if (utils.isBrowser() && isExternalWalletEnabled && (chainNamespaces.has(baseControllers.CHAIN_NAMESPACES.SOLANA) || chainNamespaces.has(baseControllers.CHAIN_NAMESPACES.EIP155))) { const { walletConnectV2Connector } = await Promise.resolve().then(function () { return require('./connectors/wallet-connect-v2-connector/index.js'); }); connectorFns.push(walletConnectV2Connector()); } const connectors = connectorFns.map(connectorFn => connectorFn(config)); this.setConnectors(connectors); } async initPlugins() { const { chains, plugins } = this.coreOptions; const pluginFns = plugins || []; const isWsSupportedChain = chains.some(x => x.chainNamespace === baseControllers.CHAIN_NAMESPACES.EIP155 || x.chainNamespace === baseControllers.CHAIN_NAMESPACES.SOLANA); if (isWsSupportedChain) { pluginFns.push(plugin.walletServicesPlugin()); } for (const pluginFn of pluginFns) { const plugin = pluginFn(); if (!this.plugins[plugin.name]) this.plugins[plugin.name] = plugin; } } setConnectors(connectors) { const getConnectorKey = connector => `${connector.connectorNamespace}-${connector.name}`; const connectorSet = new Set(this.connectors.map(getConnectorKey)); const newConnectors = connectors.map(connector => { const key = getConnectorKey(connector); if (connectorSet.has(key)) return null; connectorSet.add(key); return connector; }).filter(connector => connector !== null); if (newConnectors.length > 0) { this.connectors = [...this.connectors, ...newConnectors]; // only emit new connectors this.emit(constants.CONNECTOR_EVENTS.CONNECTORS_UPDATED, { connectors: newConnectors }); } } subscribeToConnectorEvents(connector) { connector.on(constants.CONNECTOR_EVENTS.CONNECTED, async data => { var _this$currentChain3, _accountAbstractionCo; if (!this.commonJRPCProvider) throw index.WalletInitializationError.notFound(`CommonJrpcProvider not found`); const { provider, identityTokenInfo } = data; if (identityTokenInfo) { this.setState({ idToken: identityTokenInfo.idToken }); } // when ssr is enabled, we need to get the idToken from the connector. if (this.coreOptions.ssr) { try { const data = await connector.getIdentityToken(); if (!data.idToken) throw index.WalletLoginError.connectionError("No idToken found"); this.setState({ idToken: data.idToken }); } catch (error) { loglevel.log.error(error); this.status = constants.CONNECTOR_STATUS.ERRORED; this.emit(constants.CONNECTOR_EVENTS.ERRORED, error, this.loginMode); return; } } let finalProvider = provider.provider || provider; // setup AA provider if AA is enabled const { accountAbstractionConfig } = this.coreOptions; const isAaSupportedForCurrentChain = ((_this$currentChain3 = this.currentChain) === null || _this$currentChain3 === void 0 ? void 0 : _this$currentChain3.chainNamespace) === baseControllers.CHAIN_NAMESPACES.EIP155 && (accountAbstractionConfig === null || accountAbstractionConfig === void 0 || (_accountAbstractionCo = accountAbstractionConfig.chains) === null || _accountAbstractionCo === void 0 ? void 0 : _accountAbstractionCo.some(chain => { var _this$currentChain4; return chain.chainId === ((_this$currentChain4 = this.currentChain) === null || _this$currentChain4 === void 0 ? void 0 : _this$currentChain4.chainId); })); if (isAaSupportedForCurrentChain && (data.connector === index$1.WALLET_CONNECTORS.AUTH || this.coreOptions.useAAWithExternalWallet)) { var _accountAbstractionCo2; const { accountAbstractionProvider, toEoaProvider } = await Promise.resolve().then(function () { return require('./providers/account-abstraction-provider/index.js'); }); // for embedded wallets, we use ws-embed provider which is AA provider, need to derive EOA provider const eoaProvider = data.connector === index$1.WALLET_CONNECTORS.AUTH ? await toEoaProvider(provider) : provider; const aaChainIds = new Set((accountAbstractionConfig === null || accountAbstractionConfig === void 0 || (_accountAbstractionCo2 = accountAbstractionConfig.chains) === null || _accountAbstractionCo2 === void 0 ? void 0 : _accountAbstractionCo2.map(chain => chain.chainId)) || []); const aaProvider = await accountAbstractionProvider({ accountAbstractionConfig, provider: eoaProvider, chain: this.currentChain, chains: this.coreOptions.chains.filter(chain => aaChainIds.has(chain.chainId)), useProviderAsTransport: data.connector === index$1.WALLET_CONNECTORS.AUTH }); this.aaProvider = aaProvider; // if external wallet is used and AA is enabled for external wallets, use AA provider // for embedded wallets, we use ws-embed provider which already supports AA if (data.connector !== index$1.WALLET_CONNECTORS.AUTH && this.coreOptions.useAAWithExternalWallet) { finalProvider = this.aaProvider; } } this.commonJRPCProvider.updateProviderEngineProxy(finalProvider); this.setState({ connectedConnectorName: data.connector }); this.cacheWallet(data.connector); this.status = constants.CONNECTOR_STATUS.CONNECTED; loglevel.log.debug("connected", this.status, this.connectedConnectorName); this.connectToPlugins(_objectSpread(_objectSpread({}, data), {}, { connector: data.connector })); this.emit(constants.CONNECTOR_EVENTS.CONNECTED, _objectSpread(_objectSpread({}, data), {}, { loginMode: this.loginMode })); }); connector.on(constants.CONNECTOR_EVENTS.DISCONNECTED, async () => { // re-setup commonJRPCProvider this.commonJRPCProvider.removeAllListeners(); this.setupCommonJRPCProvider(); // get back to ready state for rehydrating. this.status = constants.CONNECTOR_STATUS.READY; const cachedConnector = this.state.cachedConnector; if (this.connectedConnectorName === cachedConnector) { this.clearCache(); } loglevel.log.debug("disconnected", this.status, this.connectedConnectorName); await Promise.all(Object.values(this.plugins).map(async plugin => { if (!plugin.SUPPORTED_CONNECTORS.includes(connector.name)) return; if (plugin.status !== IPlugin.PLUGIN_STATUS.CONNECTED) return; return plugin.disconnect().catch(error => { // swallow error if connector doesn't supports this plugin. if (error.code === 5211) { return; } // throw error; loglevel.log.error(error); }); })); this.setState({ connectedConnectorName: null }); this.emit(constants.CONNECTOR_EVENTS.DISCONNECTED); }); connector.on(constants.CONNECTOR_EVENTS.CONNECTING, data => { this.status = constants.CONNECTOR_STATUS.CONNECTING; this.emit(constants.CONNECTOR_EVENTS.CONNECTING, data); loglevel.log.debug("connecting", this.status, this.connectedConnectorName); }); connector.on(constants.CONNECTOR_EVENTS.ERRORED, data => { this.status = constants.CONNECTOR_STATUS.ERRORED; this.clearCache(); this.emit(constants.CONNECTOR_EVENTS.ERRORED, data, this.loginMode); loglevel.log.debug("errored", this.status, this.connectedConnectorName); }); connector.on(constants.CONNECTOR_EVENTS.REHYDRATION_ERROR, error => { this.status = constants.CONNECTOR_STATUS.READY; this.clearCache(); this.emit(constants.CONNECTOR_EVENTS.REHYDRATION_ERROR, error); }); connector.on(constants.CONNECTOR_EVENTS.CONNECTOR_DATA_UPDATED, data => { loglevel.log.debug("connector data updated", data); this.emit(constants.CONNECTOR_EVENTS.CONNECTOR_DATA_UPDATED, data); }); connector.on(constants.CONNECTOR_EVENTS.CACHE_CLEAR, data => { loglevel.log.debug("connector cache clear", data); this.clearCache(); }); connector.on(constants.CONNECTOR_EVENTS.MFA_ENABLED, isMFAEnabled => { var _authConnector$authIn4; loglevel.log.debug("mfa enabled", isMFAEnabled); const authConnector = this.connectedConnector; // mfa_enabled event is only emitted when using "popup" ux_mode // TODO: handle mfa_enabled event when using "redirect" ux_mode this.analytics.track(analytics.ANALYTICS_EVENTS.MFA_ENABLEMENT_COMPLETED, { connector: this.connectedConnector.name, auth_ux_mode: (_authConnector$authIn4 = authConnector.authInstance) === null || _authConnector$authIn4 === void 0 || (_authConnector$authIn4 = _authConnector$authIn4.options) === null || _authConnector$authIn4 === void 0 ? void 0 : _authConnector$authIn4.uxMode, is_mfa_enabled: isMFAEnabled }); this.emit(constants.CONNECTOR_EVENTS.MFA_ENABLED, isMFAEnabled); }); connector.on(constants.CONNECTOR_EVENTS.AUTHORIZING, data => { this.status = constants.CONNECTOR_STATUS.AUTHORIZING; this.emit(constants.CONNECTOR_EVENTS.AUTHORIZING, data); loglevel.log.debug("authorizing", this.status, this.connectedConnectorName); }); connector.on(constants.CONNECTOR_EVENTS.AUTHORIZED, data => { this.status = constants.CONNECTOR_STATUS.AUTHORIZED; this.setState({ idToken: data.identityTokenInfo.idToken }); this.emit(constants.CONNECTOR_EVENTS.AUTHORIZED, data); loglevel.log.debug("authorized", this.status, this.connectedConnectorName); }); } checkInitRequirements() { if (this.status === constants.CONNECTOR_STATUS.READY) throw index.WalletInitializationError.notReady("Connector is already initialized"); } checkIfAutoConnect(connector) { var _this$currentChain5; let autoConnect = this.cachedConnector === connector.name; if (autoConnect && (_this$currentChain5 = this.currentChain) !== null && _this$currentChain5 !== void 0 && _this$currentChain5.chainNamespace) { if (connector.connectorNamespace === IChainInterface.CONNECTOR_NAMESPACES.MULTICHAIN) autoConnect = true;else autoConnect = connector.connectorNamespace === this.currentChain.chainNamespace; } return autoConnect; } /** * Gets the initial chain configuration for a connector * @throws WalletInitializationError If no chain is found for the connector's namespace */ getInitialChainIdForConnector(connector) { var _initialChain; let initialChain = this.currentChain; if (((_initialChain = initialChain) === null || _initialChain === void 0 ? void 0 : _initialChain.chainNamespace) !== connector.connectorNamespace && connector.connectorNamespace !== IChainInterface.CONNECTOR_NAMESPACES.MULTICHAIN) { initialChain = this.coreOptions.chains.find(x => x.chainNamespace === connector.connectorNamespace); if (!initialChain) throw index.WalletInitializationError.invalidParams(`No chain found for ${connector.connectorNamespace}`); } return initialChain; } cacheWallet(walletName) { this.setState({ cachedConnector: walletName }); } setCurrentChain(chainId) { if (chainId === this.currentChainId) return; const newChain = this.coreOptions.chains.find(chain => chain.chainId === chainId); if (!newChain) throw index.WalletInitializationError.invalidParams(`Invalid chainId: ${chainId}`); this.setState({ currentChainId: chainId }); } connectToPlugins(data) { Object.values(this.plugins).map(async plugin => { try { var _this$currentChain6; // skip if it's not compatible with the connector if (!plugin.SUPPORTED_CONNECTORS.includes(data.connector)) return; // skip if it's not compatible with the current chain if (plugin.pluginNamespace !== IPlugin.PLUGIN_NAMESPACES.MULTICHAIN && plugin.pluginNamespace !== ((_this$currentChain6 = this.currentChain) === null || _this$currentChain6 === void 0 ? void 0 : _this$currentChain6.chainNamespace)) return; // skip if it's already connected if (plugin.status === IPlugin.PLUGIN_STATUS.CONNECTED) return; await plugin.initWithWeb3Auth(this, this.coreOptions.uiConfig, this.analytics); await plugin.connect(); } catch (error) { // swallow error if connector connector doesn't supports this plugin. if (error.code === 5211) { return; } loglevel.log.error(error); } }); } setState(newState) { this.state = _objectSpread(_o