UNPKG

@wagmi/connectors

Version:

Collection of connectors for Wagmi

242 lines 11.2 kB
import { ChainNotConfiguredError, createConnector } from '@wagmi/core'; import { getAddress, numberToHex, ResourceUnavailableRpcError, SwitchChainError, UserRejectedRequestError, withRetry, withTimeout, } from 'viem'; metaMask.type = 'metaMask'; export function metaMask(parameters = {}) { let metamask; let metamaskPromise; return createConnector((config) => ({ id: 'metaMaskSDK', name: 'MetaMask', rdns: ['io.metamask', 'io.metamask.mobile'], type: metaMask.type, async connect({ chainId, isReconnecting, withCapabilities } = {}) { const instance = await this.getInstance(); const provider = instance.getProvider(); let accounts = []; if (isReconnecting) accounts = await this.getAccounts().catch(() => []); try { let signResponse; let connectWithResponse; if (!accounts?.length) { const chainIds = config.chains.map((chain) => numberToHex(chain.id)); if (parameters.connectAndSign || parameters.connectWith) { if (parameters.connectAndSign) signResponse = await instance.connectAndSign({ chainIds, message: parameters.connectAndSign, }); else if (parameters.connectWith) connectWithResponse = await instance.connectWith({ chainIds, method: parameters.connectWith.method, params: parameters.connectWith.params, }); accounts = await this.getAccounts(); } else { const result = await instance.connect({ chainIds }); accounts = result.accounts.map((x) => getAddress(x)); } } // Switch to chain if provided let currentChainId = await this.getChainId(); if (chainId && currentChainId !== chainId) { const chain = await this.switchChain({ chainId }).catch((error) => { if (error.code === UserRejectedRequestError.code) throw error; return { id: currentChainId }; }); currentChainId = chain?.id ?? currentChainId; } if (signResponse) provider.emit('connectAndSign', { accounts, chainId: numberToHex(currentChainId), signResponse, }); else if (connectWithResponse) provider.emit('connectWith', { accounts, chainId: numberToHex(currentChainId), connectWithResponse, }); return { // TODO(v3): Make `withCapabilities: true` default behavior accounts: (withCapabilities ? accounts.map((address) => ({ address, capabilities: {} })) : accounts), chainId: currentChainId, }; } catch (err) { const error = err; if (error.code === UserRejectedRequestError.code) throw new UserRejectedRequestError(error); if (error.code === ResourceUnavailableRpcError.code) throw new ResourceUnavailableRpcError(error); throw error; } }, async disconnect() { const instance = await this.getInstance(); return instance.disconnect(); }, async getAccounts() { const instance = await this.getInstance(); if (instance.accounts.length) return instance.accounts.map((x) => getAddress(x)); // Fallback to provider if SDK doesn't return accounts const provider = instance.getProvider(); const accounts = (await provider.request({ method: 'eth_accounts', })); return accounts.map((x) => getAddress(x)); }, async getChainId() { const instance = await this.getInstance(); if (instance.getChainId()) return Number(instance.getChainId()); // Fallback to provider if SDK doesn't return chainId const provider = instance.getProvider(); const chainId = await provider.request({ method: 'eth_chainId' }); return Number(chainId); }, async getProvider() { const instance = await this.getInstance(); return instance.getProvider(); }, async isAuthorized() { try { // MetaMask mobile provider sometimes fails to immediately resolve // JSON-RPC requests on page load const timeout = 10; const accounts = await withRetry(async () => withTimeout(async () => { const accounts = await this.getAccounts(); if (!accounts.length) throw new Error('try again'); return accounts; }, { timeout }), { delay: timeout + 1, retryCount: 3 }); return Boolean(accounts.length); } catch { return false; } }, async switchChain({ addEthereumChainParameter, chainId }) { const chain = config.chains.find(({ id }) => id === Number(chainId)); if (!chain) throw new SwitchChainError(new ChainNotConfiguredError()); const hexChainId = numberToHex(chainId); try { const instance = await this.getInstance(); await instance.switchChain({ chainId: hexChainId, chainConfiguration: { blockExplorerUrls: addEthereumChainParameter?.blockExplorerUrls ? [...addEthereumChainParameter.blockExplorerUrls] : chain.blockExplorers?.default.url ? [chain.blockExplorers.default.url] : undefined, chainId: hexChainId, chainName: addEthereumChainParameter?.chainName ?? chain.name, iconUrls: addEthereumChainParameter?.iconUrls, nativeCurrency: addEthereumChainParameter?.nativeCurrency ?? chain.nativeCurrency, rpcUrls: addEthereumChainParameter?.rpcUrls ? [...addEthereumChainParameter.rpcUrls] : chain.rpcUrls.default?.http ? [...chain.rpcUrls.default.http] : undefined, }, }); return chain; } catch (err) { const error = err; if (error.code === UserRejectedRequestError.code) throw new UserRejectedRequestError(error); throw new SwitchChainError(error); } }, async onAccountsChanged(accounts) { config.emitter.emit('change', { accounts: accounts.map((x) => getAddress(x)), }); }, onChainChanged(chain) { const chainId = Number(chain); config.emitter.emit('change', { chainId }); }, async onConnect(connectInfo) { const accounts = await this.getAccounts(); if (accounts.length === 0) return; const chainId = Number(connectInfo.chainId); config.emitter.emit('connect', { accounts, chainId }); }, async onDisconnect(error) { // If MetaMask emits a `code: 1013` error, wait for reconnection before disconnecting // https://github.com/MetaMask/providers/pull/120 if (error && error.code === 1013) { const provider = await this.getProvider(); if (provider && Boolean((await this.getAccounts()).length)) return; } config.emitter.emit('disconnect'); }, onDisplayUri(uri) { config.emitter.emit('message', { type: 'display_uri', data: uri }); }, async getInstance() { if (!metamask) { if (!metamaskPromise) { const { createEVMClient } = await (async () => { try { return import('@metamask/connect-evm'); } catch { throw new Error('dependency "@metamask/connect-evm" not found'); } })(); const defaultDappParams = typeof window === 'undefined' ? { name: 'wagmi' } : { name: window.location.hostname, url: window.location.href }; metamaskPromise = createEVMClient({ ...parameters, api: { supportedNetworks: Object.fromEntries(config.chains.map((chain) => [ numberToHex(chain.id), chain.rpcUrls.default?.http[0] ?? '', ])), }, dapp: parameters.dapp ?? { ...defaultDappParams, ...parameters.dappMetadata, }, debug: parameters.debug ?? parameters.logging?.sdk, eventHandlers: { accountsChanged: this.onAccountsChanged.bind(this), chainChanged: this.onChainChanged.bind(this), connect: this.onConnect.bind(this), disconnect: this.onDisconnect.bind(this), displayUri: this.onDisplayUri.bind(this), }, analytics: { integrationType: 'wagmi', }, ui: { ...parameters.ui, ...(parameters.headless != null && { headless: parameters.headless, }), }, ...(parameters.mobile && { mobile: parameters.mobile }), }); } metamask = await metamaskPromise; } return metamask; }, })); } //# sourceMappingURL=metaMask.js.map