Merge pull request #3598 from nymtech/bug-fix/balanceVSfeeWarning
TX warning when fee > balance
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import { CurrencyDenom, TNodeType } from '@nymproject/types';
|
||||
import { ConfirmTx } from 'src/components/ConfirmTX';
|
||||
@@ -9,6 +9,8 @@ import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { GatewayAmount, GatewayData, Signature } from 'src/pages/bonding/types';
|
||||
import { simulateBondGateway, simulateVestingBondGateway } from 'src/requests';
|
||||
import { TBondGatewayArgs } from 'src/types';
|
||||
import { BalanceWarning } from 'src/components/FeeWarning';
|
||||
import { AppContext } from 'src/context';
|
||||
import { BondGatewayForm } from '../forms/BondGatewayForm';
|
||||
import { gatewayToTauri } from '../utils';
|
||||
|
||||
@@ -50,6 +52,7 @@ export const BondGatewayModal = ({
|
||||
const [signature, setSignature] = useState<string>();
|
||||
|
||||
const { fee, getFee, resetFeeState, feeError } = useGetFee();
|
||||
const { userBalance } = useContext(AppContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (feeError) {
|
||||
@@ -123,6 +126,9 @@ export const BondGatewayModal = ({
|
||||
value={`${amountData.amount.amount} ${amountData.amount.denom.toUpperCase()}`}
|
||||
divider
|
||||
/>
|
||||
{fee.amount?.amount && userBalance.balance && (
|
||||
<BalanceWarning fee={fee.amount?.amount} tx={amountData.amount.amount} />
|
||||
)}
|
||||
</ConfirmTx>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { CurrencyDenom, TNodeType } from '@nymproject/types';
|
||||
import { ConfirmTx } from 'src/components/ConfirmTX';
|
||||
import { ModalListItem } from 'src/components/Modals/ModalListItem';
|
||||
@@ -8,6 +8,8 @@ import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { MixnodeAmount, MixnodeData, Signature } from 'src/pages/bonding/types';
|
||||
import { simulateBondMixnode, simulateVestingBondMixnode } from 'src/requests';
|
||||
import { TBondMixNodeArgs } from 'src/types';
|
||||
import { BalanceWarning } from 'src/components/FeeWarning';
|
||||
import { AppContext } from 'src/context';
|
||||
import { BondMixnodeForm } from '../forms/BondMixnodeForm';
|
||||
import { costParamsToTauri, mixnodeToTauri } from '../utils';
|
||||
|
||||
@@ -50,6 +52,7 @@ export const BondMixnodeModal = ({
|
||||
const [signature, setSignature] = useState<string>();
|
||||
|
||||
const { fee, getFee, resetFeeState, feeError } = useGetFee();
|
||||
const { userBalance } = useContext(AppContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (feeError) {
|
||||
@@ -125,6 +128,9 @@ export const BondMixnodeModal = ({
|
||||
value={`${amountData.amount.amount} ${amountData.amount.denom.toUpperCase()}`}
|
||||
divider
|
||||
/>
|
||||
{fee.amount?.amount && userBalance.balance && (
|
||||
<BalanceWarning fee={fee.amount?.amount} tx={amountData.amount.amount} />
|
||||
)}
|
||||
</ConfirmTx>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { FeeDetails } from '@nymproject/types';
|
||||
import { ModalListItem } from 'src/components/Modals/ModalListItem';
|
||||
import { SimpleModal } from 'src/components/Modals/SimpleModal';
|
||||
import { ModalFee } from 'src/components/Modals/ModalFee';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { simulateClaimOperatorReward, simulateVestingClaimOperatorReward } from 'src/requests';
|
||||
import { TBondedMixnode } from 'src/context';
|
||||
import { AppContext, TBondedMixnode } from 'src/context';
|
||||
import { BalanceWarning } from 'src/components/FeeWarning';
|
||||
import { Box } from '@mui/material';
|
||||
|
||||
export const RedeemRewardsModal = ({
|
||||
node,
|
||||
@@ -19,6 +21,7 @@ export const RedeemRewardsModal = ({
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const { fee, getFee, isFeeLoading, feeError } = useGetFee();
|
||||
const { userBalance } = useContext(AppContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (feeError) onError(feeError);
|
||||
@@ -49,7 +52,13 @@ export const RedeemRewardsModal = ({
|
||||
divider
|
||||
/>
|
||||
<ModalFee fee={fee} isLoading={isFeeLoading} divider />
|
||||
<ModalListItem label="Balance" value={userBalance.balance?.printable_balance.toUpperCase()} divider />
|
||||
<ModalListItem label="Rewards will be transferred to the account you are logged in with" value="" />
|
||||
{userBalance.balance?.amount.amount && fee?.amount?.amount && (
|
||||
<Box sx={{ my: 2 }}>
|
||||
<BalanceWarning fee={fee?.amount?.amount} />
|
||||
</Box>
|
||||
)}
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ import { decCoinToDisplay, validateAmount } from 'src/utils';
|
||||
import { simulateUpdateBond, simulateVestingUpdateBond } from 'src/requests';
|
||||
import { TSimulateUpdateBondArgs, TUpdateBondArgs } from 'src/types';
|
||||
import { AppContext, TBondedMixnode } from 'src/context';
|
||||
import { BalanceWarning } from 'src/components/FeeWarning';
|
||||
import { TPoolOption } from '../../TokenPoolSelector';
|
||||
|
||||
export const UpdateBondAmountModal = ({
|
||||
@@ -30,7 +31,7 @@ export const UpdateBondAmountModal = ({
|
||||
const [newBond, setNewBond] = useState<DecCoin | undefined>();
|
||||
const [errorAmount, setErrorAmount] = useState(false);
|
||||
|
||||
const { printBalance, printVestedBalance } = useContext(AppContext);
|
||||
const { printBalance, printVestedBalance, userBalance } = useContext(AppContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (feeError) {
|
||||
@@ -104,6 +105,11 @@ export const UpdateBondAmountModal = ({
|
||||
>
|
||||
<ModalListItem label="New bond details" value={newBondToDisplay()} divider />
|
||||
<ModalListItem label="Change bond details" value={`${currentBond.amount} ${currentBond.denom}`} divider />
|
||||
{userBalance.balance?.amount.amount && fee?.amount?.amount && (
|
||||
<Box sx={{ my: 2 }}>
|
||||
<BalanceWarning fee={fee?.amount?.amount} tx={newBond?.amount} />
|
||||
</Box>
|
||||
)}
|
||||
</ConfirmTx>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React, { useCallback, useContext, useState } from 'react';
|
||||
import { Box, Typography, SxProps } from '@mui/material';
|
||||
import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField';
|
||||
import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField';
|
||||
@@ -7,6 +7,7 @@ import { Console } from 'src/utils/console';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { simulateDelegateToMixnode, simulateVestingDelegateToMixnode, tryConvertIdentityToMixId } from 'src/requests';
|
||||
import { debounce } from 'lodash';
|
||||
import { AppContext } from 'src/context';
|
||||
import { SimpleModal } from '../Modals/SimpleModal';
|
||||
import { ModalListItem } from '../Modals/ModalListItem';
|
||||
import { checkTokenBalance, validateAmount, validateKey } from '../../utils';
|
||||
@@ -15,6 +16,7 @@ import { ConfirmTx } from '../ConfirmTX';
|
||||
|
||||
import { getMixnodeStakeSaturation } from '../../requests';
|
||||
import { ErrorModal } from '../Modals/ErrorModal';
|
||||
import { BalanceWarning } from '../FeeWarning';
|
||||
|
||||
const MIN_AMOUNT_TO_DELEGATE = 10;
|
||||
|
||||
@@ -73,6 +75,7 @@ export const DelegateModal: FCWithChildren<{
|
||||
const [mixIdError, setMixIdError] = useState<string>();
|
||||
|
||||
const { fee, getFee, resetFeeState, feeError } = useGetFee();
|
||||
const { userBalance } = useContext(AppContext);
|
||||
|
||||
const handleCheckStakeSaturation = async (newMixId: number) => {
|
||||
try {
|
||||
@@ -213,6 +216,11 @@ export const DelegateModal: FCWithChildren<{
|
||||
onPrev={resetFeeState}
|
||||
onConfirm={handleOk}
|
||||
>
|
||||
{userBalance.balance?.amount.amount && fee?.amount?.amount && (
|
||||
<Box sx={{ my: 2 }}>
|
||||
<BalanceWarning fee={fee?.amount?.amount} tx={amount} />
|
||||
</Box>
|
||||
)}
|
||||
<ModalListItem label="Node identity key" value={identityKey} divider />
|
||||
<ModalListItem label="Amount" value={`${amount} ${denom.toUpperCase()}`} divider />
|
||||
</ConfirmTx>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { Box, SxProps } from '@mui/material';
|
||||
import React, { useEffect } from 'react';
|
||||
import { FeeDetails } from '@nymproject/types';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { simulateUndelegateFromMixnode, simulateVestingUndelegateFromMixnode } from 'src/requests';
|
||||
import { AppContext } from 'src/context';
|
||||
import { ModalFee } from '../Modals/ModalFee';
|
||||
import { ModalListItem } from '../Modals/ModalListItem';
|
||||
import { SimpleModal } from '../Modals/SimpleModal';
|
||||
import { BalanceWarning } from '../FeeWarning';
|
||||
|
||||
export const UndelegateModal: FCWithChildren<{
|
||||
open: boolean;
|
||||
@@ -20,6 +22,7 @@ export const UndelegateModal: FCWithChildren<{
|
||||
backdropProps?: object;
|
||||
}> = ({ mixId, identityKey, open, onClose, onOk, amount, currency, usesVestingContractTokens, sx, backdropProps }) => {
|
||||
const { fee, isFeeLoading, feeError, getFee } = useGetFee();
|
||||
const { userBalance } = useContext(AppContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (usesVestingContractTokens) getFee(simulateVestingUndelegateFromMixnode, { mixId });
|
||||
@@ -50,6 +53,11 @@ export const UndelegateModal: FCWithChildren<{
|
||||
<ModalListItem label="Delegation amount" value={`${amount} ${currency.toUpperCase()}`} divider />
|
||||
<ModalFee fee={fee} isLoading={isFeeLoading} error={feeError} divider />
|
||||
<ModalListItem label=" Tokens will be transferred to account you are logged in with now" value="" divider />
|
||||
{userBalance.balance?.amount.amount && fee?.amount?.amount && (
|
||||
<Box sx={{ my: 2 }}>
|
||||
<BalanceWarning fee={fee?.amount?.amount} />
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</SimpleModal>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import * as React from 'react';
|
||||
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
||||
import { Box } from '@mui/material';
|
||||
import { BalanceWarning } from './FeeWarning';
|
||||
|
||||
export default {
|
||||
title: 'Wallet / Balance warning',
|
||||
component: BalanceWarning,
|
||||
} as ComponentMeta<typeof BalanceWarning>;
|
||||
|
||||
const Template: ComponentStory<typeof BalanceWarning> = (args) => (
|
||||
<Box mt={2} height={800}>
|
||||
<BalanceWarning {...args} />
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const WithWarning = Template.bind({});
|
||||
WithWarning.args = {
|
||||
fee: '200',
|
||||
};
|
||||
@@ -1,16 +1,35 @@
|
||||
import React from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import { Warning } from '@mui/icons-material';
|
||||
import { FeeDetails } from '@nymproject/types';
|
||||
import { Alert, AlertTitle } from '@mui/material';
|
||||
import { Alert, AlertTitle, Box } from '@mui/material';
|
||||
import { isBalanceEnough } from 'src/utils';
|
||||
import { AppContext } from 'src/context';
|
||||
|
||||
export const FeeWarning = ({ fee, amount }: { fee: FeeDetails; amount: number }) => {
|
||||
if (fee.amount && +fee.amount.amount > amount) {
|
||||
return (
|
||||
<Alert color="warning" sx={{ mt: 3 }} icon={<Warning />}>
|
||||
<AlertTitle>Warning: fees are greater than the reward</AlertTitle>
|
||||
<AlertTitle>Warning: Fees are greater than the reward</AlertTitle>
|
||||
The fees for redeeming rewards will cost more than the rewards. Are you sure you want to continue?
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const BalanceWarning = ({ tx, fee }: { fee: string; tx?: string }) => {
|
||||
const { userBalance } = useContext(AppContext);
|
||||
|
||||
const hasEnoughBalanace = isBalanceEnough(fee, tx, userBalance.balance?.amount.amount);
|
||||
|
||||
if (hasEnoughBalanace) return null;
|
||||
|
||||
return (
|
||||
<Alert color="warning" icon={<Warning />}>
|
||||
<AlertTitle>Warning: Transaction amount is greater than your balance</AlertTitle>
|
||||
The transaction amount (inc fees) is greater than your current balance, which could cause this transaction to
|
||||
fail.
|
||||
<Box sx={{ mt: 0.5 }}>Do you want to continue?</Box>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField';
|
||||
import { CurrencyDenom, FeeDetails } from '@nymproject/types';
|
||||
import { SxProps } from '@mui/material';
|
||||
import { Box, SxProps } from '@mui/material';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { simulateClaimDelegatorReward, simulateVestingClaimDelegatorReward } from 'src/requests';
|
||||
import { AppContext } from 'src/context';
|
||||
import { ModalFee } from '../Modals/ModalFee';
|
||||
import { SimpleModal } from '../Modals/SimpleModal';
|
||||
import { FeeWarning } from '../FeeWarning';
|
||||
import { BalanceWarning, FeeWarning } from '../FeeWarning';
|
||||
import { ModalListItem } from '../Modals/ModalListItem';
|
||||
|
||||
export const RedeemModal: FCWithChildren<{
|
||||
@@ -23,6 +24,7 @@ export const RedeemModal: FCWithChildren<{
|
||||
usesVestingTokens: boolean;
|
||||
}> = ({ open, onClose, onOk, mixId, identityKey, amount, denom, message, usesVestingTokens, sx, backdropProps }) => {
|
||||
const { fee, isFeeLoading, feeError, getFee } = useGetFee();
|
||||
const { userBalance } = useContext(AppContext);
|
||||
|
||||
const handleOk = async () => {
|
||||
if (onOk) {
|
||||
@@ -56,6 +58,11 @@ export const RedeemModal: FCWithChildren<{
|
||||
<ModalFee fee={fee} isLoading={isFeeLoading} error={feeError} divider />
|
||||
<ModalListItem label="Rewards will be transferred to account you are logged in with now" value="" divider />
|
||||
{fee && <FeeWarning amount={amount} fee={fee} />}
|
||||
{userBalance.balance?.amount.amount && fee?.amount?.amount && (
|
||||
<Box sx={{ my: 2 }}>
|
||||
<BalanceWarning fee={fee?.amount?.amount} />
|
||||
</Box>
|
||||
)}
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React from 'react';
|
||||
import { Stack, SxProps } from '@mui/material';
|
||||
import React, { useContext } from 'react';
|
||||
import { Box, Stack, SxProps } from '@mui/material';
|
||||
import { FeeDetails, DecCoin, CurrencyDenom } from '@nymproject/types';
|
||||
import { AppContext } from 'src/context';
|
||||
import { SimpleModal } from '../Modals/SimpleModal';
|
||||
import { ModalListItem } from '../Modals/ModalListItem';
|
||||
import { ModalFee, ModalTotalAmount } from '../Modals/ModalFee';
|
||||
import { BalanceWarning } from '../FeeWarning';
|
||||
|
||||
export const SendDetailsModal = ({
|
||||
amount,
|
||||
@@ -29,37 +31,45 @@ export const SendDetailsModal = ({
|
||||
sx?: SxProps;
|
||||
backdropProps?: object;
|
||||
memo?: string;
|
||||
}) => (
|
||||
<SimpleModal
|
||||
header="Send details"
|
||||
open
|
||||
onClose={onClose}
|
||||
okLabel="Confirm"
|
||||
onOk={async () => amount && onSend({ val: amount, to: toAddress })}
|
||||
onBack={onPrev}
|
||||
sx={sx}
|
||||
backdropProps={backdropProps}
|
||||
>
|
||||
<Stack gap={0.5} sx={{ mt: 3 }}>
|
||||
<ModalListItem label="From" value={fromAddress} divider />
|
||||
<ModalListItem label="To" value={toAddress} divider />
|
||||
<ModalListItem label="Amount" value={`${amount?.amount} ${denom.toUpperCase()}`} divider />
|
||||
<ModalFee fee={fee} divider isLoading={false} />
|
||||
{memo && (
|
||||
<ModalListItem
|
||||
label="Memo"
|
||||
value={memo}
|
||||
sxValue={{
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
overflowWrap: 'anywhere',
|
||||
maxWidth: '300px',
|
||||
}}
|
||||
divider
|
||||
/>
|
||||
}) => {
|
||||
const { userBalance } = useContext(AppContext);
|
||||
return (
|
||||
<SimpleModal
|
||||
header="Send details"
|
||||
open
|
||||
onClose={onClose}
|
||||
okLabel="Confirm"
|
||||
onOk={async () => amount && onSend({ val: amount, to: toAddress })}
|
||||
onBack={onPrev}
|
||||
sx={sx}
|
||||
backdropProps={backdropProps}
|
||||
>
|
||||
<Stack gap={0.5} sx={{ mt: 3 }}>
|
||||
<ModalListItem label="From" value={fromAddress} divider />
|
||||
<ModalListItem label="To" value={toAddress} divider />
|
||||
<ModalListItem label="Amount" value={`${amount?.amount} ${denom.toUpperCase()}`} divider />
|
||||
<ModalFee fee={fee} divider isLoading={false} />
|
||||
{memo && (
|
||||
<ModalListItem
|
||||
label="Memo"
|
||||
value={memo}
|
||||
sxValue={{
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
overflowWrap: 'anywhere',
|
||||
maxWidth: '300px',
|
||||
}}
|
||||
divider
|
||||
/>
|
||||
)}
|
||||
<ModalTotalAmount fee={fee} amount={amount?.amount} divider isLoading={false} />
|
||||
</Stack>
|
||||
{userBalance.balance?.amount.amount && fee?.amount?.amount && (
|
||||
<Box sx={{ my: 2 }}>
|
||||
<BalanceWarning fee={fee?.amount?.amount} tx={amount?.amount} />
|
||||
</Box>
|
||||
)}
|
||||
<ModalTotalAmount fee={fee} amount={amount?.amount} divider isLoading={false} />
|
||||
</Stack>
|
||||
</SimpleModal>
|
||||
);
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { Account, Balance, DecCoin, OriginalVestingResponse, Period, VestingAccountInfo } from '@nymproject/types';
|
||||
import {
|
||||
getVestingCoins,
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
getVestingAccountInfo,
|
||||
getSpendableRewardCoins,
|
||||
getSpendableVestedCoins,
|
||||
userBalance,
|
||||
} from '../requests';
|
||||
import { Console } from '../utils/console';
|
||||
|
||||
@@ -101,8 +101,8 @@ export const useGetBalance = (clientDetails?: Account): TUseuserBalance => {
|
||||
setIsLoading(true);
|
||||
setError(undefined);
|
||||
try {
|
||||
const bal = await invoke('get_balance');
|
||||
setBalance(bal as Balance);
|
||||
const bal = await userBalance();
|
||||
setBalance(bal);
|
||||
} catch (err) {
|
||||
setError(err as string);
|
||||
} finally {
|
||||
|
||||
+11
-2
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { clean } from 'semver';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
@@ -18,11 +18,14 @@ import { ConfirmTx } from 'src/components/ConfirmTX';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { LoadingModal } from 'src/components/Modals/LoadingModal';
|
||||
import { updateGatewayValidationSchema } from 'src/components/Bonding/forms/gatewayValidationSchema';
|
||||
import { BalanceWarning } from 'src/components/FeeWarning';
|
||||
import { AppContext } from 'src/context';
|
||||
|
||||
export const GeneralGatewaySettings = ({ bondedNode }: { bondedNode: TBondedGateway }) => {
|
||||
const [openConfirmationModal, setOpenConfirmationModal] = useState<boolean>(false);
|
||||
const { getFee, fee, resetFeeState } = useGetFee();
|
||||
const { refresh } = useBondingContext();
|
||||
const { userBalance } = useContext(AppContext);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
@@ -78,7 +81,13 @@ export const GeneralGatewaySettings = ({ bondedNode }: { bondedNode: TBondedGate
|
||||
onConfirm={handleSubmit((d) => onSubmit(d))}
|
||||
onPrev={resetFeeState}
|
||||
onClose={resetFeeState}
|
||||
/>
|
||||
>
|
||||
{fee.amount?.amount && userBalance?.balance?.amount.amount && (
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<BalanceWarning fee={fee.amount.amount} />
|
||||
</Box>
|
||||
)}
|
||||
</ConfirmTx>
|
||||
)}
|
||||
{isSubmitting && <LoadingModal />}
|
||||
<Alert
|
||||
|
||||
+12
-3
@@ -1,8 +1,8 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { clean } from 'semver';
|
||||
import { Button, Divider, Grid, Stack, TextField, Typography } from '@mui/material';
|
||||
import { Box, Button, Divider, Grid, Stack, TextField, Typography } from '@mui/material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { isMixnode } from 'src/types';
|
||||
import { simulateUpdateMixnodeConfig, simulateVestingUpdateMixnodeConfig, updateMixnodeConfig } from 'src/requests';
|
||||
@@ -15,10 +15,13 @@ import { vestingUpdateMixnodeConfig } from 'src/requests/vesting';
|
||||
import { ConfirmTx } from 'src/components/ConfirmTX';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { LoadingModal } from 'src/components/Modals/LoadingModal';
|
||||
import { BalanceWarning } from 'src/components/FeeWarning';
|
||||
import { AppContext } from 'src/context';
|
||||
|
||||
export const GeneralMixnodeSettings = ({ bondedNode }: { bondedNode: TBondedMixnode | TBondedGateway }) => {
|
||||
const [openConfirmationModal, setOpenConfirmationModal] = useState<boolean>(false);
|
||||
const { getFee, fee, resetFeeState } = useGetFee();
|
||||
const { userBalance } = useContext(AppContext);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
@@ -72,7 +75,13 @@ export const GeneralMixnodeSettings = ({ bondedNode }: { bondedNode: TBondedMixn
|
||||
onConfirm={handleSubmit((d) => onSubmit(d))}
|
||||
onPrev={resetFeeState}
|
||||
onClose={resetFeeState}
|
||||
/>
|
||||
>
|
||||
{fee.amount?.amount && userBalance?.balance?.amount.amount && (
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<BalanceWarning fee={fee.amount.amount} />
|
||||
</Box>
|
||||
)}
|
||||
</ConfirmTx>
|
||||
)}
|
||||
{isSubmitting && <LoadingModal />}
|
||||
<Alert
|
||||
|
||||
@@ -249,7 +249,6 @@ export const Delegation: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => {
|
||||
tx = await undelegate(mixId, fee?.fee);
|
||||
}
|
||||
|
||||
// const txs = await undelegate(mixId, usesVestingContractTokens, fee);
|
||||
const balances = await getAllBalances();
|
||||
|
||||
setConfirmationModalProps({
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
import { appWindow } from '@tauri-apps/api/window';
|
||||
import bs58 from 'bs58';
|
||||
import Big from 'big.js';
|
||||
import { valid } from 'semver';
|
||||
import { add, format, fromUnixTime } from 'date-fns';
|
||||
import { DecCoin, isValidRawCoin, MixNodeCostParams } from '@nymproject/types';
|
||||
import { TPoolOption } from 'src/components';
|
||||
import {
|
||||
getCurrentInterval,
|
||||
getDefaultMixnodeCostParams,
|
||||
getLockedCoins,
|
||||
getSpendableCoins,
|
||||
userBalance,
|
||||
} from '../requests';
|
||||
import { Console } from './console';
|
||||
|
||||
export const validateKey = (key: string, bytesLength: number): boolean => {
|
||||
// it must be a valid base58 key
|
||||
try {
|
||||
const bytes = bs58.decode(key);
|
||||
// of length 32
|
||||
return bytes.length === bytesLength;
|
||||
} catch (e) {
|
||||
Console.error(e as string);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const validateAmount = async (
|
||||
majorAmountAsString: DecCoin['amount'],
|
||||
minimumAmountAsString: DecCoin['amount'],
|
||||
): Promise<boolean> => {
|
||||
// tests basic coin value requirements, like no more than 6 decimal places, value lower than total supply, etc
|
||||
if (!Number(majorAmountAsString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isValidRawCoin(majorAmountAsString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const majorValueFloat = parseInt(majorAmountAsString, Number(10));
|
||||
|
||||
return majorValueFloat >= parseInt(minimumAmountAsString, Number(10));
|
||||
|
||||
// this conversion seems really iffy but I'm not sure how to better approach it
|
||||
};
|
||||
|
||||
export const isValidHostname = (value: string) => {
|
||||
// regex for ipv4 and ipv6 and hhostname- source http://jsfiddle.net/DanielD/8S4nq/
|
||||
const hostnameRegex =
|
||||
/((^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))|(^\s*((?=.{1,255}$)(?=.*[A-Za-z].*)[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?)*)\s*$)/;
|
||||
|
||||
return hostnameRegex.test(value);
|
||||
};
|
||||
|
||||
export const validateVersion = (version: string): boolean => {
|
||||
try {
|
||||
return valid(version) !== null;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const validateLocation = (location: string): boolean => {
|
||||
const locationRegex = /^[a-z]+$/i;
|
||||
return locationRegex.test(location);
|
||||
};
|
||||
|
||||
export const validateRawPort = (rawPort: number): boolean => !Number.isNaN(rawPort) && rawPort >= 1 && rawPort <= 65535;
|
||||
|
||||
export const truncate = (text: string, trim: number) => `${text.substring(0, trim)}...`;
|
||||
|
||||
export const isGreaterThan = (a: number, b: number) => a > b;
|
||||
|
||||
export const isLessThan = (a: number, b: number) => a < b;
|
||||
|
||||
export const checkHasEnoughFunds = async (allocationValue: string): Promise<boolean> => {
|
||||
try {
|
||||
const walletValue = await userBalance();
|
||||
|
||||
const remainingBalance = +walletValue.amount.amount - +allocationValue;
|
||||
return remainingBalance >= 0;
|
||||
} catch (e) {
|
||||
Console.log(e as string);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const checkHasEnoughLockedTokens = async (allocationValue: string) => {
|
||||
try {
|
||||
const lockedTokens = await getLockedCoins();
|
||||
const spendableTokens = await getSpendableCoins();
|
||||
const remainingBalance = +lockedTokens.amount + +spendableTokens.amount - +allocationValue;
|
||||
return remainingBalance >= 0;
|
||||
} catch (e) {
|
||||
Console.error(e as string);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const randomNumberBetween = (min: number, max: number) => {
|
||||
const minCeil = Math.ceil(min);
|
||||
const maxFloor = Math.floor(max);
|
||||
return Math.floor(Math.random() * (maxFloor - minCeil + 1) + minCeil);
|
||||
};
|
||||
|
||||
export const splice = (size: number, address?: string): string => {
|
||||
if (address) {
|
||||
return `${address.slice(0, size)}...${address.slice(-size)}`;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
export const maximizeWindow = async () => {
|
||||
await appWindow.maximize();
|
||||
};
|
||||
|
||||
export function removeObjectDuplicates<T extends object, K extends keyof T>(arr: T[], id: K) {
|
||||
return arr.filter((v, i, a) => a.findIndex((v2) => v2[id] === v[id]) === i);
|
||||
}
|
||||
|
||||
export const checkTokenBalance = async (tokenPool: TPoolOption, amount: string) => {
|
||||
let hasEnoughFunds = false;
|
||||
if (tokenPool === 'locked') {
|
||||
hasEnoughFunds = await checkHasEnoughLockedTokens(amount);
|
||||
}
|
||||
|
||||
if (tokenPool === 'balance') {
|
||||
hasEnoughFunds = await checkHasEnoughFunds(amount);
|
||||
}
|
||||
|
||||
return hasEnoughFunds;
|
||||
};
|
||||
|
||||
export const isDecimal = (value: number) => value - Math.floor(value) !== 0;
|
||||
|
||||
export const attachDefaultOperatingCost = async (profitMarginPercent: string): Promise<MixNodeCostParams> =>
|
||||
getDefaultMixnodeCostParams(profitMarginPercent);
|
||||
|
||||
/**
|
||||
* Converts a stringified percentage integer (0-100) to a stringified float (0.0-1.0).
|
||||
*
|
||||
* @param value - the percentage to convert
|
||||
* @returns A stringified float
|
||||
*/
|
||||
export const toPercentFloatString = (value: string) => (Number(value) / 100).toString();
|
||||
|
||||
/**
|
||||
* Converts a stringified percentage float (0.0-1.0) to a stringified integer (0-100).
|
||||
*
|
||||
* @param value - the percentage to convert
|
||||
* @returns A stringified integer
|
||||
*/
|
||||
export const toPercentIntegerString = (value: string) => Math.round(Number(value) * 100).toString();
|
||||
|
||||
/**
|
||||
* Converts a decimal number to a pretty representation
|
||||
* with fixed decimal places.
|
||||
*
|
||||
* @param val - a decimal number of string form
|
||||
* @param dp - number of decimal places (4 by default ie. 0.0000)
|
||||
* @returns A prettified decimal number
|
||||
*/
|
||||
export const toDisplay = (val: string | number | Big, dp = 4) => {
|
||||
let displayValue;
|
||||
try {
|
||||
displayValue = Big(val).toFixed(dp);
|
||||
} catch (e: any) {
|
||||
Console.warn(`${displayValue} not a valid decimal number: ${e}`);
|
||||
}
|
||||
return displayValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes a DecCoin and prettify its amount to a representation
|
||||
* with fixed decimal places.
|
||||
*
|
||||
* @param coin - a DecCoin
|
||||
* @param dp - number of decimal places to apply to amount (4 by default ie. 0.0000)
|
||||
* @returns A DecCoin with prettified amount
|
||||
*/
|
||||
export const decCoinToDisplay = (coin: DecCoin, dp = 4) => {
|
||||
const displayCoin = { ...coin };
|
||||
try {
|
||||
displayCoin.amount = Big(coin.amount).toFixed(dp);
|
||||
} catch (e: any) {
|
||||
Console.warn(`${coin.amount} not a valid decimal number: ${e}`);
|
||||
}
|
||||
return displayCoin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a decimal number of μNYM (micro NYM) to NYM.
|
||||
*
|
||||
* @param unym - string representation of a decimal number of μNYM
|
||||
* @param dp - number of decimal places (4 by default ie. 0.0000)
|
||||
* @returns The corresponding decimal number in NYM
|
||||
*/
|
||||
export const unymToNym = (unym: string | Big, dp = 4) => {
|
||||
let nym;
|
||||
try {
|
||||
nym = Big(unym).div(1_000_000).toFixed(dp);
|
||||
} catch (e: any) {
|
||||
Console.warn(`${unym} not a valid decimal number: ${e}`);
|
||||
}
|
||||
return nym;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* Checks if the user's balance is enough to pay the fee
|
||||
* @param balance - The user's current balance
|
||||
* @param fee - The fee for the tx
|
||||
* @param tx - The amount of the tx
|
||||
* @returns boolean
|
||||
*
|
||||
*/
|
||||
|
||||
export const isBalanceEnough = (fee: string, tx: string = '0', balance: string = '0') => {
|
||||
console.log('balance', balance, fee, tx);
|
||||
try {
|
||||
return Big(balance).gte(Big(fee).plus(Big(tx)));
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const getIntervalAsDate = async () => {
|
||||
const interval = await getCurrentInterval();
|
||||
const secondsToNextInterval =
|
||||
Number(interval.epochs_in_interval - interval.current_epoch_id) * Number(interval.epoch_length_seconds);
|
||||
|
||||
const nextInterval = format(
|
||||
add(new Date(), {
|
||||
seconds: secondsToNextInterval,
|
||||
}),
|
||||
'dd/MM/yyyy, HH:mm',
|
||||
);
|
||||
|
||||
const nextEpoch = format(
|
||||
add(fromUnixTime(Number(interval.current_epoch_start_unix)), {
|
||||
seconds: Number(interval.epoch_length_seconds),
|
||||
}),
|
||||
'HH:mm',
|
||||
);
|
||||
|
||||
return { nextEpoch, nextInterval };
|
||||
};
|
||||
@@ -1,233 +1,4 @@
|
||||
import { appWindow } from '@tauri-apps/api/window';
|
||||
import bs58 from 'bs58';
|
||||
import Big from 'big.js';
|
||||
import { valid } from 'semver';
|
||||
import { add, format, fromUnixTime } from 'date-fns';
|
||||
import { DecCoin, isValidRawCoin, MixNodeCostParams } from '@nymproject/types';
|
||||
import { TPoolOption } from 'src/components';
|
||||
import {
|
||||
getCurrentInterval,
|
||||
getDefaultMixnodeCostParams,
|
||||
getLockedCoins,
|
||||
getSpendableCoins,
|
||||
userBalance,
|
||||
} from '../requests';
|
||||
import { Console } from './console';
|
||||
|
||||
export const validateKey = (key: string, bytesLength: number): boolean => {
|
||||
// it must be a valid base58 key
|
||||
try {
|
||||
const bytes = bs58.decode(key);
|
||||
// of length 32
|
||||
return bytes.length === bytesLength;
|
||||
} catch (e) {
|
||||
Console.error(e as string);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const validateAmount = async (
|
||||
majorAmountAsString: DecCoin['amount'],
|
||||
minimumAmountAsString: DecCoin['amount'],
|
||||
): Promise<boolean> => {
|
||||
// tests basic coin value requirements, like no more than 6 decimal places, value lower than total supply, etc
|
||||
if (!Number(majorAmountAsString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isValidRawCoin(majorAmountAsString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const majorValueFloat = parseInt(majorAmountAsString, Number(10));
|
||||
|
||||
return majorValueFloat >= parseInt(minimumAmountAsString, Number(10));
|
||||
|
||||
// this conversion seems really iffy but I'm not sure how to better approach it
|
||||
};
|
||||
|
||||
export const isValidHostname = (value: string) => {
|
||||
// regex for ipv4 and ipv6 and hhostname- source http://jsfiddle.net/DanielD/8S4nq/
|
||||
const hostnameRegex =
|
||||
/((^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))|(^\s*((?=.{1,255}$)(?=.*[A-Za-z].*)[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?)*)\s*$)/;
|
||||
|
||||
return hostnameRegex.test(value);
|
||||
};
|
||||
|
||||
export const validateVersion = (version: string): boolean => {
|
||||
try {
|
||||
return valid(version) !== null;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const validateLocation = (location: string): boolean => {
|
||||
const locationRegex = /^[a-z]+$/i;
|
||||
return locationRegex.test(location);
|
||||
};
|
||||
|
||||
export const validateRawPort = (rawPort: number): boolean => !Number.isNaN(rawPort) && rawPort >= 1 && rawPort <= 65535;
|
||||
|
||||
export const truncate = (text: string, trim: number) => `${text.substring(0, trim)}...`;
|
||||
|
||||
export const isGreaterThan = (a: number, b: number) => a > b;
|
||||
|
||||
export const isLessThan = (a: number, b: number) => a < b;
|
||||
|
||||
export const checkHasEnoughFunds = async (allocationValue: string): Promise<boolean> => {
|
||||
try {
|
||||
const walletValue = await userBalance();
|
||||
|
||||
const remainingBalance = +walletValue.amount.amount - +allocationValue;
|
||||
return remainingBalance >= 0;
|
||||
} catch (e) {
|
||||
Console.log(e as string);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const checkHasEnoughLockedTokens = async (allocationValue: string) => {
|
||||
try {
|
||||
const lockedTokens = await getLockedCoins();
|
||||
const spendableTokens = await getSpendableCoins();
|
||||
const remainingBalance = +lockedTokens.amount + +spendableTokens.amount - +allocationValue;
|
||||
return remainingBalance >= 0;
|
||||
} catch (e) {
|
||||
Console.error(e as string);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const randomNumberBetween = (min: number, max: number) => {
|
||||
const minCeil = Math.ceil(min);
|
||||
const maxFloor = Math.floor(max);
|
||||
return Math.floor(Math.random() * (maxFloor - minCeil + 1) + minCeil);
|
||||
};
|
||||
|
||||
export const splice = (size: number, address?: string): string => {
|
||||
if (address) {
|
||||
return `${address.slice(0, size)}...${address.slice(-size)}`;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
export const maximizeWindow = async () => {
|
||||
await appWindow.maximize();
|
||||
};
|
||||
|
||||
export function removeObjectDuplicates<T extends object, K extends keyof T>(arr: T[], id: K) {
|
||||
return arr.filter((v, i, a) => a.findIndex((v2) => v2[id] === v[id]) === i);
|
||||
}
|
||||
|
||||
export const checkTokenBalance = async (tokenPool: TPoolOption, amount: string) => {
|
||||
let hasEnoughFunds = false;
|
||||
if (tokenPool === 'locked') {
|
||||
hasEnoughFunds = await checkHasEnoughLockedTokens(amount);
|
||||
}
|
||||
|
||||
if (tokenPool === 'balance') {
|
||||
hasEnoughFunds = await checkHasEnoughFunds(amount);
|
||||
}
|
||||
|
||||
return hasEnoughFunds;
|
||||
};
|
||||
|
||||
export const isDecimal = (value: number) => value - Math.floor(value) !== 0;
|
||||
|
||||
export const attachDefaultOperatingCost = async (profitMarginPercent: string): Promise<MixNodeCostParams> =>
|
||||
getDefaultMixnodeCostParams(profitMarginPercent);
|
||||
|
||||
/**
|
||||
* Converts a stringified percentage integer (0-100) to a stringified float (0.0-1.0).
|
||||
*
|
||||
* @param value - the percentage to convert
|
||||
* @returns A stringified float
|
||||
*/
|
||||
export const toPercentFloatString = (value: string) => (Number(value) / 100).toString();
|
||||
|
||||
/**
|
||||
* Converts a stringified percentage float (0.0-1.0) to a stringified integer (0-100).
|
||||
*
|
||||
* @param value - the percentage to convert
|
||||
* @returns A stringified integer
|
||||
*/
|
||||
export const toPercentIntegerString = (value: string) => Math.round(Number(value) * 100).toString();
|
||||
|
||||
/**
|
||||
* Converts a decimal number to a pretty representation
|
||||
* with fixed decimal places.
|
||||
*
|
||||
* @param val - a decimal number of string form
|
||||
* @param dp - number of decimal places (4 by default ie. 0.0000)
|
||||
* @returns A prettified decimal number
|
||||
*/
|
||||
export const toDisplay = (val: string | number | Big, dp = 4) => {
|
||||
let displayValue;
|
||||
try {
|
||||
displayValue = Big(val).toFixed(dp);
|
||||
} catch (e: any) {
|
||||
Console.warn(`${displayValue} not a valid decimal number: ${e}`);
|
||||
}
|
||||
return displayValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes a DecCoin and prettify its amount to a representation
|
||||
* with fixed decimal places.
|
||||
*
|
||||
* @param coin - a DecCoin
|
||||
* @param dp - number of decimal places to apply to amount (4 by default ie. 0.0000)
|
||||
* @returns A DecCoin with prettified amount
|
||||
*/
|
||||
export const decCoinToDisplay = (coin: DecCoin, dp = 4) => {
|
||||
const displayCoin = { ...coin };
|
||||
try {
|
||||
displayCoin.amount = Big(coin.amount).toFixed(dp);
|
||||
} catch (e: any) {
|
||||
Console.warn(`${coin.amount} not a valid decimal number: ${e}`);
|
||||
}
|
||||
return displayCoin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a decimal number of μNYM (micro NYM) to NYM.
|
||||
*
|
||||
* @param unym - string representation of a decimal number of μNYM
|
||||
* @param dp - number of decimal places (4 by default ie. 0.0000)
|
||||
* @returns The corresponding decimal number in NYM
|
||||
*/
|
||||
export const unymToNym = (unym: string | Big, dp = 4) => {
|
||||
let nym;
|
||||
try {
|
||||
nym = Big(unym).div(1_000_000).toFixed(dp);
|
||||
} catch (e: any) {
|
||||
Console.warn(`${unym} not a valid decimal number: ${e}`);
|
||||
}
|
||||
return nym;
|
||||
};
|
||||
|
||||
export const getIntervalAsDate = async () => {
|
||||
const interval = await getCurrentInterval();
|
||||
const secondsToNextInterval =
|
||||
Number(interval.epochs_in_interval - interval.current_epoch_id) * Number(interval.epoch_length_seconds);
|
||||
|
||||
const nextInterval = format(
|
||||
add(new Date(), {
|
||||
seconds: secondsToNextInterval,
|
||||
}),
|
||||
'dd/MM/yyyy, HH:mm',
|
||||
);
|
||||
|
||||
const nextEpoch = format(
|
||||
add(fromUnixTime(Number(interval.current_epoch_start_unix)), {
|
||||
seconds: Number(interval.epoch_length_seconds),
|
||||
}),
|
||||
'HH:mm',
|
||||
);
|
||||
|
||||
return { nextEpoch, nextInterval };
|
||||
};
|
||||
|
||||
export * from './common';
|
||||
export * from './fireRequests';
|
||||
export * from './console';
|
||||
export { default as fireRequests } from './fireRequests';
|
||||
|
||||
Reference in New Issue
Block a user