WIP
This commit is contained in:
@@ -39,8 +39,17 @@ export const trimAddress = (address = '', trimBy = 6) => {
|
||||
};
|
||||
|
||||
export default function ConnectKeplrWallet() {
|
||||
const { username, connect, disconnect, wallet, openView, address, getCosmWasmClient, isWalletConnected } =
|
||||
useChain('nyx');
|
||||
const {
|
||||
username,
|
||||
connect,
|
||||
disconnect,
|
||||
wallet,
|
||||
openView,
|
||||
address,
|
||||
getCosmWasmClient,
|
||||
isWalletConnected,
|
||||
isWalletConnecting,
|
||||
} = useChain('nyx');
|
||||
const isClient = useIsClient();
|
||||
const theme = useTheme();
|
||||
|
||||
@@ -70,9 +79,9 @@ export default function ConnectKeplrWallet() {
|
||||
if (!isClient) return null;
|
||||
|
||||
const getGlobalbutton = () => {
|
||||
// if (globalStatus === 'Connecting') {
|
||||
// return <Button onClick={() => connect()}>{`Connecting ${wallet?.prettyName}`}</Button>;
|
||||
// }
|
||||
if (isWalletConnecting) {
|
||||
return <Button onClick={() => connect()}>{`Connecting ${wallet?.prettyName}`}</Button>;
|
||||
}
|
||||
if (isWalletConnected) {
|
||||
return (
|
||||
<Box display={'flex'} alignItems={'center'} gap={2}>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import { FeeDetails } from '@nymproject/types';
|
||||
import { Box } from '@mui/material';
|
||||
import { useTheme, Theme } from '@mui/material/styles';
|
||||
import { SimpleModal } from './SimpleModal';
|
||||
import { ModalFee } from './ModalFee';
|
||||
import { ModalDivider } from './ModalDivider';
|
||||
import { backDropStyles, modalStyles } from './styles';
|
||||
|
||||
const storybookStyles = (theme: Theme, isStorybook?: boolean, backdropProps?: object) =>
|
||||
isStorybook
|
||||
? {
|
||||
backdropProps: { ...backDropStyles(theme), ...backdropProps },
|
||||
sx: modalStyles(theme),
|
||||
}
|
||||
: {};
|
||||
|
||||
export const ConfirmTx: FCWithChildren<{
|
||||
open: boolean;
|
||||
header: string;
|
||||
subheader?: string;
|
||||
fee: FeeDetails;
|
||||
onConfirm: () => Promise<void>;
|
||||
onClose?: () => void;
|
||||
onPrev: () => void;
|
||||
isStorybook?: boolean;
|
||||
children?: React.ReactNode;
|
||||
}> = ({ open, fee, onConfirm, onClose, header, subheader, onPrev, children, isStorybook }) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<SimpleModal
|
||||
open={open}
|
||||
header={header}
|
||||
subHeader={subheader}
|
||||
okLabel="Confirm"
|
||||
onOk={onConfirm}
|
||||
onClose={onClose}
|
||||
onBack={onPrev}
|
||||
{...storybookStyles(theme, isStorybook)}
|
||||
>
|
||||
<Box sx={{ mt: 3 }}>
|
||||
{children}
|
||||
<ModalFee fee={fee} isLoading={false} />
|
||||
<ModalDivider />
|
||||
</Box>
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
@@ -3,7 +3,7 @@ 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 { Console } from '../utils/console';
|
||||
import { useGetFee } from '../hooks/useGetFee';
|
||||
// import { simulateDelegateToMixnode, simulateVestingDelegateToMixnode, tryConvertIdentityToMixId } from 'src/requests';
|
||||
import { debounce } from 'lodash';
|
||||
@@ -12,15 +12,17 @@ import { ModalListItem } from './ModalListItem';
|
||||
// import { AppContext } from 'src/context';
|
||||
// import { SimpleModal } from '../Modals/SimpleModal';
|
||||
// import { ModalListItem } from '../Modals/ModalListItem';
|
||||
// import { checkTokenBalance, validateAmount, validateKey } from '../../utils';
|
||||
import { TPoolOption, checkTokenBalance, validateAmount, validateKey } from '../utils';
|
||||
// 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';
|
||||
import { uNYMtoNYM } from '../utils';
|
||||
import { ErrorModal } from './ErrorModal';
|
||||
import { ConfirmTx } from './ConfirmTX';
|
||||
import { BalanceWarning } from './FeeWarning';
|
||||
|
||||
const MIN_AMOUNT_TO_DELEGATE = 10;
|
||||
|
||||
@@ -31,7 +33,7 @@ export const DelegateModal: FCWithChildren<{
|
||||
mixId: number,
|
||||
identityKey: string,
|
||||
amount: DecCoin,
|
||||
// tokenPool: TPoolOption,
|
||||
tokenPool: TPoolOption,
|
||||
fee?: FeeDetails,
|
||||
) => Promise<void>;
|
||||
identityKey?: string;
|
||||
@@ -102,7 +104,6 @@ export const DelegateModal: FCWithChildren<{
|
||||
getBalance(address);
|
||||
}
|
||||
}, [address, getCosmWasmClient]);
|
||||
// const { userBalance } = useContext(AppContext);
|
||||
|
||||
// const handleCheckStakeSaturation = async (newMixId: number) => {
|
||||
// try {
|
||||
@@ -138,10 +139,10 @@ export const DelegateModal: FCWithChildren<{
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (amount && !(await validateAmount(amount, '0'))) {
|
||||
// newValidatedValue = false;
|
||||
// errorAmountMessage = 'Please enter a valid amount';
|
||||
// }
|
||||
if (amount && !(await validateAmount(amount, '0'))) {
|
||||
newValidatedValue = false;
|
||||
errorAmountMessage = 'Please enter a valid amount';
|
||||
}
|
||||
|
||||
if (amount && Number(amount) < MIN_AMOUNT_TO_DELEGATE) {
|
||||
errorAmountMessage = `Min. delegation amount: ${MIN_AMOUNT_TO_DELEGATE} ${denom.toUpperCase()}`;
|
||||
@@ -166,21 +167,22 @@ export const DelegateModal: FCWithChildren<{
|
||||
|
||||
const handleOk = async () => {
|
||||
if (onOk && amount && identityKey && mixId) {
|
||||
onOk(mixId, identityKey, { amount, denom }, fee); //tokenPool, fee);
|
||||
onOk(mixId, identityKey, { amount, denom }, 'balance', fee);
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirm = async ({ mixId: id, value }: { mixId: number; value: DecCoin }) => {
|
||||
const hasEnoughTokens = true; // await checkTokenBalance(tokenPool, value.amount);
|
||||
const tokenPool = 'balance';
|
||||
const hasEnoughTokens = await checkTokenBalance(tokenPool, value.amount);
|
||||
|
||||
if (!hasEnoughTokens) {
|
||||
setErrorAmount('Not enough funds');
|
||||
return;
|
||||
}
|
||||
|
||||
// if (tokenPool === 'balance') {
|
||||
// getFee(simulateDelegateToMixnode, { mixId: id, amount: value });
|
||||
// }
|
||||
// if (tokenPool === 'balance') {
|
||||
// getFee(simulateDelegateToMixnode, { mixId: id, amount: value });
|
||||
// }
|
||||
|
||||
// if (tokenPool === 'locked') {
|
||||
// getFee(simulateVestingDelegateToMixnode, { mixId: id, amount: value });
|
||||
@@ -217,7 +219,7 @@ export const DelegateModal: FCWithChildren<{
|
||||
try {
|
||||
// res = await tryConvertIdentityToMixId(idKey);
|
||||
} catch (e) {
|
||||
// Console.warn(`failed to resolve mix_id for "${idKey}": ${e}`);
|
||||
Console.warn(`failed to resolve mix_id for "${idKey}": ${e}`);
|
||||
return;
|
||||
}
|
||||
if (res) {
|
||||
@@ -234,38 +236,38 @@ export const DelegateModal: FCWithChildren<{
|
||||
resolveMixId(identityKey);
|
||||
}, [identityKey]);
|
||||
|
||||
// if (fee) {
|
||||
// return (
|
||||
// <ConfirmTx
|
||||
// open
|
||||
// header="Delegation details"
|
||||
// fee={fee}
|
||||
// onClose={onClose}
|
||||
// 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>
|
||||
// );
|
||||
// }
|
||||
if (fee) {
|
||||
return (
|
||||
<ConfirmTx
|
||||
open
|
||||
header="Delegation details"
|
||||
fee={fee}
|
||||
onClose={onClose}
|
||||
onPrev={resetFeeState}
|
||||
onConfirm={handleOk}
|
||||
>
|
||||
{balance.data && 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>
|
||||
);
|
||||
}
|
||||
|
||||
// if (feeError) {
|
||||
// return (
|
||||
// <ErrorModal
|
||||
// title="Something went wrong while calculating fee. Are you sure you entered a valid node address?"
|
||||
// message={feeError}
|
||||
// sx={sx}
|
||||
// open={open}
|
||||
// onClose={onClose}
|
||||
// />
|
||||
// );
|
||||
// }
|
||||
if (feeError) {
|
||||
return (
|
||||
<ErrorModal
|
||||
title="Something went wrong while calculating fee. Are you sure you entered a valid node address?"
|
||||
message={feeError}
|
||||
sx={sx}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SimpleModal
|
||||
@@ -318,7 +320,7 @@ export const DelegateModal: FCWithChildren<{
|
||||
/>
|
||||
</Box>
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<ModalListItem label="Account balance" value={balance.data} divider fontWeight={600} />
|
||||
<ModalListItem label="Account balance" value={`${balance.data} NYM`} divider fontWeight={600} />
|
||||
</Box>
|
||||
|
||||
<ModalListItem label="Rewards payout interval" value={rewardInterval} hidden divider />
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { Warning } from '@mui/icons-material';
|
||||
import { FeeDetails } from '@nymproject/types';
|
||||
import { Alert, AlertTitle, Box } from '@mui/material';
|
||||
import { isBalanceEnough } from '../utils';
|
||||
import { AppContext } from '../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>
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Box, SxProps } from '@mui/material';
|
||||
|
||||
export const ModalDivider: FCWithChildren<{
|
||||
sx?: SxProps;
|
||||
}> = ({ sx }) => <Box borderTop="1px solid" borderColor="rgba(141, 147, 153, 0.2)" my={1} sx={sx} />;
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { FeeDetails } from '@nymproject/types';
|
||||
import { CircularProgress } from '@mui/material';
|
||||
import { ModalListItem } from './ModalListItem';
|
||||
import { ModalDivider } from './ModalDivider';
|
||||
|
||||
type TFeeProps = { fee?: FeeDetails; isLoading: boolean; error?: string; divider?: boolean };
|
||||
type TTotalAmountProps = { fee?: FeeDetails; amount?: string; isLoading: boolean; error?: string; divider?: boolean };
|
||||
|
||||
const getValue = ({ fee, amount, isLoading, error }: TTotalAmountProps) => {
|
||||
if (isLoading) return <CircularProgress size={15} />;
|
||||
if (error && !isLoading) return 'n/a';
|
||||
if (fee) {
|
||||
const numericFee = Number(fee.amount?.amount);
|
||||
const numericAmountToTransfer = Number(amount);
|
||||
return amount
|
||||
? `${numericFee + numericAmountToTransfer} ${fee.amount?.denom.toUpperCase()}`
|
||||
: `${fee.amount?.amount} ${fee.amount?.denom.toUpperCase()}`;
|
||||
}
|
||||
return '-';
|
||||
};
|
||||
|
||||
export const ModalFee = ({ fee, isLoading, error, divider }: TFeeProps) => (
|
||||
<>
|
||||
<ModalListItem label="Fee for this transaction" value={getValue({ fee, isLoading, error })} />
|
||||
{divider && <ModalDivider />}
|
||||
</>
|
||||
);
|
||||
|
||||
export const ModalTotalAmount = ({ fee, amount, isLoading, error, divider }: TTotalAmountProps) => (
|
||||
<>
|
||||
<ModalListItem label="Total amount" value={getValue({ fee, amount, isLoading, error })} fontWeight={600} />
|
||||
{divider && <ModalDivider />}
|
||||
</>
|
||||
);
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Theme } from '@mui/material/styles';
|
||||
|
||||
export const backDropStyles = (theme: Theme) => {
|
||||
const { mode } = theme.palette;
|
||||
return {
|
||||
style: {
|
||||
left: mode === 'light' ? '0' : '50%',
|
||||
width: '50%',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const modalStyles = (theme: Theme) => {
|
||||
const { mode } = theme.palette;
|
||||
return { left: mode === 'light' ? '25%' : '75%' };
|
||||
};
|
||||
|
||||
export const dialogStyles = (theme: Theme) => {
|
||||
const { mode } = theme.palette;
|
||||
return { left: mode === 'light' ? '-50%' : '50%' };
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { sign } from '../requests';
|
||||
// import { Console } from 'src/utils/console';
|
||||
import { Console } from '../utils/console';
|
||||
// import { AppContext } from './main';
|
||||
|
||||
export type TBuyContext = {
|
||||
@@ -34,7 +34,7 @@ export const BuyContextProvider: FCWithChildren = ({ children }): JSX.Element =>
|
||||
try {
|
||||
signature = await sign(message);
|
||||
} catch (e: any) {
|
||||
// Console.log(`Sign message operation failed: ${e}`);
|
||||
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 {
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
} from '../requests';
|
||||
import Big from 'big.js';
|
||||
|
||||
// import { Console } from '../utils/console';
|
||||
import { Console } from '../utils/console';
|
||||
import { createSignInWindow, getReactState, setReactState } from '../requests/app';
|
||||
// import { toDisplay } from '../utils';
|
||||
|
||||
@@ -27,8 +27,7 @@ const toDisplay = (val: string | number | Big, dp = 4) => {
|
||||
try {
|
||||
displayValue = Big(val).toFixed(dp);
|
||||
} catch (e: any) {
|
||||
// Console.warn(`${displayValue} not a valid decimal number: ${e}`);
|
||||
console.log('object :>> ', `${displayValue} not a valid decimal number: ${e}`);
|
||||
Console.warn(`${displayValue} not a valid decimal number: ${e}`);
|
||||
}
|
||||
return displayValue;
|
||||
};
|
||||
@@ -149,7 +148,7 @@ export const AppProvider: FCWithChildren = ({ children }) => {
|
||||
setClientDetails(client);
|
||||
} catch (e) {
|
||||
enqueueSnackbar('Error loading account', { variant: 'error' });
|
||||
// Console.error(e as string);
|
||||
Console.error(e as string);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -164,7 +163,7 @@ export const AppProvider: FCWithChildren = ({ children }) => {
|
||||
const mixnode = await getMixnodeBondDetails();
|
||||
setMixnodeDetails(mixnode);
|
||||
} catch (e) {
|
||||
// Console.error(e as string);
|
||||
Console.error(e as string);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -180,7 +179,7 @@ export const AppProvider: FCWithChildren = ({ children }) => {
|
||||
const modeFromStorage = await forage.getItem({ key: 'nym-wallet-mode' })();
|
||||
if (modeFromStorage) setMode(modeFromStorage);
|
||||
} catch (e) {
|
||||
// Console.error(e);
|
||||
Console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -231,15 +230,15 @@ export const AppProvider: FCWithChildren = ({ children }) => {
|
||||
if (adminAddresses.length) {
|
||||
newValue = adminAddresses.includes(clientDetails?.client_address);
|
||||
if (newValue) {
|
||||
// Console.log('Wallet is in admin mode: ', {
|
||||
// network,
|
||||
// adminAddress: adminAddressMap[network],
|
||||
// clientAddress: clientDetails?.client_address,
|
||||
// });
|
||||
Console.log('Wallet is in admin mode: ', {
|
||||
network,
|
||||
adminAddress: adminAddressMap[network],
|
||||
clientAddress: clientDetails?.client_address,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Console.error('Failed to check admin addresses', e);
|
||||
Console.error('Failed to check admin addresses', e);
|
||||
}
|
||||
}
|
||||
setIsAdminAddress(newValue);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useContext, useEffect, useState } from 'react';
|
||||
// import { Console } from '../utils/console';
|
||||
import { Console } from '../utils/console';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import { AppContext } from '../context/main';
|
||||
import { checkGatewayOwnership, checkMixnodeOwnership, getVestingPledgeInfo } from '../requests';
|
||||
@@ -43,7 +43,7 @@ export const useCheckOwnership = () => {
|
||||
|
||||
setOwnership(status);
|
||||
} catch (e) {
|
||||
// Console.error(e as string);
|
||||
Console.error(e as string);
|
||||
setError(e as string);
|
||||
setOwnership(initial);
|
||||
} finally {
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
getSpendableVestedCoins,
|
||||
userBalance,
|
||||
} from '../requests';
|
||||
// import { Console } from '../utils/console';
|
||||
import { Console } from '../utils/console';
|
||||
|
||||
type TTokenAllocation = {
|
||||
[key in
|
||||
@@ -92,7 +92,7 @@ export const useGetBalance = (clientDetails?: Account): TUseuserBalance => {
|
||||
} catch (e) {
|
||||
clearTokenAllocation();
|
||||
clearOriginalVesting();
|
||||
// Console.error(e as string);
|
||||
Console.error(e as string);
|
||||
}
|
||||
}
|
||||
setIsLoading(false);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DecCoin, FeeDetails } from '@nymproject/types';
|
||||
import { useState } from 'react';
|
||||
// import { Console } from 'src/utils/console';
|
||||
import { Console } from '../utils/console';
|
||||
import { getCustomFees } from '../requests';
|
||||
|
||||
export function useGetFee() {
|
||||
@@ -26,7 +26,7 @@ export function useGetFee() {
|
||||
const fees = await getCustomFees({ feesAmount: amount });
|
||||
setFee(fees);
|
||||
} catch (e) {
|
||||
// Console.error(e);
|
||||
Console.error(e);
|
||||
setFeeError(e as string);
|
||||
}
|
||||
setIsFeeLoading(false);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
declare type FCWithChildren<P = {}> = React.FC<React.PropsWithChildren<P>>;
|
||||
@@ -0,0 +1,9 @@
|
||||
declare module '*.jpeg' {
|
||||
const value: any;
|
||||
export default value;
|
||||
}
|
||||
|
||||
declare module '*.jpg' {
|
||||
const value: any;
|
||||
export default value;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
declare module '*.json' {
|
||||
const content: any;
|
||||
export default content;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
declare module '*.png' {
|
||||
const content: any;
|
||||
export default content;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
declare module '*.svg' {
|
||||
const content: any;
|
||||
export default content;
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
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 {
|
||||
getCurrentInterval,
|
||||
getDefaultMixnodeCostParams,
|
||||
getLockedCoins,
|
||||
getSpendableCoins,
|
||||
userBalance,
|
||||
} from '../requests';
|
||||
import { Console } from './console';
|
||||
|
||||
export type TPoolOption = 'balance' | 'locked';
|
||||
|
||||
export const uNYMtoNYM = (unym: string, rounding = 6) => {
|
||||
const nym = Big(unym).div(1000000).toFixed(rounding);
|
||||
|
||||
return {
|
||||
asString: () => nym,
|
||||
asNumber: () => Number(nym),
|
||||
};
|
||||
};
|
||||
|
||||
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 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 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 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 };
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
/* eslint-disable no-console */
|
||||
import { config } from '../config';
|
||||
|
||||
export const Console = {
|
||||
log: (message?: any, ...optionalParams: any[]) =>
|
||||
config.IS_DEV_MODE ? console.log(message, ...optionalParams) : undefined,
|
||||
warn: (message?: any, ...optionalParams: any[]) =>
|
||||
config.IS_DEV_MODE ? console.warn(message, ...optionalParams) : undefined,
|
||||
error: (message?: any, ...optionalParams: any[]) => console.error(message, ...optionalParams),
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Console } from './console';
|
||||
|
||||
export type TauriReq<Req extends Function & ((a: any, b?: any) => Promise<any>)> = {
|
||||
name: Req['name'];
|
||||
request: () => ReturnType<Req>;
|
||||
onFulfilled: (value: Awaited<ReturnType<Req>>) => void;
|
||||
};
|
||||
|
||||
async function fireRequests(requests: TauriReq<any>[]) {
|
||||
const promises = await Promise.allSettled(requests.map((r) => r.request()));
|
||||
|
||||
promises.forEach((res, index) => {
|
||||
if (res.status === 'rejected') {
|
||||
Console.warn(`${requests[index].name} request fails`, res.reason);
|
||||
}
|
||||
if (res.status === 'fulfilled') {
|
||||
requests[index].onFulfilled(res.value as any);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default fireRequests;
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './common';
|
||||
export * from './fireRequests';
|
||||
export * from './console';
|
||||
export { default as fireRequests } from './fireRequests';
|
||||
@@ -0,0 +1,3 @@
|
||||
export const sleep = (delayMilliseconds: number) =>
|
||||
// eslint-disable-next-line no-promise-executor-return
|
||||
new Promise((resolve) => setTimeout(resolve, delayMilliseconds));
|
||||
Reference in New Issue
Block a user