From 231ababb31508fec73bb979320bd62ea8158fe96 Mon Sep 17 00:00:00 2001 From: Polybius93 Date: Wed, 6 Dec 2023 15:53:52 +0100 Subject: [PATCH] feat: cleaned up the ethereum provider detection and login flow --- .../select-wallet-modal.tsx | 8 +- src/app/hooks/use-ethereum.ts | 143 +++++++++--------- src/shared/models/wallet.ts | 3 +- 3 files changed, 76 insertions(+), 78 deletions(-) diff --git a/src/app/components/modals/select-wallet-modal/select-wallet-modal.tsx b/src/app/components/modals/select-wallet-modal/select-wallet-modal.tsx index 2fa0d193..04d47173 100644 --- a/src/app/components/modals/select-wallet-modal/select-wallet-modal.tsx +++ b/src/app/components/modals/select-wallet-modal/select-wallet-modal.tsx @@ -7,7 +7,7 @@ import { ModalLayout } from "@components/modals/components/modal.layout"; import { SelectWalletMenu } from "@components/modals/select-wallet-modal/components/select-wallet-menu"; import { SelectNetworkButton } from "@components/select-network-button/select-network-button"; import { Network } from "@models/network"; -import { ethereumWallets } from "@models/wallet"; +import { WalletType, ethereumWallets } from "@models/wallet"; import { BlockchainContext } from "../../../providers/blockchain-context-provider"; @@ -21,9 +21,9 @@ export function SelectWalletModal({ undefined, ); - async function handleLogin() { + async function handleLogin(walletType: WalletType) { if (!currentNetwork) throw new Error("No network selected"); - await ethereum?.requestEthereumAccount(currentNetwork); + await ethereum?.requestEthereumAccount(currentNetwork, walletType); setCurrentNetwork(undefined); handleClose(); } @@ -62,7 +62,7 @@ export function SelectWalletModal({ handleLogin(WalletType.Metamask)} /> ))} diff --git a/src/app/hooks/use-ethereum.ts b/src/app/hooks/use-ethereum.ts index f4443075..4e3b323e 100644 --- a/src/app/hooks/use-ethereum.ts +++ b/src/app/hooks/use-ethereum.ts @@ -5,12 +5,12 @@ import { useSelector } from "react-redux"; import { customShiftValue } from "@common/utilities"; import { EthereumError } from "@models/error-types"; import { + EthereumNetwork, Network, addNetworkParams, ethereumNetworks, hexChainIDs, } from "@models/network"; -import { EthereumNetwork } from "@models/network"; import { RawVault, Vault, VaultState } from "@models/vault"; import { RootState, store } from "@store/index"; import { accountActions } from "@store/slices/account/account.actions"; @@ -18,6 +18,7 @@ import { vaultActions } from "@store/slices/vault/vault.actions"; import { Contract, Signer, ethers } from "ethers"; import { Logger } from "ethers/lib/utils"; +import { WalletType } from "@models/wallet"; import { useVaults } from "./use-vaults"; export interface UseEthereumReturnType { @@ -29,7 +30,10 @@ export interface UseEthereumReturnType { lockedBTCBalance: number | undefined; getLockedBTCBalance: () => Promise; totalSupply: number | undefined; - requestEthereumAccount: (network: Network) => Promise; + requestEthereumAccount: ( + network: Network, + walletType: WalletType, + ) => Promise; getAllVaults: () => Promise; getVault: (vaultUUID: string, vaultState: VaultState) => Promise; setupVault: (btcDepositAmount: number) => Promise; @@ -52,7 +56,11 @@ function throwEthereumError(message: string, error: any): void { export function useEthereum(): UseEthereumReturnType { const { fundedVaults } = useVaults(); - const { address, network } = useSelector((state: RootState) => state.account); + const { + address, + network, + walletType: currentWalletType, + } = useSelector((state: RootState) => state.account); const [isLoaded, setIsLoaded] = useState(false); @@ -82,12 +90,12 @@ export function useEthereum(): UseEthereumReturnType { }, [network]); useEffect(() => { - if (!address || !network) return; + if (!address || !network || !currentWalletType) return; if (!protocolContract && !dlcManagerContract && !dlcBTCContract) { const setupConfiguration = async () => { setIsLoaded(false); - await setupEthereumConfiguration(network); + await setupEthereumConfiguration(network, currentWalletType); setIsLoaded(true); }; setupConfiguration(); @@ -105,6 +113,35 @@ export function useEthereum(): UseEthereumReturnType { }; } + function getProvider(ethereum: any, walletType: WalletType): any { + if ("providers" in ethereum) { + return ethereum.providers.find( + (provider: any) => provider[`is${walletType}`], + ); + } + return ethereum[`is${walletType}`] ? ethereum : undefined; + } + + function checkWalletProvider(ethereum: any, walletType: WalletType): any { + const ethereumWalletProvider = getProvider(ethereum, walletType); + if (!ethereumWalletProvider) { + alert(`Install ${walletType}!`); + throw new EthereumError(`${walletType} wallet not found`); + } + return ethereumWalletProvider; + } + + function getWalletProvider(walletType: WalletType): any { + const { ethereum } = window; + + if (!ethereum) { + alert("Install MetaMask!"); + throw new EthereumError("No ethereum wallet found"); + } + + return checkWalletProvider(ethereum, walletType); + } + async function getTotalSupply() { const provider = ethers.providers.getDefaultProvider( "https://ethereum-sepolia.publicnode.com/", @@ -130,20 +167,12 @@ export function useEthereum(): UseEthereumReturnType { async function addEthereumNetwork( newEthereumNetwork: EthereumNetwork, + walletType: WalletType, ): Promise { - const { ethereum } = window; - - let metamaskProvider; - if ("providers" in ethereum) { - metamaskProvider = ethereum.providers.find( - (provider: any) => provider.isMetaMask, - ); - } else { - metamaskProvider = ethereum; - } + const walletProvider = getWalletProvider(walletType); try { - await metamaskProvider.request({ + await walletProvider.request({ method: "wallet_addEthereumChain", params: addNetworkParams[newEthereumNetwork], }); @@ -154,34 +183,29 @@ export function useEthereum(): UseEthereumReturnType { async function switchEthereumNetwork( newEthereumNetwork: EthereumNetwork, + walletType: WalletType, ): Promise { - const { ethereum } = window; - - let metamaskProvider; - if ("providers" in ethereum) { - metamaskProvider = ethereum.providers.find( - (provider: any) => provider.isMetaMask, - ); - } else { - metamaskProvider = ethereum; - } + const walletProvider = getWalletProvider(walletType); try { - await metamaskProvider.request({ + await walletProvider.request({ method: "wallet_switchEthereumChain", params: [{ chainId: hexChainIDs[newEthereumNetwork] }], }); } catch (error: any) { if (error.code === 4902) { - await addEthereumNetwork(newEthereumNetwork); + await addEthereumNetwork(newEthereumNetwork, walletType); } else { throwEthereumError(`Could not switch Ethereum network: `, error); } } } - async function setupEthereumConfiguration(network: Network): Promise { - const ethereumProvider = await getEthereumProvider(network); + async function setupEthereumConfiguration( + network: Network, + walletType: WalletType, + ): Promise { + const ethereumProvider = await getEthereumProvider(network, walletType); if (!ethereumProvider) { throw new EthereumError("Failed to get Ethereum provider"); } @@ -194,20 +218,12 @@ export function useEthereum(): UseEthereumReturnType { await getEthereumContracts(walletNetworkChainID, signer); } - async function getEthereumProvider(network: Network) { + async function getEthereumProvider(network: Network, walletType: WalletType) { try { - const { ethereum } = window; + const walletProvider = getWalletProvider(walletType); - let metamaskProvider; - if ("providers" in ethereum) { - metamaskProvider = ethereum.providers.find( - (provider: any) => provider.isMetaMask, - ); - } else { - metamaskProvider = ethereum; - } const browserProvider = new ethers.providers.Web3Provider( - metamaskProvider, + walletProvider, "any", ); const signer = browserProvider.getSigner(); @@ -216,7 +232,7 @@ export function useEthereum(): UseEthereumReturnType { const walletNetworkChainID = walletNetwork.chainId.toString(); if (walletNetworkChainID !== network?.id) { - await switchEthereumNetwork(network?.id); + await switchEthereumNetwork(network?.id, walletType); window.location.reload(); } return { walletNetworkChainID, signer }; @@ -269,36 +285,26 @@ export function useEthereum(): UseEthereumReturnType { } } - async function requestEthereumAccount(network: Network) { + async function requestEthereumAccount( + network: Network, + walletType: WalletType, + ) { try { - const { ethereum } = window; - - let metamaskProvider; - if ("providers" in ethereum) { - metamaskProvider = ethereum.providers.find( - (provider: any) => provider.isMetaMask, - ); - } else { - metamaskProvider = ethereum; - } - - if (!metamaskProvider) { - alert("Install MetaMask!"); - return; - } + if (!walletType) throw new Error("Wallet not initialized"); + const walletProvider = getWalletProvider(walletType); - const ethereumAccounts = await metamaskProvider.request({ + const ethereumAccounts = await walletProvider.request({ method: "eth_requestAccounts", }); const accountInformation = { - walletType: "metamask", + walletType: WalletType.Metamask, address: ethereumAccounts[0], network, }; setIsLoaded(false); - await setupEthereumConfiguration(network); + await setupEthereumConfiguration(network, walletType); setIsLoaded(true); store.dispatch(accountActions.login(accountInformation)); @@ -437,19 +443,10 @@ export function useEthereum(): UseEthereumReturnType { async function recommendTokenToMetamask(): Promise { try { - const { ethereum } = window; - - let metamaskProvider; - if ("providers" in ethereum) { - metamaskProvider = ethereum.providers.find( - (provider: any) => provider.isMetaMask, - ); - } else { - metamaskProvider = ethereum; - } + if (!currentWalletType) throw new Error("Wallet not initialized"); + const walletProvider = getWalletProvider(currentWalletType); - if (!metamaskProvider) return false; - const response = await metamaskProvider.request({ + const response = await walletProvider.request({ method: "wallet_watchAsset", params: { type: "ERC20", diff --git a/src/shared/models/wallet.ts b/src/shared/models/wallet.ts index a159f27e..a7d2cb4f 100644 --- a/src/shared/models/wallet.ts +++ b/src/shared/models/wallet.ts @@ -1,5 +1,6 @@ export enum WalletType { - Metamask = "metamask", + Metamask = "MetaMask", + Coinbase = "CoinbaseWallet", } export interface Wallet {