UNPKG

@web3modal/base

Version:

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

1,072 lines • 52 kB
import { ConstantsUtil, PresetsUtil, HelpersUtil } from '@web3modal/scaffold-utils'; import { ConstantsUtil as CommonConstantsUtil } from '@web3modal/common'; import EthereumProvider, { OPTIONAL_METHODS } from '@walletconnect/ethereum-provider'; import { getChainsFromAccounts } from '@walletconnect/utils'; import { ethers, utils } from 'ethers5'; import { EthersConstantsUtil, EthersHelpersUtil, EthersStoreUtil } from '@web3modal/scaffold-utils/ethers'; import { W3mFrameProvider, W3mFrameHelpers, W3mFrameRpcConstants } from '@web3modal/wallet'; import { NetworkUtil } from '@web3modal/common'; import { SafeLocalStorage } from '../../../utils/SafeLocalStorage.js'; export class EVMEthers5Client { constructor(options) { this.appKit = undefined; this.hasSyncedConnectedAccount = false; this.EIP6963Providers = []; this.projectId = ''; this.options = undefined; this.chain = CommonConstantsUtil.CHAIN.EVM; this.siweControllerClient = this.options?.siweConfig; this.defaultChain = undefined; this.tokens = HelpersUtil.getCaipTokens(this.options?.tokens); const { ethersConfig, siweConfig, chains, defaultChain, tokens } = options; if (!ethersConfig) { throw new Error('web3modal:constructor - ethersConfig is undefined'); } this.ethersConfig = ethersConfig; this.siweControllerClient = siweConfig; this.defaultChain = EthersHelpersUtil.getCaipDefaultChain(defaultChain); this.tokens = HelpersUtil.getCaipTokens(tokens); this.chains = chains; this.networkControllerClient = { switchCaipNetwork: async (caipNetwork) => { const chainId = NetworkUtil.caipNetworkIdToNumber(caipNetwork?.id); if (chainId) { try { EthersStoreUtil.setError(undefined); await this.switchNetwork(chainId); } catch (error) { EthersStoreUtil.setError(error); throw new Error('networkControllerClient:switchCaipNetwork - unable to switch chain'); } } }, getApprovedCaipNetworksData: async () => new Promise(async (resolve) => { const walletChoice = SafeLocalStorage.getItem(EthersConstantsUtil.WALLET_ID); if (walletChoice?.includes(ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID)) { const provider = await this.getWalletConnectProvider(); if (!provider) { throw new Error('networkControllerClient:getApprovedCaipNetworks - provider is undefined'); } const ns = provider.signer?.session?.namespaces; const nsMethods = ns?.[ConstantsUtil.EIP155]?.methods; const nsChains = getChainsFromAccounts(ns?.[ConstantsUtil.EIP155]?.accounts || []); const result = { supportsAllNetworks: nsMethods?.includes(ConstantsUtil.ADD_CHAIN_METHOD) ?? false, approvedCaipNetworkIds: nsChains }; resolve(result); } else { const result = { approvedCaipNetworkIds: undefined, supportsAllNetworks: true }; resolve(result); } }) }; this.connectionControllerClient = { connectWalletConnect: async (onUri) => { const WalletConnectProvider = await this.getWalletConnectProvider(); if (!WalletConnectProvider) { throw new Error('connectionControllerClient:getWalletConnectUri - provider is undefined'); } WalletConnectProvider.on('display_uri', (uri) => { onUri(uri); }); const params = await siweConfig?.getMessageParams?.(); if (siweConfig?.options?.enabled && params && Object.keys(params || {}).length > 0) { const { SIWEController, getDidChainId, getDidAddress } = await import('@web3modal/siwe'); const chainId = NetworkUtil.caipNetworkIdToNumber(this.appKit?.getCaipNetwork()?.id); let reorderedChains = params.chains; if (chainId) { reorderedChains = [chainId, ...params.chains.filter(c => c !== chainId)]; } const result = await WalletConnectProvider.authenticate({ nonce: await siweConfig.getNonce(), methods: [...OPTIONAL_METHODS], ...params, 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 = WalletConnectProvider.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 WalletConnectProvider.disconnect().catch(console.error); await SIWEController.signOut().catch(console.error); throw error; } } } else { await WalletConnectProvider.connect({ optionalChains: this.chains.map(c => c.chainId) }); } await this.setWalletConnectProvider(); }, connectExternal: async ({ id, info, provider }) => { this.appKit?.setClientId(null); if (id === ConstantsUtil.INJECTED_CONNECTOR_ID) { const InjectedProvider = ethersConfig.injected; if (!InjectedProvider) { throw new Error('connectionControllerClient:connectInjected - provider is undefined'); } try { EthersStoreUtil.setError(undefined); await InjectedProvider.request({ method: 'eth_requestAccounts' }); this.setInjectedProvider(ethersConfig); } catch (error) { EthersStoreUtil.setError(error); } } else if (id === ConstantsUtil.EIP6963_CONNECTOR_ID && info && provider) { try { EthersStoreUtil.setError(undefined); await provider.request({ method: 'eth_requestAccounts' }); } catch (error) { EthersStoreUtil.setError(error); } this.setEIP6963Provider(provider, info.name); } else if (id === ConstantsUtil.COINBASE_SDK_CONNECTOR_ID) { const CoinbaseProvider = ethersConfig.coinbase; if (!CoinbaseProvider) { throw new Error('connectionControllerClient:connectCoinbase - connector is undefined'); } try { EthersStoreUtil.setError(undefined); await CoinbaseProvider.request({ method: 'eth_requestAccounts' }); this.setCoinbaseProvider(ethersConfig); } catch (error) { EthersStoreUtil.setError(error); throw new Error(error.message); } } }, checkInstalled: (ids) => { if (!ids) { return Boolean(window.ethereum); } if (ethersConfig.injected) { if (!window?.ethereum) { return false; } } return ids.some(id => Boolean(window.ethereum?.[String(id)])); }, disconnect: async () => { const provider = EthersStoreUtil.state.provider; const providerType = EthersStoreUtil.state.providerType; SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID); EthersStoreUtil.reset(); this.appKit?.setClientId(null); if (siweConfig?.options?.signOutOnDisconnect) { const { SIWEController } = await import('@web3modal/siwe'); await SIWEController.signOut(); } if (providerType === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID || providerType === 'coinbaseWalletSDK') { const ethProvider = provider; await ethProvider.disconnect(); } else if (providerType === ConstantsUtil.EIP6963_CONNECTOR_ID && provider) { await this.revokeProviderPermissions(provider); } else if (providerType === ConstantsUtil.INJECTED_CONNECTOR_ID) { const InjectedProvider = ethersConfig.injected; if (InjectedProvider) { await this.revokeProviderPermissions(InjectedProvider); } } provider?.emit?.('disconnect'); SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID); EthersStoreUtil.reset(); }, signMessage: async (message) => { const provider = EthersStoreUtil.state.provider; if (!provider) { throw new Error('connectionControllerClient:signMessage - provider is undefined'); } const hexMessage = utils.isHexString(message) ? message : utils.hexlify(utils.toUtf8Bytes(message)); const signature = await provider.request({ method: 'personal_sign', params: [hexMessage, this.getAddress()] }); return signature; }, parseUnits: (value, decimals) => ethers.utils.parseUnits(value, decimals).toBigInt(), formatUnits: (value, decimals) => ethers.utils.formatUnits(value, decimals), sendTransaction: async (data) => { const provider = EthersStoreUtil.state.provider; const address = EthersStoreUtil.state.address; if (data.chainNamespace && data.chainNamespace !== 'eip155') { throw new Error('connectionControllerClient:sendTransaction - invalid chain namespace'); } if (!provider) { throw new Error('connectionControllerClient:sendTransaction - provider is undefined'); } if (!address) { throw new Error('connectionControllerClient:sendTransaction - address is undefined'); } const txParams = { to: data.to, value: data.value, gasLimit: data.gas, gasPrice: data.gasPrice, data: data.data, type: 0 }; const browserProvider = new ethers.providers.Web3Provider(provider); const signer = browserProvider.getSigner(); const txResponse = await signer.sendTransaction(txParams); const txReceipt = await txResponse.wait(); return txReceipt?.blockHash || null; } }; } construct(appKit, options) { if (!options.projectId) { throw new Error('web3modal:initialize - projectId is undefined'); } this.appKit = appKit; this.options = options; this.projectId = options.projectId; this.metadata = this.ethersConfig.metadata; if (this.defaultChain) { this.appKit?.setCaipNetwork(this.defaultChain); } this.createProvider(); EthersStoreUtil.subscribeKey('address', () => { this.syncAccount(); }); EthersStoreUtil.subscribeKey('chainId', () => { this.syncNetwork(); }); this.appKit?.subscribeCaipNetworkChange(network => { if (!this.getChainId() && network) { EthersStoreUtil.setChainId(NetworkUtil.caipNetworkIdToNumber(network.id)); } }); this.appKit?.subscribeShouldUpdateToAddress((address) => { if (!address) { return; } EthersStoreUtil.setAddress(utils.getAddress(address)); }); this.syncRequestedNetworks(this.chains, this.options?.chainImages); this.syncConnectors(this.ethersConfig); if (this.ethersConfig.injected) { this.checkActiveInjectedProvider(this.ethersConfig); } if (this.ethersConfig.auth?.email || this.ethersConfig.auth?.socials?.length) { this.syncAuthConnector(this.options.projectId, this.ethersConfig.auth); } if (this.ethersConfig.coinbase) { this.checkActiveCoinbaseProvider(this.ethersConfig); } if (typeof window !== 'undefined') { this.listenConnectors(true); this.checkActive6963Provider(); } this.appKit?.setEIP6963Enabled(this.ethersConfig.EIP6963); } getState() { const state = this.appKit?.getState(); return { ...state, selectedNetworkId: NetworkUtil.caipNetworkIdToNumber(state?.selectedNetworkId) }; } subscribeState(callback) { return this.appKit?.subscribeState(state => callback({ ...state, selectedNetworkId: NetworkUtil.caipNetworkIdToNumber(state.selectedNetworkId) })); } setAddress(address) { const originalAddress = address ? utils.getAddress(address) : undefined; EthersStoreUtil.setAddress(originalAddress); } getAddress() { const { address } = EthersStoreUtil.state; return address ? utils.getAddress(address) : address; } getError() { return EthersStoreUtil.state.error; } getChainId() { return EthersStoreUtil.state.chainId; } getStatus() { return EthersStoreUtil.state.status; } getIsConnected() { return EthersStoreUtil.state.isConnected; } getWalletProvider() { return EthersStoreUtil.state.provider; } getWalletProviderType() { return EthersStoreUtil.state.providerType; } subscribeProvider(callback) { return EthersStoreUtil.subscribe(callback); } async disconnect() { const { provider, providerType } = EthersStoreUtil.state; SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID); EthersStoreUtil.reset(); this.appKit?.setClientId(null); if (providerType === 'injected' || providerType === 'eip6963') { provider?.emit('disconnect'); } else { await provider.disconnect(); } } createProvider() { if (!this.walletConnectProviderInitPromise && typeof window !== 'undefined') { this.walletConnectProviderInitPromise = this.initWalletConnectProvider(); } return this.walletConnectProviderInitPromise; } async initWalletConnectProvider() { const walletConnectProviderOptions = { projectId: this.projectId, showQrModal: false, rpcMap: this.chains ? this.chains.reduce((map, chain) => { map[chain.chainId] = chain.rpcUrl; return map; }, {}) : {}, optionalChains: [...this.chains.map(chain => chain.chainId)], metadata: { name: this.metadata ? this.metadata.name : '', description: this.metadata ? this.metadata.description : '', url: this.metadata ? this.metadata.url : '', icons: this.metadata ? this.metadata.icons : [''] } }; this.walletConnectProvider = await EthereumProvider.init(walletConnectProviderOptions); await this.checkActiveWalletConnectProvider(); } async getWalletConnectProvider() { if (!this.walletConnectProvider) { try { EthersStoreUtil.setError(undefined); await this.createProvider(); } catch (error) { EthersStoreUtil.setError(error); } } return this.walletConnectProvider; } syncRequestedNetworks(chains, chainImages) { const requestedCaipNetworks = chains?.map(chain => ({ id: `${ConstantsUtil.EIP155}:${chain.chainId}`, name: chain.name, imageId: PresetsUtil.EIP155NetworkImageIds[chain.chainId], imageUrl: chainImages?.[chain.chainId], chain: CommonConstantsUtil.CHAIN.EVM })); this.appKit?.setRequestedCaipNetworks(requestedCaipNetworks ?? [], this.chain); } async checkActiveWalletConnectProvider() { const WalletConnectProvider = await this.getWalletConnectProvider(); const walletId = SafeLocalStorage.getItem(EthersConstantsUtil.WALLET_ID); if (WalletConnectProvider) { if (walletId === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID) { await this.setWalletConnectProvider(); } } const isConnected = EthersStoreUtil.state.isConnected; EthersStoreUtil.setStatus(isConnected ? 'connected' : 'disconnected'); } checkActiveInjectedProvider(config) { const InjectedProvider = config.injected; const walletId = SafeLocalStorage.getItem(EthersConstantsUtil.WALLET_ID); if (InjectedProvider) { if (walletId === ConstantsUtil.INJECTED_CONNECTOR_ID) { this.setInjectedProvider(config); this.watchInjected(config); } } } checkActiveCoinbaseProvider(config) { const CoinbaseProvider = config.coinbase; const walletId = SafeLocalStorage.getItem(EthersConstantsUtil.WALLET_ID); if (CoinbaseProvider) { if (walletId === ConstantsUtil.COINBASE_SDK_CONNECTOR_ID) { if (CoinbaseProvider._addresses && CoinbaseProvider._addresses?.length > 0) { this.setCoinbaseProvider(config); this.watchCoinbase(config); } else { SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID); EthersStoreUtil.reset(); } } } } checkActive6963Provider() { const currentActiveWallet = SafeLocalStorage.getItem(EthersConstantsUtil.WALLET_ID); if (currentActiveWallet) { const currentProvider = this.EIP6963Providers.find(provider => provider.info.name === currentActiveWallet); if (currentProvider) { this.setEIP6963Provider(currentProvider.provider, currentProvider.info.name); } } } async setWalletConnectProvider() { SafeLocalStorage.setItem(EthersConstantsUtil.WALLET_ID, ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID); const WalletConnectProvider = await this.getWalletConnectProvider(); if (WalletConnectProvider) { EthersStoreUtil.setChainId(WalletConnectProvider.chainId); EthersStoreUtil.setProviderType('walletConnect'); EthersStoreUtil.setProvider(WalletConnectProvider); EthersStoreUtil.setStatus('connected'); EthersStoreUtil.setIsConnected(true); this.appKit?.setAllAccounts(WalletConnectProvider.accounts.map(address => ({ address, type: 'eoa' })), this.chain); this.setAddress(WalletConnectProvider.accounts?.[0]); this.watchWalletConnect(); } } async setEIP6963Provider(provider, name) { SafeLocalStorage.setItem(EthersConstantsUtil.WALLET_ID, name); if (provider) { const { addresses, chainId } = await EthersHelpersUtil.getUserInfo(provider); if (addresses?.[0] && chainId) { EthersStoreUtil.setChainId(chainId); EthersStoreUtil.setProviderType('eip6963'); EthersStoreUtil.setProvider(provider); EthersStoreUtil.setStatus('connected'); EthersStoreUtil.setIsConnected(true); this.appKit?.setAllAccounts(addresses.map(address => ({ address, type: 'eoa' })), this.chain); this.setAddress(addresses[0]); this.watchEIP6963(provider); } } } async setInjectedProvider(config) { SafeLocalStorage.setItem(EthersConstantsUtil.WALLET_ID, ConstantsUtil.INJECTED_CONNECTOR_ID); const InjectedProvider = config.injected; if (InjectedProvider) { const { addresses, chainId } = await EthersHelpersUtil.getUserInfo(InjectedProvider); if (addresses?.[0] && chainId) { EthersStoreUtil.setChainId(chainId); EthersStoreUtil.setProviderType('injected'); EthersStoreUtil.setProvider(config.injected); EthersStoreUtil.setStatus('connected'); EthersStoreUtil.setIsConnected(true); this.appKit?.setAllAccounts(addresses.map(address => ({ address, type: 'eoa' })), this.chain); this.setAddress(addresses[0]); this.watchCoinbase(config); } } } async setCoinbaseProvider(config) { SafeLocalStorage.setItem(EthersConstantsUtil.WALLET_ID, ConstantsUtil.COINBASE_SDK_CONNECTOR_ID); const CoinbaseProvider = config.coinbase; if (CoinbaseProvider) { const { addresses, chainId } = await EthersHelpersUtil.getUserInfo(CoinbaseProvider); if (addresses?.[0] && chainId) { EthersStoreUtil.setChainId(chainId); EthersStoreUtil.setProviderType('coinbaseWalletSDK'); EthersStoreUtil.setProvider(config.coinbase); EthersStoreUtil.setStatus('connected'); EthersStoreUtil.setIsConnected(true); this.appKit?.setAllAccounts(addresses.map(address => ({ address, type: 'eoa' })), this.chain); this.setAddress(addresses[0]); this.watchCoinbase(config); } } } async setAuthProvider() { SafeLocalStorage.setItem(EthersConstantsUtil.WALLET_ID, ConstantsUtil.AUTH_CONNECTOR_ID); if (this.authProvider) { this.appKit?.setLoading(true); const { address, chainId, smartAccountDeployed, preferredAccountType, accounts = [] } = await this.authProvider.connect({ chainId: this.getChainId() }); const { smartAccountEnabledNetworks } = await this.authProvider.getSmartAccountEnabledNetworks(); this.appKit?.setSmartAccountEnabledNetworks(smartAccountEnabledNetworks, this.chain); if (address && chainId) { this.appKit?.setAllAccounts(accounts.length > 0 ? accounts : [{ address, type: preferredAccountType }], this.chain); EthersStoreUtil.setChainId(NetworkUtil.parseEvmChainId(chainId)); EthersStoreUtil.setProviderType(ConstantsUtil.AUTH_CONNECTOR_ID); EthersStoreUtil.setProvider(this.authProvider); EthersStoreUtil.setStatus('connected'); EthersStoreUtil.setIsConnected(true); EthersStoreUtil.setAddress(address); EthersStoreUtil.setPreferredAccountType(preferredAccountType); this.appKit?.setSmartAccountDeployed(Boolean(smartAccountDeployed), this.chain); this.watchAuth(); this.watchModal(); } this.appKit?.setLoading(false); } } async watchWalletConnect() { const WalletConnectProvider = await this.getWalletConnectProvider(); function disconnectHandler() { SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID); EthersStoreUtil.reset(); WalletConnectProvider?.removeListener('disconnect', disconnectHandler); WalletConnectProvider?.removeListener('accountsChanged', accountsChangedHandler); WalletConnectProvider?.removeListener('chainChanged', chainChangedHandler); } function chainChangedHandler(chainId) { if (chainId) { const chain = EthersHelpersUtil.hexStringToNumber(chainId); EthersStoreUtil.setChainId(chain); } } const accountsChangedHandler = async (accounts) => { if (accounts.length > 0) { await this.setWalletConnectProvider(); } }; if (WalletConnectProvider) { WalletConnectProvider.on('disconnect', disconnectHandler); WalletConnectProvider.on('accountsChanged', accountsChangedHandler); WalletConnectProvider.on('chainChanged', chainChangedHandler); } } watchInjected(config) { const InjectedProvider = config.injected; function disconnectHandler() { SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID); EthersStoreUtil.reset(); InjectedProvider?.removeListener('disconnect', disconnectHandler); InjectedProvider?.removeListener('accountsChanged', accountsChangedHandler); InjectedProvider?.removeListener('chainChanged', chainChangedHandler); } function accountsChangedHandler(accounts) { const currentAccount = accounts?.[0]; if (currentAccount) { EthersStoreUtil.setAddress(utils.getAddress(currentAccount)); } else { SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID); EthersStoreUtil.reset(); } } function chainChangedHandler(chainId) { if (chainId) { const chain = typeof chainId === 'string' ? EthersHelpersUtil.hexStringToNumber(chainId) : Number(chainId); EthersStoreUtil.setChainId(chain); } } if (InjectedProvider) { InjectedProvider.on('disconnect', disconnectHandler); InjectedProvider.on('accountsChanged', accountsChangedHandler); InjectedProvider.on('chainChanged', chainChangedHandler); } } watchEIP6963(provider) { const appKit = this.appKit; const namespace = this.chain; function disconnectHandler() { SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID); EthersStoreUtil.reset(); provider.removeListener('disconnect', disconnectHandler); provider.removeListener('accountsChanged', accountsChangedHandler); provider.removeListener('chainChanged', chainChangedHandler); } function accountsChangedHandler(accounts) { const currentAccount = accounts?.[0]; if (currentAccount) { EthersStoreUtil.setAddress(utils.getAddress(currentAccount)); appKit?.setAllAccounts(accounts.map(address => ({ address, type: 'eoa' })), namespace); } else { appKit?.setAllAccounts([], namespace); SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID); EthersStoreUtil.reset(); } } function chainChangedHandler(chainId) { if (chainId) { const chain = typeof chainId === 'string' ? EthersHelpersUtil.hexStringToNumber(chainId) : Number(chainId); EthersStoreUtil.setChainId(chain); } } provider.on('disconnect', disconnectHandler); provider.on('accountsChanged', accountsChangedHandler); provider.on('chainChanged', chainChangedHandler); } watchCoinbase(config) { const CoinbaseProvider = config.coinbase; const walletId = SafeLocalStorage.getItem(EthersConstantsUtil.WALLET_ID); function disconnectHandler() { SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID); EthersStoreUtil.reset(); CoinbaseProvider?.removeListener('disconnect', disconnectHandler); CoinbaseProvider?.removeListener('accountsChanged', accountsChangedHandler); CoinbaseProvider?.removeListener('chainChanged', chainChangedHandler); } function accountsChangedHandler(accounts) { if (accounts.length === 0) { SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID); EthersStoreUtil.reset(); } else { EthersStoreUtil.setAddress(accounts[0]); } } function chainChangedHandler(chainId) { if (chainId && walletId === ConstantsUtil.COINBASE_SDK_CONNECTOR_ID) { const chain = Number(chainId); EthersStoreUtil.setChainId(chain); } } if (CoinbaseProvider) { CoinbaseProvider.on('disconnect', disconnectHandler); CoinbaseProvider.on('accountsChanged', accountsChangedHandler); CoinbaseProvider.on('chainChanged', chainChangedHandler); } } watchAuth() { if (this.authProvider) { this.authProvider.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); } }); this.authProvider.onRpcError(() => { const isModalOpen = this.appKit?.isOpen(); if (isModalOpen) { if (this.appKit?.isTransactionStackEmpty()) { this.appKit?.close(); } else { this.appKit?.popTransactionStack(true); } } }); this.authProvider.onRpcSuccess((_, request) => { const isSafeRequest = W3mFrameHelpers.checkIfRequestIsSafe(request); if (isSafeRequest) { return; } if (this.appKit?.isTransactionStackEmpty()) { this.appKit?.close(); } else { this.appKit?.popTransactionStack(); } }); this.authProvider.onNotConnected(() => { this.appKit?.setIsConnected(false, this.chain); this.appKit?.setLoading(false); }); this.authProvider.onIsConnected(({ preferredAccountType }) => { this.appKit?.setIsConnected(true, this.chain); this.appKit?.setLoading(false); EthersStoreUtil.setPreferredAccountType(preferredAccountType); }); this.authProvider.onSetPreferredAccount(({ address, type }) => { if (!address) { return; } this.appKit?.setLoading(true); const chainId = NetworkUtil.caipNetworkIdToNumber(this.appKit?.getCaipNetwork()?.id); EthersStoreUtil.setAddress(address); EthersStoreUtil.setChainId(chainId); EthersStoreUtil.setStatus('connected'); EthersStoreUtil.setIsConnected(true); EthersStoreUtil.setPreferredAccountType(type); this.syncAccount().then(() => this.appKit?.setLoading(false)); }); } } watchModal() { if (this.authProvider) { this.subscribeState(val => { if (!val.open) { this.authProvider?.rejectRpcRequests(); } }); } } async syncAccount() { const address = EthersStoreUtil.state.address; const chainId = EthersStoreUtil.state.chainId; const isConnected = EthersStoreUtil.state.isConnected; this.appKit?.resetAccount(this.chain); if (isConnected && address && chainId) { const caipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; this.appKit?.setIsConnected(isConnected, this.chain); this.appKit?.setCaipAddress(caipAddress, this.chain); this.syncConnectedWalletInfo(); await Promise.all([ this.syncProfile(address), this.syncBalance(address), this.appKit?.setApprovedCaipNetworksData(this.chain) ]); this.hasSyncedConnectedAccount = true; } else if (!isConnected && this.hasSyncedConnectedAccount) { this.appKit?.resetWcConnection(); this.appKit?.resetNetwork(); this.appKit?.setAllAccounts([], this.chain); } } async syncNetwork() { const chainImages = this.options?.chainImages; const address = EthersStoreUtil.state.address; const chainId = EthersStoreUtil.state.chainId; const isConnected = EthersStoreUtil.state.isConnected; if (this.chains) { const chain = this.chains.find(c => c.chainId === chainId); if (chain) { const caipChainId = `${ConstantsUtil.EIP155}:${chain.chainId}`; this.appKit?.setCaipNetwork({ id: caipChainId, name: chain.name, imageId: PresetsUtil.EIP155NetworkImageIds[chain.chainId], imageUrl: chainImages?.[chain.chainId], chain: this.chain }); if (isConnected && address) { const caipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; this.appKit?.setCaipAddress(caipAddress, this.chain); if (chain.explorerUrl) { const url = `${chain.explorerUrl}/address/${address}`; this.appKit?.setAddressExplorerUrl(url, this.chain); } else { this.appKit?.setAddressExplorerUrl(undefined, this.chain); } if (this.hasSyncedConnectedAccount) { await this.syncBalance(address); } } } else if (isConnected) { this.appKit?.setCaipNetwork({ id: `${ConstantsUtil.EIP155}:${chainId}`, chain: this.chain }); } } } async syncWalletConnectName(address) { 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) { const chainId = EthersStoreUtil.state.chainId; try { const identity = await this.appKit?.fetchIdentity({ address }); const name = identity?.name; const avatar = identity?.avatar; this.appKit?.setProfileName(name, this.chain); this.appKit?.setProfileImage(avatar, this.chain); if (!name) { await this.syncWalletConnectName(address); } } catch { if (chainId === 1) { const ensProvider = new ethers.providers.InfuraProvider('mainnet'); const name = await ensProvider.lookupAddress(address); const avatar = await ensProvider.getAvatar(address); if (name) { this.appKit?.setProfileName(name, this.chain); } if (avatar) { this.appKit?.setProfileImage(avatar, this.chain); } } else { this.appKit?.setProfileName(null, this.chain); this.appKit?.setProfileImage(null, this.chain); } } } async syncBalance(address) { const chainId = EthersStoreUtil.state.chainId; if (chainId && this.chains) { const chain = this.chains.find(c => c.chainId === chainId); if (chain) { const JsonRpcProvider = new ethers.providers.JsonRpcProvider(chain.rpcUrl, { chainId, name: chain.name }); if (JsonRpcProvider) { const balance = await JsonRpcProvider.getBalance(address); const formattedBalance = utils.formatEther(balance); this.appKit?.setBalance(formattedBalance, chain.currency, this.chain); } } } } syncConnectedWalletInfo() { const currentActiveWallet = SafeLocalStorage.getItem(EthersConstantsUtil.WALLET_ID); const providerType = EthersStoreUtil.state.providerType; if (providerType === ConstantsUtil.EIP6963_CONNECTOR_ID) { if (currentActiveWallet) { const currentProvider = this.EIP6963Providers.find(provider => provider.info.name === currentActiveWallet); if (currentProvider) { this.appKit?.setConnectedWalletInfo({ ...currentProvider.info }, this.chain); } } } else if (providerType === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID) { const provider = EthersStoreUtil.state.provider; if (provider.session) { this.appKit?.setConnectedWalletInfo({ ...provider.session.peer.metadata, name: provider.session.peer.metadata.name, icon: provider.session.peer.metadata.icons?.[0] }, this.chain); } } else if (currentActiveWallet) { this.appKit?.setConnectedWalletInfo({ name: currentActiveWallet }, this.chain); } } async switchNetwork(chainId) { const provider = EthersStoreUtil.state.provider; const providerType = EthersStoreUtil.state.providerType; if (this.chains) { const chain = this.chains.find(c => c.chainId === chainId); if (providerType === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID && chain) { const WalletConnectProvider = provider; if (WalletConnectProvider) { try { await WalletConnectProvider.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: EthersHelpersUtil.numberToHexString(chain.chainId) }] }); EthersStoreUtil.setChainId(chainId); } catch (switchError) { const message = switchError?.message; if (/(?<temp1>user rejected)/u.test(message?.toLowerCase())) { throw new Error('Chain is not supported'); } await EthersHelpersUtil.addEthereumChain(WalletConnectProvider, chain); } } } else if (providerType === ConstantsUtil.INJECTED_CONNECTOR_ID && chain) { const InjectedProvider = provider; if (InjectedProvider) { try { await InjectedProvider.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: EthersHelpersUtil.numberToHexString(chain.chainId) }] }); EthersStoreUtil.setChainId(chain.chainId); } catch (switchError) { if (switchError.code === EthersConstantsUtil.ERROR_CODE_UNRECOGNIZED_CHAIN_ID || switchError.code === EthersConstantsUtil.ERROR_CODE_DEFAULT || switchError?.data?.originalError?.code === EthersConstantsUtil.ERROR_CODE_UNRECOGNIZED_CHAIN_ID) { await EthersHelpersUtil.addEthereumChain(InjectedProvider, chain); } else { throw new Error('Chain is not supported'); } } } } else if (providerType === ConstantsUtil.EIP6963_CONNECTOR_ID && chain) { const EIP6963Provider = provider; if (EIP6963Provider) { try { await EIP6963Provider.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: EthersHelpersUtil.numberToHexString(chain.chainId) }] }); EthersStoreUtil.setChainId(chain.chainId); } catch (switchError) { if (switchError.code === EthersConstantsUtil.ERROR_CODE_UNRECOGNIZED_CHAIN_ID || switchError.code === EthersConstantsUtil.ERROR_CODE_DEFAULT || switchError?.data?.originalError?.code === EthersConstantsUtil.ERROR_CODE_UNRECOGNIZED_CHAIN_ID) { await EthersHelpersUtil.addEthereumChain(EIP6963Provider, chain); } else { throw new Error('Chain is not supported'); } } } } else if (providerType === ConstantsUtil.COINBASE_SDK_CONNECTOR_ID && chain) { const CoinbaseProvider = provider; if (CoinbaseProvider) { try { await CoinbaseProvider.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: EthersHelpersUtil.numberToHexString(chain.chainId) }] }); EthersStoreUtil.setChainId(chain.chainId); } catch (switchError) { if (switchError.code === EthersConstantsUtil.ERROR_CODE_UNRECOGNIZED_CHAIN_ID || switchError.code === EthersConstantsUtil.ERROR_CODE_DEFAULT || switchError?.data?.originalError?.code === EthersConstantsUtil.ERROR_CODE_UNRECOGNIZED_CHAIN_ID) { await EthersHelpersUtil.addEthereumChain(CoinbaseProvider, chain); } else { throw new Error('Error switching network'); } } } } else if (providerType === ConstantsUtil.AUTH_CONNECTOR_ID) { if (this.authProvider && chain?.chainId) { try { this.appKit?.setLoading(true); await this.authProvider.switchNetwork(chain?.chainId); EthersStoreUtil.setChainId(chain.chainId); const { address, preferredAccountType } = await this.authProvider.connect({ chainId: chain?.chainId }); EthersStoreUtil.setAddress(address); EthersStoreUtil.setPreferredAccountType(preferredAccountType); await this.syncAccount(); } catch { throw new Error('Switching chain failed'); } finally { this.appKit?.setLoading(false); } } } } } syncConnectors(config) { const w3mConnectors = []; const connectorType = PresetsUtil.ConnectorTypesMap[ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID]; if (connectorType) { w3mConnectors.push({ id: ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID, explorerId: PresetsUtil.ConnectorExplorerIds[ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID], imageId: PresetsUtil.ConnectorImageIds[ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID], imageUrl: this.options?.connectorImages?.[ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID], name: PresetsUtil.ConnectorNamesMap[ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID], type: connectorType, chain: this.chain }); } if (config.injected) { const injectedConnectorType = PresetsUtil.ConnectorTypesMap[ConstantsUtil.INJECTED_CONNECTOR_ID]; if (injectedConnectorType) { w3mConnectors.push({ id: ConstantsUtil.INJECTED_CONNECTOR_ID, explorerId: PresetsUtil.ConnectorExplorerIds[ConstantsUtil.INJECTED_CONNECTOR_ID], imageId: PresetsUtil.ConnectorImageIds[ConstantsUtil.INJECTED_CONNECTOR_ID], imageUrl: this.options?.connectorImages?.[ConstantsUtil.INJECTED_CONNECTOR_ID], name: PresetsUtil.ConnectorNamesMap[ConstantsUtil.INJECTED_CONNECTOR_ID], type: injectedConnectorType, chain: this.chain }); } } if (config.coinbase) { w3mConnectors.push({ id: ConstantsUtil.COINBASE_SDK_CONNECTOR_ID, explorerId: PresetsUtil.ConnectorExplorerIds[ConstantsUtil.COINBASE_SDK_CONNECTOR_ID], imageId: PresetsUtil.ConnectorImageIds[ConstantsUtil.COINBASE_SDK_CONNECTOR_ID], imageUrl: this.options?.connectorImages?.[ConstantsUtil.COINBASE_SDK_CONNECTOR_ID], name: PresetsUtil.ConnectorNamesMap[ConstantsUtil.COINBASE_SDK_CONNECTOR_ID], type: 'EXTERNAL', chain: this.chain }); } this.appKit?.setConnectors(w3mConnectors); } async syncAuthConnector(projectId, auth) { if (typeof window !== 'undefined') { this.authProvider = new W3mFrameProvider(projectId); this.appKit?.addConnector({ id: ConstantsUtil.AUTH_CONNECTOR_ID, type: 'AUTH', name: 'Auth', provider: this.authProvider, email: auth?.email, socials: auth?.socials, showWallets: auth?.showWallets === undefined ? true : auth.showWallets, chain: this.chain, walletFeatures: auth?.walletFeatures }); this.appKit?.setLoading(true); const isLoginEmailUsed = this.authProvider.getLoginEmailUsed(); this.appKit?.setLoading(isLoginEmailUsed); const { isConnected } = await this.authProvider.isConnected(); if (isConnected) { await this.setAuthProvider(); } else { this.appKit?.setLoading(false); } } } eip6963EventHandler(event) { if (event.detail) { const { info, provider } = event.detail; const connectors = this.appKit?.getConnectors(); const existingCo