From 0fe8f2a73894a190c7d33f4cdf1d0a94d50b56c0 Mon Sep 17 00:00:00 2001 From: kingsleydon <10992364+kingsleydon@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:18:34 +0800 Subject: [PATCH] refactor subbridge ethereum --- apps/app/next.config.mjs | 4 +- .../components/ConnectWalletButton.tsx | 7 +- .../useEthereumProviderInitialization.ts | 71 +++++-------------- apps/subbridge/hooks/useEthersProvider.ts | 48 +++---------- apps/subbridge/hooks/useSwitchNetwork.ts | 12 ++-- apps/subbridge/next.config.js | 4 +- apps/subbridge/store/ethers.ts | 9 +-- 7 files changed, 46 insertions(+), 109 deletions(-) diff --git a/apps/app/next.config.mjs b/apps/app/next.config.mjs index fd409fd7..a5f99aa8 100644 --- a/apps/app/next.config.mjs +++ b/apps/app/next.config.mjs @@ -66,8 +66,8 @@ const nextConfig = { }, experimental: { swcPlugins: [ - // ['@swc-jotai/debug-label', {}], - // ['@swc-jotai/react-refresh', {}], + ['@swc-jotai/debug-label', {}], + ['@swc-jotai/react-refresh', {}], ], }, } diff --git a/apps/subbridge/components/ConnectWalletButton.tsx b/apps/subbridge/components/ConnectWalletButton.tsx index ad5fdf33..c387f064 100644 --- a/apps/subbridge/components/ConnectWalletButton.tsx +++ b/apps/subbridge/components/ConnectWalletButton.tsx @@ -1,3 +1,4 @@ +import {useEthersWeb3Provider} from '@/hooks/useEthersProvider' import {fromChainAtom} from '@/store/bridge' import {ethereumProviderAtom, evmAccountAtom} from '@/store/ethers' import {polkadotWalletModalOpenAtom} from '@/store/polkadotWalletModal' @@ -15,17 +16,17 @@ import {type FC, useState} from 'react' const ConnectEvmWalletButton: FC = (props) => { const [open, setOpen] = useState(false) - const [ethereum] = useAtom(ethereumProviderAtom) + const ethersWeb3Provider = useEthersWeb3Provider() const [, setEvmAccount] = useAtom(evmAccountAtom) const [loading, setLoading] = useState(false) const handleClick = (): void => { - if (ethereum == null) { + if (ethersWeb3Provider == null) { setOpen(true) return } setLoading(true) - void ethereum + void ethersWeb3Provider.provider .request?.({method: 'eth_requestAccounts'}) .then((accounts) => { if (Array.isArray(accounts) && accounts.length > 0) { diff --git a/apps/subbridge/hooks/useEthereumProviderInitialization.ts b/apps/subbridge/hooks/useEthereumProviderInitialization.ts index 03e92e22..a1a449c2 100644 --- a/apps/subbridge/hooks/useEthereumProviderInitialization.ts +++ b/apps/subbridge/hooks/useEthereumProviderInitialization.ts @@ -1,32 +1,27 @@ import { - ethereumProviderAtom, + ethersWeb3ProviderAtom, evmAccountAtom, evmChainIdAtom, - isEvmWalletAuthorizedAtom, } from '@/store/ethers' -import type {ethers} from 'ethers' +import detectEthereumProvider from '@metamask/detect-provider' +import {ethers} from 'ethers' import {useAtom} from 'jotai' -import {useEffect} from 'react' +import {useEffect, useRef} from 'react' export const useEthereumProviderInitialization = (): void => { - const [evmAccount] = useAtom(evmAccountAtom) - const [ethereumProvider, setEthereumProvider] = useAtom(ethereumProviderAtom) + const [, setEthersWeb3Provider] = useAtom(ethersWeb3ProviderAtom) const [, setEvmAccount] = useAtom(evmAccountAtom) const [, setEvmChainId] = useAtom(evmChainIdAtom) - const [isEvmWalletAuthorized, setIsEvmWalletAuthorized] = useAtom( - isEvmWalletAuthorizedAtom, - ) + const runRef = useRef(false) useEffect(() => { - if (ethereumProvider != null) return + if (runRef.current) return + runRef.current = true const init = async (): Promise => { - const {default: detectEthereumProvider} = await import( - '@metamask/detect-provider' - ) - const provider = await detectEthereumProvider({silent: true}) - if (provider == null) return - const ethereum = provider as ethers.providers.ExternalProvider - setEthereumProvider(ethereum) + const ethereum = await detectEthereumProvider({silent: true}) + if (ethereum == null) return + const provider = new ethers.providers.Web3Provider(ethereum) + setEthersWeb3Provider(provider) const updateAccounts = (accounts: unknown): void => { const account = (accounts as string[])[0] setEvmAccount(account ?? null) @@ -34,42 +29,14 @@ export const useEthereumProviderInitialization = (): void => { const updateChainId = (chainId: unknown): void => { setEvmChainId(Number.parseInt(chainId as string, 16)) } - // biome-ignore lint/suspicious/noExplicitAny: - ;(ethereum as any).on('accountsChanged', updateAccounts) - // biome-ignore lint/suspicious/noExplicitAny: - ;(ethereum as any).on('chainChanged', updateChainId) - void ethereum.request?.({method: 'eth_chainId'}).then(updateChainId) - if (isEvmWalletAuthorized) { - ethereum - .request?.({method: 'eth_requestAccounts'}) - .then((accounts) => { - const account = (accounts as string[])[0] - setEvmAccount(account ?? null) - }) - .catch((error) => { - // User rejected - if (error.code === 4001) { - setIsEvmWalletAuthorized(false) - } else { - throw error - } - }) - } + await provider.listAccounts().then(updateAccounts) + ethereum.on('accountsChanged', updateAccounts) + ethereum.on('chainChanged', updateChainId) + await provider.provider + .request?.({method: 'eth_chainId'}) + .then(updateChainId) } void init() - }, [ - ethereumProvider, - isEvmWalletAuthorized, - setEthereumProvider, - setIsEvmWalletAuthorized, - setEvmAccount, - setEvmChainId, - ]) - - useEffect(() => { - if (evmAccount != null) { - setIsEvmWalletAuthorized(true) - } - }, [evmAccount, setIsEvmWalletAuthorized]) + }, [setEthersWeb3Provider, setEvmAccount, setEvmChainId]) } diff --git a/apps/subbridge/hooks/useEthersProvider.ts b/apps/subbridge/hooks/useEthersProvider.ts index 2b6ef03d..d3074c74 100644 --- a/apps/subbridge/hooks/useEthersProvider.ts +++ b/apps/subbridge/hooks/useEthersProvider.ts @@ -1,42 +1,16 @@ import {fromChainAtom} from '@/store/bridge' -import {ethereumProviderAtom, evmChainIdAtom} from '@/store/ethers' -import {ethers} from 'ethers' +import {ethersWeb3ProviderAtom} from '@/store/ethers' +import type {ethers} from 'ethers' import {useAtomValue} from 'jotai' -import useSWRImmutable from 'swr/immutable' -const ethersWeb3ProviderFetcher = async ([ethereumProvider, _]: [ - ethers.providers.ExternalProvider, - number, -]): Promise => { - const provider = new ethers.providers.Web3Provider(ethereumProvider) - return provider -} +export const useEthersWeb3Provider = + (): ethers.providers.Web3Provider | null => { + const fromChain = useAtomValue(fromChainAtom) + const ethersWeb3Provider = useAtomValue(ethersWeb3ProviderAtom) -export const useEthersWeb3Provider = (): - | ethers.providers.Web3Provider - | undefined => { - const fromChain = useAtomValue(fromChainAtom) - const evmChainId = useAtomValue(evmChainIdAtom) - const ethereumProvider = useAtomValue(ethereumProviderAtom) - const {data} = useSWRImmutable( - fromChain.kind === 'evm' && - fromChain.evmChainId === evmChainId && - ethereumProvider != null && [ethereumProvider, evmChainId], - ethersWeb3ProviderFetcher, - ) + if (fromChain.kind === 'evm') { + return ethersWeb3Provider + } - return data -} - -const ethersJsonRpcProviderFetcher = async ( - url: string, -): Promise => { - return new ethers.providers.StaticJsonRpcProvider(url) -} - -export const useEthersJsonRpcProvider = ( - url?: string, -): ethers.providers.StaticJsonRpcProvider | undefined => { - const {data} = useSWRImmutable(url, ethersJsonRpcProviderFetcher) - return data -} + return null + } diff --git a/apps/subbridge/hooks/useSwitchNetwork.ts b/apps/subbridge/hooks/useSwitchNetwork.ts index dcc56fc1..1d685ddb 100644 --- a/apps/subbridge/hooks/useSwitchNetwork.ts +++ b/apps/subbridge/hooks/useSwitchNetwork.ts @@ -1,23 +1,23 @@ import evmChainsData from '@/assets/evm_chains_data.json' import {fromChainAtom} from '@/store/bridge' -import {ethereumProviderAtom} from '@/store/ethers' import {useAtom} from 'jotai' import {useCallback} from 'react' +import {useEthersWeb3Provider} from './useEthersProvider' export const useSwitchNetwork = (): (() => Promise) => { const [fromChain] = useAtom(fromChainAtom) - const [ethereum] = useAtom(ethereumProviderAtom) + const ethersWeb3Provider = useEthersWeb3Provider() const switchNetwork = useCallback(async () => { - if (ethereum == null || fromChain.kind !== 'evm') return + if (ethersWeb3Provider == null || fromChain.kind !== 'evm') return try { - await ethereum.request?.({ + await ethersWeb3Provider.provider.request?.({ method: 'wallet_switchEthereumChain', params: [{chainId: `0x${fromChain.evmChainId.toString(16)}`}], }) } catch (switchError) { if ((switchError as {code: number}).code === 4902) { - await ethereum.request?.({ + await ethersWeb3Provider.provider.request?.({ method: 'wallet_addEthereumChain', params: [ (evmChainsData as Readonly>)[ @@ -27,7 +27,7 @@ export const useSwitchNetwork = (): (() => Promise) => { }) } } - }, [ethereum, fromChain]) + }, [ethersWeb3Provider, fromChain]) return switchNetwork } diff --git a/apps/subbridge/next.config.js b/apps/subbridge/next.config.js index c1ec888e..20db437d 100644 --- a/apps/subbridge/next.config.js +++ b/apps/subbridge/next.config.js @@ -41,8 +41,8 @@ const nextConfig = { }, experimental: { swcPlugins: [ - // ['@swc-jotai/debug-label', {}], - // ['@swc-jotai/react-refresh', {}], + ['@swc-jotai/debug-label', {}], + ['@swc-jotai/react-refresh', {}], ], }, } diff --git a/apps/subbridge/store/ethers.ts b/apps/subbridge/store/ethers.ts index 4c03fe19..c95f2e07 100644 --- a/apps/subbridge/store/ethers.ts +++ b/apps/subbridge/store/ethers.ts @@ -1,14 +1,9 @@ import type {ethers} from 'ethers' import {atom} from 'jotai' -import {atomWithStorage} from 'jotai/utils' import {fromChainAtom} from './bridge' -export const isEvmWalletAuthorizedAtom = atomWithStorage( - 'jotai:is_evm_wallet_authorized', - false, -) -export const ethereumProviderAtom = - atom(null) +export const ethersWeb3ProviderAtom = + atom(null) export const evmAccountAtom = atom(null) export const evmChainIdAtom = atom(null) export const isNetworkWrongAtom = atom((get) => {