From b39dda20e99464d5c68d341511bd4ed350a025e3 Mon Sep 17 00:00:00 2001 From: Sebastian Gerske <13647606+H34D@users.noreply.github.com> Date: Thu, 23 Feb 2023 12:49:33 +0100 Subject: [PATCH] fix use-metamask not able to retrieve current values from use-masa refactor use-masa provider / wallet fix react query requests loading / fetching state properly expose reload methods many smaller tweaks --- .../masa-interface/masa-interface.tsx | 26 +- .../pages/authenticate/authenticate.tsx | 6 +- .../pages/connected/connected.tsx | 8 +- .../pages/connector/connector.tsx | 6 +- .../create-credit-score.tsx | 4 +- .../pages/create-identity/create-identity.tsx | 4 +- .../pages/switch-chain/switch-chain.tsx | 5 +- src/helpers/masa.ts | 30 +-- src/provider/masa-context-provider.tsx | 245 ++++++++---------- src/provider/masa-context.tsx | 71 +---- src/provider/masa-provider.tsx | 5 +- src/provider/masa-query-client.tsx | 8 +- src/provider/masa-shape.ts | 99 +++++++ .../modules/credit-scores/credit-scores.ts | 50 ++-- src/provider/modules/green/green.ts | 29 ++- src/provider/modules/identity/identity.ts | 38 ++- src/provider/modules/index.ts | 3 + src/provider/modules/modal/index.ts | 1 + src/provider/modules/modal/modal.ts | 44 ++++ src/provider/modules/network/index.ts | 1 + src/provider/modules/network/network.ts | 72 +++++ src/provider/modules/provider/index.ts | 1 + src/provider/modules/provider/provider.ts | 21 ++ src/provider/modules/session/session.ts | 85 +++--- src/provider/modules/soulnames/soulnames.ts | 50 ++-- src/provider/modules/wallet/wallet.ts | 48 ++-- src/provider/use-masa.tsx | 3 +- src/provider/use-metamask.ts | 141 +++++----- stories/masa.stories.tsx | 2 +- 29 files changed, 654 insertions(+), 452 deletions(-) create mode 100644 src/provider/masa-shape.ts create mode 100644 src/provider/modules/modal/index.ts create mode 100644 src/provider/modules/modal/modal.ts create mode 100644 src/provider/modules/network/index.ts create mode 100644 src/provider/modules/network/network.ts create mode 100644 src/provider/modules/provider/index.ts create mode 100644 src/provider/modules/provider/provider.ts diff --git a/src/components/masa-interface/masa-interface.tsx b/src/components/masa-interface/masa-interface.tsx index 8f8ecc65..a7be8d19 100644 --- a/src/components/masa-interface/masa-interface.tsx +++ b/src/components/masa-interface/masa-interface.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from 'react'; -import { useMasa } from '../../provider'; +import { useMasa, useMetamask } from '../../provider'; import { ModalComponent } from '../modal'; import { InterfaceAuthenticate, @@ -11,9 +11,11 @@ import { import { InterfaceSwitchChain } from './pages/switch-chain'; const pages = { - connector: ({ disable }: { disable?: boolean }): JSX.Element => ( - - ), + connector: ({ + disableMetamask, + }: { + disableMetamask?: boolean; + }): JSX.Element => , createIdentity: , connectedState: , authenticate: , @@ -22,16 +24,18 @@ const pages = { }; export const MasaInterface = ({ - disable, + disableMetamask, }: { - disable?: boolean; + disableMetamask?: boolean; }): JSX.Element => { + useMetamask({ disabled: disableMetamask }); + const { isModalOpen, setModalOpen, isConnected, identity, - loggedIn, + isLoggedIn, closeModal, scope, creditScores, @@ -41,15 +45,15 @@ export const MasaInterface = ({ if (!isConnected) return 'connector'; // if (network && !chain?.name.includes(network)) return 'switchNetwork'; - if (!loggedIn) return 'authenticate'; + if (!isLoggedIn) return 'authenticate'; if (!identity?.identityId && scope?.includes('identity')) return 'createIdentity'; if (identity && !creditScores?.length && scope?.includes('credit-score')) return 'createCreditScore'; - if (isConnected && loggedIn) return 'connectedState'; + if (isConnected && isLoggedIn) return 'connectedState'; return 'connector'; - }, [isConnected, identity, loggedIn, scope, creditScores]); + }, [isConnected, identity, isLoggedIn, scope, creditScores]); return ( <> @@ -58,7 +62,7 @@ export const MasaInterface = ({ close={(): void => closeModal?.()} setOpen={setModalOpen as (val: boolean) => void} > - {page === 'connector' ? pages[page]({ disable }) : pages[page]} + {page === 'connector' ? pages[page]({ disableMetamask }) : pages[page]} ); diff --git a/src/components/masa-interface/pages/authenticate/authenticate.tsx b/src/components/masa-interface/pages/authenticate/authenticate.tsx index 0cc1ac43..62019d98 100644 --- a/src/components/masa-interface/pages/authenticate/authenticate.tsx +++ b/src/components/masa-interface/pages/authenticate/authenticate.tsx @@ -3,7 +3,7 @@ import { useMasa } from '../../../../provider'; import { Spinner } from '../../../spinner'; export const InterfaceAuthenticate = (): JSX.Element => { - const { handleLogin, walletAddress, loading } = useMasa(); + const { handleLogin, walletAddress, isLoading } = useMasa(); const [copied, setCopied] = useState(false); @@ -21,7 +21,7 @@ export const InterfaceAuthenticate = (): JSX.Element => { } }, [walletAddress]); - if (loading) { + if (isLoading) { return ; } return ( @@ -45,7 +45,7 @@ export const InterfaceAuthenticate = (): JSX.Element => { className="masa-button authenticate-button" onClick={handleLogin} > - {loading ? 'loading...' : 'Get Started'} + {isLoading ? 'loading...' : 'Get Started'}
diff --git a/src/components/masa-interface/pages/connected/connected.tsx b/src/components/masa-interface/pages/connected/connected.tsx index 484418c8..26d922b4 100644 --- a/src/components/masa-interface/pages/connected/connected.tsx +++ b/src/components/masa-interface/pages/connected/connected.tsx @@ -4,17 +4,17 @@ import { MasaLoading } from '../../../masa-loading'; import { Spinner } from '../../../spinner'; export const InterfaceConnected = (): JSX.Element => { - const { loading, closeModal } = useMasa(); + const { closeModal, isLoading } = useMasa(); useEffect(() => { - if (!loading) { + if (!isLoading) { setTimeout(() => { closeModal?.(); }, 3000); } - }, [loading, closeModal]); + }, [isLoading, closeModal]); - if (loading) return ; + if (isLoading) return ; return (
diff --git a/src/components/masa-interface/pages/connector/connector.tsx b/src/components/masa-interface/pages/connector/connector.tsx index 226c33cd..905f8fe8 100644 --- a/src/components/masa-interface/pages/connector/connector.tsx +++ b/src/components/masa-interface/pages/connector/connector.tsx @@ -2,11 +2,11 @@ import React from 'react'; import { useMetamask } from '../../../../provider'; export const InterfaceConnector = ({ - disable, + disableMetamask, }: { - disable?: boolean; + disableMetamask?: boolean; }): JSX.Element => { - const { connect } = useMetamask({ disable }); + const { connect } = useMetamask({ disabled: disableMetamask }); return (
diff --git a/src/components/masa-interface/pages/create-credit-score/create-credit-score.tsx b/src/components/masa-interface/pages/create-credit-score/create-credit-score.tsx index 857dca21..d5be68b5 100644 --- a/src/components/masa-interface/pages/create-credit-score/create-credit-score.tsx +++ b/src/components/masa-interface/pages/create-credit-score/create-credit-score.tsx @@ -3,7 +3,7 @@ import { useMasa } from '../../../../provider'; import { MasaLoading } from '../../../masa-loading'; export const InterfaceCreateCreditScore = (): JSX.Element => { - const { handleCreateCreditScore, loading } = useMasa(); + const { handleCreateCreditScore, isLoading } = useMasa(); const [error, setError] = useState(null); const createCreditScore = useCallback(async () => { @@ -14,7 +14,7 @@ export const InterfaceCreateCreditScore = (): JSX.Element => { setError('There is not enough data for generating a credit report'); }, [handleCreateCreditScore]); - if (loading) return ; + if (isLoading) return ; return (
diff --git a/src/components/masa-interface/pages/create-identity/create-identity.tsx b/src/components/masa-interface/pages/create-identity/create-identity.tsx index 1aaebca8..972c5f02 100644 --- a/src/components/masa-interface/pages/create-identity/create-identity.tsx +++ b/src/components/masa-interface/pages/create-identity/create-identity.tsx @@ -3,13 +3,13 @@ import { useMasa } from '../../../../provider'; import { MasaLoading } from '../../../masa-loading'; export const InterfaceCreateIdentity = (): JSX.Element => { - const { handlePurchaseIdentity, handleLogout, loading } = useMasa(); + const { handlePurchaseIdentity, handleLogout, isLoading } = useMasa(); const createIdentity = useCallback(async () => { await handlePurchaseIdentity?.(); }, [handlePurchaseIdentity]); - if (loading) return ; + if (isLoading) return ; return (
diff --git a/src/components/masa-interface/pages/switch-chain/switch-chain.tsx b/src/components/masa-interface/pages/switch-chain/switch-chain.tsx index aae27087..1e695106 100644 --- a/src/components/masa-interface/pages/switch-chain/switch-chain.tsx +++ b/src/components/masa-interface/pages/switch-chain/switch-chain.tsx @@ -4,7 +4,8 @@ import { MasaLoading } from '../../../masa-loading'; import { Network } from '../../../../helpers'; export const InterfaceSwitchChain = (): JSX.Element => { - const { networkName, loading, switchNetwork, SupportedNetworks } = useMasa(); + const { networkName, isLoading, switchNetwork, SupportedNetworks } = + useMasa(); const currentNetwork: Network | null = useMemo(() => { if (SupportedNetworks && networkName) @@ -27,7 +28,7 @@ export const InterfaceSwitchChain = (): JSX.Element => { } }, [switchNetwork, currentNetwork]); - if (loading) return ; + if (isLoading) return ; return (
diff --git a/src/helpers/masa.ts b/src/helpers/masa.ts index 1deb8ada..e59603e1 100644 --- a/src/helpers/masa.ts +++ b/src/helpers/masa.ts @@ -1,20 +1,13 @@ -import { Environment, environments, Masa } from '@masa-finance/masa-sdk'; -import { ethers, Wallet } from 'ethers'; +import { + createRandomWallet, + Environment, + environments, + Masa, +} from '@masa-finance/masa-sdk'; +import { ethers } from 'ethers'; import { ArweaveConfig, getWeb3Provider } from '../provider'; import { getNetworkNameByChainId } from './networks'; -export const createRandomWallet = (): Wallet | null => { - console.info('Creating random wallet!'); - const wallet = ethers.Wallet.createRandom(); - const provider = getWeb3Provider(); - - if (provider) { - return wallet.connect(provider); - } - - return null; -}; - export const createNewMasa = async ({ signer, environmentName, @@ -25,20 +18,19 @@ export const createNewMasa = async ({ environmentName: string; arweaveConfig?: ArweaveConfig; verbose: boolean; -}): Promise => { +}): Promise => { const newSigner: ethers.Signer | null = signer ? signer - : createRandomWallet(); + : createRandomWallet(getWeb3Provider()); - if (!newSigner) return null; + if (!newSigner) return; const environment = environments.find( (environment: Environment) => environment.name === environmentName ); - if (!environment) return null; + if (!environment) return; const chainId: number = await newSigner.getChainId(); - console.log({ NETWORK: chainId }); return new Masa({ wallet: newSigner, diff --git a/src/provider/masa-context-provider.tsx b/src/provider/masa-context-provider.tsx index abec4497..fc04496c 100644 --- a/src/provider/masa-context-provider.tsx +++ b/src/provider/masa-context-provider.tsx @@ -1,16 +1,20 @@ import { EnvironmentName, Masa, NetworkName } from '@masa-finance/masa-sdk'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { createNewMasa, Network, SupportedNetworks } from '../helpers'; +import { createNewMasa, SupportedNetworks } from '../helpers'; import { useCreditScores, useGreen, useIdentity, + useModal, + useNetwork, + useProvider, useSession, useSoulnames, useWallet, } from './modules'; -import { providers, Signer, utils, Wallet } from 'ethers'; -import { MASA_CONTEXT, MasaShape } from './masa-context'; +import { Signer, Wallet } from 'ethers'; +import { MASA_CONTEXT } from './masa-context'; +import { MasaShape } from './masa-shape'; export interface ArweaveConfig { port?: string; @@ -37,86 +41,93 @@ export const MasaContextProvider = ({ company, environmentName = 'dev' as EnvironmentNameEx, verbose = false, - signer: externalSigner, + signer, noWallet, arweaveConfig, networkName, }: MasaContextProviderProps): JSX.Element => { - const [masaInstance, setMasaInstance] = useState(null); + // masa + const [masaInstance, setMasaInstance] = useState(); + // scope + const [scope, setScope] = useState([]); - const [provider, setProvider] = useState(null); - const [missingProvider, setMissingProvider] = useState(); + // provider + const { provider, setProvider, isProviderMissing, setIsProviderMissing } = + useProvider(signer); - const [isModalOpen, setModalOpen] = useState(false); - const [modalCallback, setModalCallback] = useState<(() => void) | null>(null); + // wallet + const { walletAddress, isWalletLoading, isConnected } = useWallet( + masaInstance, + provider + ); + // session + const { isLoggedIn, handleLogin, handleLogout, isSessionLoading } = + useSession(masaInstance, walletAddress); - const [scope, setScope] = useState([]); + // network + const { switchNetwork, network } = useNetwork(provider); - // Modules - const { - walletAddress, - isLoading: walletLoading, - network, - }: { - walletAddress: string | undefined; - isLoading: boolean; - network: providers.Network | null; - } = useWallet(masaInstance, provider); + // modal + const { isModalOpen, setModalOpen, setModalCallback, closeModal } = useModal( + networkName, + isLoggedIn, + isConnected, + network + ); + // identity const { identity, handlePurchaseIdentity, - isLoading: identityLoading, + isIdentityLoading, + reloadIdentity, } = useIdentity(masaInstance, walletAddress); - const { soulnames } = useSoulnames(masaInstance, walletAddress, identity); + // soul names + const { soulnames, isSoulnamesLoading, reloadSoulnames } = useSoulnames( + masaInstance, + walletAddress, + identity + ); + // credit scores const { creditScores, - isLoading: creditScoreLoading, + isCreditScoresLoading, handleCreateCreditScore, + reloadCreditScores, } = useCreditScores(masaInstance, walletAddress, identity); + // greens const { greens, - isLoading: greenLoading, + isGreensLoading, handleGenerateGreen, handleCreateGreen, + reloadGreens, } = useGreen(masaInstance, walletAddress); - const { - loggedIn, - login: handleLogin, - logout: handleLogout, - isLoading: sessionLoading, - } = useSession(masaInstance, walletAddress); - - // Logic - - const loading = useMemo(() => { + // global loading flag + const isLoading = useMemo(() => { return ( !masaInstance || - sessionLoading || - creditScoreLoading || - identityLoading || - walletLoading || - greenLoading + isWalletLoading || + isSessionLoading || + isIdentityLoading || + isSoulnamesLoading || + isCreditScoresLoading || + isGreensLoading ); }, [ - sessionLoading, - creditScoreLoading, - identityLoading, - walletLoading, - greenLoading, masaInstance, + isWalletLoading, + isSessionLoading, + isIdentityLoading, + isSoulnamesLoading, + isCreditScoresLoading, + isGreensLoading, ]); - useEffect(() => { - if (externalSigner) { - setProvider(externalSigner); - } - }, [externalSigner]); - const connect = useCallback( (options?: { scope?: string[]; callback?: () => void }) => { setModalOpen(true); @@ -128,40 +139,18 @@ export const MasaContextProvider = ({ [setModalOpen, setModalCallback] ); - const isConnected = useMemo(() => { - return !!walletAddress; - }, [walletAddress]); - - const closeModal = useCallback(() => { - setModalOpen(false); - if ( - modalCallback && - loggedIn && - isConnected && - (networkName ? !network?.name.includes(networkName) : true) - ) { - modalCallback(); - } - }, [ - modalCallback, - setModalOpen, - loggedIn, - isConnected, - network, - networkName, - ]); - useEffect(() => { const loadMasa = async (): Promise => { if (!provider) return; - const masa: Masa | null = await createNewMasa({ + + const masa: Masa | undefined = await createNewMasa({ signer: provider, environmentName, arweaveConfig, verbose, }); - void setMasaInstance(masa); + setMasaInstance(masa); }; void loadMasa(); @@ -175,78 +164,70 @@ export const MasaContextProvider = ({ network, ]); - const addNetwork = useCallback(async (networkDetails: Network) => { - try { - if (typeof window !== 'undefined' && networkDetails) { - await window?.ethereum?.request({ - method: 'wallet_addEthereumChain', - params: [ - { - ...networkDetails, - chainId: utils.hexValue(networkDetails.chainId), - }, - ], - }); - } - } catch (error) { - console.error( - `error ocuured while adding new chain with chainId:${networkDetails?.chainId}` - ); - } - }, []); - - const switchNetwork = useCallback( - async (chainId: number) => { - try { - if (typeof window !== 'undefined') { - await window?.ethereum?.request({ - method: 'wallet_switchEthereumChain', - params: [{ chainId: utils.hexValue(chainId) }], - }); - console.log(`switched to chainId: ${chainId} successfully`); - } - } catch (err) { - const error = err as { code: number }; - if (error.code === 4902) { - void addNetwork(SupportedNetworks[chainId]); - } - } - }, - [addNetwork] - ); - const context: MasaShape = { - setProvider, + // masa instance + masa: masaInstance as Masa, + // global loading + isLoading, + + // masa-react global connect + connect, + + // general config + scope, + company, + + // provider handling provider, + setProvider, + isProviderMissing, + setIsProviderMissing, + + // modal isModalOpen, setModalOpen, - masa: masaInstance as Masa, - isConnected, - loading, + closeModal, + + // wallet walletAddress, + isWalletLoading, + isConnected, + + // identity identity, - loggedIn, + isIdentityLoading, + handlePurchaseIdentity, + reloadIdentity, + + // session + isLoggedIn, + isSessionLoading, handleLogin, handleLogout, - handlePurchaseIdentity, - connect, - closeModal, - scope, - company, - handleCreateCreditScore, + + // credit scores creditScores, + isCreditScoresLoading, + handleCreateCreditScore, + reloadCreditScores, + + // soul names soulnames, - logginLoading: sessionLoading, - missingProvider, - setMissingProvider, + isSoulnamesLoading, + reloadSoulnames, + + // greens greens, - greenLoading, + isGreensLoading, handleGenerateGreen, handleCreateGreen, + reloadGreens, + + // network + networkName, network, - switchNetwork, SupportedNetworks, - networkName, + switchNetwork, }; return ( diff --git a/src/provider/masa-context.tsx b/src/provider/masa-context.tsx index a1528425..cb4cf946 100644 --- a/src/provider/masa-context.tsx +++ b/src/provider/masa-context.tsx @@ -1,71 +1,4 @@ -import React, { createContext } from 'react'; -import { - GenerateGreenResult, - ICreditScore, - IGreen, - Masa, - NetworkName, - SoulNameDetails, - VerifyGreenResult, -} from '@masa-finance/masa-sdk'; -import { BigNumber, ethers } from 'ethers'; -import { Network } from '../helpers'; +import { createContext } from 'react'; +import { MasaShape } from './masa-shape'; export const MASA_CONTEXT = createContext({}); - -export interface MasaShape { - children?: React.ReactNode; - setProvider?: (provider: ethers.Wallet | ethers.Signer | null) => void; - provider?: ethers.Wallet | ethers.Signer | null; - isModalOpen?: boolean; - setModalOpen?: (val: boolean) => void; - masa?: Masa; - isConnected?: boolean; - loading?: boolean; - walletAddress?: string | undefined; - identity?: { - identityId?: BigNumber | undefined; - address?: string | undefined; - }; - loggedIn?: boolean; - handleLogin?: () => void; - handleLogout?: (callback?: () => void) => void; - handlePurchaseIdentity?: () => void; - connect?: (options?: { scope?: string[]; callback?: () => void }) => void; - closeModal?: () => void; - scope?: string[]; - company?: string; - handleCreateCreditScore?: () => void; - creditScores?: - | { - tokenId: BigNumber; - tokenUri: string; - metadata?: ICreditScore | undefined; - }[] - | null; - loadCreditScores?: () => void; - soulnames?: SoulNameDetails[] | null; - loadSoulnames?: () => void; - logginLoading?: boolean; - missingProvider?: boolean; - setMissingProvider?: (value: boolean) => void; - greens?: - | { - tokenId: BigNumber; - tokenUri: string; - metadata?: IGreen | undefined; - }[] - | undefined; - greenLoading?: boolean; - handleCreateGreen?: ( - phoneNumber: string, - code: string - ) => Promise; - handleGenerateGreen?: ( - phoneNumber: string - ) => Promise; - network?: ethers.providers.Network | null; - switchNetwork?: (chainId: number) => void; - SupportedNetworks?: Partial<{ [index in NetworkName]: Network }>; - networkName?: NetworkName; -} diff --git a/src/provider/masa-provider.tsx b/src/provider/masa-provider.tsx index fe58e116..33a6d195 100644 --- a/src/provider/masa-provider.tsx +++ b/src/provider/masa-provider.tsx @@ -5,7 +5,6 @@ import { import React from 'react'; import { QueryClientProvider } from 'react-query'; -import { useMetamask } from './use-metamask'; import { queryClient } from './masa-query-client'; import '../../styles.scss'; @@ -15,12 +14,10 @@ export const MasaProvider = ({ children, ...args }: MasaContextProviderProps): JSX.Element => { - useMetamask({ disable: args.noWallet }); - return ( - + {children} diff --git a/src/provider/masa-query-client.tsx b/src/provider/masa-query-client.tsx index 0fd037d3..6fc1d9a6 100644 --- a/src/provider/masa-query-client.tsx +++ b/src/provider/masa-query-client.tsx @@ -1,3 +1,9 @@ import { QueryClient } from 'react-query'; -export const queryClient = new QueryClient(); +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, // default: true + }, + }, +}); diff --git a/src/provider/masa-shape.ts b/src/provider/masa-shape.ts new file mode 100644 index 00000000..5e6fd675 --- /dev/null +++ b/src/provider/masa-shape.ts @@ -0,0 +1,99 @@ +import React from 'react'; +import { + GenerateGreenResult, + ICreditScore, + IGreen, + Masa, + NetworkName, + SoulNameDetails, + VerifyGreenResult, +} from '@masa-finance/masa-sdk'; +import { BigNumber, ethers } from 'ethers'; +import { Network } from '../helpers'; + +export interface MasaShape { + children?: React.ReactNode; + + // masa + masa?: Masa; + // global loading + isLoading?: boolean; + + // global connect + connect?: (options?: { scope?: string[]; callback?: () => void }) => void; + + // general config + scope?: string[]; + company?: string; + + // provider + provider?: ethers.Wallet | ethers.Signer; + setProvider?: (provider?: ethers.Wallet | ethers.Signer) => void; + isProviderMissing?: boolean; + setIsProviderMissing?: (value: boolean) => void; + + // modal + isModalOpen?: boolean; + setModalOpen?: (val: boolean) => void; + closeModal?: () => void; + + // wallet + walletAddress?: string; + isWalletLoading?: boolean; + isConnected?: boolean; + + // identity + identity?: { + identityId?: BigNumber; + address?: string; + }; + isIdentityLoading?: boolean; + handlePurchaseIdentity?: () => void; + reloadIdentity?: () => void; + + // session + isLoggedIn?: boolean; + isSessionLoading?: boolean; + handleLogin?: () => void; + handleLogout?: (logoutCallback?: () => void) => void; + + // credit scores + creditScores?: + | { + tokenId: BigNumber; + tokenUri: string; + metadata?: ICreditScore | undefined; + }[]; + isCreditScoresLoading?: boolean; + handleCreateCreditScore?: () => void; + reloadCreditScores?: () => void; + + // soul names + soulnames?: SoulNameDetails[]; + isSoulnamesLoading?: boolean; + reloadSoulnames?: () => void; + + // greens + greens?: + | { + tokenId: BigNumber; + tokenUri: string; + metadata?: IGreen; + }[] + | undefined; + isGreensLoading?: boolean; + handleGenerateGreen?: ( + phoneNumber: string + ) => Promise; + handleCreateGreen?: ( + phoneNumber: string, + code: string + ) => Promise; + reloadGreens?: () => void; + + // network + networkName?: NetworkName; + network?: ethers.providers.Network; + SupportedNetworks?: Partial<{ [index in NetworkName]: Network }>; + switchNetwork?: (chainId: number) => void; +} diff --git a/src/provider/modules/credit-scores/credit-scores.ts b/src/provider/modules/credit-scores/credit-scores.ts index c4871e7e..90c45ee9 100644 --- a/src/provider/modules/credit-scores/credit-scores.ts +++ b/src/provider/modules/credit-scores/credit-scores.ts @@ -5,14 +5,12 @@ import { ICreditScore, Masa } from '@masa-finance/masa-sdk'; import { BigNumber } from 'ethers'; export const useCreditScores = ( - masa: Masa | null, - walletAddress: string | undefined, - identity: - | { - identityId?: BigNumber | undefined; - address?: string | undefined; - } - | undefined + masa?: Masa, + walletAddress?: string, + identity?: { + identityId?: BigNumber | undefined; + address?: string | undefined; + } ): { creditScores: | { @@ -23,7 +21,8 @@ export const useCreditScores = ( | undefined; handleCreateCreditScore: () => void; status: string; - isLoading: boolean; + isCreditScoresLoading: boolean; + reloadCreditScores: () => void; error: unknown; } => { const queryKey: (string | undefined)[] = useMemo(() => { @@ -34,31 +33,46 @@ export const useCreditScores = ( data: creditScores, status, isLoading, + isFetching, + refetch: reloadCreditScores, error, - } = useQuery(queryKey, () => masa?.creditScore.list(), { - enabled: - !!masa && - masa.config.network !== 'unknown' && - !!walletAddress && - !!identity?.identityId, + } = useQuery< + | { + tokenId: BigNumber; + tokenUri: string; + metadata?: ICreditScore; + }[] + | undefined + >(queryKey, () => masa?.creditScore.list(), { + enabled: !!masa && !!walletAddress && !!identity?.identityId, retry: false, + onSuccess: ( + creditScores?: { + tokenId: BigNumber; + tokenUri: string; + metadata?: ICreditScore; + }[] + ) => { + if (masa?.config.verbose) { + console.log({ creditScores, network: masa?.config.network }); + } + }, }); const handleCreateCreditScore = useCallback(async (): Promise< boolean | undefined > => { const response = await masa?.creditScore.create(); - await queryClient.invalidateQueries(queryKey); - return response?.success; }, [masa, queryKey]); return { creditScores, + isCreditScoresLoading: isLoading || isFetching, handleCreateCreditScore, + reloadCreditScores, status, - isLoading, error, }; }; diff --git a/src/provider/modules/green/green.ts b/src/provider/modules/green/green.ts index b99f6e13..81bdc46d 100644 --- a/src/provider/modules/green/green.ts +++ b/src/provider/modules/green/green.ts @@ -10,8 +10,8 @@ import { import { BigNumber } from 'ethers'; export const useGreen = ( - masa: Masa | null, - walletAddress: string | undefined + masa?: Masa, + walletAddress?: string ): { greens: | { @@ -28,23 +28,33 @@ export const useGreen = ( code: string ) => Promise; status: string; - isLoading: boolean; + isGreensLoading: boolean; + reloadGreens: () => void; error: unknown; } => { const queryKey: (string | undefined)[] = useMemo(() => { return ['green', walletAddress, masa?.config.network]; - }, [walletAddress, masa]); + }, [masa, walletAddress]); const { data: greens, status, isLoading, + isFetching, + refetch: reloadGreens, error, - } = useQuery(queryKey, () => masa?.green.list(), { - enabled: !!masa && masa.config.network !== 'unknown' && !!walletAddress, + } = useQuery< + | { + tokenId: BigNumber; + tokenUri: string; + metadata?: IGreen | undefined; + }[] + | undefined + >(queryKey, () => masa?.green.list(), { + enabled: !!masa && !!walletAddress, retry: false, onSuccess: ( - greens: { + greens?: { tokenId: BigNumber; tokenUri: string; metadata?: IGreen | undefined; @@ -62,9 +72,7 @@ export const useGreen = ( code: string ): Promise => { const response = await masa?.green.create(phoneNumber, code); - await queryClient.invalidateQueries(queryKey); - return response; }, [masa, queryKey] @@ -84,10 +92,11 @@ export const useGreen = ( return { greens, + isGreensLoading: isLoading || isFetching, handleGenerateGreen, handleCreateGreen, + reloadGreens, status, - isLoading, error, }; }; diff --git a/src/provider/modules/identity/identity.ts b/src/provider/modules/identity/identity.ts index 6d296cb0..b59b7730 100644 --- a/src/provider/modules/identity/identity.ts +++ b/src/provider/modules/identity/identity.ts @@ -5,8 +5,8 @@ import { Masa } from '@masa-finance/masa-sdk'; import { BigNumber } from 'ethers'; export const useIdentity = ( - masa: Masa | null, - walletAddress: string | undefined + masa?: Masa, + walletAddress?: string ): { identity: | { @@ -16,7 +16,8 @@ export const useIdentity = ( | undefined; handlePurchaseIdentity: () => void; status: string; - isLoading: boolean; + isIdentityLoading: boolean; + reloadIdentity: () => void; error: unknown; } => { const queryKey: (string | undefined)[] = useMemo(() => { @@ -27,19 +28,34 @@ export const useIdentity = ( data: identity, status, isLoading, + isFetching, + refetch: reloadIdentity, error, - } = useQuery(queryKey, () => masa?.identity.load(walletAddress), { - enabled: !!masa && masa.config.network !== 'unknown' && !!walletAddress, - retry: false, - onSuccess: (identity: { identityId?: BigNumber; address?: string }) => { - console.log({ identity }); - }, - }); + } = useQuery<{ identityId?: BigNumber; address?: string } | undefined>( + queryKey, + () => masa?.identity.load(walletAddress), + { + enabled: !!masa && !!walletAddress, + retry: false, + onSuccess: (identity?: { identityId?: BigNumber; address?: string }) => { + if (masa?.config.verbose) { + console.log({ identity, network: masa?.config.network }); + } + }, + } + ); const handlePurchaseIdentity = useCallback(async () => { await masa?.identity.create(); await queryClient.invalidateQueries(queryKey); }, [masa, queryKey]); - return { identity, handlePurchaseIdentity, status, isLoading, error }; + return { + identity, + isIdentityLoading: isLoading || isFetching, + handlePurchaseIdentity, + reloadIdentity, + status, + error, + }; }; diff --git a/src/provider/modules/index.ts b/src/provider/modules/index.ts index da27f85e..d1dba02d 100644 --- a/src/provider/modules/index.ts +++ b/src/provider/modules/index.ts @@ -1,6 +1,9 @@ export * from './credit-scores'; export * from './green'; export * from './identity'; +export * from './network'; export * from './session'; export * from './soulnames'; export * from './wallet'; +export * from './modal'; +export * from './provider'; diff --git a/src/provider/modules/modal/index.ts b/src/provider/modules/modal/index.ts new file mode 100644 index 00000000..133aa742 --- /dev/null +++ b/src/provider/modules/modal/index.ts @@ -0,0 +1 @@ +export * from './modal'; diff --git a/src/provider/modules/modal/modal.ts b/src/provider/modules/modal/modal.ts new file mode 100644 index 00000000..619d003c --- /dev/null +++ b/src/provider/modules/modal/modal.ts @@ -0,0 +1,44 @@ +import { useCallback, useState } from 'react'; +import { NetworkName } from '@masa-finance/masa-sdk'; +import { providers } from 'ethers'; + +export const useModal = ( + networkName?: NetworkName, + isLoggedIn?: boolean, + isConnected?: boolean, + network?: providers.Network +): { + isModalOpen: boolean; + closeModal: () => void; + setModalOpen: (modalOpen: boolean) => void; + setModalCallback: (callback: () => void) => void; +} => { + const [isModalOpen, setModalOpen] = useState(false); + const [modalCallback, setModalCallback] = useState<(() => void) | null>(null); + + const closeModal = useCallback(() => { + setModalOpen(false); + if ( + modalCallback && + isLoggedIn && + isConnected && + (networkName ? !network?.name.includes(networkName) : true) + ) { + modalCallback(); + } + }, [ + modalCallback, + setModalOpen, + isLoggedIn, + isConnected, + network, + networkName, + ]); + + return { + isModalOpen, + closeModal, + setModalOpen, + setModalCallback, + }; +}; diff --git a/src/provider/modules/network/index.ts b/src/provider/modules/network/index.ts new file mode 100644 index 00000000..a31b9ed5 --- /dev/null +++ b/src/provider/modules/network/index.ts @@ -0,0 +1 @@ +export * from './network'; diff --git a/src/provider/modules/network/network.ts b/src/provider/modules/network/network.ts new file mode 100644 index 00000000..f6f17c1b --- /dev/null +++ b/src/provider/modules/network/network.ts @@ -0,0 +1,72 @@ +import { providers, Signer, utils, Wallet } from 'ethers'; +import { useCallback, useEffect, useState } from 'react'; +import { Network, SupportedNetworks } from '../../../helpers'; + +export const useNetwork = ( + provider?: Wallet | Signer +): { + addNetwork: (networkDetails: Network) => void; + switchNetwork: (chainId: number) => void; + network?: providers.Network; +} => { + const [network, setNetwork] = useState(); + + const addNetwork = useCallback(async (networkDetails: Network) => { + try { + if (typeof window !== 'undefined' && networkDetails) { + await window?.ethereum?.request({ + method: 'wallet_addEthereumChain', + params: [ + { + ...networkDetails, + chainId: utils.hexValue(networkDetails.chainId), + }, + ], + }); + } + } catch (error) { + console.error( + `error occurred while adding new chain with chainId:${networkDetails?.chainId}` + ); + } + }, []); + + const loadNetwork = useCallback(async (): Promise => { + if (!provider) return; + + const newNetwork = await provider.provider?.getNetwork(); + setNetwork(newNetwork ?? undefined); + }, [provider]); + + const switchNetwork = useCallback( + async (chainId: number) => { + try { + if (typeof window !== 'undefined') { + await window?.ethereum?.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: utils.hexValue(chainId) }], + }); + console.log(`switched to chainId: ${chainId} successfully`); + } + } catch (err) { + const error = err as { code: number }; + if (error.code === 4902) { + await addNetwork(SupportedNetworks[chainId]); + } + } + + await loadNetwork(); + }, + [addNetwork, loadNetwork] + ); + + useEffect(() => { + void loadNetwork(); + }, [loadNetwork]); + + return { + addNetwork, + switchNetwork, + network, + }; +}; diff --git a/src/provider/modules/provider/index.ts b/src/provider/modules/provider/index.ts new file mode 100644 index 00000000..03be03e5 --- /dev/null +++ b/src/provider/modules/provider/index.ts @@ -0,0 +1 @@ +export * from './provider'; diff --git a/src/provider/modules/provider/provider.ts b/src/provider/modules/provider/provider.ts new file mode 100644 index 00000000..2c8fa276 --- /dev/null +++ b/src/provider/modules/provider/provider.ts @@ -0,0 +1,21 @@ +import { useState } from 'react'; +import { Signer, Wallet } from 'ethers'; + +export const useProvider = ( + signer?: Wallet | Signer +): { + provider: Wallet | Signer | undefined; + setProvider: (provider: Wallet | Signer | undefined) => void; + isProviderMissing?: boolean; + setIsProviderMissing: (providerMissing: boolean) => void; +} => { + const [provider, setProvider] = useState(signer); + const [isProviderMissing, setIsProviderMissing] = useState(); + + return { + provider, + setProvider, + isProviderMissing, + setIsProviderMissing, + }; +}; diff --git a/src/provider/modules/session/session.ts b/src/provider/modules/session/session.ts index d0193cfe..3c0d570b 100644 --- a/src/provider/modules/session/session.ts +++ b/src/provider/modules/session/session.ts @@ -4,21 +4,25 @@ import { queryClient } from '../../masa-query-client'; import { ISession, Masa } from '@masa-finance/masa-sdk'; export const useSession = ( - masa: Masa | null, - walletAddress: string | undefined + masa?: Masa, + walletAddress?: string ): { - loggedIn?: boolean; - login: () => void; - logout: () => void; + isLoggedIn?: boolean; + isSessionLoading: boolean; + handleLogin: () => void; + handleLogout: (logoutCallback?: () => void) => void; status: string; - isLoading: boolean; error: unknown; } => { const queryKeySessionData: (string | undefined)[] = useMemo(() => { - return ['session-data', walletAddress]; + return ['session', 'data', walletAddress]; }, [walletAddress]); - const { data: sessionData } = useQuery( + const { + data: sessionData, + isLoading: isSessionDataLoading, + isFetching: isSessionDataFetching, + } = useQuery( queryKeySessionData, () => masa?.session.getSession(), { @@ -32,9 +36,10 @@ export const useSession = ( }, [walletAddress]); const { - data: loggedIn, + data: isLoggedIn, status, - isLoading, + isLoading: isSessionCheckLoading, + isFetching: isSessionCheckFetching, error, } = useQuery( queryKeySession, @@ -45,41 +50,45 @@ export const useSession = ( } ); - const doLogout = useCallback(async (): Promise => { - await masa?.session.logout(); - await queryClient.invalidateQueries(queryKeySession); - await queryClient.refetchQueries(); - }, [masa, queryKeySession]); + const clearSession = useCallback(async () => { + await queryClient.invalidateQueries(['wallet']); + await queryClient.invalidateQueries(['session']); + }, []); - useEffect(() => { - if (sessionData && sessionData.user.address !== walletAddress) { - void doLogout(); - } - }, [sessionData, walletAddress, masa, queryKeySession, doLogout]); + const handleLogout = useCallback( + async (logoutCallback?: () => void): Promise => { + await masa?.session.logout(); + await clearSession(); - useEffect(() => { - if (loggedIn && !walletAddress) { - void queryClient.invalidateQueries(queryKeySession); - } - }, [walletAddress, loggedIn, queryKeySession]); + logoutCallback?.(); + }, + [masa, clearSession] + ); - const login = useCallback(async () => { + const handleLogin = useCallback(async (): Promise => { const isLoggedIn = await masa?.session.login(); if (isLoggedIn) { - await queryClient.invalidateQueries(queryKeySession); - await queryClient.invalidateQueries(queryKeySessionData); - await queryClient.refetchQueries(); + await clearSession(); } - }, [masa, queryKeySession, queryKeySessionData]); + }, [masa, clearSession]); - const logout = useCallback( - async (callback?: () => void) => { - await doLogout(); - callback?.(); - }, - [doLogout] - ); + useEffect(() => { + if (sessionData && sessionData.user.address !== walletAddress) { + void handleLogout(); + } + }, [sessionData, walletAddress, handleLogout]); - return { loggedIn, login, logout, status, isLoading, error }; + return { + isLoggedIn, + isSessionLoading: + isSessionCheckLoading || + isSessionCheckFetching || + isSessionDataLoading || + isSessionDataFetching, + handleLogin, + handleLogout, + status, + error, + }; }; diff --git a/src/provider/modules/soulnames/soulnames.ts b/src/provider/modules/soulnames/soulnames.ts index fe2ebc5b..edce4845 100644 --- a/src/provider/modules/soulnames/soulnames.ts +++ b/src/provider/modules/soulnames/soulnames.ts @@ -4,39 +4,49 @@ import { BigNumber } from 'ethers'; import { useMemo } from 'react'; export const useSoulnames = ( - masa: Masa | null, - walletAddress: string | undefined, - identity: - | { - identityId?: BigNumber | undefined; - address?: string | undefined; - } - | undefined + masa?: Masa, + walletAddress?: string, + identity?: { + identityId?: BigNumber | undefined; + address?: string | undefined; + } ): { soulnames: SoulNameDetails[] | undefined; status: string; - isLoading: boolean; + isSoulnamesLoading: boolean; + reloadSoulnames: () => void; error: unknown; } => { const queryKey: (string | undefined)[] = useMemo(() => { return ['soulnames', walletAddress, masa?.config.network]; }, [walletAddress, masa]); - console.log(queryKey); - const { data: soulnames, status, isLoading, + isFetching, + refetch: reloadSoulnames, error, - } = useQuery(queryKey, () => masa?.soulName.list(), { - enabled: - !!masa && - masa.config.network !== 'unknown' && - !!walletAddress && - !!identity?.identityId, - retry: false, - }); + } = useQuery( + queryKey, + () => masa?.soulName.list(), + { + enabled: !!masa && !!walletAddress && !!identity?.identityId, + retry: false, + onSuccess: (soulNames?: SoulNameDetails[]) => { + if (masa?.config.verbose) { + console.log({ soulNames, network: masa?.config.network }); + } + }, + } + ); - return { soulnames, status, isLoading, error }; + return { + soulnames, + isSoulnamesLoading: isLoading || isFetching, + reloadSoulnames, + status, + error, + }; }; diff --git a/src/provider/modules/wallet/wallet.ts b/src/provider/modules/wallet/wallet.ts index 291d27f5..2fb5eddb 100644 --- a/src/provider/modules/wallet/wallet.ts +++ b/src/provider/modules/wallet/wallet.ts @@ -1,50 +1,46 @@ import { useQuery } from 'react-query'; import { Masa } from '@masa-finance/masa-sdk'; -import { providers, Signer, Wallet } from 'ethers'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { Signer, Wallet } from 'ethers'; +import { useMemo } from 'react'; export const useWallet = ( - masa: Masa | null, - provider: Wallet | Signer | null + masa?: Masa, + provider?: Wallet | Signer ): { walletAddress: string | undefined; + isWalletLoading: boolean; + isConnected: boolean; status: string; - isLoading: boolean; error: unknown; - network: providers.Network | null; } => { - const queryKey: (string | boolean)[] = useMemo(() => { - return ['wallet', !!provider]; - }, [provider]); + const queryKey: (string | undefined)[] = useMemo(() => { + return ['wallet', masa?.config.network]; + }, [masa]); const { data: walletAddress, status, isLoading, + isFetching, error, - } = useQuery(queryKey, () => masa?.config.wallet.getAddress(), { - enabled: !!masa && !!provider, - retry: false, - }); - - const [network, setNetwork] = useState(null); - - const loadNetwork = useCallback(async (): Promise => { - if (provider) { - const newNetwork = await provider.provider?.getNetwork(); - setNetwork(newNetwork ?? null); + } = useQuery( + queryKey, + () => masa?.config.wallet.getAddress(), + { + enabled: !!masa && !!provider, + retry: false, } - }, [provider]); + ); - useEffect(() => { - void loadNetwork(); - }, [loadNetwork]); + const isConnected = useMemo(() => { + return !!walletAddress; + }, [walletAddress]); return { walletAddress: !provider ? undefined : walletAddress, + isWalletLoading: isLoading || isFetching, + isConnected, status, - isLoading, error, - network, }; }; diff --git a/src/provider/use-masa.tsx b/src/provider/use-masa.tsx index 6d215575..5b28f8c5 100644 --- a/src/provider/use-masa.tsx +++ b/src/provider/use-masa.tsx @@ -1,5 +1,6 @@ import { useContext } from 'react'; -import { MASA_CONTEXT, MasaShape } from './masa-context'; +import { MASA_CONTEXT } from './masa-context'; +import { MasaShape } from './masa-shape'; export const useMasa = (): MasaShape => { return useContext(MASA_CONTEXT); diff --git a/src/provider/use-metamask.ts b/src/provider/use-metamask.ts index 9ce3059c..a6bc0024 100644 --- a/src/provider/use-metamask.ts +++ b/src/provider/use-metamask.ts @@ -1,98 +1,102 @@ -import { ethers } from 'ethers'; +import { providers } from 'ethers'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { queryClient } from './masa-query-client'; import { useMasa } from './use-masa'; -export const getWeb3Provider = (): ethers.providers.Web3Provider | null => { +export const getWeb3Provider = (): providers.Web3Provider | undefined => { if ( typeof window !== 'undefined' && typeof window?.ethereum !== 'undefined' ) { - return new ethers.providers.Web3Provider( - window?.ethereum as unknown as ethers.providers.ExternalProvider + return new providers.Web3Provider( + window?.ethereum as unknown as providers.ExternalProvider ); } - return null; + return; }; export const useMetamask = ({ - disable, + disabled, }: { - disable?: boolean; + disabled?: boolean; }): { connect: () => void } => { - const [walletsConnected, setWalletsConnected] = useState([]); - const { setProvider, setMissingProvider, handleLogout } = useMasa(); + const [connectedAccounts, setConnectedAccounts] = useState([]); + const { + setProvider, + setIsProviderMissing, + handleLogout, + isConnected, + walletAddress, + } = useMasa(); + + // use metamask can only be used inside the scope of masa-react + // otherwise everything from useMasa is undefined + if (Object.keys(useMasa()).length < 1) { + throw new Error('useMetamask must be used inside the masa provider scope'); + } - const provider = useMemo((): ethers.providers.Web3Provider | null => { + const provider = useMemo((): providers.Web3Provider | undefined => { return getWeb3Provider(); }, []); useEffect(() => { - if (setMissingProvider) { - if (provider) { - setMissingProvider(false); - } else { - setMissingProvider(true); + setIsProviderMissing?.(!provider); + }, [provider, setIsProviderMissing]); + + const loadSignerFromProvider = useCallback( + async (provider: providers.Web3Provider) => { + await provider.send('eth_requestAccounts', []); + + const signer = provider.getSigner(); + if (signer) { + setProvider?.(signer); } - } - }, [provider, setMissingProvider]); + }, + [setProvider] + ); const connect = useCallback(async () => { - console.log('DISABLE', disable); - if (!disable) { - if (provider && window?.ethereum) { - await provider.send('eth_requestAccounts', []); - - const signer = provider.getSigner(); - if (signer && setProvider) { - setProvider(signer); - onConnect(); - } - } + console.log({ disabled }); + + if (!disabled && provider && window?.ethereum) { + await loadSignerFromProvider(provider); } - }, [setProvider, provider, disable]); + }, [provider, disabled, loadSignerFromProvider]); useEffect(() => { const connectWalletOnPageLoad = async (): Promise => { - if (localStorage.getItem('isWalletConnected') === 'true') { - try { - await connect(); - } catch (error) { - console.error('Connect failed!', error); + if (isConnected) return; + + try { + await connect(); + } catch (error) { + if (error instanceof Error) { + console.error('Connect failed!', error.message); } } }; void connectWalletOnPageLoad(); - }, [connect]); - - const onConnect = (): void => { - localStorage.setItem('isWalletConnected', 'true'); - }; + }, [isConnected, connect]); - const disconnect = useCallback(async () => { - await handleLogout?.(); - localStorage.setItem('isWalletConnected', 'false'); - - setProvider?.(null); - - await queryClient.invalidateQueries(['wallet']); - await queryClient.invalidateQueries(['session']); - }, [handleLogout, setProvider]); - - const detectWalletChange = useCallback(async () => { - const deduplicatedWallets = new Set(walletsConnected); - console.log({ deduplicatedWallets }); - - if (deduplicatedWallets.size > 1) { - console.log('DISCONNECTING, MORE THAN ONE WALLET'); - await disconnect(); + const disconnect = useCallback(async (): Promise => { + if (isConnected) { + await handleLogout?.(); } - }, [walletsConnected, disconnect]); + }, [isConnected, handleLogout]); useEffect(() => { + const detectWalletChange = async (): Promise => { + if ( + walletAddress && + connectedAccounts.length > 0 && + !connectedAccounts.includes(walletAddress) + ) { + await disconnect(); + } + }; + void detectWalletChange(); - }, [detectWalletChange]); + }, [connectedAccounts, disconnect, walletAddress]); useEffect(() => { if (typeof window !== 'undefined') { @@ -100,31 +104,18 @@ export const useMetamask = ({ 'accountsChanged', async (accounts: unknown): Promise => { const accountsArray = accounts as string[]; - if (accountsArray.length === 0) { - await disconnect(); - setWalletsConnected([]); - } else { - setWalletsConnected([...walletsConnected, ...accountsArray]); - } + setConnectedAccounts(accountsArray); } ); window?.ethereum?.on('networkChanged', async () => { const newProvider = getWeb3Provider(); if (newProvider) { - await newProvider.send('eth_requestAccounts', []); - - const signer = newProvider.getSigner(); - if (signer && setProvider) { - setProvider(signer); - onConnect(); - - await queryClient.invalidateQueries(['wallet']); - } + await loadSignerFromProvider(newProvider); } }); } - }, [handleLogout, disconnect, setProvider, walletsConnected]); + }, [loadSignerFromProvider, setConnectedAccounts]); return { connect }; }; diff --git a/stories/masa.stories.tsx b/stories/masa.stories.tsx index fbe7b517..c7f56a1c 100644 --- a/stories/masa.stories.tsx +++ b/stories/masa.stories.tsx @@ -33,7 +33,7 @@ const Component = (): JSX.Element => { }, [connect]); const loadCR = async (): Promise => { - await queryClient.invalidateQueries('wallet'); + await queryClient.invalidateQueries(['wallet']); }; const mintGreen = async (): Promise => {