create wallet context + wallet components

create wallet context using cosmoskit + include nymclient + create wallet components
This commit is contained in:
fmtabbara
2024-03-12 10:16:06 +00:00
parent 28a0c3eb17
commit f762062f9f
7 changed files with 211 additions and 79 deletions
@@ -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 <Button onClick={() => connect()}>{`Connecting ${wallet?.prettyName}`}</Button>;
}
if (isWalletConnected) {
return (
<Stack direction="row" spacing={1}>
{!isMobile && (
<Box display="flex" alignItems="center" gap={1}>
<TokenSVG />
<Typography variant="body1" fontWeight={600}>
{balance.data} NYM
</Typography>
</Box>
)}{' '}
<Box display="flex" alignItems="center" gap={1}>
<ElipsSVG />
<Typography variant="body1" fontWeight={600}>
{trimAddress(address, 7)}
</Typography>
</Box>
<IconButton
size="small"
onClick={async () => {
await disconnect();
}}
>
<CloseIcon fontSize="small" sx={{ color: 'white' }} />
</IconButton>
</Stack>
);
}
return (
<Button variant="outlined" onClick={() => connect()}>
Connect {isMobile ? '' : ' Wallet'}
</Button>
);
};
return <Box>{getGlobalbutton()}</Box>;
};
@@ -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 (
<Stack direction="row" spacing={1}>
<WalletBalance />
<WalletAddress />
<IconButton
size="small"
onClick={async () => {
await disconnectWallet();
}}
>
<CloseIcon fontSize="small" sx={{ color: 'white' }} />
</IconButton>
</Stack>
);
}
return (
<Button
variant="outlined"
onClick={() => connectWallet()}
disabled={isWalletConnecting}
endIcon={isWalletConnecting && <CircularProgress size={14} color="inherit" />}
>
Connect {isMobile ? '' : ' Wallet'}
</Button>
);
};
@@ -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 (
<Box display="flex" alignItems="center" gap={0.5}>
<ElipsSVG />
<Typography variant="body1" fontWeight={600}>
{displayAddress}
</Typography>
</Box>
);
};
@@ -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 (
<Box display="flex" alignItems="center" gap={1}>
<TokenSVG />
<Typography variant="body1" fontWeight={600}>
{balance.data} NYM
</Typography>
</Box>
);
};
+2 -1
View File
@@ -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 }) => (
<ChainProvider chains={chains} assetLists={assets} wallets={[...keplr, ...ledger]}>
<ChainProvider chains={chains} assetLists={assets} wallets={[...keplr, ...ledger, ...cosmosstation]}>
{children}
</ChainProvider>
);
+90
View File
@@ -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<void>;
disconnectWallet: () => Promise<void>;
}
export const WalletContext = createContext<WalletState>({
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<WalletState['balance']>({ 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 <WalletContext.Provider value={contextValue}>{children}</WalletContext.Provider>;
};
export const useWalletContext = () => useContext(WalletContext);
+31
View File
@@ -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<MixnetClient>();
const [nymQueryClient, setNymQueryClient] = useState<MixnetQueryClient>();
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 };
};