From afb06d51997ed0570e20ae62fc05bfbc28b0081d Mon Sep 17 00:00:00 2001 From: Glitch <66949816+glitch-txs@users.noreply.github.com> Date: Mon, 1 Apr 2024 15:29:01 -0300 Subject: [PATCH] add wallet info (#2099) --- .../core/src/controllers/AccountController.ts | 8 ++- packages/core/src/utils/TypeUtil.ts | 15 +++++- packages/ethers/exports/react.tsx | 3 +- packages/ethers/exports/vue.ts | 3 +- packages/ethers/src/client.ts | 54 ++++++++++++++----- packages/ethers5/exports/react.tsx | 3 +- packages/ethers5/exports/vue.ts | 3 +- packages/ethers5/src/client.ts | 51 ++++++++++++++---- packages/scaffold-react/index.ts | 16 +++++- packages/scaffold-vue/index.ts | 18 +++++++ packages/scaffold/src/client.ts | 17 +++++- packages/wagmi/exports/react/index.ts | 3 +- packages/wagmi/exports/vue.ts | 3 +- packages/wagmi/src/client.ts | 25 ++++++++- 14 files changed, 187 insertions(+), 35 deletions(-) diff --git a/packages/core/src/controllers/AccountController.ts b/packages/core/src/controllers/AccountController.ts index 6656d98a03..7798d77b5a 100644 --- a/packages/core/src/controllers/AccountController.ts +++ b/packages/core/src/controllers/AccountController.ts @@ -1,7 +1,7 @@ import { subscribeKey as subKey } from 'valtio/utils' import { proxy, ref, subscribe as sub } from 'valtio/vanilla' import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' -import type { CaipAddress } from '../utils/TypeUtil.js' +import type { CaipAddress, ConnectedWalletInfo } from '../utils/TypeUtil.js' import type { Balance } from '@web3modal/common' import { BlockchainApiController } from './BlockchainApiController.js' import { SnackController } from './SnackController.js' @@ -19,6 +19,7 @@ export interface AccountControllerState { addressExplorerUrl?: string smartAccountDeployed?: boolean tokenBalance?: Balance[] + connectedWalletInfo?: ConnectedWalletInfo } type StateKey = keyof AccountControllerState @@ -86,6 +87,10 @@ export const AccountController = { } }, + setConnectedWalletInfo(connectedWalletInfo: AccountControllerState['connectedWalletInfo']) { + state.connectedWalletInfo = connectedWalletInfo + }, + async fetchTokenBalance() { try { if (state.address) { @@ -110,5 +115,6 @@ export const AccountController = { state.profileImage = undefined state.addressExplorerUrl = undefined state.tokenBalance = [] + state.connectedWalletInfo = undefined } } diff --git a/packages/core/src/utils/TypeUtil.ts b/packages/core/src/utils/TypeUtil.ts index 7eeaeb2fde..d58952ecb1 100644 --- a/packages/core/src/utils/TypeUtil.ts +++ b/packages/core/src/utils/TypeUtil.ts @@ -20,6 +20,14 @@ export interface CaipNetwork { imageUrl?: string } +export type ConnectedWalletInfo = + | { + name?: string + icon?: string + [key: string]: unknown + } + | undefined + export interface LinkingRecord { redirect: string href: string @@ -38,7 +46,12 @@ export type Connector = { imageId?: string explorerId?: string imageUrl?: string - info?: { rdns?: string } + info?: { + uuid?: string + name?: string + icon?: string + rdns?: string + } provider?: unknown } diff --git a/packages/ethers/exports/react.tsx b/packages/ethers/exports/react.tsx index 58ae46920d..edc27fd03d 100644 --- a/packages/ethers/exports/react.tsx +++ b/packages/ethers/exports/react.tsx @@ -81,7 +81,8 @@ export { useWeb3ModalTheme, useWeb3Modal, useWeb3ModalState, - useWeb3ModalEvents + useWeb3ModalEvents, + useWalletInfo } from '@web3modal/scaffold-react' // -- Universal Exports ------------------------------------------------------- diff --git a/packages/ethers/exports/vue.ts b/packages/ethers/exports/vue.ts index a009cfdffd..ab8da42171 100644 --- a/packages/ethers/exports/vue.ts +++ b/packages/ethers/exports/vue.ts @@ -116,7 +116,8 @@ export { useWeb3ModalTheme, useWeb3Modal, useWeb3ModalState, - useWeb3ModalEvents + useWeb3ModalEvents, + useWalletInfo } from '@web3modal/scaffold-vue' // -- Universal Exports ------------------------------------------------------- diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index 3cbf49c33d..f5a322b2d6 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -70,16 +70,11 @@ interface Info { rdns: string } -interface Wallet { +interface EIP6963ProviderDetail { info: Info provider: Provider } -interface IEIP6963Provider { - name: string - provider: Provider -} - interface ExternalProvider extends EthereumProvider { _addresses?: string[] } @@ -88,7 +83,7 @@ interface ExternalProvider extends EthereumProvider { export class Web3Modal extends Web3ModalScaffold { private hasSyncedConnectedAccount = false - private EIP6963Providers: IEIP6963Provider[] = [] + private EIP6963Providers: EIP6963ProviderDetail[] = [] private walletConnectProvider?: EthereumProvider @@ -516,10 +511,10 @@ export class Web3Modal extends Web3ModalScaffold { const currentActiveWallet = window?.localStorage.getItem(EthersConstantsUtil.WALLET_ID) if (currentActiveWallet) { const currentProvider = this.EIP6963Providers.find( - provider => provider.name === currentActiveWallet + provider => provider.info.name === currentActiveWallet ) if (currentProvider) { - this.setEIP6963Provider(currentProvider.provider, currentProvider.name) + this.setEIP6963Provider(currentProvider.provider, currentProvider.info.name) } } } @@ -831,7 +826,7 @@ export class Web3Modal extends Web3ModalScaffold { this.setIsConnected(isConnected) this.setCaipAddress(caipAddress) - + this.syncConnectedWalletInfo() await Promise.all([ this.syncProfile(address), this.syncBalance(address), @@ -924,6 +919,39 @@ export class Web3Modal extends Web3ModalScaffold { } } + private syncConnectedWalletInfo() { + const currentActiveWallet = window?.localStorage.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.setConnectedWalletInfo({ + ...currentProvider.info + }) + } + } + } else if (providerType === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID) { + const provider = EthersStoreUtil.state.provider as unknown as EthereumProvider + + if (provider.session) { + this.setConnectedWalletInfo({ + ...provider.session.peer.metadata, + name: provider.session.peer.metadata.name, + icon: provider.session.peer.metadata.icons?.[0] + }) + } + } else if (currentActiveWallet) { + this.setConnectedWalletInfo({ + name: currentActiveWallet + }) + } + } + public async switchNetwork(chainId: number) { const provider = EthersStoreUtil.state.provider const providerType = EthersStoreUtil.state.providerType @@ -1107,7 +1135,7 @@ export class Web3Modal extends Web3ModalScaffold { } } - private eip6963EventHandler(event: CustomEventInit) { + private eip6963EventHandler(event: CustomEventInit) { if (event.detail) { const { info, provider } = event.detail const connectors = this.getConnectors() @@ -1127,8 +1155,8 @@ export class Web3Modal extends Web3ModalScaffold { }) const eip6963ProviderObj = { - name: info.name, - provider + provider, + info } this.EIP6963Providers.push(eip6963ProviderObj) diff --git a/packages/ethers5/exports/react.tsx b/packages/ethers5/exports/react.tsx index 599b508e8c..f94a68353e 100644 --- a/packages/ethers5/exports/react.tsx +++ b/packages/ethers5/exports/react.tsx @@ -81,7 +81,8 @@ export { useWeb3ModalTheme, useWeb3Modal, useWeb3ModalState, - useWeb3ModalEvents + useWeb3ModalEvents, + useWalletInfo } from '@web3modal/scaffold-react' // -- Universal Exports ------------------------------------------------------- diff --git a/packages/ethers5/exports/vue.ts b/packages/ethers5/exports/vue.ts index 1ab5816ff7..d6bf370774 100644 --- a/packages/ethers5/exports/vue.ts +++ b/packages/ethers5/exports/vue.ts @@ -118,7 +118,8 @@ export { useWeb3ModalTheme, useWeb3Modal, useWeb3ModalState, - useWeb3ModalEvents + useWeb3ModalEvents, + useWalletInfo } from '@web3modal/scaffold-vue' // -- Universal Exports ------------------------------------------------------- diff --git a/packages/ethers5/src/client.ts b/packages/ethers5/src/client.ts index 8e7103cd7a..de09b66a12 100644 --- a/packages/ethers5/src/client.ts +++ b/packages/ethers5/src/client.ts @@ -61,16 +61,11 @@ interface Info { rdns: string } -interface Wallet { +interface EIP6963ProviderDetail { info: Info provider: Provider } -interface IEIP6963Provider { - name: string - provider: Provider -} - interface ExternalProvider extends EthereumProvider { _addresses?: string[] } @@ -79,7 +74,7 @@ interface ExternalProvider extends EthereumProvider { export class Web3Modal extends Web3ModalScaffold { private hasSyncedConnectedAccount = false - private EIP6963Providers: IEIP6963Provider[] = [] + private EIP6963Providers: EIP6963ProviderDetail[] = [] private walletConnectProvider?: EthereumProvider @@ -485,10 +480,10 @@ export class Web3Modal extends Web3ModalScaffold { const currentActiveWallet = window?.localStorage.getItem(EthersConstantsUtil.WALLET_ID) if (currentActiveWallet) { const currentProvider = this.EIP6963Providers.find( - provider => provider.name === currentActiveWallet + provider => provider.info.name === currentActiveWallet ) if (currentProvider) { - this.setEIP6963Provider(currentProvider.provider, currentProvider.name) + this.setEIP6963Provider(currentProvider.provider, currentProvider.info.name) } } } @@ -714,6 +709,7 @@ export class Web3Modal extends Web3ModalScaffold { this.setIsConnected(isConnected) this.setCaipAddress(caipAddress) + this.syncConnectedWalletInfo() await Promise.all([ this.syncProfile(address), @@ -804,6 +800,39 @@ export class Web3Modal extends Web3ModalScaffold { } } + private syncConnectedWalletInfo() { + const currentActiveWallet = window?.localStorage.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.setConnectedWalletInfo({ + ...currentProvider.info + }) + } + } + } else if (providerType === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID) { + const provider = EthersStoreUtil.state.provider as unknown as EthereumProvider + + if (provider.session) { + this.setConnectedWalletInfo({ + ...provider.session.peer.metadata, + name: provider.session.peer.metadata.name, + icon: provider.session.peer.metadata.icons?.[0] + }) + } + } else if (currentActiveWallet) { + this.setConnectedWalletInfo({ + name: currentActiveWallet + }) + } + } + public async switchNetwork(chainId: number) { const provider = EthersStoreUtil.state.provider const providerType = EthersStoreUtil.state.providerType @@ -954,7 +983,7 @@ export class Web3Modal extends Web3ModalScaffold { this.setConnectors(w3mConnectors) } - private eip6963EventHandler(event: CustomEventInit) { + private eip6963EventHandler(event: CustomEventInit) { if (event.detail) { const { info, provider } = event.detail const connectors = this.getConnectors() @@ -973,7 +1002,7 @@ export class Web3Modal extends Web3ModalScaffold { }) const eip6963ProviderObj = { - name: info.name, + info, provider } diff --git a/packages/scaffold-react/index.ts b/packages/scaffold-react/index.ts index aec57f02d8..f817822d1d 100644 --- a/packages/scaffold-react/index.ts +++ b/packages/scaffold-react/index.ts @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useEffect, useState, useSyncExternalStore } from 'react' import type { W3mAccountButton, W3mButton, @@ -86,6 +86,20 @@ export function useWeb3Modal() { return { open, close } } +export function useWalletInfo() { + if (!modal) { + throw new Error('Please call "createWeb3Modal" before using "useWeb3Modal" hook') + } + + const walletInfo = useSyncExternalStore( + modal.subscribeWalletInfo, + modal.getWalletInfo, + modal.getWalletInfo + ) + + return { walletInfo } +} + export function useWeb3ModalState() { if (!modal) { throw new Error('Please call "createWeb3Modal" before using "useWeb3ModalState" hook') diff --git a/packages/scaffold-vue/index.ts b/packages/scaffold-vue/index.ts index 6bedead2aa..4a246109b7 100644 --- a/packages/scaffold-vue/index.ts +++ b/packages/scaffold-vue/index.ts @@ -85,6 +85,24 @@ export function useWeb3Modal() { }) } +export function useWalletInfo() { + if (!modal) { + throw new Error('Please call "createWeb3Modal" before using "useWeb3Modal" composable') + } + + const walletInfo = ref(modal.getWalletInfo()) + + const unsubscribe = modal.subscribeWalletInfo(newValue => { + walletInfo.value = newValue + }) + + onUnmounted(() => { + unsubscribe?.() + }) + + return { walletInfo } +} + export function useWeb3ModalState() { if (!modal) { throw new Error('Please call "createWeb3Modal" before using "useWeb3ModalState" composable') diff --git a/packages/scaffold/src/client.ts b/packages/scaffold/src/client.ts index 788282766d..3d2475cfe6 100644 --- a/packages/scaffold/src/client.ts +++ b/packages/scaffold/src/client.ts @@ -8,7 +8,8 @@ import type { ThemeControllerState, ThemeMode, ThemeVariables, - ModalControllerState + ModalControllerState, + ConnectedWalletInfo } from '@web3modal/core' import { AccountController, @@ -108,6 +109,14 @@ export class Web3ModalScaffold { return ThemeController.subscribe(callback) } + public getWalletInfo() { + return AccountController.state.connectedWalletInfo + } + + public subscribeWalletInfo(callback: (newState: ConnectedWalletInfo) => void) { + return AccountController.subscribeKey('connectedWalletInfo', callback) + } + public getState() { return { ...PublicStateController.state } } @@ -206,6 +215,12 @@ export class Web3ModalScaffold { isDeployed => { AccountController.setSmartAccountDeployed(isDeployed) } + + protected setConnectedWalletInfo: (typeof AccountController)['setConnectedWalletInfo'] = + connectedWalletInfo => { + AccountController.setConnectedWalletInfo(connectedWalletInfo) + } + // -- Private ------------------------------------------------------------------ private async initControllers(options: ScaffoldOptions) { NetworkController.setClient(options.networkControllerClient) diff --git a/packages/wagmi/exports/react/index.ts b/packages/wagmi/exports/react/index.ts index 453f9b55f9..33ba7b5f2b 100644 --- a/packages/wagmi/exports/react/index.ts +++ b/packages/wagmi/exports/react/index.ts @@ -23,5 +23,6 @@ export { useWeb3ModalTheme, useWeb3Modal, useWeb3ModalState, - useWeb3ModalEvents + useWeb3ModalEvents, + useWalletInfo } from '@web3modal/scaffold-react' diff --git a/packages/wagmi/exports/vue.ts b/packages/wagmi/exports/vue.ts index cb58b4d99c..5f494f0d1a 100644 --- a/packages/wagmi/exports/vue.ts +++ b/packages/wagmi/exports/vue.ts @@ -26,7 +26,8 @@ export { useWeb3ModalTheme, useWeb3Modal, useWeb3ModalState, - useWeb3ModalEvents + useWeb3ModalEvents, + useWalletInfo } from '@web3modal/scaffold-vue' // -- Universal Exports ------------------------------------------------------- diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index 6955815d46..dd5a3ef1ec 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -232,8 +232,9 @@ export class Web3Modal extends Web3ModalScaffold { this.setRequestedCaipNetworks(requestedCaipNetworks ?? []) } - private async syncAccount({ address, isConnected, chainId }: GetAccountReturnType) { + private async syncAccount({ address, isConnected, chainId, connector }: GetAccountReturnType) { this.resetAccount() + // TOD0: Check with Sven. Now network is synced when acc is synced. this.syncNetwork() if (isConnected && address && chainId) { @@ -243,6 +244,7 @@ export class Web3Modal extends Web3ModalScaffold { await Promise.all([ this.syncProfile(address, chainId), this.syncBalance(address, chainId), + this.syncConnectedWalletInfo(connector), this.fetchTokenBalance(), this.getApprovedCaipNetworksData() ]) @@ -329,6 +331,27 @@ export class Web3Modal extends Web3ModalScaffold { this.setBalance(undefined, undefined) } + private async syncConnectedWalletInfo(connector: GetAccountReturnType['connector']) { + if (!connector) { + throw Error('syncConnectedWalletInfo - connector is undefined') + } + + if (connector.id === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID && connector.getProvider) { + const walletConnectProvider = (await connector.getProvider()) as Awaited< + ReturnType<(typeof EthereumProvider)['init']> + > + if (walletConnectProvider.session) { + this.setConnectedWalletInfo({ + ...walletConnectProvider.session.peer.metadata, + name: walletConnectProvider.session.peer.metadata.name, + icon: walletConnectProvider.session.peer.metadata.icons?.[0] + }) + } + } else { + this.setConnectedWalletInfo({ name: connector.name, icon: connector.icon }) + } + } + private syncConnectors( connectors: Web3ModalClientOptions['wagmiConfig']['connectors'] ) {