diff --git a/explorer/src/components/Delegations/components/DelegateModal.tsx b/explorer/src/components/Delegations/components/DelegateModal.tsx index d4069687b16..e28384ec001 100644 --- a/explorer/src/components/Delegations/components/DelegateModal.tsx +++ b/explorer/src/components/Delegations/components/DelegateModal.tsx @@ -1,29 +1,25 @@ -import React, { useCallback, useContext, useState, useEffect, ChangeEvent } from 'react'; +import React, { useState, useEffect, ChangeEvent } from 'react'; import { Box, Typography, SxProps, TextField } from '@mui/material'; import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField'; import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'; -import { CurrencyDenom, FeeDetails, DecCoin, decimalToFloatApproximation, Coin } from '@nymproject/types'; - +import { CurrencyDenom, DecCoin } from '@nymproject/types'; import { SimpleModal } from './SimpleModal'; import { ModalListItem } from './ModalListItem'; - -import { TPoolOption, validateAmount } from '../utils'; - +import { Console, urls, validateAmount } from '../utils'; import { useChain } from '@cosmos-kit/react'; import { StdFee } from '@cosmjs/amino'; import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'; - import { uNYMtoNYM } from '../utils'; -import { ErrorModal } from './ErrorModal'; +import { DelegationModalProps } from './DelegationModal'; const MIN_AMOUNT_TO_DELEGATE = 10; const MIXNET_CONTRACT_ADDRESS = 'n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr'; -const sandboxContractAddress = 'n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav'; +// const sandboxContractAddress = 'n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav'; export const DelegateModal: FCWithChildren<{ open: boolean; onClose: () => void; - onOk?: () => void; + onOk?: (delegationModalProps: DelegationModalProps) => void; identityKey?: string; onIdentityKeyChanged?: (identityKey: string) => void; onAmountChanged?: (amount: string) => void; @@ -61,8 +57,10 @@ export const DelegateModal: FCWithChildren<{ const [errorIdentityKey, setErrorIdentityKey] = useState(); const [mixIdError, setMixIdError] = useState(); const [cosmWasmSignerClient, setCosmWasmSignerClient] = useState(); - - // const { fee, getFee, resetFeeState, feeError } = useGetFee(); + const [balance, setBalance] = useState<{ + status: 'loading' | 'success'; + data?: string; + }>({ status: 'loading', data: undefined }); const { username, @@ -77,11 +75,6 @@ export const DelegateModal: FCWithChildren<{ estimateFee, } = useChain('nyx'); - const [balance, setBalance] = useState<{ - status: 'loading' | 'success'; - data?: string; - }>({ status: 'loading', data: undefined }); - useEffect(() => { const getClient = async () => { await getSigningCosmWasmClient() @@ -95,15 +88,14 @@ export const DelegateModal: FCWithChildren<{ isWalletConnected && getClient(); }, [isWalletConnected]); - useEffect(() => { - const getBalance = async (walletAddress: string) => { - const account = await getCosmWasmClient(); - const uNYMBalance = await account.getBalance(walletAddress, 'unym'); - const NYMBalance = uNYMtoNYM(uNYMBalance.amount).asString(); - - setBalance({ status: 'success', data: NYMBalance }); - }; + const getBalance = async (walletAddress: string) => { + const account = await getCosmWasmClient(); + const uNYMBalance = await account.getBalance(walletAddress, 'unym'); + const NYMBalance = uNYMtoNYM(uNYMBalance.amount).asString(); + setBalance({ status: 'success', data: NYMBalance }); + }; + useEffect(() => { if (address) { getBalance(address); } @@ -182,14 +174,32 @@ export const DelegateModal: FCWithChildren<{ const fee = { gas: '1000000', amount: [{ amount: '25000', denom: 'unym' }] }; if (mixId && amount && onOk && cosmWasmSignerClient) { - console.log('trying to delegate :>> '); - console.log('balance.data :>> ', balance.data); - onOk(); - console.log('amount :>> ', amount); - console.log('fee :>> ', fee); - await delegateToMixnode({ mixId }, fee, memo, [amount]) - .then((res) => console.log('res :>> ', res)) - .catch((err) => console.log('err :>> ', err)); + onOk({ + status: 'loading', + action: 'delegate', + }); + try { + await delegateToMixnode({ mixId }, fee, memo, [amount]).then((res) => { + console.log('res :>> ', res); + }); + const tx = await delegateToMixnode({ mixId }, fee, memo, [amount]); + + onOk({ + status: 'success', + action: 'delegate', + message: 'This operation can take up to one hour to process', + transactions: [ + { url: `${urls('MAINNET').blockExplorer}/transaction/${tx.transactionHash}`, hash: tx.transactionHash }, + ], + }); + } catch (e) { + Console.error('Failed to addDelegation', e); + onOk({ + status: 'error', + action: 'delegate', + message: (e as Error).message, + }); + } } }; @@ -218,39 +228,6 @@ export const DelegateModal: FCWithChildren<{ validate(); }, [amount, identityKey, mixId]); - // if (fee) { - // return ( - // - // {balance.data && fee?.amount?.amount && ( - // - // - // - // )} - // - // - // - // ); - // } - - // if (feeError) { - // return ( - // - // ); - // } - return ( - {/* ); diff --git a/explorer/src/components/Delegations/components/DelegationModal.tsx b/explorer/src/components/Delegations/components/DelegationModal.tsx new file mode 100644 index 00000000000..54343ed5398 --- /dev/null +++ b/explorer/src/components/Delegations/components/DelegationModal.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { Typography, SxProps, Stack } from '@mui/material'; +import { Link } from '@nymproject/react/link/Link'; +import { LoadingModal } from './LoadingModal'; +import { ConfirmationModal } from './ConfirmationModal'; +import { ErrorModal } from './ErrorModal'; + +export type ActionType = 'delegate' | 'undelegate' | 'redeem' | 'redeem-all' | 'compound'; + +const actionToHeader = (action: ActionType): string => { + // eslint-disable-next-line default-case + switch (action) { + case 'redeem': + return 'Rewards redeemed successfully'; + case 'redeem-all': + return 'All rewards redeemed successfully'; + case 'delegate': + return 'Delegation successful'; + case 'undelegate': + return 'Undelegation successful'; + case 'compound': + return 'Rewards compounded successfully'; + default: + throw new Error('Unknown type'); + } +}; + +export type DelegationModalProps = { + status: 'loading' | 'success' | 'error'; + action: ActionType; + message?: string; + transactions?: { + url: string; + hash: string; + }[]; +}; + +export const DelegationModal: FCWithChildren< + DelegationModalProps & { + open: boolean; + onClose: () => void; + sx?: SxProps; + backdropProps?: object; + children?: React.ReactNode; + } +> = ({ status, action, message, transactions, open, onClose, children, sx, backdropProps }) => { + if (status === 'loading') return ; + + if (status === 'error') { + return ( + + {children} + + ); + } + + return ( + {})} + title={actionToHeader(action)} + confirmButton="Done" + > + + {message && {message}} + {transactions?.length === 1 && ( + + )} + {transactions && transactions.length > 1 && ( + + View the transactions on blockchain: + {transactions.map(({ url, hash }) => ( + + ))} + + )} + + + ); +}; diff --git a/explorer/src/components/Delegations/components/LoadingModal.tsx b/explorer/src/components/Delegations/components/LoadingModal.tsx new file mode 100644 index 00000000000..07367d117f5 --- /dev/null +++ b/explorer/src/components/Delegations/components/LoadingModal.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { Box, CircularProgress, Modal, Stack, Typography, SxProps } from '@mui/material'; + +const modalStyle: SxProps = { + position: 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + width: 500, + bgcolor: 'background.paper', + boxShadow: 24, + borderRadius: '16px', + p: 4, +}; + +export const LoadingModal: FCWithChildren<{ + text?: string; + sx?: SxProps; + backdropProps?: object; +}> = ({ sx, backdropProps, text = 'Please wait...' }) => ( + + `1px solid grey`, ...modalStyle, ...sx }} textAlign="center"> + + + {text} + + + +); diff --git a/explorer/src/components/Delegations/utils/common.ts b/explorer/src/components/Delegations/utils/common.ts index c738df4d2a3..b07c21e724e 100644 --- a/explorer/src/components/Delegations/utils/common.ts +++ b/explorer/src/components/Delegations/utils/common.ts @@ -6,6 +6,7 @@ import { add, format, fromUnixTime } from 'date-fns'; import { DecCoin, isValidRawCoin, MixNodeCostParams } from '@nymproject/types'; import { getCurrentInterval, getDefaultMixnodeCostParams, getLockedCoins, getSpendableCoins } from '../requests'; import { Console } from './console'; +import { Network } from '../types'; export type TPoolOption = 'balance' | 'locked'; @@ -253,3 +254,15 @@ export const getIntervalAsDate = async () => { return { nextEpoch, nextInterval }; }; + +export const urls = (networkName?: Network) => + networkName === 'MAINNET' + ? { + mixnetExplorer: 'https://mixnet.explorers.guru/', + blockExplorer: 'https://blocks.nymtech.net', + networkExplorer: 'https://explorer.nymtech.net', + } + : { + blockExplorer: `https://${networkName}-blocks.nymtech.net`, + networkExplorer: `https://${networkName}-explorer.nymtech.net`, + }; diff --git a/explorer/src/components/TableToolbar.tsx b/explorer/src/components/TableToolbar.tsx index bdd179ac7c0..286b740f0dd 100644 --- a/explorer/src/components/TableToolbar.tsx +++ b/explorer/src/components/TableToolbar.tsx @@ -7,7 +7,7 @@ import { DelegateModal } from './Delegations/components/DelegateModal'; import { ChainProvider } from '@cosmos-kit/react'; import { assets, chains } from 'chain-registry'; import { wallets as keplr } from '@cosmos-kit/keplr'; -import { CurrencyDenom, FeeDetails, DecCoin, decimalToFloatApproximation, Coin } from '@nymproject/types'; +import { DelegationModal } from './Delegations/components/DelegationModal'; const fieldsHeight = '42.25px'; @@ -45,9 +45,9 @@ export const TableToolbar: FCWithChildren = ({ const [confirmationModalProps, setConfirmationModalProps] = useState(); const assetsFixedUp = useMemo(() => { - const nyx = assets.find((a) => a.chain_name === 'sandbox'); + const nyx = assets.find((a) => a.chain_name === 'nyx'); if (nyx) { - const nyxCoin = nyx.assets.find((a) => a.name === 'sandbox'); + const nyxCoin = nyx.assets.find((a) => a.name === 'nyx'); if (nyxCoin) { nyxCoin.coingecko_id = 'nyx'; } @@ -72,42 +72,10 @@ export const TableToolbar: FCWithChildren = ({ return chains; }, [chains]); - const handleNewDelegation = () => { - // setConfirmationModalProps({ - // status: 'loading', - // action: 'delegate', - // }); + const handleNewDelegation = (delegationModalProps: DelegationModalProps) => { setShowNewDelegationModal(false); - // setCurrentDelegationListActionItem(undefined); - // try { - // const tx = await addDelegation( - // { - // mix_id, - // amount, - // }, - // tokenPool, - // fee, - // ); - - // const balances = await getAllBalances(); - - // setConfirmationModalProps({ - // status: 'success', - // action: 'delegate', - // message: 'This operation can take up to one hour to process', - // ...balances, - // transactions: [ - // { url: `${urls(network).blockExplorer}/transaction/${tx.transaction_hash}`, hash: tx.transaction_hash }, - // ], - // }); - // } catch (e) { - // Console.error('Failed to addDelegation', e); - // setConfirmationModalProps({ - // status: 'error', - // action: 'delegate', - // message: (e as Error).message, - // }); - // } + console.log('res In Toolbar:>> ', delegationModalProps); + setConfirmationModalProps(delegationModalProps); }; const isMobile = useIsMobile(); @@ -200,12 +168,22 @@ export const TableToolbar: FCWithChildren = ({ header="Delegate" buttonText="Delegate stake" denom={'nym'} // clientDetails?.display_mix_denom || 'nym'} - onOk={handleNewDelegation} + onOk={(delegationModalProps: DelegationModalProps) => handleNewDelegation(delegationModalProps)} // accountBalance={balance?.printable_balance} - {...confirmationModalProps} /> )} + + {confirmationModalProps && ( + { + setConfirmationModalProps(undefined); + // await fetchBalance(); + }} + /> + )} ); };