UNPKG

@web3modal/base

Version:

#### 🔗 [Website](https://web3modal.com)

577 lines • 26.6 kB
import { EthereumProvider, OPTIONAL_METHODS } from '@walletconnect/ethereum-provider'; import { connect, disconnect, signMessage, getBalance, getEnsAvatar as wagmiGetEnsAvatar, getEnsName, watchAccount, watchConnectors, estimateGas as wagmiEstimateGas, writeContract as wagmiWriteContract, getAccount, getEnsAddress as wagmiGetEnsAddress, switchChain, waitForTransactionReceipt, getConnections, switchAccount, reconnect } from '@wagmi/core'; import { mainnet } from 'viem/chains'; import { prepareTransactionRequest, sendTransaction as wagmiSendTransaction } from '@wagmi/core'; import { formatUnits, parseUnits } from 'viem'; import { ConstantsUtil, PresetsUtil, HelpersUtil } from '@web3modal/scaffold-utils'; import { ConstantsUtil as CommonConstants } from '@web3modal/common'; import { getCaipDefaultChain, getEmailCaipNetworks, getWalletConnectCaipNetworks, requireCaipAddress } from './utils/helpers.js'; import { W3mFrameHelpers, W3mFrameRpcConstants } from '@web3modal/wallet'; import { NetworkUtil } from '@web3modal/common'; import { normalize } from 'viem/ens'; import { ConstantsUtil as CommonConstantsUtil } from '@web3modal/common'; export class EVMWagmiClient { constructor(options) { this.appKit = undefined; this.options = undefined; this.chain = CommonConstantsUtil.CHAIN.EVM; this.defaultChain = undefined; this.tokens = HelpersUtil.getCaipTokens(this.options?.tokens); this.getCaipDefaultChain = this.options?.defaultChain; this.siweControllerClient = this.options?.siweConfig; const { wagmiConfig, defaultChain } = options; if (!wagmiConfig) { throw new Error('wagmiConfig is undefined'); } this.wagmiConfig = wagmiConfig; this.defaultChain = getCaipDefaultChain(defaultChain); this.siweControllerClient = options.siweConfig; this.networkControllerClient = { switchCaipNetwork: async (caipNetwork) => { const chainId = NetworkUtil.caipNetworkIdToNumber(caipNetwork?.id); if (chainId) { await switchChain(this.wagmiConfig, { chainId }); } }, getApprovedCaipNetworksData: async () => new Promise(resolve => { const connections = new Map(this.wagmiConfig.state.connections); const connection = connections.get(this.wagmiConfig.state.current || ''); if (connection?.connector?.id === ConstantsUtil.AUTH_CONNECTOR_ID) { resolve(getEmailCaipNetworks()); } else if (connection?.connector?.id === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID) { const connector = this.wagmiConfig.connectors.find(c => c.id === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID); resolve(getWalletConnectCaipNetworks(connector)); } resolve({ approvedCaipNetworkIds: undefined, supportsAllNetworks: true }); }) }; this.connectionControllerClient = { connectWalletConnect: async (onUri) => { const siweConfig = this.options?.siweConfig; const connector = this.wagmiConfig.connectors.find(c => c.id === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID); if (!connector) { throw new Error('connectionControllerClient:getWalletConnectUri - connector is undefined'); } const provider = (await connector.getProvider()); provider.on('display_uri', data => { onUri(data); }); const clientId = await provider.signer?.client?.core?.crypto?.getClientId(); if (clientId) { this.appKit?.setClientId(clientId); } const chainId = NetworkUtil.caipNetworkIdToNumber(this.appKit?.getCaipNetwork()?.id); const siweParams = await siweConfig?.getMessageParams?.(); if (siweConfig?.options?.enabled && typeof provider?.authenticate === 'function' && siweParams && Object.keys(siweParams || {}).length > 0) { const { SIWEController, getDidChainId, getDidAddress } = await import('@web3modal/siwe'); await connector.setRequestedChainsIds(siweParams.chains); let reorderedChains = siweParams.chains; if (chainId) { reorderedChains = [chainId, ...siweParams.chains.filter(c => c !== chainId)]; } const result = await provider.authenticate({ nonce: await siweConfig.getNonce(), methods: [...OPTIONAL_METHODS], ...siweParams, chains: reorderedChains }); const signedCacao = result?.auths?.[0]; if (signedCacao) { const { p, s } = signedCacao; const cacaoChainId = getDidChainId(p.iss) || ''; const address = getDidAddress(p.iss); if (address && cacaoChainId) { SIWEController.setSession({ address, chainId: parseInt(cacaoChainId, 10) }); } try { const message = provider.signer.client.formatAuthMessage({ request: p, iss: p.iss }); await SIWEController.verifyMessage({ message, signature: s.s, cacao: signedCacao }); } catch (error) { console.error('Error verifying message', error); await provider.disconnect().catch(console.error); await SIWEController.signOut().catch(console.error); throw error; } } this.wagmiConfig.state.current = ''; } await connect(this.wagmiConfig, { connector, chainId }); }, connectExternal: async ({ id, provider, info }) => { const connector = this.wagmiConfig.connectors.find(c => c.id === id); if (!connector) { throw new Error('connectionControllerClient:connectExternal - connector is undefined'); } this.appKit?.setClientId(null); if (provider && info && connector.id === ConstantsUtil.EIP6963_CONNECTOR_ID) { connector.setEip6963Wallet?.({ provider, info }); } const chainId = NetworkUtil.caipNetworkIdToNumber(this.appKit?.getCaipNetwork()?.id); await connect(this.wagmiConfig, { connector, chainId }); }, checkInstalled: ids => { const injectedConnector = this.appKit ?.getConnectors() .find((c) => c.type === 'INJECTED'); if (!ids) { return Boolean(window.ethereum); } if (injectedConnector) { if (!window?.ethereum) { return false; } return ids.some(id => Boolean(window.ethereum?.[String(id)])); } return false; }, disconnect: async () => { await disconnect(this.wagmiConfig); this.appKit?.setClientId(null); if (this.options?.siweConfig?.options?.signOutOnDisconnect) { const { SIWEController } = await import('@web3modal/siwe'); await SIWEController.signOut(); } }, signMessage: async (message) => { const caipAddress = this.appKit?.getCaipAddress() || ''; const account = requireCaipAddress(caipAddress); return signMessage(this.wagmiConfig, { message, account }); }, estimateGas: async (args) => { if (args.chainNamespace && args.chainNamespace !== 'eip155') { throw new Error('connectionControllerClient:estimateGas - invalid chain namespace'); } try { return await wagmiEstimateGas(this.wagmiConfig, { account: args.address, to: args.to, data: args.data, type: 'legacy' }); } catch (error) { return 0n; } }, sendTransaction: async (data) => { if (data.chainNamespace && data.chainNamespace !== 'eip155') { throw new Error('connectionControllerClient:sendTransaction - invalid chain namespace'); } const { chainId } = getAccount(this.wagmiConfig); const txParams = { account: data.address, to: data.to, value: data.value, gas: data.gas, gasPrice: data.gasPrice, data: data.data, chainId, type: 'legacy' }; await prepareTransactionRequest(this.wagmiConfig, txParams); const tx = await wagmiSendTransaction(this.wagmiConfig, txParams); await waitForTransactionReceipt(this.wagmiConfig, { hash: tx, timeout: 25000 }); return tx; }, writeContract: async (data) => { const caipAddress = this.appKit?.getCaipAddress() || ''; const account = requireCaipAddress(caipAddress); const chainId = NetworkUtil.caipNetworkIdToNumber(this.appKit?.getCaipNetwork()?.id); const tx = await wagmiWriteContract(this.wagmiConfig, { chainId, address: data.tokenAddress, account, abi: data.abi, functionName: data.method, args: [data.receiverAddress, data.tokenAmount] }); return tx; }, getEnsAddress: async (value) => { try { const chainId = NetworkUtil.caipNetworkIdToNumber(this.appKit?.getCaipNetwork()?.id); let ensName = false; let wcName = false; if (value?.endsWith(CommonConstants.WC_NAME_SUFFIX)) { wcName = (await this.appKit?.resolveWalletConnectName(value)) || false; } if (chainId === mainnet.id) { ensName = await wagmiGetEnsAddress(this.wagmiConfig, { name: normalize(value), chainId }); } return ensName || wcName || false; } catch { return false; } }, getEnsAvatar: async (value) => { const chainId = NetworkUtil.caipNetworkIdToNumber(this.appKit?.getCaipNetwork()?.id); if (chainId !== mainnet.id) { return false; } const avatar = await wagmiGetEnsAvatar(this.wagmiConfig, { name: normalize(value), chainId }); return avatar || false; }, parseUnits, formatUnits }; } construct(appKit, options) { if (!options.projectId) { throw new Error('projectId is undefined'); } this.appKit = appKit; this.options = options; this.tokens = HelpersUtil.getCaipTokens(options.tokens); this.syncRequestedNetworks([...this.wagmiConfig.chains]); this.syncConnectors(this.wagmiConfig.connectors); this.initAuthConnectorListeners([...this.wagmiConfig.connectors]); watchConnectors(this.wagmiConfig, { onChange: connectors => this.syncConnectors(connectors) }); watchAccount(this.wagmiConfig, { onChange: accountData => this.syncAccount({ ...accountData }) }); this.appKit?.setEIP6963Enabled(options.enableEIP6963 !== false); this.appKit?.subscribeShouldUpdateToAddress((newAddress) => { if (newAddress) { const connections = getConnections(this.wagmiConfig); const connector = connections[0]?.connector; if (connector) { switchAccount(this.wagmiConfig, { connector }).then(response => this.syncAccount({ address: newAddress, isConnected: true, addresses: response.accounts, connector, chainId: response.chainId })); } } }); } subscribeState(callback) { return this.appKit?.subscribeState((state) => callback({ ...state, selectedNetworkId: NetworkUtil.caipNetworkIdToNumber(state.selectedNetworkId) })); } syncRequestedNetworks(chains) { const requestedCaipNetworks = chains?.map(chain => ({ id: `${ConstantsUtil.EIP155}:${chain.id}`, name: chain.name, imageId: PresetsUtil.EIP155NetworkImageIds[chain.id], imageUrl: this.options?.chainImages?.[chain.id], chain: this.chain })); this.appKit?.setRequestedCaipNetworks(requestedCaipNetworks ?? [], this.chain); } async syncAccount({ address, chainId, connector, addresses, status }) { const caipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; if (this.appKit?.getCaipAddress() === caipAddress) { return; } if (status === 'connected' && address && chainId) { this.syncNetwork(address, chainId, true); this.appKit?.setIsConnected(true, this.chain); this.appKit?.setCaipAddress(caipAddress, this.chain); await Promise.all([ this.syncProfile(address, chainId), this.syncBalance(address, chainId), this.syncConnectedWalletInfo(connector), this.appKit?.setApprovedCaipNetworksData(this.chain) ]); if (connector) { this.syncConnectedWalletInfo(connector); } const isAuthConnector = connector?.id === ConstantsUtil.AUTH_CONNECTOR_ID; if (!isAuthConnector && addresses?.length) { this.appKit?.setAllAccounts(addresses.map(addr => ({ address: addr, type: 'eoa' })), this.chain); } } else if (status === 'disconnected') { this.appKit?.resetAccount(this.chain); this.appKit?.resetWcConnection(); this.appKit?.resetNetwork(); this.appKit?.setAllAccounts([], this.chain); this.appKit?.setIsConnected(false, this.chain); } } async syncNetwork(address, chainId, isConnected) { const chain = this.wagmiConfig.chains.find((c) => c.id === chainId); if (chain || chainId) { const name = chain?.name ?? chainId?.toString(); const id = Number(chain?.id ?? chainId); const caipChainId = `${ConstantsUtil.EIP155}:${id}`; this.appKit?.setCaipNetwork({ id: caipChainId, name, imageId: PresetsUtil.EIP155NetworkImageIds[id], imageUrl: this.options?.chainImages?.[id], chain: this.chain }); if (isConnected && address && chainId) { const caipAddress = `${ConstantsUtil.EIP155}:${id}:${address}`; this.appKit?.setCaipAddress(caipAddress, this.chain); if (chain?.blockExplorers?.default?.url) { const url = `${chain.blockExplorers.default.url}/address/${address}`; this.appKit?.setAddressExplorerUrl(url, this.chain); } else { this.appKit?.setAddressExplorerUrl(undefined, this.chain); } await this.syncBalance(address, chainId); } } } async syncWalletConnectName(address) { if (!this.appKit) { throw new Error('syncWalletConnectName - appKit is undefined'); } try { const registeredWcNames = await this.appKit.getWalletConnectName(address); if (registeredWcNames[0]) { const wcName = registeredWcNames[0]; this.appKit?.setProfileName(wcName.name, this.chain); } else { this.appKit?.setProfileName(null, this.chain); } } catch { this.appKit?.setProfileName(null, this.chain); } } async syncProfile(address, chainId) { if (!this.appKit) { throw new Error('syncProfile - appKit is undefined'); } try { const { name, avatar } = await this.appKit.fetchIdentity({ address }); this.appKit?.setProfileName(name, this.chain); this.appKit?.setProfileImage(avatar, this.chain); if (!name) { await this.syncWalletConnectName(address); } } catch { if (chainId === mainnet.id) { const profileName = await getEnsName(this.wagmiConfig, { address, chainId }); if (profileName) { this.appKit?.setProfileName(profileName, this.chain); const profileImage = await wagmiGetEnsAvatar(this.wagmiConfig, { name: profileName, chainId }); if (profileImage) { this.appKit?.setProfileImage(profileImage, this.chain); } } else { await this.syncWalletConnectName(address); this.appKit?.setProfileImage(null, this.chain); } } else { await this.syncWalletConnectName(address); this.appKit?.setProfileImage(null, this.chain); } } } async syncBalance(address, chainId) { const chain = this.wagmiConfig.chains.find((c) => c.id === chainId); if (chain) { const balance = await getBalance(this.wagmiConfig, { address, chainId: chain.id, token: this.options?.tokens?.[chain.id]?.address }); this.appKit?.setBalance(balance.formatted, balance.symbol, this.chain); return; } this.appKit?.setBalance(undefined, undefined, this.chain); } async syncConnectedWalletInfo(connector) { if (!connector) { throw Error('syncConnectedWalletInfo - connector is undefined'); } if (connector.id === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID && connector.getProvider) { const walletConnectProvider = (await connector.getProvider()); if (walletConnectProvider.session) { this.appKit?.setConnectedWalletInfo({ ...walletConnectProvider.session.peer.metadata, name: walletConnectProvider.session.peer.metadata.name, icon: walletConnectProvider.session.peer.metadata.icons?.[0] }, this.chain); } } else { const wagmiConnector = this.appKit?.getConnectors().find(c => c.id === connector.id); this.appKit?.setConnectedWalletInfo({ name: connector.name, icon: connector.icon || this.appKit.getConnectorImage(wagmiConnector) }, this.chain); } } syncConnectors(connectors) { const uniqueIds = new Set(); const filteredConnectors = connectors.filter(item => !uniqueIds.has(item.id) && uniqueIds.add(item.id)); const w3mConnectors = []; filteredConnectors.forEach(({ id, name, type, icon }) => { const shouldSkip = ConstantsUtil.AUTH_CONNECTOR_ID === id; if (!shouldSkip) { w3mConnectors.push({ id, explorerId: PresetsUtil.ConnectorExplorerIds[id], imageUrl: this.options?.connectorImages?.[id] ?? icon, name: PresetsUtil.ConnectorNamesMap[id] ?? name, imageId: PresetsUtil.ConnectorImageIds[id], type: PresetsUtil.ConnectorTypesMap[type] ?? 'EXTERNAL', info: { rdns: id }, chain: this.chain }); } }); this.appKit?.setConnectors(w3mConnectors); this.syncAuthConnector(filteredConnectors); } async syncAuthConnector(connectors) { const authConnector = connectors.find(({ id }) => id === ConstantsUtil.AUTH_CONNECTOR_ID); if (authConnector) { const provider = await authConnector.getProvider(); this.appKit?.addConnector({ id: ConstantsUtil.AUTH_CONNECTOR_ID, type: 'AUTH', name: 'Auth', provider, email: authConnector.email, socials: authConnector.socials, showWallets: authConnector.showWallets, chain: this.chain, walletFeatures: authConnector.walletFeatures }); } } async initAuthConnectorListeners(connectors) { const authConnector = connectors.find(({ id }) => id === ConstantsUtil.AUTH_CONNECTOR_ID); if (authConnector) { await this.listenAuthConnector(authConnector); await this.listenModal(authConnector); } } async listenAuthConnector(connector) { if (typeof window !== 'undefined' && connector) { this.appKit?.setLoading(true); const provider = (await connector.getProvider()); const isLoginEmailUsed = provider.getLoginEmailUsed(); this.appKit?.setLoading(isLoginEmailUsed); if (isLoginEmailUsed) { this.appKit?.setIsConnected(false, this.chain); } provider.onRpcRequest((request) => { if (W3mFrameHelpers.checkIfRequestExists(request)) { if (!W3mFrameHelpers.checkIfRequestIsSafe(request)) { this.appKit?.handleUnsafeRPCRequest(); } } else { this.appKit?.open(); console.error(W3mFrameRpcConstants.RPC_METHOD_NOT_ALLOWED_MESSAGE, { method: request.method }); setTimeout(() => { this.appKit?.showErrorMessage(W3mFrameRpcConstants.RPC_METHOD_NOT_ALLOWED_UI_MESSAGE); }, 300); provider.rejectRpcRequests(); } }); provider.onRpcError(() => { const isModalOpen = this.appKit?.isOpen(); if (isModalOpen) { if (this.appKit?.isTransactionStackEmpty()) { this.appKit?.close(); } else { this.appKit?.popTransactionStack(true); } } }); provider.onRpcSuccess((_, request) => { const isSafeRequest = W3mFrameHelpers.checkIfRequestIsSafe(request); if (isSafeRequest) { return; } if (this.appKit?.isTransactionStackEmpty()) { this.appKit?.close(); } else { this.appKit?.popTransactionStack(); } }); provider.onNotConnected(() => { const isConnected = this.appKit?.getIsConnectedState(); if (!isConnected) { this.appKit?.setIsConnected(false, this.chain); this.appKit?.setLoading(false); } }); provider.onIsConnected(req => { this.appKit?.setIsConnected(true, this.chain); this.appKit?.setSmartAccountDeployed(Boolean(req.smartAccountDeployed), this.chain); this.appKit?.setPreferredAccountType(req.preferredAccountType, this.chain); this.appKit?.setLoading(false); this.appKit?.setAllAccounts(req.accounts || [ { address: req.address, type: (req.preferredAccountType || 'eoa') } ], this.chain); }); provider.onGetSmartAccountEnabledNetworks(networks => { this.appKit?.setSmartAccountEnabledNetworks(networks, this.chain); }); provider.onSetPreferredAccount(({ address, type }) => { if (!address) { return; } this.appKit?.setPreferredAccountType(type, this.chain); reconnect(this.wagmiConfig, { connectors: [connector] }); }); } } async listenModal(connector) { const provider = (await connector.getProvider()); this.subscribeState(val => { if (!val.open) { provider.rejectRpcRequests(); } }); } } //# sourceMappingURL=client.js.map