From 513877ff4370df87ae5f35f4c30cfa890ea3331f Mon Sep 17 00:00:00 2001 From: Garey Simpson Date: Sat, 5 Mar 2022 14:11:54 -0500 Subject: [PATCH] Show connected providers on disconnected UI --- src/components/App/App.css | 14 +++++++++++++ .../Disconnected/ConnectWalletButton.tsx | 3 ++- src/components/Disconnected/Disconnected.tsx | 14 ++++++------- src/hooks/useWeb3/connectWithProvider.ts | 15 +++++++++----- .../useWeb3/connectors/coinbaseWallet.ts | 13 ++++++++---- src/hooks/useWeb3/connectors/metaMask.ts | 15 +++++++++----- src/hooks/useWeb3/connectors/walletConnect.ts | 13 ++++++------ src/hooks/useWeb3/useWeb3.ts | 20 +++++++++++++++++-- .../dappInfo.ts => utils/constants.ts} | 0 src/utils/types.ts | 12 +++-------- 10 files changed, 78 insertions(+), 41 deletions(-) rename src/{hooks/useWeb3/dappInfo.ts => utils/constants.ts} (100%) diff --git a/src/components/App/App.css b/src/components/App/App.css index ff8b3c7..a1c9166 100644 --- a/src/components/App/App.css +++ b/src/components/App/App.css @@ -37,9 +37,23 @@ small { } button { + cursor: pointer; font-size: 20px; + display: flex; + flex-direction: row; + align-content: space-between; + justify-content: center; } button.change-provider { background-color: lightpink; } + +button .provider { + flex: 1; + line-height: 28px; +} + +button > div { + height: 28px; +} \ No newline at end of file diff --git a/src/components/Disconnected/ConnectWalletButton.tsx b/src/components/Disconnected/ConnectWalletButton.tsx index 25481bb..118d82c 100644 --- a/src/components/Disconnected/ConnectWalletButton.tsx +++ b/src/components/Disconnected/ConnectWalletButton.tsx @@ -16,7 +16,8 @@ export const ConnectWalletButton = memo( text, }: ConnectWalletButtonProps) => ( ) ); diff --git a/src/components/Disconnected/Disconnected.tsx b/src/components/Disconnected/Disconnected.tsx index b8fd579..6bbc3ae 100644 --- a/src/components/Disconnected/Disconnected.tsx +++ b/src/components/Disconnected/Disconnected.tsx @@ -1,9 +1,8 @@ import { memo } from "react"; import { ConnectWalletButton } from "./ConnectWalletButton"; -import { initCoinbaseWalletProvider } from "../../hooks/useWeb3/connectors/coinbaseWallet"; import type { ProviderStringType } from "../../utils/types"; -import { initMetaMaskProvider } from "../../hooks/useWeb3/connectors/metaMask"; -import { initWalletConnectProvider } from "../../hooks/useWeb3/connectors/walletConnect"; +import { providers } from "../../hooks/useWeb3/useWeb3"; +import WalletConnectProvider from "@walletconnect/web3-provider"; type DisconnectedProps = { handleConnect: (selectedProvider: ProviderStringType) => Promise; @@ -36,23 +35,22 @@ export const Disconnected = memo(({ handleConnect }: DisconnectedProps) => { }); function isProviderConnected(providerString: ProviderStringType) { + const provider = providers[providerString]; switch (providerString) { case "coinbase": { - const provider = initCoinbaseWalletProvider(); - return !!provider.selectedAddress; + return !!provider?.selectedAddress; } case "metamask": { - const provider = initMetaMaskProvider(); return ( provider && provider.isMetaMask && + // @ts-expect-error checking because coinbase wallet mocks metamask !provider.isCoinbaseWallet && !!provider.selectedAddress ); } case "walletconnect": { - const provider = initWalletConnectProvider(); - return provider.wc.connected; + return (provider as WalletConnectProvider).wc.connected; } } } diff --git a/src/hooks/useWeb3/connectWithProvider.ts b/src/hooks/useWeb3/connectWithProvider.ts index 5b171f1..c5096d3 100644 --- a/src/hooks/useWeb3/connectWithProvider.ts +++ b/src/hooks/useWeb3/connectWithProvider.ts @@ -2,8 +2,12 @@ import Web3 from "web3"; import { connectCoinbaseWallet } from "./connectors/coinbaseWallet"; import { connectMetaMask } from "./connectors/metaMask"; import { connectWalletConnect } from "./connectors/walletConnect"; +import { CoinbaseWalletProvider } from "@coinbase/wallet-sdk"; +import { MetaMaskInpageProvider } from "@metamask/providers"; +import WalletConnectProvider from "@walletconnect/web3-provider"; import type { ConnectedReturnType, + EthereumProvider, ProviderStringType, } from "../../utils/types"; @@ -18,15 +22,16 @@ import type { * This function only returns these values if the user successfully connects */ export const connectWithProvider = async ( - provider: ProviderStringType + providerString: ProviderStringType, + provider: EthereumProvider ): Promise => { - switch (provider) { + switch (providerString) { case "coinbase": - return connectCoinbaseWallet(); + return connectCoinbaseWallet(provider as CoinbaseWalletProvider); case "metamask": - return connectMetaMask(); + return connectMetaMask(provider as MetaMaskInpageProvider); case "walletconnect": - return connectWalletConnect(); + return connectWalletConnect(provider as WalletConnectProvider); default: // BEGIN COMMENT // // THIS WILL NEVER HAPPEN BECAUSE WE DON'T SUPPORT ANY OTHER WALLETS diff --git a/src/hooks/useWeb3/connectors/coinbaseWallet.ts b/src/hooks/useWeb3/connectors/coinbaseWallet.ts index f276220..2f45a41 100644 --- a/src/hooks/useWeb3/connectors/coinbaseWallet.ts +++ b/src/hooks/useWeb3/connectors/coinbaseWallet.ts @@ -1,11 +1,13 @@ import Web3 from "web3"; -import CoinbaseWalletSDK from "@coinbase/wallet-sdk"; +import CoinbaseWalletSDK, { + CoinbaseWalletProvider, +} from "@coinbase/wallet-sdk"; import { DEFAULT_CHAIN_ID, APP_NAME, APP_LOGO_URL, INFURA_RPC_URL, -} from "../dappInfo"; +} from "../../../utils/constants"; import type { ConnectedReturnType } from "../../../utils/types"; /** @@ -22,11 +24,14 @@ export const initCoinbaseWalletProvider = () => { return coinbaseWallet.makeWeb3Provider(INFURA_RPC_URL, DEFAULT_CHAIN_ID); }; -export const connectCoinbaseWallet = async (): Promise => { +export const connectCoinbaseWallet = async < + TProvider extends CoinbaseWalletProvider +>( + provider: TProvider +): Promise => { // If the user selected Coinbase Wallet to connect // We initialize the Coinbase Wallet SDK instance and // we create the ethereum provider for Coinbase Wallet SDK - const provider = initCoinbaseWalletProvider(); // We initialize the Web3 instance const web3 = new Web3(provider); diff --git a/src/hooks/useWeb3/connectors/metaMask.ts b/src/hooks/useWeb3/connectors/metaMask.ts index 69da154..552fd27 100644 --- a/src/hooks/useWeb3/connectors/metaMask.ts +++ b/src/hooks/useWeb3/connectors/metaMask.ts @@ -1,16 +1,21 @@ import Web3 from "web3"; import type { MetaMaskInpageProvider } from "@metamask/providers"; -import type { ConnectedReturnType } from "../../../utils/types"; +import type { provider as Provider } from "web3-core"; +import type { + ConnectedReturnType, + EthereumProvider, +} from "../../../utils/types"; // Initializes the MetaMask provider using the provider at window.ethereum // We will prefer a provider where the property `isMetaMask` is set to true -export const initMetaMaskProvider = () => +export const initMetaMaskProvider = (): MetaMaskInpageProvider => (window.ethereum as any)?.providers?.find( (p: MetaMaskInpageProvider) => !!p.isMetaMask ) ?? window.ethereum; -export const connectMetaMask = async (): Promise => { - const provider = initMetaMaskProvider(); +export const connectMetaMask = async ( + provider: EthereumProvider +): Promise => { // If the user selected MetaMask to connect // We make sure that the user has MetaMask installed in their browser if (!provider || !provider.isMetaMask || !window.ethereum) { @@ -22,7 +27,7 @@ export const connectMetaMask = async (): Promise => { } // We initialize the Web3 instance - const web3 = new Web3(provider); + const web3 = new Web3(provider as unknown as Provider); // This opens the wallet provider prompt to connect to this dapp // If the user was already connected, they will not be prompted const accounts: string[] = await provider.request({ diff --git a/src/hooks/useWeb3/connectors/walletConnect.ts b/src/hooks/useWeb3/connectors/walletConnect.ts index e28ccd3..6769e2e 100644 --- a/src/hooks/useWeb3/connectors/walletConnect.ts +++ b/src/hooks/useWeb3/connectors/walletConnect.ts @@ -1,7 +1,7 @@ import Web3 from "web3"; import WalletConnectProvider from "@walletconnect/web3-provider"; -import { INFURA_PROJECT_ID } from "../dappInfo"; -import type { provider } from "web3-core"; +import { INFURA_PROJECT_ID } from "../../../utils/constants"; +import type { provider as Provider } from "web3-core"; import type { ConnectedReturnType } from "../../../utils/types"; // Initializes the WalletConnect Provider @@ -10,14 +10,13 @@ export const initWalletConnectProvider = () => infuraId: INFURA_PROJECT_ID, }); -export const connectWalletConnect = async (): Promise => { +export const connectWalletConnect = async ( + provider: WalletConnectProvider +): Promise => { // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { - // If the user selected WalletConnect Wallet to connect - // Initialize the WalletConnectProvider - const provider = initWalletConnectProvider(); // We initialize the Web3 instance - const web3 = new Web3(provider as unknown as provider); + const web3 = new Web3(provider as unknown as Provider); // This controls whether or not we fire the 'disconnect' listener // This is because WalletConnect does not provide an removeEventListener diff --git a/src/hooks/useWeb3/useWeb3.ts b/src/hooks/useWeb3/useWeb3.ts index 290167b..5379b6c 100644 --- a/src/hooks/useWeb3/useWeb3.ts +++ b/src/hooks/useWeb3/useWeb3.ts @@ -1,7 +1,18 @@ import Web3 from "web3"; import { useCallback, useEffect, useState } from "react"; import { connectWithProvider } from "./connectWithProvider"; -import type { EthereumProvider, ProviderStringType } from "../../utils/types"; +import type { EthereumProvider } from "../../utils/types"; +import type { ProviderStringType } from "../../utils/types"; +import { initCoinbaseWalletProvider } from "./connectors/coinbaseWallet"; +import { initMetaMaskProvider } from "./connectors/metaMask"; +import { initWalletConnectProvider } from "./connectors/walletConnect"; + +// This is all of the supported provider's EthereumProviders +export const providers = { + coinbase: initCoinbaseWalletProvider(), + metamask: initMetaMaskProvider(), + walletconnect: initWalletConnectProvider(), +} as const; // The localstorage key for the selected provider // If defined, value is either 'coinbase', 'metamask', or 'walletconnect' @@ -33,12 +44,16 @@ export const useWeb3 = () => { // Accepts the user's wallet provider selection // Coinbase Wallet, MetaMask, or WalletConnect async (selectedProvider: ProviderStringType) => { + if (!providers) return; try { const { provider: connectedProvider, web3: web3Instance, accounts, - } = await connectWithProvider(selectedProvider); + } = await connectWithProvider( + selectedProvider, + providers[selectedProvider] + ); // Set the localstorage key with the selected wallet provider // 'coinbase', 'metamask', or 'walletconnect' // We will use this key to connect the user automatically @@ -146,6 +161,7 @@ export const useWeb3 = () => { }, []); return { + providers, providerString, connectProvider, changeProvider, diff --git a/src/hooks/useWeb3/dappInfo.ts b/src/utils/constants.ts similarity index 100% rename from src/hooks/useWeb3/dappInfo.ts rename to src/utils/constants.ts diff --git a/src/utils/types.ts b/src/utils/types.ts index 76052be..0c4a7e3 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -1,15 +1,9 @@ import type Web3 from "web3"; -import type { CoinbaseWalletProvider } from "@coinbase/wallet-sdk"; -import type { MetaMaskInpageProvider } from "@metamask/providers"; -import type WalletConnectProvider from "@walletconnect/web3-provider"; +import { providers } from "../hooks/useWeb3/useWeb3"; -export type EthereumProvider = - | CoinbaseWalletProvider - | MetaMaskInpageProvider - | WalletConnectProvider; +export type ProviderStringType = keyof typeof providers; -// Our supported wallet providers are Coinbase Wallet, MetaMask, and WalletConnect -export type ProviderStringType = "coinbase" | "metamask" | "walletconnect"; +export type EthereumProvider = typeof providers[ProviderStringType]; /** * This represents the return type of the connectProvider function, which contains