@web3auth/no-modal
Version:
Multi chain wallet aggregator for web3Auth
980 lines (953 loc) • 49.4 kB
JavaScript
import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties';
import _objectSpread from '@babel/runtime/helpers/objectSpread2';
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import { CHAIN_NAMESPACES, BUTTON_POSITION, CONFIRMATION_STRATEGY } from '@toruslabs/base-controllers';
import { SafeEventEmitter, serializeError, UX_MODE, cloneDeep, MemoryStore } from '@web3auth/auth';
import deepmerge from 'deepmerge';
import { cookieStorage } from './base/cookie.js';
import { deserialize } from './base/deserialize.js';
import { LOGIN_MODE, SMART_ACCOUNT_WALLET_SCOPE, WEB3AUTH_STATE_STORAGE_KEY } from './base/constants.js';
import { Analytics, ANALYTICS_INTEGRATION_TYPE, ANALYTICS_SDK_TYPE, ANALYTICS_EVENTS } from './base/analytics.js';
import { CONNECTOR_STATUS, CONNECTOR_INITIAL_AUTHENTICATION_MODE, CONNECTOR_EVENTS } from './base/connector/constants.js';
import { sdkVersion, fetchProjectConfig, withAbort, getErrorAnalyticsProperties, getCaipChainId, isHexStrict, getHostname, getWhitelabelAnalyticsProperties, getAaAnalyticsProperties, getWalletServicesAnalyticsProperties, isBrowser } from './base/utils.js';
import { CommonJRPCProvider } from './providers/base-provider/CommonJRPCProvider.js';
import { authConnector } from './connectors/auth-connector/authConnector.js';
import { walletServicesPlugin } from './plugins/wallet-services-plugin/plugin.js';
import { storageAvailable } from './base/connector/utils.js';
import { metaMaskConnector } from './connectors/metamask-connector/metamaskConnector.js';
import { WalletInitializationError, WalletLoginError } from './base/errors/index.js';
import { log } from './base/loglevel.js';
import { WALLET_CONNECTORS } from './base/wallet/index.js';
import { CONNECTOR_NAMESPACES } from './base/chain/IChainInterface.js';
import { CONNECTED_STATUSES, CAN_AUTHORIZE_STATUSES } from './base/connector/connectorStatus.js';
import { PLUGIN_STATUS, PLUGIN_NAMESPACES } from './base/plugin/IPlugin.js';
const _excluded = ["walletScope"];
class Web3AuthNoModal extends SafeEventEmitter {
constructor(options, initialState) {
super();
_defineProperty(this, "coreOptions", void 0);
_defineProperty(this, "status", 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", LOGIN_MODE.NO_MODAL);
if (!options.clientId) throw WalletInitializationError.invalidParams("Please provide a valid clientId in constructor");
if (options.enableLogging) log.enableAll();else log.setLevel("error");
if (!options.storageType) options.storageType = "local";
this.coreOptions = options;
this.storage = this.getStorageMethod();
this.analytics = new Analytics();
if (options.disableAnalytics) {
this.analytics.disable();
}
this.analytics.setGlobalProperties({
integration_type: 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 === CONNECTOR_INITIAL_AUTHENTICATION_MODE.CONNECT_AND_SIGN ? CONNECTOR_STATUS.AUTHORIZED : 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 !== 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_SDK_TYPE.WEB_NO_MODAL,
sdk_version: 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 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 serializeError(e);
log.error("Failed to fetch project configurations", error);
throw 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 withAbort(() => this.setupCommonJRPCProvider(), signal);
// initialize connectors
this.on(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 withAbort(() => Promise.all(newConnectors.map(this.setupConnector.bind(this))), signal, onAbortHandler);
// emit connector ready event
if (this.status === CONNECTOR_STATUS.NOT_READY) {
this.status = CONNECTOR_STATUS.READY;
this.emit(CONNECTOR_EVENTS.READY);
}
});
await withAbort(() => this.loadConnectors({
projectConfig
}), signal);
await withAbort(() => this.initPlugins(), signal);
// track completion event
const authConnector = this.getConnector(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_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_EVENTS.SDK_INITIALIZATION_FAILED, _objectSpread(_objectSpread(_objectSpread({}, trackData), getErrorAnalyticsProperties(error)), {}, {
duration: Date.now() - startTime
}));
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 === 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 WalletInitializationError.invalidParams("Invalid chainId");
if (CONNECTED_STATUSES.includes(this.status) && this.connectedConnector) {
await this.connectedConnector.switchChain(params);
return;
}
if (this.commonJRPCProvider) {
await this.commonJRPCProvider.switchChain(params);
return;
}
throw 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 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 === CONNECTOR_INITIAL_AUTHENTICATION_MODE.CONNECT_AND_SIGN
});
// track connection started event
const startTime = Date.now();
let eventData;
if (connectorName === WALLET_CONNECTORS.AUTH) {
var _authInstance;
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: 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: (_authInstance = connector.authInstance) === null || _authInstance === void 0 || (_authInstance = _authInstance.options) === null || _authInstance === void 0 ? void 0 : _authInstance.uxMode
};
} else {
eventData = {
connector: connectorName,
connector_type: connector.type,
is_injected: connector.isInjected,
chain_id: getCaipChainId(initialChain),
chain_name: initialChain.displayName,
chain_namespace: initialChain.chainNamespace
};
}
// track connection started event
this.analytics.track(ANALYTICS_EVENTS.CONNECTION_STARTED, eventData);
return new Promise((resolve, reject) => {
let connectedEventCompleted = false;
let authorizedEventReceived = false;
const cleanup = () => {
this.removeListener(CONNECTOR_EVENTS.CONNECTED, onConnected);
this.removeListener(CONNECTOR_EVENTS.ERRORED, onErrored);
this.removeListener(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_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_EVENTS.CONNECTION_FAILED, _objectSpread(_objectSpread(_objectSpread({}, eventData), getErrorAnalyticsProperties(err)), {}, {
duration: Date.now() - startTime
}));
cleanup();
reject(err);
};
this.once(CONNECTOR_EVENTS.CONNECTED, onConnected);
if (finalLoginParams.getIdentityToken) {
this.once(CONNECTOR_EVENTS.AUTHORIZED, onAuthorized);
}
this.once(CONNECTOR_EVENTS.ERRORED, onErrored);
connector.connect(finalLoginParams);
this.setCurrentChain(initialChain.chainId);
});
}
async logout(options = {
cleanup: false
}) {
if (!CONNECTED_STATUSES.includes(this.status) || !this.connectedConnector) throw WalletLoginError.notConnectedError(`No wallet is connected`);
if (this.connectedConnector.status === CONNECTOR_STATUS.DISCONNECTING) return;
await this.connectedConnector.disconnect(options);
}
async getUserInfo() {
var _this$connectedConnec;
log.debug("Getting user info", this.status, (_this$connectedConnec = this.connectedConnector) === null || _this$connectedConnec === void 0 ? void 0 : _this$connectedConnec.name);
if (!CAN_AUTHORIZE_STATUSES.includes(this.status) || !this.connectedConnector) throw WalletLoginError.notConnectedError(`No wallet is connected`);
return this.connectedConnector.getUserInfo();
}
async enableMFA(loginParams) {
var _authConnector$authIn2;
if (!CONNECTED_STATUSES.includes(this.status) || !this.connectedConnector) throw WalletLoginError.notConnectedError(`No wallet is connected`);
if (this.connectedConnector.name !== WALLET_CONNECTORS.AUTH) throw 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_EVENTS.MFA_ENABLEMENT_STARTED, trackData);
await this.connectedConnector.enableMFA(loginParams);
} catch (error) {
this.analytics.track(ANALYTICS_EVENTS.MFA_ENABLEMENT_FAILED, _objectSpread(_objectSpread({}, trackData), getErrorAnalyticsProperties(error)));
throw error;
}
}
async manageMFA(loginParams) {
var _authConnector$authIn3;
if (!CONNECTED_STATUSES.includes(this.status) || !this.connectedConnector) throw WalletLoginError.notConnectedError(`No wallet is connected`);
if (this.connectedConnector.name !== WALLET_CONNECTORS.AUTH) throw 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_EVENTS.MFA_MANAGEMENT_SELECTED, trackData);
await this.connectedConnector.manageMFA(loginParams);
} catch (error) {
this.analytics.track(ANALYTICS_EVENTS.MFA_MANAGEMENT_FAILED, _objectSpread(_objectSpread({}, trackData), getErrorAnalyticsProperties(error)));
throw error;
}
}
async getIdentityToken() {
if (!CAN_AUTHORIZE_STATUSES.includes(this.status) || !this.connectedConnector) throw WalletLoginError.notConnectedError(`No wallet is connected`);
const trackData = {
connector: this.connectedConnector.name
};
try {
this.analytics.track(ANALYTICS_EVENTS.IDENTITY_TOKEN_STARTED, trackData);
const identityToken = await this.connectedConnector.getIdentityToken();
this.analytics.track(ANALYTICS_EVENTS.IDENTITY_TOKEN_COMPLETED, trackData);
return identityToken;
} catch (error) {
this.analytics.track(ANALYTICS_EVENTS.IDENTITY_TOKEN_FAILED, _objectSpread(_objectSpread({}, trackData), 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) {
log.error("chain info not found. Please configure chains on dashboard at https://dashboard.web3auth.io");
throw WalletInitializationError.invalidParams("Please configure chains on dashboard at https://dashboard.web3auth.io");
}
const validChainNamespaces = new Set(Object.values(CHAIN_NAMESPACES));
for (const chain of this.coreOptions.chains) {
if (!chain.chainNamespace || !validChainNamespaces.has(chain.chainNamespace)) {
log.error(`Please provide a valid chainNamespace in chains for chain ${chain.chainId}`);
throw WalletInitializationError.invalidParams(`Please provide a valid chainNamespace in chains for chain ${chain.chainId}`);
}
if (chain.chainNamespace !== CHAIN_NAMESPACES.OTHER && !isHexStrict(chain.chainId)) {
log.error(`Please provide a valid chainId in chains for chain ${chain.chainId}`);
throw WalletInitializationError.invalidParams(`Please provide a valid chainId as hex string in chains for chain ${chain.chainId}`);
}
if (chain.chainNamespace !== CHAIN_NAMESPACES.OTHER) {
try {
new URL(chain.rpcTarget);
} catch (error) {
// TODO: add support for chain.wsTarget
log.error(`Please provide a valid rpcTarget in chains for chain ${chain.chainId}`, error);
throw 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) {
log.error("Please configure chains for smart accounts on dashboard at https://dashboard.web3auth.io");
throw WalletInitializationError.invalidParams("Please configure chains for smart accounts on dashboard at https://dashboard.web3auth.io");
}
for (const chain of this.coreOptions.accountAbstractionConfig.chains) {
if (!isHexStrict(chain.chainId)) {
log.error(`Please provide a valid chainId in accountAbstractionConfig.chains for chain ${chain.chainId}`);
throw 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) {
log.error(`Please provide a valid bundlerConfig.url in accountAbstractionConfig.chains for chain ${chain.chainId}`, error);
throw WalletInitializationError.invalidParams(`Please provide a valid bundlerConfig.url in accountAbstractionConfig.chains for chain ${chain.chainId}`);
}
if (!chainMap.has(chain.chainId)) {
log.error(`Please provide chain config for AA chain in accountAbstractionConfig.chains for chain ${chain.chainId}`);
throw 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 === SMART_ACCOUNT_WALLET_SCOPE.ALL;
}
}
initUIConfig(projectConfig) {
this.coreOptions.uiConfig = deepmerge.all([{
mode: "light",
uxMode: UX_MODE.POPUP
}, 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 && !isHexStrict(this.coreOptions.defaultChainId)) throw 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 = 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 ? CONFIRMATION_STRATEGY.MODAL : 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 => 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 => 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 ? 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
}, getWhitelabelAnalyticsProperties(this.coreOptions.uiConfig)), getAaAnalyticsProperties(this.coreOptions.accountAbstractionConfig)), getWalletServicesAnalyticsProperties(this.coreOptions.walletServicesConfig));
} catch (error) {
log.error("Failed to get initialization track data", error);
return {};
}
}
async setupCommonJRPCProvider() {
this.commonJRPCProvider = await 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 === CONNECTOR_INITIAL_AUTHENTICATION_MODE.CONNECT_AND_SIGN
});
} catch (e) {
log.error(e, connector.name);
}
}
async loadConnectors({
projectConfig,
modalMode
}) {
var _this$coreOptions$mul;
// always add auth connector
const connectorFns = [...(this.coreOptions.connectors || []), 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 (isBrowser() && chainNamespaces.has(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(modalMode ? {
headless: true
} : undefined));
}
if (isMipdEnabled && isBrowser()) {
// Solana chains
if (chainNamespaces.has(CHAIN_NAMESPACES.SOLANA)) {
const {
createSolanaMipd,
hasSolanaWalletStandardFeatures,
walletStandardConnector
} = await import('./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(CHAIN_NAMESPACES.EIP155)) {
const {
createMipd,
injectedEvmConnector
} = await import('./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 (isBrowser() && isExternalWalletEnabled && (chainNamespaces.has(CHAIN_NAMESPACES.SOLANA) || chainNamespaces.has(CHAIN_NAMESPACES.EIP155))) {
const {
walletConnectV2Connector
} = await import('./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 === CHAIN_NAMESPACES.EIP155 || x.chainNamespace === CHAIN_NAMESPACES.SOLANA);
if (isWsSupportedChain) {
pluginFns.push(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(CONNECTOR_EVENTS.CONNECTORS_UPDATED, {
connectors: newConnectors
});
}
}
subscribeToConnectorEvents(connector) {
connector.on(CONNECTOR_EVENTS.CONNECTED, async data => {
var _this$currentChain3, _accountAbstractionCo;
if (!this.commonJRPCProvider) throw 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 WalletLoginError.connectionError("No idToken found");
this.setState({
idToken: data.idToken
});
} catch (error) {
log.error(error);
this.status = CONNECTOR_STATUS.ERRORED;
this.emit(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) === 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 === WALLET_CONNECTORS.AUTH || this.coreOptions.useAAWithExternalWallet)) {
var _accountAbstractionCo2;
const {
accountAbstractionProvider,
toEoaProvider
} = await import('./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 === 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 === 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 !== WALLET_CONNECTORS.AUTH && this.coreOptions.useAAWithExternalWallet) {
finalProvider = this.aaProvider;
}
}
this.commonJRPCProvider.updateProviderEngineProxy(finalProvider);
this.setState({
connectedConnectorName: data.connector
});
this.cacheWallet(data.connector);
this.status = CONNECTOR_STATUS.CONNECTED;
log.debug("connected", this.status, this.connectedConnectorName);
this.connectToPlugins(_objectSpread(_objectSpread({}, data), {}, {
connector: data.connector
}));
this.emit(CONNECTOR_EVENTS.CONNECTED, _objectSpread(_objectSpread({}, data), {}, {
loginMode: this.loginMode
}));
});
connector.on(CONNECTOR_EVENTS.DISCONNECTED, async () => {
// re-setup commonJRPCProvider
this.commonJRPCProvider.removeAllListeners();
this.setupCommonJRPCProvider();
// get back to ready state for rehydrating.
this.status = CONNECTOR_STATUS.READY;
const cachedConnector = this.state.cachedConnector;
if (this.connectedConnectorName === cachedConnector) {
this.clearCache();
}
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 !== 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;
log.error(error);
});
}));
this.setState({
connectedConnectorName: null
});
this.emit(CONNECTOR_EVENTS.DISCONNECTED);
});
connector.on(CONNECTOR_EVENTS.CONNECTING, data => {
this.status = CONNECTOR_STATUS.CONNECTING;
this.emit(CONNECTOR_EVENTS.CONNECTING, data);
log.debug("connecting", this.status, this.connectedConnectorName);
});
connector.on(CONNECTOR_EVENTS.ERRORED, data => {
this.status = CONNECTOR_STATUS.ERRORED;
this.clearCache();
this.emit(CONNECTOR_EVENTS.ERRORED, data, this.loginMode);
log.debug("errored", this.status, this.connectedConnectorName);
});
connector.on(CONNECTOR_EVENTS.REHYDRATION_ERROR, error => {
this.status = CONNECTOR_STATUS.READY;
this.clearCache();
this.emit(CONNECTOR_EVENTS.REHYDRATION_ERROR, error);
});
connector.on(CONNECTOR_EVENTS.CONNECTOR_DATA_UPDATED, data => {
log.debug("connector data updated", data);
this.emit(CONNECTOR_EVENTS.CONNECTOR_DATA_UPDATED, data);
});
connector.on(CONNECTOR_EVENTS.CACHE_CLEAR, data => {
log.debug("connector cache clear", data);
this.clearCache();
});
connector.on(CONNECTOR_EVENTS.MFA_ENABLED, isMFAEnabled => {
var _authConnector$authIn4;
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_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(CONNECTOR_EVENTS.MFA_ENABLED, isMFAEnabled);
});
connector.on(CONNECTOR_EVENTS.AUTHORIZING, data => {
this.status = CONNECTOR_STATUS.AUTHORIZING;
this.emit(CONNECTOR_EVENTS.AUTHORIZING, data);
log.debug("authorizing", this.status, this.connectedConnectorName);
});
connector.on(CONNECTOR_EVENTS.AUTHORIZED, data => {
this.status = CONNECTOR_STATUS.AUTHORIZED;
this.setState({
idToken: data.identityTokenInfo.idToken
});
this.emit(CONNECTOR_EVENTS.AUTHORIZED, data);
log.debug("authorized", this.status, this.connectedConnectorName);
});
}
checkInitRequirements() {
if (this.status === CONNECTOR_STATUS.READY) throw 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 === 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 !== CONNECTOR_NAMESPACES.MULTICHAIN) {
initialChain = this.coreOptions.chains.find(x => x.chainNamespace === connector.connectorNamespace);
if (!initialChain) throw 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 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 !== 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 === 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;
}
log.error(error);
}
});
}
setState(newState) {
this.state = _objectSpread(_objectSpread({}, this.state), newState);
this.storage.setItem(WEB3AUTH_STATE_STORAGE_KEY, JSON.stringify(this.state));
}
loadState(initialState) {
if (initialState) {
this.state = initialState;
return;
}
const state = this.storage.getItem(WEB3AUTH_STATE_STORAGE_KEY);
if (!state) return;
this.state = deserialize(state);
}
getStorageMethod() {
if (this.coreOptions.ssr || this.coreOptions.storageType === "cookies") return cookieStorage({
expiry: this.coreOptions.sessionTime
});
if (this.coreOptions.storageType === "session" && storageAvailable("sessionStorage")) return window.sessionStorage;
if (this.coreOptions.storageType === "local" && storageAvailable("localStorage")) return window.localStorage;
// If no storage is available, use a memory store.
return new MemoryStore();
}
}
export { Web3AuthNoModal };