UNPKG

@turnkey/core

Version:

A core JavaScript web and React Native package for interfacing with Turnkey's infrastructure.

178 lines (174 loc) 8.89 kB
'use strict'; var stamper = require('../stamper.js'); var connector = require('../connector.js'); var ethereum = require('./native/ethereum.js'); var solana = require('./native/solana.js'); var enums = require('../../__types__/enums.js'); var client = require('../wallet-connect/client.js'); var base = require('../wallet-connect/base.js'); var utils = require('../../utils.js'); class WebWalletManager { /** * Constructs a WebWalletManager instance based on the provided configuration. * * - Enables native Ethereum and/or Solana wallet support if configured. * - Enables WalletConnect support for Ethereum and/or Solana chains if namespaces are provided. * - Sets up `CrossPlatformWalletStamper` and `CrossPlatformWalletConnector` if auth or connecting features are enabled. * * @param cfg - Wallet manager configuration. */ constructor(cfg) { // queue of async initialization functions this.initializers = []; // mapping of wallet interface types to their wallet instances this.wallets = {}; // maps a blockchain chain to its corresponding wallet interface types this.chainToInterfaces = {}; /** * Registers a wallet interface type as supporting a specific blockchain chain. * * @param chain - Chain (e.g., Ethereum, Solana). * @param interfaceType - Wallet interface type to associate with the chain. */ this.addChainInterface = (chain, interfaceType) => { if (!this.chainToInterfaces[chain]) this.chainToInterfaces[chain] = []; this.chainToInterfaces[chain].push(interfaceType); }; const enableNativeEvm = cfg.chains.ethereum?.native ?? false; const enableNativeSol = cfg.chains.solana?.native ?? false; const ethereumNamespaces = cfg.chains.ethereum?.walletConnectNamespaces ?? []; const solanaNamespaces = cfg.chains.solana?.walletConnectNamespaces ?? []; const enableWalletConnectEvm = ethereumNamespaces.length > 0; const enableWalletConnectSol = solanaNamespaces.length > 0; const enableWalletConnect = enableWalletConnectEvm || enableWalletConnectSol; // set up native Ethereum wallet support if (enableNativeEvm) { this.wallets[enums.WalletInterfaceType.Ethereum] = new ethereum.EthereumWallet(); this.addChainInterface(enums.Chain.Ethereum, enums.WalletInterfaceType.Ethereum); } // set up native Solana wallet support if (enableNativeSol) { this.wallets[enums.WalletInterfaceType.Solana] = new solana.SolanaWallet(); this.addChainInterface(enums.Chain.Solana, enums.WalletInterfaceType.Solana); } // if WalletConnect is configured, set it up if (cfg.walletConnect && enableWalletConnect) { this.wcClient = new client.WalletConnectClient(); const wcUnified = new base.WalletConnectWallet(this.wcClient, () => this.ensureWalletConnectReady(), { ethereumNamespaces, solanaNamespaces }); this.wallets[enums.WalletInterfaceType.WalletConnect] = wcUnified; // add async init step to the initializer queue this.initializers.push(() => utils.withTimeout(wcUnified.init(), 15000, "WalletConnect wallet").catch((err) => { // WalletConnect can be a bit unreliable, we don't want to load forever // so we limit the initialization time to 15 seconds. After that we emit a // failure event and remove WalletConnect from the available wallets wcUnified.abortInit(err); this.removeWalletInterface(enums.WalletInterfaceType.WalletConnect); })); // register WalletConnect as a wallet interface for each enabled chain if (enableWalletConnectEvm) this.addChainInterface(enums.Chain.Ethereum, enums.WalletInterfaceType.WalletConnect); if (enableWalletConnectSol) this.addChainInterface(enums.Chain.Solana, enums.WalletInterfaceType.WalletConnect); } if (cfg.features?.auth) { this.stamper = new stamper.CrossPlatformWalletStamper(this.wallets); } if (cfg.features?.connecting) { this.connector = new connector.CrossPlatformWalletConnector(this.wallets); } } /** * Initializes WalletConnect components and any registered wallet interfaces. * * - Initializes the low-level WalletConnect client with the provided config. * - Runs any registered async wallet initializers (currently only `WalletConnectWallet`). * - WalletConnect initialization happens in the background and does not block this method. * * @param cfg - Wallet manager configuration used for initializing the WalletConnect client. */ async init(cfg) { if (this.wcClient) { // we start WalletConnect initialization in the background (this is non-blocking) this.wcInitPromise = (async () => { try { await this.wcClient.init(cfg.walletConnect); // initialize the high-level WalletConnectWallet after client is ready await Promise.all(this.initializers.map((fn) => fn())); } catch (error) { // WalletConnect can be a bit unreliable, so instead of throwing an error // we handle failures silently to avoid blocking the rest of the client // from initializing. If setup fails, we also remove WalletConnect // from the available wallets list console.error("Failed to initialize WalletConnect", error); this.removeWalletInterface(enums.WalletInterfaceType.WalletConnect); } })(); } } /** * Ensures WalletConnect is fully initialized before proceeding. * This should be called before any WalletConnect operations. * * @returns A promise that resolves when WalletConnect is ready. */ async ensureWalletConnectReady() { if (this.wcInitPromise) { await this.wcInitPromise; } } /** * Retrieves available wallet providers, optionally filtered by chain. * * - If a chain is specified, filters wallet interfaces that support that chain. * - Aggregates providers across all wallet interfaces and filters WalletConnect results accordingly. * - Returns native wallet providers immediately without waiting for WalletConnect. * - WalletConnect providers will only be included if WalletConnect has already initialized. * * @param chain - Optional chain to filter providers by (e.g., Ethereum, Solana). * @returns A promise that resolves to an array of `WalletProvider` objects. * @throws {Error} If no wallet interface is registered for the given chain. */ async getProviders(chain) { if (chain) { const interfaceTypes = this.chainToInterfaces[chain]; if (!interfaceTypes || interfaceTypes.length === 0) throw new Error(`No wallet supports chain: ${chain}`); const walletsToQuery = interfaceTypes .map((iface) => this.wallets[iface]) .filter(Boolean); const providersArrays = await Promise.all(walletsToQuery.map((wallet) => wallet.getProviders())); // we still need to filter by chain because WalletConnect can return providers for multiple chains return providersArrays .flat() .filter((p) => p.chainInfo.namespace === chain); } // collect all providers from all initialized wallets const providersArrays = await Promise.all(Object.values(this.wallets).map((wallet) => wallet.getProviders())); return providersArrays.flat(); } /** * Removes a wallet interface from the manager. * * - Deletes the wallet instance from the wallets map. * - Cleans up references to the wallet interface in chainToInterfaces. * - Removes the chain entry entirely if no interfaces remain. * * @param type - The WalletInterfaceType to remove. */ removeWalletInterface(type) { delete this.wallets[type]; for (const chain of Object.keys(this.chainToInterfaces)) { const filtered = (this.chainToInterfaces[chain] ?? []).filter((interfaceType) => interfaceType !== type); if (filtered.length > 0) { this.chainToInterfaces[chain] = filtered; } else { delete this.chainToInterfaces[chain]; } } } } exports.WebWalletManager = WebWalletManager; //# sourceMappingURL=manager.js.map