@web3auth/no-modal
Version:
Multi chain wallet aggregator for web3Auth
582 lines (578 loc) • 28.2 kB
JavaScript
'use strict';
var _objectSpread = require('@babel/runtime/helpers/objectSpread2');
var _defineProperty = require('@babel/runtime/helpers/defineProperty');
var connectEvm = require('@metamask/connect-evm');
var connectMultichain = require('@metamask/connect-multichain');
var connectSolana = require('@metamask/connect-solana');
var baseControllers = require('@toruslabs/base-controllers');
var metadataHelpers = require('@toruslabs/metadata-helpers');
var features = require('@wallet-standard/features');
var wsEmbed = require('@web3auth/ws-embed');
var siwe = require('viem/siwe');
var analytics = require('../../base/analytics.js');
var IChainInterface = require('../../base/chain/IChainInterface.js');
var baseConnector = require('../../base/connector/baseConnector.js');
require('../../base/connector/connectorStatus.js');
var constants = require('../../base/connector/constants.js');
require('@web3auth/auth');
require('jwt-decode');
require('../../base/constants.js');
var index$1 = require('../../base/errors/index.js');
var loglevel = require('../../base/loglevel.js');
require('../../base/plugin/errors.js');
require('../../base/plugin/IPlugin.js');
var utils = require('../../base/utils.js');
var index = require('../../base/wallet/index.js');
var utils$1 = require('../utils.js');
var solana = require('../../base/wallet/solana.js');
// `@metamask/connect-evm` announces its SDK-backed provider over EIP-6963 with this
// RDNS value. Web3Auth uses the dedicated `metaMaskConnector` for MetaMask, so MIPD
// discovery should ignore this provider to avoid treating the QR/deeplink transport
// as a native injected wallet.
const METAMASK_ERC_6963_PROVIDER_RDNS = "io.metamask.mmc";
class MetaMaskConnector extends baseConnector.BaseConnector {
constructor(connectorOptions) {
super(connectorOptions);
_defineProperty(this, "connectorNamespace", IChainInterface.CONNECTOR_NAMESPACES.MULTICHAIN);
_defineProperty(this, "currentChainNamespace", baseControllers.CHAIN_NAMESPACES.OTHER);
_defineProperty(this, "type", constants.CONNECTOR_CATEGORY.EXTERNAL);
_defineProperty(this, "name", index.WALLET_CONNECTORS.METAMASK);
_defineProperty(this, "status", constants.CONNECTOR_STATUS.NOT_READY);
_defineProperty(this, "evmClient", null);
_defineProperty(this, "evmProvider", null);
_defineProperty(this, "solanaClient", null);
_defineProperty(this, "solanaProvider", null);
_defineProperty(this, "initializationPromise", null);
_defineProperty(this, "multichainClient", null);
_defineProperty(this, "connectorSettings", void 0);
_defineProperty(this, "analytics", void 0);
this.connectorSettings = connectorOptions.connectorSettings;
this.analytics = connectorOptions.analytics;
}
get provider() {
if (this.status !== constants.CONNECTOR_STATUS.NOT_READY && this.evmProvider) {
return this.evmProvider;
}
return null;
}
get solanaWallet() {
return this.solanaProvider;
}
async init(options) {
var _this$connectorSettin, _this$connectorSettin2, _this$connectorSettin3, _this$connectorSettin4, _this$connectorSettin5, _this$connectorSettin6, _this$connectorSettin7, _this$connectorSettin8, _this$connectorSettin9;
const chainConfig = this.coreOptions.chains.find(x => x.chainId === options.chainId);
super.checkInitializationRequirements({
chainConfig
});
// Build supported networks in CAIP-2 format for multichain client
const caipSupportedNetworks = {};
for (const chain of this.coreOptions.chains) {
caipSupportedNetworks[utils.getCaipChainId(chain)] = chain.rpcTarget;
}
// Build supported networks in hex format for EVM client
const hexSupportedNetworks = {};
for (const chain of this.coreOptions.chains) {
if (chain.chainNamespace === baseControllers.CHAIN_NAMESPACES.EIP155) {
hexSupportedNetworks[chain.chainId] = chain.rpcTarget;
}
}
// Build supported networks for Solana client (MetaMask expects mainnet | testnet | devnet keys)
const solanaSupportedNetworks = {};
for (const chain of this.coreOptions.chains) {
if (chain.chainNamespace !== baseControllers.CHAIN_NAMESPACES.SOLANA) continue;
const solanaChainId = solana.getSolanaChainByChainConfig(chain);
if (!solanaChainId) continue;
const networkName = solanaChainId.split(":")[1];
if (networkName) solanaSupportedNetworks[networkName] = chain.rpcTarget;
}
// Detect app metadata
const appName = utils$1.getSiteName(window) || ((_this$connectorSettin = this.connectorSettings) === null || _this$connectorSettin === void 0 || (_this$connectorSettin = _this$connectorSettin.dapp) === null || _this$connectorSettin === void 0 ? void 0 : _this$connectorSettin.name) || "web3auth";
const appUrl = ((_this$connectorSettin2 = this.connectorSettings) === null || _this$connectorSettin2 === void 0 || (_this$connectorSettin2 = _this$connectorSettin2.dapp) === null || _this$connectorSettin2 === void 0 ? void 0 : _this$connectorSettin2.url) || window.location.origin || "https://web3auth.io";
const appIconUrl = (_this$connectorSettin3 = this.connectorSettings) === null || _this$connectorSettin3 === void 0 || (_this$connectorSettin3 = _this$connectorSettin3.dapp) === null || _this$connectorSettin3 === void 0 ? void 0 : _this$connectorSettin3.iconUrl;
const dapp = _objectSpread({
name: appName,
url: appUrl
}, appIconUrl && {
iconUrl: appIconUrl
});
const ui = {
preferExtension: (_this$connectorSettin4 = (_this$connectorSettin5 = this.connectorSettings) === null || _this$connectorSettin5 === void 0 || (_this$connectorSettin5 = _this$connectorSettin5.ui) === null || _this$connectorSettin5 === void 0 ? void 0 : _this$connectorSettin5.preferExtension) !== null && _this$connectorSettin4 !== void 0 ? _this$connectorSettin4 : true,
showInstallModal: (_this$connectorSettin6 = (_this$connectorSettin7 = this.connectorSettings) === null || _this$connectorSettin7 === void 0 || (_this$connectorSettin7 = _this$connectorSettin7.ui) === null || _this$connectorSettin7 === void 0 ? void 0 : _this$connectorSettin7.showInstallModal) !== null && _this$connectorSettin6 !== void 0 ? _this$connectorSettin6 : false,
headless: (_this$connectorSettin8 = (_this$connectorSettin9 = this.connectorSettings) === null || _this$connectorSettin9 === void 0 || (_this$connectorSettin9 = _this$connectorSettin9.ui) === null || _this$connectorSettin9 === void 0 ? void 0 : _this$connectorSettin9.headless) !== null && _this$connectorSettin8 !== void 0 ? _this$connectorSettin8 : true
};
let initResolve;
let initReject;
this.initializationPromise = new Promise((resolve, reject) => {
initResolve = resolve;
initReject = reject;
});
const hasEvmChains = Object.keys(hexSupportedNetworks).length > 0;
const hasSolanaChains = Object.keys(solanaSupportedNetworks).length > 0;
try {
var _this$connectorSettin0;
// Initialize the multichain client (singleton) first
this.multichainClient = await connectMultichain.createMultichainClient({
dapp,
api: {
supportedNetworks: caipSupportedNetworks
},
ui,
debug: (_this$connectorSettin0 = this.connectorSettings) === null || _this$connectorSettin0 === void 0 ? void 0 : _this$connectorSettin0.debug,
analytics: {
integrationType: "web3auth",
enabled: !this.coreOptions.disableAnalytics
}
});
// Listen for QR code URI from the multichain client (for mobile wallet connection)
this.multichainClient.on("display_uri", uri => {
if (uri) {
this.updateConnectorData({
uri
});
}
});
// Create the EVM client only when EVM chains are configured
// (createEVMClient requires at least one entry in supportedNetworks)
if (hasEvmChains) {
var _this$connectorSettin1;
this.evmClient = await connectEvm.createEVMClient({
dapp,
eventHandlers: {
accountsChanged: _accounts => {
if (_accounts.length === 0 && this.connected) {
this.disconnect();
}
},
chainChanged: _chainId => {
// Keep Web3Auth state aligned with the wallet's actual EVM chain after connect/switch.
this.updateConnectorData({
chainId: _chainId
});
},
connect: _result => {},
disconnect: () => {
if (this.connected) {
this.disconnect();
}
}
},
api: {
supportedNetworks: hexSupportedNetworks
},
ui,
debug: (_this$connectorSettin1 = this.connectorSettings) === null || _this$connectorSettin1 === void 0 ? void 0 : _this$connectorSettin1.debug,
analytics: {
integrationType: "web3auth",
enabled: !this.coreOptions.disableAnalytics
}
});
this.evmProvider = this.evmClient.getProvider();
}
// Create the Solana client only when Solana chains are configured
if (hasSolanaChains) {
this.solanaClient = await connectSolana.createSolanaClient({
dapp,
api: {
supportedNetworks: solanaSupportedNetworks
},
skipAutoRegister: true,
analytics: {
integrationType: "web3auth",
enabled: !this.coreOptions.disableAnalytics
}
});
this.solanaProvider = this.solanaClient.getWallet();
}
this.isInjected = await connectMultichain.hasExtension();
initResolve();
} catch (error) {
initReject(index$1.WalletLoginError.connectionError("Failed to initialize MetaMask Connect SDK", error));
}
if (!this.multichainClient) {
this.status = constants.CONNECTOR_STATUS.ERRORED;
this.emit(constants.CONNECTOR_EVENTS.ERRORED, new Error("Failed to initialize MetaMask Connect."));
return;
}
const coreStatus = this.multichainClient.status;
// only connect if the multichain client is connected and autoConnect is true (i.e during the rehydration)
if (coreStatus === "connected" && options.autoConnect) {
this.status = constants.CONNECTOR_STATUS.CONNECTED;
this.rehydrated = true;
// Force sync the chain state before connect with rehydrated state.
// during rehydration, `createEVMClient` (from above) cause re-creating session for connected the evm provider,
// triggering `switchChain` internally and unintentionally update the connector data with the new chain id.
// This creates issue in rehydration flow, where Web3Auth connects to the incorrect chain id, not the rehydrated chain id.
// Force sync the chain state with the rehydrated chain id to avoid this issue.
await this.forceSyncChainState(chainConfig);
this.emit(constants.CONNECTOR_EVENTS.CONNECTED, {
connectorName: index.WALLET_CONNECTORS.METAMASK,
reconnected: true,
ethereumProvider: this.evmProvider,
solanaWallet: this.solanaProvider,
connectorNamespace: this.connectorNamespace
});
try {
await this.authorizeOrDisconnect(options.getAuthTokenInfo, options.chainId);
} catch (error) {
this.emit(constants.CONNECTOR_EVENTS.REHYDRATION_ERROR, error);
}
} else if (coreStatus === "connected" || coreStatus === "loaded" || coreStatus === "disconnected" || coreStatus === "pending") {
this.status = constants.CONNECTOR_STATUS.READY;
this.emit(constants.CONNECTOR_EVENTS.READY, index.WALLET_CONNECTORS.METAMASK);
} else {
// Something unexpected happened
this.status = constants.CONNECTOR_STATUS.ERRORED;
this.emit(constants.CONNECTOR_EVENTS.ERRORED, new Error("Failed to initialize MetaMask Connect."));
}
}
async connect({
chainId,
getAuthTokenInfo,
caipAccountIds: caipAccountIdsFromParams
}) {
super.checkConnectionRequirements();
await this.ensureInitialized();
const chainConfig = this.coreOptions.chains.find(x => x.chainId === chainId);
if (!chainConfig) throw index$1.WalletLoginError.connectionError("Chain config is not available");
const scopes = this.coreOptions.chains.map(c => utils.getCaipChainId(c));
const caipAccountIds = caipAccountIdsFromParams || [];
// Skip tracking for rehydration since only new connections are tracked
const shouldTrack = !this.rehydrated;
const startTime = Date.now();
const eventData = {
connector: this.name,
connector_type: this.type,
is_injected: this.isInjected,
chain_id: utils.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) {
this.status = constants.CONNECTOR_STATUS.CONNECTING;
this.emit(constants.CONNECTOR_EVENTS.CONNECTING, {
connector: index.WALLET_CONNECTORS.METAMASK
});
const evmConnectedPromise = new Promise(resolve => {
if (this.evmClient.status === "connected") {
resolve();
} else {
var _this$evmProvider;
// Wait for EVM provider to be ready
(_this$evmProvider = this.evmProvider) === null || _this$evmProvider === void 0 || _this$evmProvider.once("connect", () => {
resolve();
});
}
});
// Connect using the multichain client
await this.multichainClient.connect(scopes, caipAccountIds, {
solana_accountChanged_notifications: true
});
// Solana wallet-standard: `standard:events` change is not emitted from multichain
// connect alone — MetamaskWallet syncs accounts via `standard:connect` → updateSession.
if (this.solanaProvider) {
await this.solanaProvider.features[features.StandardConnect].connect();
}
// Wait for EVM provider to be ready
if (this.evmProvider) {
await evmConnectedPromise;
}
}
// sync the chain state after connect
// metamask might not be connected to the requested chain, so we need to sync the chain state to/from Web3Auth state after connect.
await this.forceSyncChainState(chainConfig);
// check if connected
if (this.multichainClient.status !== "connected") {
throw index$1.WalletLoginError.notConnectedError("Failed to connect with MetaMask wallet");
}
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
}));
}
this.emit(constants.CONNECTOR_EVENTS.CONNECTED, {
connectorName: index.WALLET_CONNECTORS.METAMASK,
reconnected: this.rehydrated,
ethereumProvider: this.evmProvider,
solanaWallet: this.solanaProvider,
connectorNamespace: this.connectorNamespace
});
await this.authorizeOrDisconnect(getAuthTokenInfo, chainId);
return {
ethereumProvider: this.evmProvider,
solanaWallet: this.solanaProvider,
connectorName: this.name,
connectorNamespace: this.connectorNamespace
};
} 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 (index$1.isUserRejectedError(error)) throw index$1.WalletOperationsError.userRejected();
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.multichainClient) throw index$1.WalletLoginError.connectionError("Multichain client is not available");
this.checkDisconnectionRequirements();
await this.clearMultichainWalletSessions();
// Disconnect using the multichain client
if (this.multichainClient.status === "connected") {
await this.multichainClient.disconnect();
}
if (options.cleanup) {
this.status = constants.CONNECTOR_STATUS.NOT_READY;
this.initializationPromise = null;
this.multichainClient = null;
this.evmClient = null;
this.evmProvider = null;
this.solanaClient = null;
this.solanaProvider = null;
} else {
// Ready to be connected again
this.status = constants.CONNECTOR_STATUS.READY;
}
this.rehydrated = false;
this.emit(constants.CONNECTOR_EVENTS.DISCONNECTED, {
connector: index.WALLET_CONNECTORS.METAMASK
});
}
async getAuthTokenInfo(chainId) {
if (!this.canAuthorize) throw index$1.WalletLoginError.notConnectedError();
// In multichain sessions both providers can exist at the same time, so auth must
// follow the caller-selected chain instead of inferring from provider availability.
const activeChainConfig = this.resolveAuthChainConfig(chainId);
if (!activeChainConfig) throw index$1.WalletLoginError.connectionError("Chain config is not available");
const {
chainNamespace
} = activeChainConfig;
const accounts = chainNamespace === baseControllers.CHAIN_NAMESPACES.SOLANA && this.solanaProvider ? this.solanaProvider.accounts.map(a => a.address) : this.evmProvider ? await this.evmProvider.request({
method: wsEmbed.EVM_METHOD_TYPES.GET_ACCOUNTS
}) : [];
if (accounts && accounts.length > 0) {
const cached = await this.getCachedOrNullAuthTokenInfo(accounts[0]);
if (cached) return cached;
}
this.status = constants.CONNECTOR_STATUS.AUTHORIZING;
this.emit(constants.CONNECTOR_EVENTS.AUTHORIZING, {
connector: index.WALLET_CONNECTORS.METAMASK
});
const authServer = utils.citadelServerUrl(this.coreOptions.authBuildEnv);
const {
challenge,
signature
} = await this.generateChallengeAndSign(authServer, accounts, activeChainConfig.chainId);
return this.verifyAndAuthorize({
chainNamespace,
signedMessage: signature,
challenge,
authServer
});
}
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);
await this.ensureInitialized();
const targetChainConfig = this.coreOptions.chains.find(c => c.chainId === params.chainId);
if (!targetChainConfig) throw index$1.WalletLoginError.connectionError("Chain config is not available");
if (targetChainConfig.chainNamespace === baseControllers.CHAIN_NAMESPACES.SOLANA) {
if (!this.solanaWallet) {
throw index$1.WalletLoginError.unsupportedOperation("switchChain requires a Solana client, but no Solana chains are configured.");
}
// For solana case, we don't have the `switchChain` method like `evmClient`.
// So, we just need to sync with the connected solana chain to the Web3Auth state.
await this.forceSyncChainState(targetChainConfig);
return;
}
if (!this.evmClient) {
throw index$1.WalletLoginError.unsupportedOperation("switchChain requires an EVM client, but no EVM chains are configured.");
}
const chainConfiguration = targetChainConfig ? {
chainId: params.chainId,
chainName: targetChainConfig.displayName,
rpcUrls: [targetChainConfig.rpcTarget],
blockExplorerUrls: targetChainConfig.blockExplorerUrl ? [targetChainConfig.blockExplorerUrl] : undefined,
nativeCurrency: {
name: targetChainConfig.tickerName,
symbol: targetChainConfig.ticker,
decimals: targetChainConfig.decimals || 18
},
iconUrls: targetChainConfig.logo ? [targetChainConfig.logo] : undefined
} : undefined;
await this.evmClient.switchChain({
chainId: params.chainId,
chainConfiguration
});
this.updateConnectorData({
chainId: params.chainId
});
}
async generateChallengeAndSign(authServerUrl, accounts, chainId) {
const activeChainConfig = this.resolveAuthChainConfig(chainId);
if (!activeChainConfig) throw index$1.WalletLoginError.connectionError("Chain config is not available");
const {
chainNamespace
} = activeChainConfig;
const accountsToUse = accounts || (chainNamespace === baseControllers.CHAIN_NAMESPACES.SOLANA && this.solanaProvider ? this.solanaProvider.accounts.map(a => a.address) : this.evmProvider ? await this.evmProvider.request({
method: wsEmbed.EVM_METHOD_TYPES.GET_ACCOUNTS
}) : []);
if (!accountsToUse || accountsToUse.length === 0) {
throw index$1.WalletLoginError.notConnectedError("Not connected with wallet, Please login/connect first");
}
const authServer = authServerUrl || utils.citadelServerUrl(this.coreOptions.authBuildEnv);
const payload = {
domain: window.location.origin,
uri: window.location.href,
address: accountsToUse[0],
chainId: parseInt(activeChainConfig.chainId, 16),
version: "1",
nonce: siwe.generateSiweNonce(),
issuedAt: new Date().toISOString()
};
const challenge = await baseControllers.signChallenge(payload, chainNamespace, authServer);
let signedMessage;
if (chainNamespace === baseControllers.CHAIN_NAMESPACES.SOLANA && this.solanaProvider) {
signedMessage = await solana.walletSignMessage(this.solanaProvider, challenge, accountsToUse[0]);
} else if (this.evmProvider) {
const hexChallenge = metadataHelpers.bytesToHexPrefixedString(metadataHelpers.utf8ToBytes(challenge));
signedMessage = await this.evmProvider.request({
method: wsEmbed.EVM_METHOD_TYPES.PERSONAL_SIGN,
params: [hexChallenge, accountsToUse[0]]
});
} else {
throw index$1.WalletLoginError.notConnectedError("No provider available for signing");
}
return {
challenge,
signature: signedMessage,
chainNamespace
};
}
async enableMFA() {
throw new Error("Method Not implemented");
}
async manageMFA() {
throw new Error("Method Not implemented");
}
/**
* Ensures the connector is initialized
*/
async ensureInitialized() {
if (!this.initializationPromise) {
throw index$1.WalletLoginError.notConnectedError("Connector is not initialized. Call init() first.");
}
await this.initializationPromise;
}
/**
* Syncs the chain state with the Web3Auth state.
* @param chainConfig - The chain config to sync.
*/
async forceSyncChainState(chainConfig) {
var _this$evmProvider2;
// EVM connectors can switch chains, so align the wallet with the requested chain
// before Web3Auth persists the active chain in controller state.
if (chainConfig.chainNamespace === baseControllers.CHAIN_NAMESPACES.EIP155 && ((_this$evmProvider2 = this.evmProvider) === null || _this$evmProvider2 === void 0 ? void 0 : _this$evmProvider2.chainId) !== chainConfig.chainId) {
await this.switchChain({
chainId: chainConfig.chainId
}, true);
} else if (chainConfig.chainNamespace === baseControllers.CHAIN_NAMESPACES.SOLANA) {
// For solana case, metamask connect the first available scope in priority order: mainnet > devnet > testnet.
// So, if the user requested chain is different from the connected chain,
// we need to update the connector data with the connected chain id to keep the Web3Auth state aligned.
if ("scope" in this.solanaProvider && typeof this.solanaProvider.scope === "string") {
var _this$coreOptions$cha;
const connectedSolChain = this.solanaProvider.scope;
const connectedChainConfig = (_this$coreOptions$cha = this.coreOptions.chains) === null || _this$coreOptions$cha === void 0 ? void 0 : _this$coreOptions$cha.find(chain => {
return utils.getCaipChainId(chain) === connectedSolChain && chain.chainNamespace === baseControllers.CHAIN_NAMESPACES.SOLANA;
});
if (!connectedChainConfig) {
throw index$1.WalletLoginError.connectionError("Connected chain is not available in the chains config");
}
this.updateConnectorData({
chainId: chainConfig.chainId
});
}
}
}
resolveAuthChainConfig(chainId) {
var _this$evmProvider3, _this$coreOptions$cha2;
if (chainId) {
return this.coreOptions.chains.find(x => x.chainId === chainId);
}
const evmChainId = ((_this$evmProvider3 = this.evmProvider) === null || _this$evmProvider3 === void 0 ? void 0 : _this$evmProvider3.chainId) || ((_this$coreOptions$cha2 = this.coreOptions.chains.find(x => x.chainNamespace === baseControllers.CHAIN_NAMESPACES.EIP155)) === null || _this$coreOptions$cha2 === void 0 ? void 0 : _this$coreOptions$cha2.chainId);
const isSolanaOnly = !this.evmProvider && !!this.solanaProvider;
// Keep the old fallback for callers that do not pass a chainId yet.
return isSolanaOnly ? this.coreOptions.chains.find(x => x.chainNamespace === baseControllers.CHAIN_NAMESPACES.SOLANA) : this.evmProvider ? this.coreOptions.chains.find(x => x.chainId === evmChainId) : undefined;
}
async clearMultichainWalletSessions() {
const addresses = new Set();
if (this.evmProvider) {
const evmAccounts = await this.evmProvider.request({
method: wsEmbed.EVM_METHOD_TYPES.GET_ACCOUNTS
});
evmAccounts.forEach(account => addresses.add(account));
}
if (this.solanaProvider) {
this.solanaProvider.accounts.forEach(account => addresses.add(account.address));
}
if (addresses.size === 0) {
await this.clearWalletSession();
return;
}
// MetaMask can authorize both EVM and Solana accounts in one session, but BaseConnector
// caches SIWW tokens under one address at a time. Clear every connected address on disconnect.
for (const address of addresses) {
this.initSessionManager(address);
await this.clearWalletSession().catch(error => {
loglevel.log.error("Failed to clear multichain wallet session", error);
});
}
}
}
/**
* Factory function to create a MetaMask connector
*
* @param params - Configuration options for the MetaMask SDK
* @returns A connector function that creates a MetaMaskConnector instance
*
* @example
* ```typescript
* const connector = metaMaskConnector({
* dapp: { name: 'My DApp', url: 'https://mydapp.com' },
* debug: true,
* });
* ```
*/
const metaMaskConnector = params => {
return ({
coreOptions,
analytics
}) => {
return new MetaMaskConnector({
connectorSettings: params,
coreOptions,
analytics
});
};
};
exports.METAMASK_ERC_6963_PROVIDER_RDNS = METAMASK_ERC_6963_PROVIDER_RDNS;
exports.metaMaskConnector = metaMaskConnector;