Skip to content

Commit

Permalink
Connect delegate modal to keplr balance
Browse files Browse the repository at this point in the history
  • Loading branch information
yanok87 committed Nov 7, 2023
1 parent 42b5472 commit 23d08b9
Show file tree
Hide file tree
Showing 41 changed files with 1,671 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { useCallback, useContext, useState } from 'react';
import React, { useCallback, useContext, useState, useEffect } from 'react';
import { Box, Typography, SxProps } from '@mui/material';
import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField';
import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField';
import { CurrencyDenom, FeeDetails, DecCoin, decimalToFloatApproximation } from '@nymproject/types';
// import { Console } from 'src/utils/console';
// import { useGetFee } from 'src/hooks/useGetFee';
import { useGetFee } from '../hooks/useGetFee';
// import { simulateDelegateToMixnode, simulateVestingDelegateToMixnode, tryConvertIdentityToMixId } from 'src/requests';
import { debounce } from 'lodash';
import { SimpleModal } from './SimpleModal';
Expand All @@ -13,12 +13,14 @@ import { ModalListItem } from './ModalListItem';
// import { SimpleModal } from '../Modals/SimpleModal';
// import { ModalListItem } from '../Modals/ModalListItem';
// import { checkTokenBalance, validateAmount, validateKey } from '../../utils';
// import { TokenPoolSelector, TPoolOption } from '../TokenPoolSelector';
// import { TokenPoolSelector, TPoolOption } from './TokenPoolSelector';
// import { ConfirmTx } from '../ConfirmTX';

// import { getMixnodeStakeSaturation } from '../../requests';
// import { ErrorModal } from '../Modals/ErrorModal';
// import { BalanceWarning } from '../FeeWarning';
import { useChain } from '@cosmos-kit/react';
import { uNYMtoNYM } from '../../ConnectKeplrWallet';

const MIN_AMOUNT_TO_DELEGATE = 10;

Expand All @@ -30,7 +32,7 @@ export const DelegateModal: FCWithChildren<{
identityKey: string,
amount: DecCoin,
// tokenPool: TPoolOption,
// fee?: FeeDetails,
fee?: FeeDetails,
) => Promise<void>;
identityKey?: string;
onIdentityKeyChanged?: (identityKey: string) => void;
Expand Down Expand Up @@ -76,7 +78,30 @@ export const DelegateModal: FCWithChildren<{
const [errorIdentityKey, setErrorIdentityKey] = useState<string>();
const [mixIdError, setMixIdError] = useState<string>();

// const { fee, getFee, resetFeeState, feeError } = useGetFee();
const { fee, getFee, resetFeeState, feeError } = useGetFee();

const { username, connect, disconnect, wallet, openView, address, getCosmWasmClient, isWalletConnected } =
useChain('nyx');
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).asString();

setBalance({ status: 'success', data: NYMBalance });
};

if (address) {
getBalance(address);
}
}, [address, getCosmWasmClient]);
// const { userBalance } = useContext(AppContext);

// const handleCheckStakeSaturation = async (newMixId: number) => {
Expand Down Expand Up @@ -141,7 +166,7 @@ export const DelegateModal: FCWithChildren<{

const handleOk = async () => {
if (onOk && amount && identityKey && mixId) {
onOk(mixId, identityKey, { amount, denom }); //tokenPool, fee);
onOk(mixId, identityKey, { amount, denom }, fee); //tokenPool, fee);
}
};

Expand Down Expand Up @@ -293,7 +318,7 @@ export const DelegateModal: FCWithChildren<{
/>
</Box>
<Box sx={{ mt: 3 }}>
<ModalListItem label="Account balance" value={accountBalance?.toUpperCase()} divider fontWeight={600} />
<ModalListItem label="Account balance" value={balance.data} divider fontWeight={600} />
</Box>

<ModalListItem label="Rewards payout interval" value={rewardInterval} hidden divider />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { Box, Button, Modal, SxProps, Typography } from '@mui/material';
import { modalStyle } from '../../../../nym-wallet/src/components/Modals/styles';
import { modalStyle } from '../../../../../nym-wallet/src/components/Modals/styles';

export const ErrorModal: FCWithChildren<{
open: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { Box, Stack, SxProps, Typography, TypographyProps } from '@mui/material';
import { ModalDivider } from '../../../../nym-wallet/src/components/Modals/ModalDivider';
import { ModalDivider } from '../../../../../nym-wallet/src/components/Modals/ModalDivider';

export const ModalListItem: FCWithChildren<{
label: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Box, Button, Modal, Stack, SxProps, Typography } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import ErrorOutline from '@mui/icons-material/ErrorOutline';
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
import { modalStyle } from '../../../../nym-wallet/src/components/Modals/styles';
import { modalStyle } from '../../../../../nym-wallet/src/components/Modals/styles';

import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';

Expand Down
4 changes: 4 additions & 0 deletions explorer/src/components/Delegations/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const config = {
IS_DEV_MODE: process.env.NODE_ENV === 'development',
LOG_TAURI_OPERATIONS: process.env.NODE_ENV === 'development',
};
170 changes: 170 additions & 0 deletions explorer/src/components/Delegations/context/accounts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import React, { createContext, Dispatch, SetStateAction, useContext, useEffect, useMemo, useState } from 'react';
import { AccountEntry } from '@nymproject/types';
import { addAccount as addAccountRequest, renameAccount, showMnemonicForAccount } from '../requests';
import { useSnackbar } from 'notistack';
import { AppContext } from './main';

type TAccounts = {
accounts?: AccountEntry[];
selectedAccount?: AccountEntry;
accountToEdit?: AccountEntry;
dialogToDisplay?: TAccountsDialog;
isLoading: boolean;
error?: string;
accountMnemonic: TAccountMnemonic;
setError: Dispatch<SetStateAction<string | undefined>>;
setAccountMnemonic: Dispatch<SetStateAction<TAccountMnemonic>>;
handleAddAccount: (data: { accountName: string; mnemonic: string; password: string }) => void;
setDialogToDisplay: (dialog?: TAccountsDialog) => void;
handleSelectAccount: (data: { accountName: string; password: string }) => Promise<boolean>;
handleAccountToEdit: (accountId: string | undefined) => void;
handleEditAccount: ({
account,
newAccountName,
password,
}: {
account: AccountEntry;
newAccountName: string;
password: string;
}) => Promise<void>;
handleImportAccount: (account: AccountEntry) => void;
handleGetAccountMnemonic: (data: { password: string; accountName: string }) => void;
};

export type TAccountsDialog = 'Accounts' | 'Add' | 'Edit' | 'Import' | 'Mnemonic';
export type TAccountMnemonic = { value?: string; accountName?: string };

export const AccountsContext = createContext({} as TAccounts);

export const AccountsProvider: FCWithChildren = ({ children }) => {
const [accounts, setAccounts] = useState<AccountEntry[]>([]);
const [selectedAccount, setSelectedAccount] = useState<AccountEntry>();
const [accountToEdit, setAccountToEdit] = useState<AccountEntry>();
const [dialogToDisplay, setDialogToDisplay] = useState<TAccountsDialog>();
const [accountMnemonic, setAccountMnemonic] = useState<TAccountMnemonic>({
value: undefined,
accountName: undefined,
});
const [error, setError] = useState<string>();
const [isLoading, setIsLoading] = useState(false);
const { onAccountChange, storedAccounts } = useContext(AppContext);
const { enqueueSnackbar } = useSnackbar();

const handleAddAccount = async ({
accountName,
mnemonic,
password,
}: {
accountName: string;
mnemonic: string;
password: string;
}) => {
setIsLoading(true);
try {
const newAccount = await addAccountRequest({
accountName,
mnemonic,
password,
});
setAccounts((accs) => [...accs, newAccount]);
enqueueSnackbar('New account created', { variant: 'success' });
} catch (e) {
setError(`Error adding account: ${e}`);
throw new Error();
} finally {
setIsLoading(false);
}
};
const handleEditAccount = async ({
account,
newAccountName,
password,
}: {
account: AccountEntry;
newAccountName: string;
password: string;
}) => {
setIsLoading(true);
try {
await renameAccount({ accountName: account.id, newAccountName, password });
setAccounts((accs) =>
accs?.map((acc) => (acc.address === account.address ? { ...acc, id: newAccountName } : acc)),
);
if (selectedAccount?.id === account.id) {
setSelectedAccount({ ...selectedAccount, id: newAccountName });
}
setDialogToDisplay('Accounts');
} catch (e) {
throw new Error(`Error editing account: ${e}`);
} finally {
setIsLoading(false);
}
};

const handleImportAccount = (account: AccountEntry) => setAccounts((accs) => [...(accs ? [...accs] : []), account]);

const handleAccountToEdit = (accountName: string | undefined) =>
setAccountToEdit(accounts?.find((acc) => acc.id === accountName));

const handleSelectAccount = async ({ accountName, password }: { accountName: string; password: string }) => {
try {
await onAccountChange({ accountId: accountName, password });
const match = accounts?.find((acc) => acc.id === accountName);
setSelectedAccount(match);
return true;
} catch (e) {
setError('Error switching account. Please check your password');
return false;
}
};

const handleGetAccountMnemonic = async ({ password, accountName }: { password: string; accountName: string }) => {
try {
setIsLoading(true);
const mnemonic = await showMnemonicForAccount({ password, accountName });
setAccountMnemonic({ value: mnemonic, accountName });
} catch (e) {
setError(e as string);
} finally {
setIsLoading(false);
}
};

useEffect(() => {
if (storedAccounts) {
setAccounts(storedAccounts);
}

if (storedAccounts && !selectedAccount) {
setSelectedAccount(storedAccounts[0]);
}
}, [storedAccounts]);

return (
<AccountsContext.Provider
value={useMemo(
() => ({
error,
setError,
accounts,
selectedAccount,
accountToEdit,
dialogToDisplay,
accountMnemonic,
setDialogToDisplay,
setAccountMnemonic,
isLoading,
handleAddAccount,
handleEditAccount,
handleAccountToEdit,
handleSelectAccount,
handleImportAccount,
handleGetAccountMnemonic,
}),
[accounts, selectedAccount, accountToEdit, dialogToDisplay, isLoading, error, accountMnemonic],
)}
>
{children}
</AccountsContext.Provider>
);
};
59 changes: 59 additions & 0 deletions explorer/src/components/Delegations/context/buy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { sign } from '../requests';
// import { Console } from 'src/utils/console';
// import { AppContext } from './main';

export type TBuyContext = {
loading: boolean;
error?: string;
signMessage: (message: string) => Promise<string | undefined>;
refresh: () => Promise<void>;
};

export const BuyContext = createContext<TBuyContext>({
loading: false,
signMessage: async () => '',
refresh: async () => undefined,
});

export const BuyContextProvider: FCWithChildren = ({ children }): JSX.Element => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string>();

const refresh = useCallback(async () => {
setError(undefined);
}, []);

useEffect(() => {
refresh();
}, [refresh]);

const signMessage = async (message: string) => {
let signature;
setLoading(true);
try {
signature = await sign(message);
} catch (e: any) {
// Console.log(`Sign message operation failed: ${e}`);
console.log('`Sign message operation failed: ${e}` :>> ', `Sign message operation failed: ${e}`);
setError(`Sign message operation failed: ${e}`);
} finally {
setLoading(false);
}
return signature;
};

const memoizedValue = useMemo(
() => ({
loading,
error,
refresh,
signMessage,
}),
[loading, error],
);

return <BuyContext.Provider value={memoizedValue}>{children}</BuyContext.Provider>;
};

export const useBuyContext = () => useContext<TBuyContext>(BuyContext);
3 changes: 3 additions & 0 deletions explorer/src/components/Delegations/context/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './main';
export * from './accounts';
export * from './buy';
Loading

0 comments on commit 23d08b9

Please sign in to comment.