UNPKG

@web3auth/wallet-connect-v2-adapter

Version:
956 lines (940 loc) 37.3 kB
import { CHAIN_NAMESPACES, WalletLoginError, log, getChainConfig, BaseAdapter, WALLET_ADAPTERS, ADAPTER_NAMESPACES, ADAPTER_CATEGORY, ADAPTER_STATUS, WalletInitializationError, ADAPTER_EVENTS, Web3AuthError, getSavedToken, checkIfTokenIsExpired, saveToken } from '@web3auth/base'; import _objectSpread from '@babel/runtime/helpers/objectSpread2'; import _defineProperty from '@babel/runtime/helpers/defineProperty'; import { signChallenge, verifySignedChallenge } from '@toruslabs/base-controllers'; import Client from '@walletconnect/sign-client'; import { getAccountsFromNamespaces, parseAccountId, getSdkError, isValidArray } from '@walletconnect/utils'; import base58 from 'bs58'; import deepmerge from 'deepmerge'; import { providerErrors, rpcErrors, JRPCEngine, providerFromEngine } from '@web3auth/auth'; import { BaseProvider } from '@web3auth/base-provider'; import { createEthMiddleware, createJsonRpcClient, createChainSwitchMiddleware } from '@web3auth/ethereum-provider'; import { createSolanaMiddleware, createJsonRpcClient as createJsonRpcClient$1 } from '@web3auth/solana-provider'; let DEFAULT_EIP155_METHODS = /*#__PURE__*/function (DEFAULT_EIP155_METHODS) { DEFAULT_EIP155_METHODS["ETH_SEND_TRANSACTION"] = "eth_sendTransaction"; DEFAULT_EIP155_METHODS["ETH_SIGN_TRANSACTION"] = "eth_signTransaction"; DEFAULT_EIP155_METHODS["ETH_SIGN"] = "eth_sign"; DEFAULT_EIP155_METHODS["PERSONAL_SIGN"] = "personal_sign"; DEFAULT_EIP155_METHODS["ETH_SIGN_TYPED_DATA"] = "eth_signTypedData"; DEFAULT_EIP155_METHODS["ETH_SIGN_TYPED_DATA_V3"] = "eth_signTypedData_v3"; DEFAULT_EIP155_METHODS["ETH_SIGN_TYPED_DATA_V4"] = "eth_signTypedData_v4"; DEFAULT_EIP155_METHODS["ADD_ETHEREUM_CHAIN"] = "wallet_addEthereumChain"; DEFAULT_EIP155_METHODS["SWITCH_ETHEREUM_CHAIN"] = "wallet_switchEthereumChain"; return DEFAULT_EIP155_METHODS; }({}); let DEFAULT_SOLANA_METHODS = /*#__PURE__*/function (DEFAULT_SOLANA_METHODS) { DEFAULT_SOLANA_METHODS["SIGN_TRANSACTION"] = "solana_signTransaction"; DEFAULT_SOLANA_METHODS["SIGN_MESSAGE"] = "solana_signMessage"; return DEFAULT_SOLANA_METHODS; }({}); let DEFAULT_EIP_155_EVENTS = /*#__PURE__*/function (DEFAULT_EIP_155_EVENTS) { DEFAULT_EIP_155_EVENTS["ETH_CHAIN_CHANGED"] = "chainChanged"; DEFAULT_EIP_155_EVENTS["ETH_ACCOUNTS_CHANGED"] = "accountsChanged"; return DEFAULT_EIP_155_EVENTS; }({}); let DEFAULT_SOLANA_EVENTS = /*#__PURE__*/function (DEFAULT_SOLANA_EVENTS) { DEFAULT_SOLANA_EVENTS["SOL_CHAIN_CHANGED"] = "chainChanged"; DEFAULT_SOLANA_EVENTS["SOL_ACCOUNTS_CHANGED"] = "accountsChanged"; return DEFAULT_SOLANA_EVENTS; }({}); const SOLANA_CAIP_CHAIN_MAP = { "0x65": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "0x66": "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z", "0x67": "EtWTRABZaYq6iMfeYKouRu166VU2xqa1" }; /** * Extracts a name for the site from the DOM */ const getSiteName = window => { const { document } = window; const siteName = document.querySelector('head > meta[property="og:site_name"]'); if (siteName) { return siteName.content; } const metaTitle = document.querySelector('head > meta[name="title"]'); if (metaTitle) { return metaTitle.content; } if (document.title && document.title.length > 0) { return document.title; } return window.location.hostname; }; /** * Returns whether the given image URL exists * @param url - the url of the image * @returns - whether the image exists */ function imgExists(url) { return new Promise((resolve, reject) => { try { const img = document.createElement("img"); img.onload = () => resolve(true); img.onerror = () => resolve(false); img.src = url; } catch (e) { reject(e); } }); } /** * Extracts an icon for the site from the DOM */ async function getSiteIcon(window) { const { document } = window; // Use the site's favicon if it exists let icon = document.querySelector('head > link[rel="shortcut icon"]'); if (icon && (await imgExists(icon.href))) { return icon.href; } // Search through available icons in no particular order icon = Array.from(document.querySelectorAll('head > link[rel="icon"]')).find(_icon => Boolean(_icon.href)) || null; if (icon && (await imgExists(icon.href))) { return icon.href; } return null; } /** * Gets site metadata and returns it * */ const getSiteMetadata = async () => ({ name: getSiteName(window), icon: await getSiteIcon(window) }); const getNamespacesFromChains = chains => { const supportedNamespaces = []; chains.forEach(chainId => { const [namespace] = chainId.split(":"); if (!supportedNamespaces.includes(namespace)) { supportedNamespaces.push(namespace); } }); return supportedNamespaces; }; const getSupportedMethodsByNamespace = namespace => { switch (namespace) { case CHAIN_NAMESPACES.EIP155: return Object.values(DEFAULT_EIP155_METHODS); case CHAIN_NAMESPACES.SOLANA: return Object.values(DEFAULT_SOLANA_METHODS); default: throw new Error(`No default methods for namespace: ${namespace}`); } }; const getSupportedEventsByNamespace = namespace => { switch (namespace) { case CHAIN_NAMESPACES.EIP155: return Object.values(DEFAULT_EIP_155_EVENTS); case CHAIN_NAMESPACES.SOLANA: return Object.values(DEFAULT_SOLANA_EVENTS); default: throw new Error(`No default events for namespace: ${namespace}`); } }; const getRequiredNamespaces = chains => { const selectedNamespaces = getNamespacesFromChains(chains); return Object.fromEntries(selectedNamespaces.map(namespace => [namespace, { methods: getSupportedMethodsByNamespace(namespace), chains: chains.filter(chain => chain.startsWith(namespace)), events: getSupportedEventsByNamespace(namespace) }])); }; const getWalletConnectV2Settings = async (namespace, chainIds, projectID) => { if (namespace === CHAIN_NAMESPACES.EIP155 || namespace === CHAIN_NAMESPACES.SOLANA) { const appMetadata = await getSiteMetadata(); const adapterSettings = { walletConnectInitOptions: { projectId: projectID, relayUrl: "wss://relay.walletconnect.com", metadata: { name: appMetadata.name, description: appMetadata.name, url: window.location.origin, icons: [appMetadata.icon || ""] } } }; const chainNamespaces = chainIds.map(chainId => { return `${namespace}:${namespace === CHAIN_NAMESPACES.SOLANA ? SOLANA_CAIP_CHAIN_MAP[chainId] : parseInt(chainId, 16)}`; }); const loginSettings = { optionalNamespaces: getRequiredNamespaces(chainNamespaces) }; return { adapterSettings, loginSettings }; } throw new Error(`Unsupported chain namespace: ${namespace}`); }; async function getLastActiveSession(signClient) { if (signClient.session.length) { const lastKeyIndex = signClient.session.keys.length - 1; return signClient.session.get(signClient.session.keys[lastKeyIndex]); } return null; } function isMobileDevice() { return /Mobi|Android|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i.test(window.navigator.userAgent); } function isSolanaChain(chainId) { return chainId.startsWith("solana:"); } async function sendJrpcRequest(signClient, chainId, method, params) { const session = await getLastActiveSession(signClient); if (!session) { throw providerErrors.disconnected(); } if (typeof window !== "undefined" && isMobileDevice()) { if (session.peer.metadata.redirect && session.peer.metadata.redirect.native) { window.open(session.peer.metadata.redirect.native, "_blank"); } } return signClient.request({ topic: session.topic, chainId, request: { method, params: isSolanaChain(chainId) ? _objectSpread(_objectSpread({}, params), {}, { pubkey: session.self.publicKey }) : params } }); } async function getAccounts(signClient) { const session = await getLastActiveSession(signClient); if (!session) { throw providerErrors.disconnected(); } const accounts = getAccountsFromNamespaces(session.namespaces); if (accounts && accounts.length) { return [...new Set(accounts.map(add => { return parseAccountId(add).address; }))]; } throw WalletLoginError.connectionError("Failed to get accounts"); } function getEthProviderHandlers({ connector, chainId }) { return { getPrivateKey: async () => { throw rpcErrors.methodNotSupported(); }, getPublicKey: async () => { throw rpcErrors.methodNotSupported(); }, getAccounts: async _ => { return getAccounts(connector); }, processTransaction: async (txParams, _) => { const methodRes = await sendJrpcRequest(connector, `eip155:${chainId}`, "eth_sendTransaction", [txParams]); return methodRes; }, processSignTransaction: async (txParams, _) => { const methodRes = await sendJrpcRequest(connector, `eip155:${chainId}`, "eth_signTransaction", [txParams]); return methodRes; }, processEthSignMessage: async (msgParams, _) => { const methodRes = await sendJrpcRequest(connector, `eip155:${chainId}`, "eth_sign", [msgParams.from, msgParams.data]); return methodRes; }, processPersonalMessage: async (msgParams, _) => { const methodRes = await sendJrpcRequest(connector, `eip155:${chainId}`, "personal_sign", [msgParams.data, msgParams.from]); return methodRes; }, processTypedMessageV4: async msgParams => { const methodRes = await sendJrpcRequest(connector, `eip155:${chainId}`, "eth_signTypedData_v4", [msgParams.from, msgParams.data]); return methodRes; } }; } function getSolProviderHandlers({ connector, chainId }) { return { requestAccounts: async _ => { return getAccounts(connector); }, getPrivateKey: async () => { throw rpcErrors.methodNotSupported(); }, getSecretKey: async () => { throw rpcErrors.methodNotSupported(); }, getPublicKey: async () => { throw rpcErrors.methodNotSupported(); }, getAccounts: async _ => { return getAccounts(connector); }, signAllTransactions: async _ => { throw rpcErrors.methodNotSupported(); }, signAndSendTransaction: async _ => { throw rpcErrors.methodNotSupported(); }, signMessage: async req => { const methodRes = await sendJrpcRequest(connector, `solana:${SOLANA_CAIP_CHAIN_MAP[chainId]}`, "solana_signMessage", { message: base58.encode(req.params.message) }); return base58.decode(methodRes.signature); }, signTransaction: async req => { const [{ PublicKey }, accounts] = await Promise.all([import('@solana/web3.js'), getAccounts(connector)]); if (accounts.length === 0) { throw providerErrors.disconnected(); } const methodRes = await sendJrpcRequest(connector, `solana:${SOLANA_CAIP_CHAIN_MAP[chainId]}`, "solana_signTransaction", { transaction: req.params.message.serialize({ requireAllSignatures: false }).toString("base64") }); const finalTransaction = req.params.message; finalTransaction.addSignature(new PublicKey(accounts[0]), Buffer.from(base58.decode(methodRes.signature))); return finalTransaction; } }; } async function switchChain({ connector, chainId, newChainId }) { await sendJrpcRequest(connector, `eip155:${chainId}`, "wallet_switchEthereumChain", [{ chainId: newChainId }]); } async function addChain({ connector, chainId, chainConfig }) { await sendJrpcRequest(connector, `eip155:${chainId}`, "wallet_addEthereumChain", [chainConfig]); } var _WalletConnectV2Provider; class WalletConnectV2Provider extends BaseProvider { constructor({ clientId, config, state, connector }) { super({ config: { chainConfig: config.chainConfig, skipLookupNetwork: !!config.skipLookupNetwork }, state: _objectSpread(_objectSpread({}, state || {}), {}, { chainId: "loading", accounts: [] }) }); _defineProperty(this, "connector", null); _defineProperty(this, "clientId", void 0); this.connector = connector || null; this.clientId = clientId; } async enable() { if (!this.connector) throw providerErrors.custom({ message: "Connector is not initialized, pass wallet connect connector in constructor", code: 4902 }); await this.setupProvider(this.connector); return this._providerEngineProxy.request({ method: "eth_accounts" }); } async setupProvider(connector) { this.onConnectorStateUpdate(connector); await this.setupEngine(connector); } async switchChain({ chainId }) { if (!this.connector) throw providerErrors.custom({ message: "Connector is not initialized, pass wallet connect connector in constructor", code: 4902 }); const currentChainConfig = this.getChainConfig(chainId); const { chainId: currentChainId } = this.config.chainConfig; const currentNumChainId = parseInt(currentChainId, 16); await switchChain({ connector: this.connector, chainId: currentNumChainId, newChainId: chainId }); this.configure({ chainConfig: currentChainConfig }); await this.setupEngine(this.connector); this.lookupNetwork(this.connector); } async addChain(chainConfig) { const { chainId: currentChainId } = this.config.chainConfig; const numChainId = parseInt(currentChainId, 16); await addChain({ connector: this.connector, chainId: numChainId, chainConfig: { chainId: chainConfig.chainId, chainName: chainConfig.displayName, nativeCurrency: { name: chainConfig.tickerName, symbol: chainConfig.ticker.toLocaleUpperCase(), decimals: chainConfig.decimals || 18 }, rpcUrls: [chainConfig.rpcTarget], blockExplorerUrls: [chainConfig.blockExplorerUrl], iconUrls: [chainConfig.logo] } }); super.addChain(chainConfig); } // no need to implement this method in wallet connect v2. async lookupNetwork(_) { const newChainId = this.config.chainConfig.chainId; this.update({ chainId: newChainId }); this.emit("chainChanged", newChainId); this.emit("connect", { chainId: newChainId }); return this.config.chainConfig.chainId; } async setupEngine(connector) { if (this.config.chainConfig.chainNamespace === CHAIN_NAMESPACES.EIP155) { return this.setupEthEngine(connector); } else if (this.config.chainConfig.chainNamespace === CHAIN_NAMESPACES.SOLANA) { return this.setupSolEngine(connector); } throw new Error(`Unsupported chainNamespace: ${this.config.chainConfig.chainNamespace}`); } async setupEthEngine(connector) { const { chainId } = this.config.chainConfig; const numChainId = parseInt(chainId, 16); const providerHandlers = getEthProviderHandlers({ connector, chainId: numChainId }); const jrpcRes = await getAccounts(connector); this.update({ accounts: jrpcRes || [] }); const ethMiddleware = createEthMiddleware(providerHandlers); const chainSwitchMiddleware = this.getEthChainSwitchMiddleware(); const engine = new JRPCEngine(); const { networkMiddleware } = createJsonRpcClient(this.config.chainConfig); engine.push(ethMiddleware); engine.push(chainSwitchMiddleware); engine.push(networkMiddleware); const provider = providerFromEngine(engine); this.updateProviderEngineProxy(provider); } async setupSolEngine(connector) { const { chainId } = this.config.chainConfig; const providerHandlers = getSolProviderHandlers({ connector, chainId }); const jrpcRes = await getAccounts(connector); this.update({ accounts: jrpcRes || [] }); const solMiddleware = createSolanaMiddleware(providerHandlers); const engine = new JRPCEngine(); const { networkMiddleware } = createJsonRpcClient$1(this.config.chainConfig); engine.push(solMiddleware); engine.push(networkMiddleware); const provider = providerFromEngine(engine); this.updateProviderEngineProxy(provider); } getEthChainSwitchMiddleware() { const chainSwitchHandlers = { addChain: async params => { const { chainId, chainName, rpcUrls, blockExplorerUrls, nativeCurrency, iconUrls } = params; this.addChain({ chainNamespace: CHAIN_NAMESPACES.EIP155, chainId, ticker: (nativeCurrency === null || nativeCurrency === void 0 ? void 0 : nativeCurrency.symbol) || "ETH", tickerName: (nativeCurrency === null || nativeCurrency === void 0 ? void 0 : nativeCurrency.name) || "Ether", displayName: chainName, rpcTarget: rpcUrls[0], blockExplorerUrl: (blockExplorerUrls === null || blockExplorerUrls === void 0 ? void 0 : blockExplorerUrls[0]) || "", decimals: (nativeCurrency === null || nativeCurrency === void 0 ? void 0 : nativeCurrency.decimals) || 18, logo: (iconUrls === null || iconUrls === void 0 ? void 0 : iconUrls[0]) || "https://images.toruswallet.io/eth.svg" }); }, switchChain: async params => { const { chainId } = params; await this.switchChain({ chainId }); } }; const chainSwitchMiddleware = createChainSwitchMiddleware(chainSwitchHandlers); return chainSwitchMiddleware; } connectedTopic() { var _this$connector; if (!this.connector) throw WalletLoginError.notConnectedError("Wallet connect connector is not connected"); if ((_this$connector = this.connector) !== null && _this$connector !== void 0 && (_this$connector = _this$connector.session) !== null && _this$connector !== void 0 && _this$connector.length) { var _this$connector$sessi; // currently we are supporting only 1 active session const lastKeyIndex = this.connector.session.keys.length - 1; return (_this$connector$sessi = this.connector.session.get(this.connector.session.keys[lastKeyIndex])) === null || _this$connector$sessi === void 0 ? void 0 : _this$connector$sessi.topic; } return undefined; } checkIfAccountAllowed(address) { if (!this.connector || !this.connectedTopic()) return false; const sessionData = this.connector.session.get(this.connectedTopic()); const allAccounts = getAccountsFromNamespaces(sessionData.namespaces); let accountAllowed = false; for (const account of allAccounts) { var _parsedAccount$addres; const parsedAccount = parseAccountId(account); if (((_parsedAccount$addres = parsedAccount.address) === null || _parsedAccount$addres === void 0 ? void 0 : _parsedAccount$addres.toLowerCase()) === (address === null || address === void 0 ? void 0 : address.toLowerCase())) { accountAllowed = true; break; } } return accountAllowed; } async onConnectorStateUpdate(connector) { connector.events.on("session_event", async payload => { log.debug("session_event data", payload); if (!this.provider) throw WalletLoginError.notConnectedError("Wallet connect connector is not connected"); const { event } = payload.params; const { name, data } = event || {}; // Check if accounts changed and trigger event if (name === "accountsChanged" && data !== null && data !== void 0 && data.length && this.state.accounts[0] !== data[0] && this.checkIfAccountAllowed(data[0])) { this.update({ accounts: data }); this.emit("accountsChanged", data); } if (event.name === "chainChanged") { if (!data) return; const connectedChainId = data; const connectedHexChainId = `0x${connectedChainId.toString(16)}`; // Check if chainId changed and trigger event if (connectedHexChainId && this.state.chainId !== connectedHexChainId) { const maybeConfig = getChainConfig(CHAIN_NAMESPACES.EIP155, connectedHexChainId, this.clientId); // Handle rpcUrl update this.configure({ chainConfig: _objectSpread(_objectSpread({}, maybeConfig), {}, { chainId: connectedHexChainId, chainNamespace: CHAIN_NAMESPACES.EIP155 }) }); await this.setupEngine(connector); } } }); } } _WalletConnectV2Provider = WalletConnectV2Provider; _defineProperty(WalletConnectV2Provider, "getProviderInstance", async params => { const providerFactory = new _WalletConnectV2Provider({ clientId: params.clientId, config: { chainConfig: params.chainConfig, skipLookupNetwork: params.skipLookupNetwork } }); await providerFactory.setupProvider(params.connector); return providerFactory; }); class WalletConnectV2Adapter extends BaseAdapter { constructor(options = {}) { super(options); _defineProperty(this, "name", WALLET_ADAPTERS.WALLET_CONNECT_V2); _defineProperty(this, "adapterNamespace", ADAPTER_NAMESPACES.MULTICHAIN); _defineProperty(this, "currentChainNamespace", CHAIN_NAMESPACES.OTHER); _defineProperty(this, "type", ADAPTER_CATEGORY.EXTERNAL); _defineProperty(this, "adapterOptions", {}); _defineProperty(this, "status", ADAPTER_STATUS.NOT_READY); _defineProperty(this, "adapterData", { uri: "" }); _defineProperty(this, "connector", null); _defineProperty(this, "activeSession", null); _defineProperty(this, "wcProvider", null); this.adapterOptions = _objectSpread({}, options); this.setAdapterSettings(options); } get connected() { return !!this.activeSession; } get provider() { if (this.status !== ADAPTER_STATUS.NOT_READY && this.wcProvider) { return this.wcProvider; } return null; } set provider(_) { throw new Error("Not implemented"); } async init(options) { var _this$adapterOptions$, _this$chainConfig, _this$chainConfig2; super.checkInitializationRequirements(); const projectId = (_this$adapterOptions$ = this.adapterOptions.adapterSettings) === null || _this$adapterOptions$ === void 0 || (_this$adapterOptions$ = _this$adapterOptions$.walletConnectInitOptions) === null || _this$adapterOptions$ === void 0 ? void 0 : _this$adapterOptions$.projectId; if (!projectId) { throw WalletInitializationError.invalidParams("Wallet connect project id is required in wallet connect v2 adapter"); } const wc2Settings = await getWalletConnectV2Settings((_this$chainConfig = this.chainConfig) === null || _this$chainConfig === void 0 ? void 0 : _this$chainConfig.chainNamespace, [(_this$chainConfig2 = this.chainConfig) === null || _this$chainConfig2 === void 0 ? void 0 : _this$chainConfig2.chainId], projectId); if (!this.adapterOptions.loginSettings || Object.keys(this.adapterOptions.loginSettings).length === 0) { this.adapterOptions.loginSettings = wc2Settings.loginSettings; } this.adapterOptions.adapterSettings = deepmerge(wc2Settings.adapterSettings || {}, this.adapterOptions.adapterSettings || {}); const { adapterSettings } = this.adapterOptions; this.connector = await Client.init(adapterSettings === null || adapterSettings === void 0 ? void 0 : adapterSettings.walletConnectInitOptions); this.wcProvider = new WalletConnectV2Provider({ clientId: this.clientId, config: { chainConfig: this.chainConfig }, connector: this.connector }); this.emit(ADAPTER_EVENTS.READY, WALLET_ADAPTERS.WALLET_CONNECT_V2); this.status = ADAPTER_STATUS.READY; log.debug("initializing wallet connect v2 adapter"); if (options.autoConnect) { await this.checkForPersistedSession(); if (this.connected) { this.rehydrated = true; try { await this.onConnectHandler(); } catch (error) { log.error("wallet auto connect", error); this.emit(ADAPTER_EVENTS.ERRORED, error); } } else { this.status = ADAPTER_STATUS.NOT_READY; this.emit(ADAPTER_EVENTS.CACHE_CLEAR); } } } async connect() { super.checkConnectionRequirements(); if (!this.connector) throw WalletInitializationError.notReady("Wallet adapter is not ready yet"); try { // if already connected if (this.connected) { await this.onConnectHandler(); return this.provider; } if (this.status !== ADAPTER_STATUS.CONNECTING) { await this.createNewSession(); } return this.provider; } catch (error) { log.error("Wallet connect v2 adapter error while connecting", error); // ready again to be connected this.status = ADAPTER_STATUS.READY; this.rehydrated = true; this.emit(ADAPTER_EVENTS.ERRORED, error); const finalError = error instanceof Web3AuthError ? error : WalletLoginError.connectionError(`Failed to login with wallet connect: ${(error === null || error === void 0 ? void 0 : error.message) || ""}`, error); throw finalError; } } // should be called only before initialization. setAdapterSettings(adapterSettings) { var _this$adapterOptions$2, _this$adapterOptions, _this$adapterOptions$3, _this$adapterOptions2, _this$adapterOptions$4; super.setAdapterSettings(adapterSettings); const { qrcodeModal, walletConnectInitOptions } = (adapterSettings === null || adapterSettings === void 0 ? void 0 : adapterSettings.adapterSettings) || {}; this.adapterOptions = _objectSpread(_objectSpread({}, this.adapterOptions), {}, { adapterSettings: (_this$adapterOptions$2 = (_this$adapterOptions = this.adapterOptions) === null || _this$adapterOptions === void 0 ? void 0 : _this$adapterOptions.adapterSettings) !== null && _this$adapterOptions$2 !== void 0 ? _this$adapterOptions$2 : {}, loginSettings: (_this$adapterOptions$3 = (_this$adapterOptions2 = this.adapterOptions) === null || _this$adapterOptions2 === void 0 ? void 0 : _this$adapterOptions2.loginSettings) !== null && _this$adapterOptions$3 !== void 0 ? _this$adapterOptions$3 : {} }); if (qrcodeModal) this.adapterOptions.adapterSettings.qrcodeModal = qrcodeModal; if (walletConnectInitOptions) this.adapterOptions.adapterSettings.walletConnectInitOptions = _objectSpread(_objectSpread({}, (_this$adapterOptions$4 = this.adapterOptions.adapterSettings.walletConnectInitOptions) !== null && _this$adapterOptions$4 !== void 0 ? _this$adapterOptions$4 : {}), walletConnectInitOptions); const { loginSettings } = adapterSettings; if (loginSettings) this.adapterOptions.loginSettings = _objectSpread(_objectSpread({}, this.adapterOptions.loginSettings || {}), loginSettings); } async addChain(chainConfig, init = false) { var _this$wcProvider; super.checkAddChainRequirements(chainConfig, init); await ((_this$wcProvider = this.wcProvider) === null || _this$wcProvider === void 0 ? void 0 : _this$wcProvider.addChain(chainConfig)); this.addChainConfig(chainConfig); } async switchChain(params, init = false) { var _this$wcProvider2; super.checkSwitchChainRequirements(params, init); await ((_this$wcProvider2 = this.wcProvider) === null || _this$wcProvider2 === void 0 ? void 0 : _this$wcProvider2.switchChain({ chainId: params.chainId })); this.setAdapterSettings({ chainConfig: this.getChainConfig(params.chainId) }); } async getUserInfo() { if (!this.connected) throw WalletLoginError.notConnectedError("Not connected with wallet, Please login/connect first"); return {}; } async disconnect(options = { cleanup: false, sessionRemovedByWallet: false }) { var _this$activeSession, _this$activeSession2; const { cleanup } = options; if (!this.connector || !this.connected || !((_this$activeSession = this.activeSession) !== null && _this$activeSession !== void 0 && _this$activeSession.topic)) throw WalletLoginError.notConnectedError("Not connected with wallet"); if (!options.sessionRemovedByWallet) await this.connector.disconnect({ topic: (_this$activeSession2 = this.activeSession) === null || _this$activeSession2 === void 0 ? void 0 : _this$activeSession2.topic, reason: getSdkError("USER_DISCONNECTED") }); this.rehydrated = false; if (cleanup) { this.connector = null; this.status = ADAPTER_STATUS.NOT_READY; this.wcProvider = null; } else { // ready to connect again this.status = ADAPTER_STATUS.READY; } this.activeSession = null; this.emit(ADAPTER_EVENTS.DISCONNECTED); } async authenticateUser() { if (!this.provider || this.status !== ADAPTER_STATUS.CONNECTED) throw WalletLoginError.notConnectedError(); const { chainNamespace, chainId } = this.chainConfig; const accounts = await this.provider.request({ method: chainNamespace === CHAIN_NAMESPACES.EIP155 ? "eth_accounts" : "getAccounts" }); if (accounts && accounts.length > 0) { const existingToken = getSavedToken(accounts[0], this.name); if (existingToken) { const isExpired = checkIfTokenIsExpired(existingToken); if (!isExpired) { return { idToken: existingToken }; } } const payload = { domain: window.location.origin, uri: window.location.href, address: accounts[0], chainId: parseInt(chainId, 16), version: "1", nonce: Math.random().toString(36).slice(2), issuedAt: new Date().toISOString() }; const challenge = await signChallenge(payload, chainNamespace); const signedMessage = await this._getSignedMessage(challenge, accounts, chainNamespace); const idToken = await verifySignedChallenge(chainNamespace, signedMessage, challenge, this.name, this.sessionTime, this.clientId, this.web3AuthNetwork); saveToken(accounts[0], this.name, idToken); return { idToken }; } throw WalletLoginError.notConnectedError("Not connected with wallet, Please login/connect first"); } async enableMFA() { throw new Error("Method Not implemented"); } async manageMFA() { throw new Error("Method Not implemented"); } cleanupPendingPairings() { if (!this.connector) throw WalletInitializationError.notReady("Wallet adapter is not ready yet"); const inactivePairings = this.connector.pairing.getAll({ active: false }); if (!isValidArray(inactivePairings)) return; inactivePairings.forEach(pairing => { if (this.connector) { this.connector.pairing.delete(pairing.topic, getSdkError("USER_DISCONNECTED")); } }); } async checkForPersistedSession() { if (!this.connector) throw WalletInitializationError.notReady("Wallet adapter is not ready yet"); if (this.connector.session.length) { const lastKeyIndex = this.connector.session.keys.length - 1; this.activeSession = this.connector.session.get(this.connector.session.keys[lastKeyIndex]); } return this.activeSession; } async createNewSession(opts = { forceNewSession: false }) { try { var _this$activeSession3, _this$adapterOptions3; if (!this.connector) throw WalletInitializationError.notReady("Wallet adapter is not ready yet"); if (!this.adapterOptions.loginSettings || Object.keys(this.adapterOptions.loginSettings).length === 0) throw WalletInitializationError.notReady("login settings are not set yet"); this.status = ADAPTER_STATUS.CONNECTING; this.emit(ADAPTER_EVENTS.CONNECTING, { adapter: WALLET_ADAPTERS.WALLET_CONNECT_V2 }); if (opts.forceNewSession && (_this$activeSession3 = this.activeSession) !== null && _this$activeSession3 !== void 0 && _this$activeSession3.topic) { var _this$activeSession4; await this.connector.disconnect({ topic: (_this$activeSession4 = this.activeSession) === null || _this$activeSession4 === void 0 ? void 0 : _this$activeSession4.topic, reason: getSdkError("USER_DISCONNECTED") }); } const { uri, approval } = await this.connector.connect(this.adapterOptions.loginSettings); const qrcodeModal = (_this$adapterOptions3 = this.adapterOptions) === null || _this$adapterOptions3 === void 0 || (_this$adapterOptions3 = _this$adapterOptions3.adapterSettings) === null || _this$adapterOptions3 === void 0 ? void 0 : _this$adapterOptions3.qrcodeModal; // Open QRCode modal if a URI was returned (i.e. we're not connecting with an existing pairing). if (uri) { if (qrcodeModal) { try { await qrcodeModal.openModal({ uri }); log.debug("EVENT", "QR Code Modal closed"); this.status = ADAPTER_STATUS.READY; this.emit(ADAPTER_EVENTS.READY, WALLET_ADAPTERS.WALLET_CONNECT_V2); } catch (error) { log.error("unable to open qr code modal"); } } else { this.updateAdapterData({ uri }); } } log.info("awaiting session approval from wallet"); // Await session approval from the wallet. const session = await approval(); this.activeSession = session; // Handle the returned session (e.g. update UI to "connected" state). await this.onConnectHandler(); if (qrcodeModal) { qrcodeModal.closeModal(); } } catch (error) { var _message; if ((_message = error.message) !== null && _message !== void 0 && _message.toLowerCase().includes("proposal expired")) { // Retry if adapter status is still connecting log.info("current adapter status: ", this.status); if (this.status === ADAPTER_STATUS.CONNECTING) { log.info("retrying to create new wallet connect session since proposal expired"); return this.createNewSession({ forceNewSession: true }); } if (this.status === ADAPTER_STATUS.READY) { log.info("ignoring proposal expired error since some other adapter is connected"); return; } } log.error("error while creating new wallet connect session", error); this.emit(ADAPTER_EVENTS.ERRORED, error); throw error; } } async onConnectHandler() { var _this$adapterOptions$5; if (!this.connector || !this.wcProvider) throw WalletInitializationError.notReady("Wallet adapter is not ready yet"); if (!this.chainConfig) throw WalletInitializationError.invalidParams("Chain config is not set"); this.subscribeEvents(); if ((_this$adapterOptions$5 = this.adapterOptions.adapterSettings) !== null && _this$adapterOptions$5 !== void 0 && _this$adapterOptions$5.qrcodeModal) { this.wcProvider = new WalletConnectV2Provider({ clientId: this.clientId, config: { chainConfig: this.chainConfig, skipLookupNetwork: true }, connector: this.connector }); } await this.wcProvider.setupProvider(this.connector); this.cleanupPendingPairings(); this.status = ADAPTER_STATUS.CONNECTED; this.emit(ADAPTER_EVENTS.CONNECTED, { adapter: WALLET_ADAPTERS.WALLET_CONNECT_V2, reconnected: this.rehydrated, provider: this.provider }); } subscribeEvents() { if (!this.connector) throw WalletInitializationError.notReady("Wallet adapter is not ready yet"); this.connector.events.on("session_update", ({ topic, params }) => { if (!this.connector) return; const { namespaces } = params; const _session = this.connector.session.get(topic); // Overwrite the `namespaces` of the existing session with the incoming one. const updatedSession = _objectSpread(_objectSpread({}, _session), {}, { namespaces }); // Integrate the updated session state into your dapp state. this.activeSession = updatedSession; }); this.connector.events.on("session_delete", () => { // Session was deleted -> reset the dapp state, clean up from user session, etc. this.disconnect({ sessionRemovedByWallet: true }); }); } async _getSignedMessage(challenge, accounts, chainNamespace) { const signedMessage = await this.provider.request({ method: chainNamespace === CHAIN_NAMESPACES.EIP155 ? "personal_sign" : "signMessage", params: chainNamespace === CHAIN_NAMESPACES.EIP155 ? [challenge, accounts[0]] : { message: Buffer.from(challenge) } }); if (chainNamespace === CHAIN_NAMESPACES.SOLANA) return base58.encode(signedMessage); return signedMessage; } } export { DEFAULT_EIP155_METHODS, DEFAULT_EIP_155_EVENTS, DEFAULT_SOLANA_EVENTS, DEFAULT_SOLANA_METHODS, SOLANA_CAIP_CHAIN_MAP, WalletConnectV2Adapter, WalletConnectV2Provider, getNamespacesFromChains, getRequiredNamespaces, getSupportedEventsByNamespace, getSupportedMethodsByNamespace, getWalletConnectV2Settings };