From f762062f9f815a2dbf3385a596a71fcfdcacb031 Mon Sep 17 00:00:00 2001 From: fmtabbara Date: Tue, 12 Mar 2024 10:16:06 +0000 Subject: [PATCH] create wallet context + wallet components create wallet context using cosmoskit + include nymclient + create wallet components --- .../src/components/ConnectKeplrWallet.tsx | 78 ---------------- .../components/Wallet/ConnectKeplrWallet.tsx | 43 +++++++++ .../src/components/Wallet/WalletAddress.tsx | 20 +++++ .../src/components/Wallet/WalletBalance.tsx | 25 ++++++ explorer/src/context/cosmos-kit.tsx | 3 +- explorer/src/context/wallet.tsx | 90 +++++++++++++++++++ explorer/src/hooks/useNymClient.tsx | 31 +++++++ 7 files changed, 211 insertions(+), 79 deletions(-) delete mode 100644 explorer/src/components/ConnectKeplrWallet.tsx create mode 100644 explorer/src/components/Wallet/ConnectKeplrWallet.tsx create mode 100644 explorer/src/components/Wallet/WalletAddress.tsx create mode 100644 explorer/src/components/Wallet/WalletBalance.tsx create mode 100644 explorer/src/context/wallet.tsx create mode 100644 explorer/src/hooks/useNymClient.tsx diff --git a/explorer/src/components/ConnectKeplrWallet.tsx b/explorer/src/components/ConnectKeplrWallet.tsx deleted file mode 100644 index 428c725eb2..0000000000 --- a/explorer/src/components/ConnectKeplrWallet.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useChain } from '@cosmos-kit/react'; -import { Box, Button, Typography, IconButton, Stack } from '@mui/material'; -import CloseIcon from '@mui/icons-material/Close'; -import { TokenSVG } from '../icons/TokenSVG'; -import { ElipsSVG } from '../icons/ElipsSVG'; -import { trimAddress } from '../utils'; -import { unymToNym } from '../utils/currency'; -import { useIsMobile } from '../hooks/useIsMobile'; - -export const ConnectKeplrWallet = () => { - const { connect, disconnect, wallet, address, getCosmWasmClient, isWalletConnected, isWalletConnecting } = - useChain('nyx'); - const isMobile = useIsMobile(1200); - - const [balance, setBalance] = useState<{ - status: 'loading' | 'success'; - data?: string; - }>({ status: 'loading', data: undefined }); - - useEffect(() => { - const getBalance = async (walletAddress: string) => { - setBalance({ status: 'loading', data: undefined }); - - const account = await getCosmWasmClient(); - const uNYMBalance = await account.getBalance(walletAddress, 'unym'); - const NYMBalance = unymToNym(uNYMBalance.amount); - - setBalance({ status: 'success', data: NYMBalance }); - }; - - if (address) { - getBalance(address); - } - }, [address, getCosmWasmClient]); - - const getGlobalbutton = () => { - if (isWalletConnecting) { - return ; - } - if (isWalletConnected) { - return ( - - {!isMobile && ( - - - - {balance.data} NYM - - - )}{' '} - - - - {trimAddress(address, 7)} - - - { - await disconnect(); - }} - > - - - - ); - } - - return ( - - ); - }; - - return {getGlobalbutton()}; -}; diff --git a/explorer/src/components/Wallet/ConnectKeplrWallet.tsx b/explorer/src/components/Wallet/ConnectKeplrWallet.tsx new file mode 100644 index 0000000000..ff5d2691c6 --- /dev/null +++ b/explorer/src/components/Wallet/ConnectKeplrWallet.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { Button, IconButton, Stack, CircularProgress } from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; +import { useIsMobile } from '@src/hooks/useIsMobile'; +import { useWalletContext } from '@src/context/wallet'; +import { WalletAddress, WalletBalance } from '@src/components/Wallet'; + +export const ConnectKeplrWallet = () => { + const { connectWallet, disconnectWallet, isWalletConnected, isWalletConnecting } = useWalletContext(); + const isMobile = useIsMobile(1200); + + if (!connectWallet || !disconnectWallet) { + return null; + } + + if (isWalletConnected) { + return ( + + + + { + await disconnectWallet(); + }} + > + + + + ); + } + + return ( + + ); +}; diff --git a/explorer/src/components/Wallet/WalletAddress.tsx b/explorer/src/components/Wallet/WalletAddress.tsx new file mode 100644 index 0000000000..8be6c9f291 --- /dev/null +++ b/explorer/src/components/Wallet/WalletAddress.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { Box, Typography } from '@mui/material'; +import { ElipsSVG } from '@src/icons/ElipsSVG'; +import { trimAddress } from '@src/utils'; +import { useWalletContext } from '@src/context/wallet'; + +export const WalletAddress = () => { + const { address } = useWalletContext(); + + const displayAddress = trimAddress(address, 7); + + return ( + + + + {displayAddress} + + + ); +}; diff --git a/explorer/src/components/Wallet/WalletBalance.tsx b/explorer/src/components/Wallet/WalletBalance.tsx new file mode 100644 index 0000000000..f94935ac23 --- /dev/null +++ b/explorer/src/components/Wallet/WalletBalance.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Box, Typography } from '@mui/material'; +import { useWalletContext } from '@src/context/wallet'; +import { useIsMobile } from '@src/hooks'; +import { TokenSVG } from '@src/icons/TokenSVG'; + +export const WalletBalance = () => { + const { balance } = useWalletContext(); + const isMobile = useIsMobile(1200); + + const showBalance = !isMobile && balance.status === 'success'; + + if (!showBalance) { + return null; + } + + return ( + + + + {balance.data} NYM + + + ); +}; diff --git a/explorer/src/context/cosmos-kit.tsx b/explorer/src/context/cosmos-kit.tsx index e696106e1a..163332bf19 100644 --- a/explorer/src/context/cosmos-kit.tsx +++ b/explorer/src/context/cosmos-kit.tsx @@ -2,10 +2,11 @@ import React from 'react'; import { ChainProvider } from '@cosmos-kit/react'; import { wallets as keplr } from '@cosmos-kit/keplr'; import { wallets as ledger } from '@cosmos-kit/ledger'; +import { wallets as cosmosstation } from '@cosmos-kit/cosmostation'; import { assets, chains } from 'chain-registry'; const CosmosKitProvider = ({ children }: { children: React.ReactNode }) => ( - + {children} ); diff --git a/explorer/src/context/wallet.tsx b/explorer/src/context/wallet.tsx new file mode 100644 index 0000000000..bb8432f284 --- /dev/null +++ b/explorer/src/context/wallet.tsx @@ -0,0 +1,90 @@ +import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; +import { useChain } from '@cosmos-kit/react'; +import { Wallet } from '@cosmos-kit/core'; + +import { unymToNym } from '@src/utils/currency'; +import { useNymClient } from '@src/hooks'; +import { MixnetClient, MixnetQueryClient } from '@nymproject/contract-clients/Mixnet.client'; + +interface WalletState { + balance: { status: 'loading' | 'success'; data?: string }; + address?: string; + isWalletConnected: boolean; + isWalletConnecting: boolean; + wallet?: Wallet; + nymClient?: MixnetClient; + nymQueryClient?: MixnetQueryClient; + connectWallet: () => Promise; + disconnectWallet: () => Promise; +} + +export const WalletContext = createContext({ + address: undefined, + balance: { status: 'loading', data: undefined }, + isWalletConnected: false, + isWalletConnecting: false, + nymClient: undefined, + nymQueryClient: undefined, + connectWallet: async () => { + throw new Error('Please connect your wallet'); + }, + disconnectWallet: async () => { + throw new Error('Please connect your wallet'); + }, +}); + +export const WalletProvider = ({ children }: { children: React.ReactNode }) => { + const [balance, setBalance] = useState({ status: 'loading', data: undefined }); + + const { connect, disconnect, wallet, address, isWalletConnected, isWalletConnecting, getCosmWasmClient } = + useChain('nyx'); + + const { nymClient, nymQueryClient } = useNymClient(address); + + const getBalance = async (walletAddress: string) => { + const account = await getCosmWasmClient(); + const uNYMBalance = await account.getBalance(walletAddress, 'unym'); + const NYMBalance = unymToNym(uNYMBalance.amount); + + return NYMBalance; + }; + + const init = async (walletAddress: string) => { + const walletBalance = await getBalance(walletAddress); + setBalance({ status: 'success', data: walletBalance }); + }; + + useEffect(() => { + if (isWalletConnected && address) { + init(address); + } + }, [address, isWalletConnected]); + + const handleConnectWallet = async () => { + await connect(); + }; + + const handleDisconnectWallet = async () => { + await disconnect(); + setBalance({ status: 'loading', data: undefined }); + }; + + const contextValue: WalletState = useMemo( + () => ({ + address, + balance, + wallet, + isWalletConnected, + isWalletConnecting, + nymClient, + nymQueryClient, + connectWallet: handleConnectWallet, + disconnectWallet: handleDisconnectWallet, + }), + [address, balance, wallet, isWalletConnected, isWalletConnecting, nymClient, nymQueryClient], + ); + + return {children}; +}; + +export const useWalletContext = () => useContext(WalletContext); diff --git a/explorer/src/hooks/useNymClient.tsx b/explorer/src/hooks/useNymClient.tsx new file mode 100644 index 0000000000..b9c5d92b67 --- /dev/null +++ b/explorer/src/hooks/useNymClient.tsx @@ -0,0 +1,31 @@ +import { useEffect, useState } from 'react'; +import { useChain } from '@cosmos-kit/react'; +import { contracts } from '@nymproject/contract-clients'; +import { MixnetClient, MixnetQueryClient } from '@nymproject/contract-clients/Mixnet.client'; +import { NYM_MIXNET_CONTRACT } from '@src/api/constants'; + +export const useNymClient = (address?: string) => { + const [nymClient, setNymClient] = useState(); + const [nymQueryClient, setNymQueryClient] = useState(); + + const { getCosmWasmClient, getSigningCosmWasmClient } = useChain('nyx'); + + useEffect(() => { + if (address) { + const init = async () => { + const cosmWasmSigningClient = await getSigningCosmWasmClient(); + const cosmWasmClient = await getCosmWasmClient(); + + const client = new contracts.Mixnet.MixnetClient(cosmWasmSigningClient as any, address, NYM_MIXNET_CONTRACT); + const queryClient = new contracts.Mixnet.MixnetQueryClient(cosmWasmClient as any, NYM_MIXNET_CONTRACT); + + setNymClient(client); + setNymQueryClient(queryClient); + }; + + init(); + } + }, [address, getCosmWasmClient, getSigningCosmWasmClient]); + + return { nymClient, nymQueryClient }; +};