UNPKG

@web3auth/no-modal

Version:
266 lines (262 loc) 11.3 kB
'use strict'; var _objectSpread = require('@babel/runtime/helpers/objectSpread2'); var _defineProperty = require('@babel/runtime/helpers/defineProperty'); var sdk = require('@metamask/sdk'); var baseControllers = require('@toruslabs/base-controllers'); var deepmerge = require('deepmerge'); var analytics = require('../../base/analytics.js'); var IChainInterface = require('../../base/chain/IChainInterface.js'); require('@web3auth/auth'); var index$1 = require('../../base/errors/index.js'); var index = require('../../base/wallet/index.js'); require('../../base/connector/connectorStatus.js'); var constants = require('../../base/connector/constants.js'); require('jwt-decode'); require('../../base/loglevel.js'); require('../../base/plugin/errors.js'); require('../../base/plugin/IPlugin.js'); var utils$1 = require('../../base/utils.js'); var baseEvmConnector = require('../base-evm-connector/baseEvmConnector.js'); var utils = require('../utils.js'); class MetaMaskConnector extends baseEvmConnector.BaseEvmConnector { constructor(connectorOptions) { super(connectorOptions); _defineProperty(this, "connectorNamespace", IChainInterface.CONNECTOR_NAMESPACES.EIP155); _defineProperty(this, "currentChainNamespace", baseControllers.CHAIN_NAMESPACES.EIP155); _defineProperty(this, "type", constants.CONNECTOR_CATEGORY.EXTERNAL); _defineProperty(this, "name", index.WALLET_CONNECTORS.METAMASK); _defineProperty(this, "status", constants.CONNECTOR_STATUS.NOT_READY); _defineProperty(this, "metamaskProvider", null); _defineProperty(this, "metamaskSDK", null); _defineProperty(this, "metamaskOptions", void 0); _defineProperty(this, "analytics", void 0); this.metamaskOptions = connectorOptions.connectorSettings; this.analytics = connectorOptions.analytics; } get provider() { if (this.status !== constants.CONNECTOR_STATUS.NOT_READY && this.metamaskProvider) { return this.metamaskProvider; } return null; } set provider(_) { throw new Error("Not implemented"); } async init(options) { await super.init(options); const chainConfig = this.coreOptions.chains.find(x => x.chainId === options.chainId); super.checkInitializationRequirements({ chainConfig }); // Detect app metadata const iconUrl = await utils.getSiteIcon(window); // TODO: handle ssr const appMetadata = { name: utils.getSiteName(window) || "web3auth", url: window.location.origin || "https://web3auth.io", iconUrl: iconUrl !== null && iconUrl !== void 0 ? iconUrl : undefined }; // initialize the MetaMask SDK const metamaskOptions = deepmerge(this.metamaskOptions || {}, { dappMetadata: appMetadata }); this.metamaskSDK = new sdk.MetaMaskSDK(_objectSpread(_objectSpread({}, metamaskOptions), {}, { _source: "web3auth", preferDesktop: true })); // Work around: in case there is an existing SDK instance in memory (window.mmsdk exists), it won't initialize the new SDK instance again // and return the existing instance instead of undefined (this is an assumption, not sure if it's a bug or feature of the MetaMask SDK) const initResult = await this.metamaskSDK.init(); if (initResult) { this.metamaskSDK = initResult; } this.isInjected = this.metamaskSDK.isExtensionActive(); this.status = constants.CONNECTOR_STATUS.READY; this.emit(constants.CONNECTOR_EVENTS.READY, index.WALLET_CONNECTORS.METAMASK); try { if (options.autoConnect) { this.rehydrated = true; const provider = await this.connect({ chainId: options.chainId, getIdentityToken: options.getIdentityToken }); if (!provider) { this.rehydrated = false; throw index$1.WalletLoginError.connectionError("Failed to rehydrate."); } } } catch (error) { this.emit(constants.CONNECTOR_EVENTS.REHYDRATION_ERROR, error); } } async connect({ chainId, getIdentityToken }) { super.checkConnectionRequirements(); if (!this.metamaskSDK) throw index$1.WalletLoginError.notConnectedError("Connector is not initialized"); const chainConfig = this.coreOptions.chains.find(x => x.chainId === chainId); if (!chainConfig) throw index$1.WalletLoginError.connectionError("Chain config is not available"); // Skip tracking for injected MetaMask since it's handled in connectTo // Skip tracking for rehydration since only new connections are tracked // Only track non-injected MetaMask when connection completes since it auto-initializes to generate QR code const shouldTrack = !this.isInjected && !this.rehydrated; const startTime = Date.now(); const eventData = { connector: this.name, connector_type: this.type, is_injected: this.isInjected, chain_id: utils$1.getCaipChainId(chainConfig), chain_name: chainConfig === null || chainConfig === void 0 ? void 0 : chainConfig.displayName, chain_namespace: chainConfig === null || chainConfig === void 0 ? void 0 : chainConfig.chainNamespace }; try { if (this.status !== constants.CONNECTOR_STATUS.CONNECTING) { var _this$metamaskOptions; this.status = constants.CONNECTOR_STATUS.CONNECTING; this.emit(constants.CONNECTOR_EVENTS.CONNECTING, { connector: index.WALLET_CONNECTORS.METAMASK }); if (!this.metamaskSDK.isExtensionActive() && (_this$metamaskOptions = this.metamaskOptions) !== null && _this$metamaskOptions !== void 0 && _this$metamaskOptions.headless) { // when metamask is not injected and headless is true, broadcast the uri to the login modal this.metamaskSDK.getProvider().on("display_uri", uri => { this.updateConnectorData({ uri }); }); } await this.metamaskSDK.connect(); } this.metamaskProvider = this.metamaskSDK.getProvider(); if (!this.metamaskProvider) throw index$1.WalletLoginError.notConnectedError("Failed to connect with provider"); // switch chain if not connected to the right chain const currentChainId = await this.metamaskProvider.request({ method: "eth_chainId" }); if (currentChainId !== chainConfig.chainId) { await this.switchChain(chainConfig, true); } // handle disconnect event const accountDisconnectHandler = accounts => { if (accounts.length === 0) this.disconnect(); }; this.metamaskProvider.on("accountsChanged", accountDisconnectHandler); this.metamaskProvider.once("disconnect", () => { this.disconnect(); }); this.status = constants.CONNECTOR_STATUS.CONNECTED; // track connection events if (shouldTrack) { var _this$analytics, _this$analytics2; (_this$analytics = this.analytics) === null || _this$analytics === void 0 || _this$analytics.track(analytics.ANALYTICS_EVENTS.CONNECTION_STARTED, eventData); (_this$analytics2 = this.analytics) === null || _this$analytics2 === void 0 || _this$analytics2.track(analytics.ANALYTICS_EVENTS.CONNECTION_COMPLETED, _objectSpread(_objectSpread({}, eventData), {}, { duration: Date.now() - startTime })); } let identityTokenInfo; this.emit(constants.CONNECTOR_EVENTS.CONNECTED, { connector: index.WALLET_CONNECTORS.METAMASK, reconnected: this.rehydrated, provider: this.metamaskProvider, identityTokenInfo }); if (getIdentityToken) { identityTokenInfo = await this.getIdentityToken(); } return this.metamaskProvider; } catch (error) { // ready again to be connected this.status = constants.CONNECTOR_STATUS.READY; if (!this.rehydrated) this.emit(constants.CONNECTOR_EVENTS.ERRORED, error); this.rehydrated = false; // track connection events if (shouldTrack) { var _this$analytics3, _this$analytics4; (_this$analytics3 = this.analytics) === null || _this$analytics3 === void 0 || _this$analytics3.track(analytics.ANALYTICS_EVENTS.CONNECTION_STARTED, eventData); (_this$analytics4 = this.analytics) === null || _this$analytics4 === void 0 || _this$analytics4.track(analytics.ANALYTICS_EVENTS.CONNECTION_FAILED, _objectSpread(_objectSpread(_objectSpread({}, eventData), baseControllers.getErrorAnalyticsProperties(error)), {}, { duration: Date.now() - startTime })); } if (error instanceof index$1.Web3AuthError) throw error; throw index$1.WalletLoginError.connectionError("Failed to login with MetaMask wallet", error); } } async disconnect(options = { cleanup: false }) { if (!this.metamaskProvider) throw index$1.WalletLoginError.connectionError("MetaMask provider is not available"); await super.disconnectSession(); if (typeof this.metamaskProvider.removeAllListeners !== "undefined") this.metamaskProvider.removeAllListeners(); await this.metamaskSDK.terminate(); if (options.cleanup) { this.status = constants.CONNECTOR_STATUS.NOT_READY; this.metamaskProvider = null; } else { // ready to be connected again this.status = constants.CONNECTOR_STATUS.READY; } await super.disconnect(); } async getUserInfo() { if (!this.canAuthorize) throw index$1.WalletLoginError.notConnectedError("Not connected with wallet, Please login/connect first"); return {}; } async switchChain(params, init = false) { super.checkSwitchChainRequirements(params, init); const requestSwitchChain = () => this.metamaskProvider.request({ method: "wallet_switchEthereumChain", params: [{ chainId: params.chainId }] }); try { await requestSwitchChain(); } catch (error) { // If the error code is 4902, the network needs to be added if ((error === null || error === void 0 ? void 0 : error.code) === 4902) { const chainConfig = this.coreOptions.chains.find(x => x.chainId === params.chainId && [baseControllers.CHAIN_NAMESPACES.EIP155].includes(x.chainNamespace)); await this.addChain(chainConfig); await requestSwitchChain(); } else { throw error; } } } async enableMFA() { throw new Error("Method Not implemented"); } async manageMFA() { throw new Error("Method Not implemented"); } async addChain(chainConfig) { if (!this.metamaskProvider) throw index$1.WalletLoginError.connectionError("Injected provider is not available"); await this.metamaskProvider.request({ method: "wallet_addEthereumChain", params: [{ chainId: chainConfig.chainId, chainName: chainConfig.displayName, rpcUrls: [chainConfig.rpcTarget], blockExplorerUrls: [chainConfig.blockExplorerUrl], nativeCurrency: { name: chainConfig.tickerName, symbol: chainConfig.ticker, decimals: chainConfig.decimals || 18 }, iconUrls: [chainConfig.logo] }] }); } } const metaMaskConnector = params => { return ({ coreOptions, analytics }) => { return new MetaMaskConnector({ connectorSettings: params, coreOptions, analytics }); }; }; exports.metaMaskConnector = metaMaskConnector;