@web3auth/no-modal
Version:
Multi chain wallet aggregator for web3Auth
266 lines (262 loc) • 11.3 kB
JavaScript
'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;