WIP: Merge branch 'develop' into 348-bonding-settings
This commit is contained in:
@@ -142,11 +142,10 @@ export const Filters = () => {
|
||||
</Snackbar>
|
||||
<Button
|
||||
size="large"
|
||||
variant="text"
|
||||
color="inherit"
|
||||
variant="contained"
|
||||
endIcon={<Tune />}
|
||||
onClick={handleToggleShowFilters}
|
||||
sx={{ textTransform: 'none' }}
|
||||
sx={{ textTransform: 'none', width: isMobile ? '100%' : 'inherit' }}
|
||||
>
|
||||
Advanced filters
|
||||
</Button>
|
||||
|
||||
@@ -5,10 +5,16 @@ import { EconomicsInfoRowWithIndex } from './types';
|
||||
|
||||
const selectionChance = (economicDynamicsStats: ApiState<MixNodeEconomicDynamicsStatsResponse> | undefined) => {
|
||||
const inclusionProbability = economicDynamicsStats?.data?.active_set_inclusion_probability;
|
||||
// TODO: when v2 will be deployed, remove cases: VeryHigh, Moderate and VeryLow
|
||||
switch (inclusionProbability) {
|
||||
case 'VeryLow':
|
||||
return 'Very Low';
|
||||
case 'VeryHigh':
|
||||
return 'Very High';
|
||||
case 'High':
|
||||
case 'Good':
|
||||
case 'Low':
|
||||
case 'Moderate':
|
||||
return inclusionProbability;
|
||||
default:
|
||||
return '-';
|
||||
|
||||
@@ -19,12 +19,16 @@ const textColour = (value: EconomicsRowsType, field: string, theme: Theme) => {
|
||||
return theme.palette.warning.main;
|
||||
}
|
||||
if (field === 'selectionChance') {
|
||||
// TODO: when v2 will be deployed, remove cases: VeryHigh, Moderate and VeryLow
|
||||
switch (fieldValue) {
|
||||
case 'High':
|
||||
case 'VeryHigh':
|
||||
return theme.palette.nym.networkExplorer.selectionChance.overModerate;
|
||||
case 'Good':
|
||||
case 'Moderate':
|
||||
return theme.palette.nym.networkExplorer.selectionChance.moderate;
|
||||
case 'Low':
|
||||
case 'VeryLow':
|
||||
return theme.palette.nym.networkExplorer.selectionChance.underModerate;
|
||||
default:
|
||||
return theme.palette.nym.wallet.fee;
|
||||
|
||||
@@ -46,7 +46,7 @@ export const MixNodeStatusDropdown: React.FC<MixNodeStatusDropdownProps> = ({ st
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
width: isMobile ? 'auto' : 200,
|
||||
width: isMobile ? '50%' : 200,
|
||||
...sx,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -30,39 +30,40 @@ export const TableToolbar: React.FC<TableToolBarProps> = ({
|
||||
width: '100%',
|
||||
marginBottom: 2,
|
||||
display: 'flex',
|
||||
flexDirection: isMobile ? 'column-reverse' : 'row',
|
||||
flexDirection: isMobile ? 'column' : 'row',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'middle' }}>
|
||||
{childrenBefore}
|
||||
<Select
|
||||
labelId="simple-select-label"
|
||||
id="simple-select"
|
||||
value={pageSize}
|
||||
onChange={onChangePageSize}
|
||||
sx={{
|
||||
width: isMobile ? 100 : 200,
|
||||
}}
|
||||
>
|
||||
<MenuItem value={10} data-testid="ten">
|
||||
10
|
||||
</MenuItem>
|
||||
<MenuItem value={30} data-testid="thirty">
|
||||
30
|
||||
</MenuItem>
|
||||
<MenuItem value={50} data-testid="fifty">
|
||||
50
|
||||
</MenuItem>
|
||||
<MenuItem value={100} data-testid="hundred">
|
||||
100
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Box sx={{ display: 'flex', flexDirection: isMobile ? 'column-reverse' : 'row', alignItems: 'middle' }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
{childrenBefore}
|
||||
<Select
|
||||
labelId="simple-select-label"
|
||||
id="simple-select"
|
||||
value={pageSize}
|
||||
onChange={onChangePageSize}
|
||||
sx={{
|
||||
width: isMobile ? '50%' : 200,
|
||||
marginRight: isMobile ? 0 : 2,
|
||||
}}
|
||||
>
|
||||
<MenuItem value={10} data-testid="ten">
|
||||
10
|
||||
</MenuItem>
|
||||
<MenuItem value={30} data-testid="thirty">
|
||||
30
|
||||
</MenuItem>
|
||||
<MenuItem value={50} data-testid="fifty">
|
||||
50
|
||||
</MenuItem>
|
||||
<MenuItem value={100} data-testid="hundred">
|
||||
100
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</Box>
|
||||
<TextField
|
||||
sx={{
|
||||
width: isMobile ? '100%' : 350,
|
||||
width: isMobile ? '100%' : 200,
|
||||
marginBottom: isMobile ? 2 : 0,
|
||||
}}
|
||||
value={searchTerm}
|
||||
@@ -70,6 +71,16 @@ export const TableToolbar: React.FC<TableToolBarProps> = ({
|
||||
placeholder="search"
|
||||
onChange={(event) => onChangeSearch(event.target.value)}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'end',
|
||||
gap: 1,
|
||||
marginTop: isMobile ? 2 : 0,
|
||||
}}
|
||||
>
|
||||
{withFilters && <Filters />}
|
||||
{childrenAfter}
|
||||
</Box>
|
||||
|
||||
@@ -215,7 +215,8 @@ export type UptimeStoryResponse = {
|
||||
|
||||
export type MixNodeEconomicDynamicsStatsResponse = {
|
||||
stake_saturation: number;
|
||||
active_set_inclusion_probability: 'High' | 'Good' | 'Low';
|
||||
// TODO: when v2 will be deployed, remove cases: VeryHigh, Moderate and VeryLow
|
||||
active_set_inclusion_probability: 'High' | 'Good' | 'Low' | 'VeryLow' | 'Moderate' | 'VeryHigh';
|
||||
reserve_set_inclusion_probability: 'High' | 'Good' | 'Low';
|
||||
estimated_total_node_reward: number;
|
||||
estimated_operator_reward: number;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Alert, Box, Paper, Dialog, DialogContent, DialogTitle, IconButton, Stack, Typography } from '@mui/material';
|
||||
import { Close } from '@mui/icons-material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
|
||||
const passwordCreationSteps = [
|
||||
'Log out',
|
||||
@@ -10,34 +11,46 @@ const passwordCreationSteps = [
|
||||
'Now you can create multiple accounts',
|
||||
];
|
||||
|
||||
export const MultiAccountHowTo = ({ show, handleClose }: { show: boolean; handleClose: () => void }) => (
|
||||
<Dialog open={show} onClose={handleClose} fullWidth PaperComponent={Paper} PaperProps={{ elevation: 0 }}>
|
||||
<DialogTitle>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="h6">Multi accounts</Typography>
|
||||
<IconButton onClick={handleClose}>
|
||||
<Close />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Typography variant="body1" sx={{ color: (theme) => theme.palette.nym.text.muted }}>
|
||||
How to set up multiple accounts
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<Stack spacing={2}>
|
||||
<Alert
|
||||
severity="warning"
|
||||
icon={false}
|
||||
sx={(t) => (t.palette.mode === 'dark' ? { bgcolor: (theme) => theme.palette.background.paper } : {})}
|
||||
>
|
||||
<Typography>In order to create multiple accounts your wallet needs a password.</Typography>
|
||||
<Typography>Follow steps below to create password.</Typography>
|
||||
</Alert>
|
||||
<Typography>How to create a password for your account</Typography>
|
||||
{passwordCreationSteps.map((step, index) => (
|
||||
<Typography key={step}>{`${index + 1}. ${step}`}</Typography>
|
||||
))}
|
||||
</Stack>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
export const MultiAccountHowTo = ({ show, handleClose }: { show: boolean; handleClose: () => void }) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Dialog
|
||||
open={show}
|
||||
onClose={handleClose}
|
||||
fullWidth
|
||||
PaperProps={{
|
||||
style: { border: `1px solid ${theme.palette.nym.nymWallet.modal.border}` },
|
||||
}}
|
||||
>
|
||||
<Paper>
|
||||
<DialogTitle>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="h6">Multi accounts</Typography>
|
||||
<IconButton onClick={handleClose}>
|
||||
<Close />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Typography variant="body1" sx={{ color: (theme) => theme.palette.nym.text.muted }}>
|
||||
How to set up multiple accounts
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<Stack spacing={2}>
|
||||
<Alert
|
||||
severity="warning"
|
||||
icon={false}
|
||||
sx={(t) => (t.palette.mode === 'dark' ? { bgcolor: (theme) => theme.palette.background.paper } : {})}
|
||||
>
|
||||
<Typography>In order to create multiple accounts your wallet needs a password.</Typography>
|
||||
<Typography>Follow steps below to create password.</Typography>
|
||||
</Alert>
|
||||
<Typography>How to create a password for your account</Typography>
|
||||
{passwordCreationSteps.map((step, index) => (
|
||||
<Typography key={step}>{`${index + 1}. ${step}`}</Typography>
|
||||
))}
|
||||
</Stack>
|
||||
</DialogContent>
|
||||
</Paper>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
Divider,
|
||||
} from '@mui/material';
|
||||
import { Add, ArrowDownwardSharp, Close } from '@mui/icons-material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { AccountsContext } from 'src/context';
|
||||
import { AccountItem } from '../AccountItem';
|
||||
import { ConfirmPasswordModal } from './ConfirmPasswordModal';
|
||||
@@ -21,6 +22,8 @@ export const AccountsModal = () => {
|
||||
useContext(AccountsContext);
|
||||
const [accountToSwitchTo, setAccountToSwitchTo] = useState<string>();
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const handleClose = () => {
|
||||
setDialogToDisplay(undefined);
|
||||
setError(undefined);
|
||||
@@ -47,48 +50,51 @@ export const AccountsModal = () => {
|
||||
open={dialogToDisplay === 'Accounts'}
|
||||
onClose={handleClose}
|
||||
fullWidth
|
||||
PaperComponent={Paper}
|
||||
PaperProps={{ elevation: 0 }}
|
||||
PaperProps={{
|
||||
style: { border: `1px solid ${theme.palette.nym.nymWallet.modal.border}` },
|
||||
}}
|
||||
>
|
||||
<DialogTitle>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="h6">Accounts</Typography>
|
||||
<IconButton onClick={handleClose}>
|
||||
<Close />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Typography fontSize="small" sx={{ color: 'grey.600' }}>
|
||||
Switch between accounts
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ padding: 0 }}>
|
||||
{accounts?.map(({ id, address }) => (
|
||||
<AccountItem
|
||||
name={id}
|
||||
address={address}
|
||||
key={address}
|
||||
onSelectAccount={() => {
|
||||
if (selectedAccount?.id !== id) {
|
||||
setAccountToSwitchTo(id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</DialogContent>
|
||||
<Divider variant="middle" sx={{ mt: 3 }} />
|
||||
<DialogActions sx={{ p: 3 }}>
|
||||
<Button startIcon={<ArrowDownwardSharp />} onClick={() => setDialogToDisplay('Import')}>
|
||||
Import account
|
||||
</Button>
|
||||
<Button
|
||||
disableElevation
|
||||
variant="contained"
|
||||
startIcon={<Add fontSize="small" />}
|
||||
onClick={() => setDialogToDisplay('Add')}
|
||||
>
|
||||
Add new account
|
||||
</Button>
|
||||
</DialogActions>
|
||||
<Paper>
|
||||
<DialogTitle>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="h6">Accounts</Typography>
|
||||
<IconButton onClick={handleClose}>
|
||||
<Close />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Typography fontSize="small" sx={{ color: 'grey.600' }}>
|
||||
Switch between accounts
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ padding: 0 }}>
|
||||
{accounts?.map(({ id, address }) => (
|
||||
<AccountItem
|
||||
name={id}
|
||||
address={address}
|
||||
key={address}
|
||||
onSelectAccount={() => {
|
||||
if (selectedAccount?.id !== id) {
|
||||
setAccountToSwitchTo(id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</DialogContent>
|
||||
<Divider variant="middle" sx={{ mt: 3 }} />
|
||||
<DialogActions sx={{ p: 3 }}>
|
||||
<Button startIcon={<ArrowDownwardSharp />} onClick={() => setDialogToDisplay('Import')}>
|
||||
Import account
|
||||
</Button>
|
||||
<Button
|
||||
disableElevation
|
||||
variant="contained"
|
||||
startIcon={<Add fontSize="small" />}
|
||||
onClick={() => setDialogToDisplay('Add')}
|
||||
>
|
||||
Add new account
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Paper>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { Box, Paper, Dialog, DialogTitle, IconButton, Typography } from '@mui/material';
|
||||
import { ArrowBack } from '@mui/icons-material';
|
||||
import { Paper, Dialog, DialogTitle, Typography } from '@mui/material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { ConfirmPassword } from 'src/components/ConfirmPassword';
|
||||
import { AccountsContext } from 'src/context';
|
||||
|
||||
@@ -14,28 +14,32 @@ export const ConfirmPasswordModal = ({
|
||||
onConfirm: (password: string) => Promise<void>;
|
||||
}) => {
|
||||
const { isLoading, error } = useContext(AccountsContext);
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={Boolean(accountName)}
|
||||
onClose={onClose}
|
||||
fullWidth
|
||||
PaperComponent={Paper}
|
||||
PaperProps={{ elevation: 0 }}
|
||||
PaperProps={{
|
||||
style: { border: `1px solid ${theme.palette.nym.nymWallet.modal.border}` },
|
||||
}}
|
||||
>
|
||||
<DialogTitle>
|
||||
<Typography variant="h6">Switch account</Typography>
|
||||
<Typography fontSize="small" sx={{ color: 'grey.600' }}>
|
||||
Confirm password
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<ConfirmPassword
|
||||
onConfirm={onConfirm}
|
||||
error={error}
|
||||
isLoading={isLoading}
|
||||
buttonTitle="Switch account"
|
||||
onCancel={onClose}
|
||||
/>
|
||||
<Paper>
|
||||
<DialogTitle>
|
||||
<Typography variant="h6">Switch account</Typography>
|
||||
<Typography fontSize="small" sx={{ color: 'grey.600' }}>
|
||||
Confirm password
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<ConfirmPassword
|
||||
onConfirm={onConfirm}
|
||||
error={error}
|
||||
isLoading={isLoading}
|
||||
buttonTitle="Switch account"
|
||||
onCancel={onClose}
|
||||
/>
|
||||
</Paper>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { Close } from '@mui/icons-material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { AccountsContext } from 'src/context';
|
||||
|
||||
export const EditAccountModal = () => {
|
||||
@@ -19,6 +20,8 @@ export const EditAccountModal = () => {
|
||||
|
||||
const { accountToEdit, dialogToDisplay, setDialogToDisplay, handleEditAccount } = useContext(AccountsContext);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
setAccountName(accountToEdit ? accountToEdit?.id : '');
|
||||
}, [accountToEdit]);
|
||||
@@ -28,48 +31,51 @@ export const EditAccountModal = () => {
|
||||
open={dialogToDisplay === 'Edit'}
|
||||
onClose={() => setDialogToDisplay('Accounts')}
|
||||
fullWidth
|
||||
PaperComponent={Paper}
|
||||
PaperProps={{ elevation: 0 }}
|
||||
PaperProps={{
|
||||
style: { border: `1px solid ${theme.palette.nym.nymWallet.modal.border}` },
|
||||
}}
|
||||
>
|
||||
<DialogTitle>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="h6">Edit account name</Typography>
|
||||
<IconButton onClick={() => setDialogToDisplay('Accounts')}>
|
||||
<Close />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Typography fontSize="small" sx={{ color: 'grey.600' }}>
|
||||
New wallet address
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ p: 0 }}>
|
||||
<Box sx={{ px: 3, mt: 1 }}>
|
||||
<TextField
|
||||
label="Account name"
|
||||
<Paper>
|
||||
<DialogTitle>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="h6">Edit account name</Typography>
|
||||
<IconButton onClick={() => setDialogToDisplay('Accounts')}>
|
||||
<Close />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Typography fontSize="small" sx={{ color: 'grey.600' }}>
|
||||
New wallet address
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ p: 0 }}>
|
||||
<Box sx={{ px: 3, mt: 1 }}>
|
||||
<TextField
|
||||
label="Account name"
|
||||
fullWidth
|
||||
value={accountName}
|
||||
onChange={(e) => setAccountName(e.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ p: 3 }}>
|
||||
<Button
|
||||
fullWidth
|
||||
value={accountName}
|
||||
onChange={(e) => setAccountName(e.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ p: 3 }}>
|
||||
<Button
|
||||
fullWidth
|
||||
disableElevation
|
||||
variant="contained"
|
||||
size="large"
|
||||
onClick={() => {
|
||||
if (accountToEdit) {
|
||||
handleEditAccount({ ...accountToEdit, id: accountName });
|
||||
setDialogToDisplay('Accounts');
|
||||
}
|
||||
}}
|
||||
disabled={!accountName?.length}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
</DialogActions>
|
||||
disableElevation
|
||||
variant="contained"
|
||||
size="large"
|
||||
onClick={() => {
|
||||
if (accountToEdit) {
|
||||
handleEditAccount({ ...accountToEdit, id: accountName });
|
||||
setDialogToDisplay('Accounts');
|
||||
}
|
||||
}}
|
||||
disabled={!accountName?.length}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Paper>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,11 +14,11 @@ export const Bond = ({
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
alignItems: 'flex-end',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<Typography>Bond a mixnode or a gateway</Typography>
|
||||
<Typography variant="body2">Bond a mixnode or a gateway</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
|
||||
@@ -157,7 +157,7 @@ const AmountFormData = ({
|
||||
|
||||
return (
|
||||
<Stack gap={3}>
|
||||
<Box display="flex" gap={3} justifyContent="center" sx={{ mt: 2 }}>
|
||||
<Box display="flex" gap={3} justifyContent="center">
|
||||
{hasVestingTokens && <TokenPoolSelector disabled={false} onSelect={(pool) => setValue('tokenPool', pool)} />}
|
||||
<CurrencyFormField
|
||||
required
|
||||
@@ -205,7 +205,7 @@ export const BondMixnodeForm = ({
|
||||
<>
|
||||
{step === 1 && (
|
||||
<>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<NodeTypeSelector disabled={false} setNodeType={onSelectNodeType} nodeType="mixnode" />
|
||||
</Box>
|
||||
<NodeFormData onNext={onValidateMixnodeData} mixnodeData={mixnodeData} />
|
||||
|
||||
@@ -152,18 +152,16 @@ export const BondMixnodeModal = ({
|
||||
subHeader={`Step ${step}/2`}
|
||||
okLabel="Next"
|
||||
>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<BondMixnodeForm
|
||||
step={step}
|
||||
denom={denom}
|
||||
mixnodeData={mixnodeData}
|
||||
amountData={amountData}
|
||||
hasVestingTokens={hasVestingTokens}
|
||||
onValidateMixnodeData={handleUpdateMixnodeData}
|
||||
onValidateAmountData={handleUpdateAmountData}
|
||||
onSelectNodeType={onSelectNodeType}
|
||||
/>
|
||||
</Box>
|
||||
<BondMixnodeForm
|
||||
step={step}
|
||||
denom={denom}
|
||||
mixnodeData={mixnodeData}
|
||||
amountData={amountData}
|
||||
hasVestingTokens={hasVestingTokens}
|
||||
onValidateMixnodeData={handleUpdateMixnodeData}
|
||||
onValidateAmountData={handleUpdateAmountData}
|
||||
onSelectNodeType={onSelectNodeType}
|
||||
/>
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -53,13 +53,15 @@ export const NodeSettings = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: this will have to be updated with allowing users to provide their operating cost in the form
|
||||
const defaultCostParams = await attachDefaultOperatingCost(toPercentFloatString(pm));
|
||||
if (pm) {
|
||||
// TODO: this will have to be updated with allowing users to provide their operating cost in the form
|
||||
const defaultCostParams = await attachDefaultOperatingCost(toPercentFloatString(pm));
|
||||
|
||||
if (isVesting) {
|
||||
await getFee(simulateVestingUpdateMixnodeCostParams, defaultCostParams);
|
||||
} else {
|
||||
await getFee(simulateUpdateMixnodeCostParams, defaultCostParams);
|
||||
if (isVesting) {
|
||||
await getFee(simulateVestingUpdateMixnodeCostParams, defaultCostParams);
|
||||
} else {
|
||||
await getFee(simulateUpdateMixnodeCostParams, defaultCostParams);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -112,7 +114,7 @@ export const NodeSettings = ({
|
||||
Set profit margin
|
||||
</Typography>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<TextField placeholder="Profit margin" value={pm} onChange={(e) => setPm(e.target.value)} fullWidth />
|
||||
<TextField label="Profit margin" value={pm} onChange={(e) => setPm(e.target.value)} fullWidth />
|
||||
{error && (
|
||||
<FormHelperText sx={{ color: 'error.main' }}>
|
||||
Profit margin should be a number between 0 and 100
|
||||
|
||||
@@ -10,9 +10,9 @@ export default {
|
||||
|
||||
const Template: ComponentStory<typeof ConfirmTx> = (args) => (
|
||||
<ConfirmTx {...args}>
|
||||
<ModalListItem label="Transaction type:" value="Bond" divider />
|
||||
<ModalListItem label="Current bond:" value="100 NYM" divider />
|
||||
<ModalListItem label="Additional bond:" value="50 NYM" divider />
|
||||
<ModalListItem label="Transaction type" value="Bond" divider />
|
||||
<ModalListItem label="Current bond" value="100 NYM" divider />
|
||||
<ModalListItem label="Additional bond" value="50 NYM" divider />
|
||||
</ConfirmTx>
|
||||
);
|
||||
|
||||
|
||||
@@ -213,8 +213,8 @@ export const DelegateModal: React.FC<{
|
||||
onPrev={resetFeeState}
|
||||
onConfirm={handleOk}
|
||||
>
|
||||
<ModalListItem label="Node identity key:" value={identityKey} divider />
|
||||
<ModalListItem label="Amount:" value={`${amount} ${denom.toUpperCase()}`} divider />
|
||||
<ModalListItem label="Node identity key" value={identityKey} divider />
|
||||
<ModalListItem label="Amount" value={`${amount} ${denom.toUpperCase()}`} divider />
|
||||
</ConfirmTx>
|
||||
);
|
||||
}
|
||||
@@ -267,7 +267,7 @@ export const DelegateModal: React.FC<{
|
||||
>
|
||||
{errorIdentityKey}
|
||||
</Typography>
|
||||
<Box display="flex" gap={3} alignItems="center" sx={{ mt: 3 }}>
|
||||
<Box display="flex" gap={2} alignItems="center" sx={{ mt: 3 }}>
|
||||
{hasVestingContract && <TokenPoolSelector disabled={false} onSelect={(pool) => setTokenPool(pool)} />}
|
||||
<CurrencyFormField
|
||||
required
|
||||
|
||||
@@ -18,7 +18,24 @@ export const Mnemonic = ({
|
||||
Below is your 24 word mnemonic, make sure to store it in a safe place for accessing your wallet in the future
|
||||
</Typography>
|
||||
</Warning>
|
||||
<TextField multiline rows={3} value={mnemonic} fullWidth />
|
||||
<TextField
|
||||
label="Mnemonic"
|
||||
type="input"
|
||||
value={mnemonic}
|
||||
multiline
|
||||
autoFocus={false}
|
||||
fullWidth
|
||||
inputProps={{
|
||||
style: {
|
||||
height: '160px',
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
'input::-webkit-textfield-decoration-container': {
|
||||
alignItems: 'start',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
color="inherit"
|
||||
|
||||
@@ -11,7 +11,10 @@ export const ErrorModal: React.FC<{
|
||||
onClose: () => void;
|
||||
}> = ({ children, open, title, message, sx, backdropProps, onClose }) => (
|
||||
<Modal open={open} onClose={onClose} BackdropProps={backdropProps}>
|
||||
<Box sx={{ ...modalStyle, ...sx }} textAlign="center">
|
||||
<Box
|
||||
sx={{ border: (t) => `1px solid ${t.palette.nym.nymWallet.modal.border}`, ...modalStyle, ...sx }}
|
||||
textAlign="center"
|
||||
>
|
||||
<Typography color={(theme) => theme.palette.error.main} mb={1}>
|
||||
{title || 'Oh no! Something went wrong...'}
|
||||
</Typography>
|
||||
|
||||
@@ -18,7 +18,10 @@ export const LoadingModal: React.FC<{
|
||||
backdropProps?: object;
|
||||
}> = ({ sx, backdropProps }) => (
|
||||
<Modal open BackdropProps={backdropProps}>
|
||||
<Box sx={sx ? { ...modalStyle, ...sx } : { ...modalStyle }} textAlign="center">
|
||||
<Box
|
||||
sx={{ border: (t) => `1px solid ${t.palette.nym.nymWallet.modal.border}`, ...modalStyle, ...sx }}
|
||||
textAlign="center"
|
||||
>
|
||||
<Stack spacing={4} direction="row" alignItems="center">
|
||||
<CircularProgress />
|
||||
<Typography sx={{ color: 'text.primary' }}>Please wait...</Typography>
|
||||
|
||||
@@ -5,17 +5,33 @@ 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, isLoading, error }: TFeeProps) => {
|
||||
const getValue = ({ fee, amount, isLoading, error }: TTotalAmountProps) => {
|
||||
if (isLoading) return <CircularProgress size={15} />;
|
||||
if (error && !isLoading) return 'n/a';
|
||||
if (fee) return `${fee.amount?.amount} ${fee.amount?.denom.toUpperCase()}`;
|
||||
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 operation" value={getValue({ fee, isLoading, error })} />
|
||||
<ModalListItem label="Fee for this transaction" value={getValue({ fee, isLoading, error })} />
|
||||
{divider && <ModalDivider />}
|
||||
</>
|
||||
);
|
||||
|
||||
export const ModalTotalAmount = ({ fee, amount, isLoading, error, divider }: TTotalAmountProps) => {
|
||||
return (
|
||||
<>
|
||||
<ModalListItem label="Total amount" value={getValue({ fee, amount, isLoading, error })} fontWeight={600} />
|
||||
{divider && <ModalDivider />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,11 +12,11 @@ export const ModalListItem: React.FC<{
|
||||
}> = ({ label, value, hidden, fontWeight, divider }) => (
|
||||
<Box sx={{ display: hidden ? 'none' : 'block' }}>
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
<Typography fontSize="smaller" fontWeight={fontWeight} sx={{ color: 'text.primary' }}>
|
||||
<Typography fontSize="smaller" fontWeight={fontWeight} sx={{ color: 'text.primary', fontSize: 14 }}>
|
||||
{label}
|
||||
</Typography>
|
||||
{value && (
|
||||
<Typography fontSize="smaller" fontWeight={fontWeight} sx={{ color: 'text.primary' }}>
|
||||
<Typography fontSize="smaller" fontWeight={fontWeight} sx={{ color: 'text.primary', fontSize: 14 }}>
|
||||
{value}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
@@ -43,7 +43,7 @@ export const SimpleModal: React.FC<{
|
||||
backdropProps,
|
||||
}) => (
|
||||
<Modal open={open} onClose={onClose} BackdropProps={backdropProps}>
|
||||
<Box sx={{ ...modalStyle, ...sx }}>
|
||||
<Box sx={{ border: (t) => `1px solid ${t.palette.nym.nymWallet.modal.border}`, ...modalStyle, ...sx }}>
|
||||
{displayErrorIcon && <ErrorOutline color="error" sx={{ mb: 3 }} />}
|
||||
{displayInfoIcon && <InfoOutlinedIcon sx={{ mb: 2, color: (theme) => theme.palette.nym.nymWallet.text.blue }} />}
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
@@ -58,7 +58,7 @@ export const SimpleModal: React.FC<{
|
||||
</Stack>
|
||||
|
||||
<Typography
|
||||
mt={0.5}
|
||||
mt={subHeader ? 0.5 : 0}
|
||||
mb={3}
|
||||
fontSize={12}
|
||||
color={(theme) => theme.palette.text.secondary}
|
||||
@@ -73,7 +73,7 @@ export const SimpleModal: React.FC<{
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mt: 2, width: buttonFullWidth ? '100%' : null }}>
|
||||
{onBack && <StyledBackButton onBack={onBack} />}
|
||||
{onOk && (
|
||||
<Button variant="contained" fullWidth size="large" onClick={onOk} disabled={okDisabled}>
|
||||
<Button variant="contained" fullWidth size="large" onClick={onOk} disabled={okDisabled} sx={{ mt: 3 }}>
|
||||
{okLabel}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -35,7 +35,7 @@ export const NymCard: React.FC<{
|
||||
{noPadding ? (
|
||||
<CardContentNoPadding>{children}</CardContentNoPadding>
|
||||
) : (
|
||||
<CardContent sx={{ p: 3 }}>{children}</CardContent>
|
||||
<CardContent sx={{ p: 3, paddingTop: 0 }}>{children}</CardContent>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -1,35 +1,52 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { AppContext } from 'src/context';
|
||||
import { Box, Stack, Typography, SxProps, Dialog, DialogTitle, DialogContent, Paper } from '@mui/material';
|
||||
import { Box, Stack, SxProps } from '@mui/material';
|
||||
import QRCode from 'qrcode.react';
|
||||
import { ClientAddress } from '../ClientAddress';
|
||||
import { ModalListItem } from '../Modals/ModalListItem';
|
||||
import { Close as CloseIcon } from '@mui/icons-material';
|
||||
import { SimpleModal } from '../Modals/SimpleModal';
|
||||
|
||||
export const ReceiveModal = ({ onClose }: { onClose: () => void; sx?: SxProps; backdropProps?: object }) => {
|
||||
const { clientDetails, mode } = useContext(AppContext);
|
||||
export const ReceiveModal = ({
|
||||
onClose,
|
||||
sx,
|
||||
backdropProps,
|
||||
}: {
|
||||
onClose: () => void;
|
||||
sx?: SxProps;
|
||||
backdropProps?: object;
|
||||
}) => {
|
||||
const { clientDetails } = useContext(AppContext);
|
||||
return (
|
||||
<Dialog open maxWidth="sm" fullWidth onClose={onClose} PaperComponent={Paper} PaperProps={{ elevation: 0 }}>
|
||||
<DialogTitle>
|
||||
<Box sx={{ mt: 1, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Typography fontSize={20} fontWeight={600}>
|
||||
Receive
|
||||
</Typography>
|
||||
<CloseIcon onClick={onClose} cursor="pointer" />
|
||||
</Box>
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ p: 0 }}>
|
||||
<Box sx={{ px: 3 }}>
|
||||
<ModalListItem label="Your address:" value={<ClientAddress withCopy showEntireAddress />} />
|
||||
</Box>
|
||||
<SimpleModal
|
||||
header="Receive"
|
||||
subHeader="Provide your address to receive tokens"
|
||||
open
|
||||
onClose={onClose}
|
||||
okLabel=""
|
||||
sx={sx}
|
||||
backdropProps={backdropProps}
|
||||
subHeaderStyles={{ mb: 0 }}
|
||||
>
|
||||
<Stack gap={3} sx={{ position: 'relative', top: '32px' }}>
|
||||
<ModalListItem label="Your address" value={<ClientAddress withCopy showEntireAddress />} />
|
||||
<Stack
|
||||
alignItems="center"
|
||||
sx={{ px: 0, py: 3, mt: 3, bgcolor: mode === 'light' ? 'rgba(251, 110, 78, 5%)' : 'nym.background.dark' }}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
left: '-32px',
|
||||
width: '598px',
|
||||
py: 4,
|
||||
bgcolor: (t) => (t.palette.mode === 'dark' ? t.palette.background.default : 'rgba(251, 110, 78, 5%)'),
|
||||
borderRadius: '0px 0px 16px 16px',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
border: (t) => `1px solid ${mode === 'light' ? t.palette.nym.highlight : t.palette.nym.text.grey} `,
|
||||
bgcolor: mode === 'light' ? 'white' : 'nym.background.main',
|
||||
border: (t) =>
|
||||
t.palette.mode === 'dark'
|
||||
? `1px solid ${t.palette.nym.nymWallet.modal.border}`
|
||||
: `1px solid ${t.palette.nym.highlight}`,
|
||||
bgcolor: (t) => (t.palette.mode === 'dark' ? 'transparent' : 'white'),
|
||||
borderRadius: 2,
|
||||
p: 3,
|
||||
}}
|
||||
@@ -37,7 +54,7 @@ export const ReceiveModal = ({ onClose }: { onClose: () => void; sx?: SxProps; b
|
||||
{clientDetails && <QRCode data-testid="qr-code" value={clientDetails?.client_address} />}
|
||||
</Box>
|
||||
</Stack>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</Stack>
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Stack, SxProps } from '@mui/material';
|
||||
import { FeeDetails, DecCoin, CurrencyDenom } from '@nymproject/types';
|
||||
import { SimpleModal } from '../Modals/SimpleModal';
|
||||
import { ModalListItem } from '../Modals/ModalListItem';
|
||||
import { ModalFee } from '../Modals/ModalFee';
|
||||
import { ModalFee, ModalTotalAmount } from '../Modals/ModalFee';
|
||||
|
||||
export const SendDetailsModal = ({
|
||||
amount,
|
||||
@@ -38,11 +38,12 @@ export const SendDetailsModal = ({
|
||||
sx={sx}
|
||||
backdropProps={backdropProps}
|
||||
>
|
||||
<Stack gap={0.5} sx={{ mt: 4 }}>
|
||||
<ModalListItem label="From:" value={fromAddress} divider />
|
||||
<ModalListItem label="To:" value={toAddress} divider />
|
||||
<ModalListItem label="Amount:" value={`${amount?.amount} ${denom.toUpperCase()}`} divider />
|
||||
<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} />
|
||||
<ModalTotalAmount fee={fee} amount={amount?.amount} divider isLoading={false} />
|
||||
</Stack>
|
||||
</SimpleModal>
|
||||
);
|
||||
|
||||
@@ -55,8 +55,8 @@ export const SendInputModal = ({
|
||||
sx={sx}
|
||||
backdropProps={backdropProps}
|
||||
>
|
||||
<Stack gap={3} sx={{ mt: 3 }}>
|
||||
<ModalListItem label="Your address:" value={fromAddress} fontWeight="light" />
|
||||
<Stack gap={3}>
|
||||
<ModalListItem label="Your address" value={fromAddress} fontWeight="light" />
|
||||
<TextField
|
||||
label="Recipient address"
|
||||
fullWidth
|
||||
@@ -77,8 +77,8 @@ export const SendInputModal = ({
|
||||
{error}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack gap={0.5}>
|
||||
<ModalListItem label="Account balance:" value={balance?.toUpperCase()} divider fontWeight={600} />
|
||||
<Stack gap={0.5} sx={{ mt: 1 }}>
|
||||
<ModalListItem label="Account balance" value={balance?.toUpperCase()} divider fontWeight={600} />
|
||||
<Typography fontSize="smaller" sx={{ color: 'text.primary' }}>
|
||||
Est. fee for this transaction will be show on the next page
|
||||
</Typography>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { Button } from '@mui/material';
|
||||
import { Button, SxProps } from '@mui/material';
|
||||
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
|
||||
|
||||
export const StyledBackButton = ({ onBack }: { onBack: () => void }) => (
|
||||
<Button disableFocusRipple size="large" variant="outlined" onClick={onBack}>
|
||||
export const StyledBackButton = ({ onBack, sx }: { onBack: () => void; sx?: SxProps }) => (
|
||||
<Button disableFocusRipple size="large" variant="outlined" onClick={onBack} sx={sx}>
|
||||
<ArrowBackIosNewIcon fontSize="small" />
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -167,16 +167,15 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
|
||||
Console.warn(`get_operator_rewards request failed: ${e}`);
|
||||
}
|
||||
if (data) {
|
||||
<<<<<<< HEAD
|
||||
const { mix_node } = data;
|
||||
const { status, stakeSaturation, numberOfDelegators } = await getAdditionalMixnodeDetails(
|
||||
data.mix_node.identity_key,
|
||||
=======
|
||||
// const { mix_node } = data;
|
||||
// const { status, stakeSaturation, numberOfDelegators } = await getAdditionalMixnodeDetails(
|
||||
// data.mix_node.identity_key,
|
||||
// );
|
||||
console.log('data', data);
|
||||
const { status, stakeSaturation } = await getAdditionalMixnodeDetails(data.bond_information.id);
|
||||
const nodeDescription = await getNodeDescription(
|
||||
data.bond_information.mix_node.host,
|
||||
data.bond_information.mix_node.http_api_port,
|
||||
>>>>>>> develop
|
||||
);
|
||||
setBondedNode({
|
||||
name: nodeDescription?.name,
|
||||
@@ -193,13 +192,20 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
|
||||
operatorRewards,
|
||||
status,
|
||||
stakeSaturation,
|
||||
host: mix_node.host.replace(/\s/g, ''),
|
||||
httpApiPort: mix_node.http_api_port,
|
||||
mixPort: mix_node.mix_port,
|
||||
profitMarginPercent: mix_node.profit_margin_percent,
|
||||
verlocPort: mix_node.verloc_port,
|
||||
version: mix_node.version,
|
||||
host: '1.2.34.5',
|
||||
httpApiPort: 8000,
|
||||
mixPort: 1789,
|
||||
profitMarginPercent: 10,
|
||||
verlocPort: 1790,
|
||||
version: '1.0.2',
|
||||
} as TBondedMixnode);
|
||||
|
||||
// host: mix_node.host.replace(/\s/g, ''),
|
||||
// httpApiPort: mix_node.http_api_port,
|
||||
// mixPort: mix_node.mix_port,
|
||||
// profitMarginPercent: mix_node.profit_margin_percent,
|
||||
// verlocPort: mix_node.verloc_port,
|
||||
// version: mix_node.version,
|
||||
}
|
||||
} catch (e: any) {
|
||||
Console.warn(e);
|
||||
|
||||
@@ -87,12 +87,12 @@ export const TransferModal = ({ onClose }: { onClose: () => void }) => {
|
||||
) : (
|
||||
<>
|
||||
<ModalListItem
|
||||
label="Unlocked transferrable tokens:"
|
||||
label="Unlocked transferrable tokens"
|
||||
value={`${userBalance.tokenAllocation?.spendable} ${clientDetails?.display_mix_denom.toUpperCase()}`}
|
||||
divider
|
||||
/>
|
||||
<ModalListItem
|
||||
label="Est. fee for this transaction:"
|
||||
label="Est. fee for this transaction"
|
||||
value={fee ? `${fee.amount?.amount} ${fee.amount?.denom}` : <CircularProgress size={15} />}
|
||||
divider
|
||||
/>
|
||||
|
||||
@@ -14,7 +14,7 @@ import { AppContext, urls } from 'src/context/main';
|
||||
import { isGateway, isMixnode, TBondGatewayArgs, TBondMixNodeArgs } from 'src/types';
|
||||
import { BondedGateway } from 'src/components/Bonding/BondedGateway';
|
||||
import { RedeemRewardsModal } from 'src/components/Bonding/modals/RedeemRewardsModal';
|
||||
import { CompoundRewardsModal } from 'src/components/Bonding/modals/CompoundRewardsModal';
|
||||
// import { CompoundRewardsModal } from 'src/components/Bonding/modals/CompoundRewardsModal';
|
||||
import { BondingContextProvider, useBondingContext } from '../../context';
|
||||
import { Box } from '@mui/material';
|
||||
|
||||
@@ -37,7 +37,7 @@ const Bonding = () => {
|
||||
unbond,
|
||||
updateMixnode,
|
||||
redeemRewards,
|
||||
compoundRewards,
|
||||
// compoundRewards,
|
||||
isLoading,
|
||||
checkOwnership,
|
||||
} = useBondingContext();
|
||||
@@ -87,15 +87,15 @@ const Bonding = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleUpdateProfitMargin = async (profitMargin: number, fee?: FeeDetails) => {
|
||||
setShowModal(undefined);
|
||||
const tx = await updateMixnode(profitMargin, fee);
|
||||
setConfirmationDetails({
|
||||
status: 'success',
|
||||
title: 'Profit margin update successful',
|
||||
txUrl: `${urls(network).blockExplorer}/transaction/${tx?.transaction_hash}`,
|
||||
});
|
||||
};
|
||||
// const handleUpdateProfitMargin = async (profitMargin: number, fee?: FeeDetails) => {
|
||||
// setShowModal(undefined);
|
||||
// const tx = await updateMixnode(profitMargin, fee);
|
||||
// setConfirmationDetails({
|
||||
// status: 'success',
|
||||
// title: 'Profit margin update successful',
|
||||
// txUrl: `${urls(network).blockExplorer}/transaction/${tx?.transaction_hash}`,
|
||||
// });
|
||||
// };
|
||||
|
||||
const handleRedeemReward = async (fee?: FeeDetails) => {
|
||||
setShowModal(undefined);
|
||||
@@ -107,16 +107,16 @@ const Bonding = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleCompoundReward = async (fee?: FeeDetails) => {
|
||||
setShowModal(undefined);
|
||||
const tx = await compoundRewards(fee);
|
||||
setConfirmationDetails({
|
||||
status: 'success',
|
||||
title: 'Rewards compounded successfully',
|
||||
txUrl: `${urls(network).blockExplorer}/transaction/${tx?.transaction_hash}`,
|
||||
});
|
||||
return undefined;
|
||||
};
|
||||
// const handleCompoundReward = async (fee?: FeeDetails) => {
|
||||
// setShowModal(undefined);
|
||||
// const tx = await compoundRewards(fee);
|
||||
// setConfirmationDetails({
|
||||
// status: 'success',
|
||||
// title: 'Rewards compounded successfully',
|
||||
// txUrl: `${urls(network).blockExplorer}/transaction/${tx?.transaction_hash}`,
|
||||
// });
|
||||
// return undefined;
|
||||
// };
|
||||
|
||||
const handleBondedMixnodeAction = (action: TBondedMixnodeActions) => {
|
||||
switch (action) {
|
||||
@@ -198,14 +198,14 @@ const Bonding = () => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{showModal === 'compound' && bondedNode && isMixnode(bondedNode) && (
|
||||
{/* {showModal === 'compound' && bondedNode && isMixnode(bondedNode) && (
|
||||
<CompoundRewardsModal
|
||||
node={bondedNode}
|
||||
onClose={() => setShowModal(undefined)}
|
||||
onConfirm={handleCompoundReward}
|
||||
onError={handleError}
|
||||
/>
|
||||
)}
|
||||
)} */}
|
||||
|
||||
{confirmationDetails && confirmationDetails.status === 'success' && (
|
||||
<ConfirmationDetailsModal
|
||||
|
||||
@@ -1,242 +1,2 @@
|
||||
<<<<<<< HEAD
|
||||
export * from './Bonding';
|
||||
export * from './node-settings';
|
||||
=======
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { FeeDetails } from '@nymproject/types';
|
||||
import { TPoolOption } from 'src/components';
|
||||
import { Bond } from 'src/components/Bonding/Bond';
|
||||
import { BondedMixnode } from 'src/components/Bonding/BondedMixnode';
|
||||
import { TBondedMixnodeActions } from 'src/components/Bonding/BondedMixnodeActions';
|
||||
import { BondGatewayModal } from 'src/components/Bonding/modals/BondGatewayModal';
|
||||
import { BondMixnodeModal } from 'src/components/Bonding/modals/BondMixnodeModal';
|
||||
import { ConfirmationDetailProps, ConfirmationDetailsModal } from 'src/components/Bonding/modals/ConfirmationModal';
|
||||
import { NodeSettings } from 'src/components/Bonding/modals/NodeSettingsModal';
|
||||
import { UnbondModal } from 'src/components/Bonding/modals/UnbondModal';
|
||||
import { ErrorModal } from 'src/components/Modals/ErrorModal';
|
||||
import { LoadingModal } from 'src/components/Modals/LoadingModal';
|
||||
import { AppContext, urls } from 'src/context/main';
|
||||
import { isGateway, isMixnode, TBondGatewayArgs, TBondMixNodeArgs } from 'src/types';
|
||||
import { BondedGateway } from 'src/components/Bonding/BondedGateway';
|
||||
import { RedeemRewardsModal } from 'src/components/Bonding/modals/RedeemRewardsModal';
|
||||
import { Box } from '@mui/material';
|
||||
import { BondingContextProvider, useBondingContext } from '../../context';
|
||||
|
||||
const Bonding = () => {
|
||||
const [showModal, setShowModal] = useState<
|
||||
'bond-mixnode' | 'bond-gateway' | 'bond-more' | 'unbond' | 'redeem' | 'compound' | 'node-settings'
|
||||
>();
|
||||
const [confirmationDetails, setConfirmationDetails] = useState<ConfirmationDetailProps>();
|
||||
|
||||
const {
|
||||
network,
|
||||
clientDetails,
|
||||
userBalance: { originalVesting },
|
||||
} = useContext(AppContext);
|
||||
|
||||
const {
|
||||
bondedNode,
|
||||
bondMixnode,
|
||||
bondGateway,
|
||||
unbond,
|
||||
updateMixnode,
|
||||
redeemRewards,
|
||||
isLoading,
|
||||
checkOwnership,
|
||||
error,
|
||||
} = useBondingContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
setShowModal(undefined);
|
||||
setConfirmationDetails({
|
||||
status: 'error',
|
||||
title: 'An error occurred',
|
||||
subtitle: error,
|
||||
});
|
||||
}
|
||||
}, [error]);
|
||||
|
||||
const handleCloseModal = async () => {
|
||||
setShowModal(undefined);
|
||||
await checkOwnership();
|
||||
};
|
||||
|
||||
const handleError = (e: string) => {
|
||||
setShowModal(undefined);
|
||||
setConfirmationDetails({
|
||||
status: 'error',
|
||||
title: 'An error occurred',
|
||||
subtitle: e,
|
||||
});
|
||||
};
|
||||
|
||||
const handleBondMixnode = async (data: TBondMixNodeArgs, tokenPool: TPoolOption) => {
|
||||
setShowModal(undefined);
|
||||
const tx = await bondMixnode(data, tokenPool);
|
||||
setConfirmationDetails({
|
||||
status: 'success',
|
||||
title: 'Bond successful',
|
||||
txUrl: `${urls(network).blockExplorer}/transaction/${tx?.transaction_hash}`,
|
||||
});
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const handleBondGateway = async (data: TBondGatewayArgs, tokenPool: TPoolOption) => {
|
||||
setShowModal(undefined);
|
||||
const tx = await bondGateway(data, tokenPool);
|
||||
setConfirmationDetails({
|
||||
status: 'success',
|
||||
title: 'Bond successful',
|
||||
txUrl: `${urls(network).blockExplorer}/transaction/${tx?.transaction_hash}`,
|
||||
});
|
||||
};
|
||||
|
||||
const handleUnbond = async (fee?: FeeDetails) => {
|
||||
setShowModal(undefined);
|
||||
const tx = await unbond(fee);
|
||||
setConfirmationDetails({
|
||||
status: 'success',
|
||||
title: 'Unbond successful',
|
||||
txUrl: `${urls(network).blockExplorer}/transaction/${tx?.transaction_hash}`,
|
||||
});
|
||||
};
|
||||
|
||||
const handleUpdateProfitMargin = async (profitMargin: string, fee?: FeeDetails) => {
|
||||
setShowModal(undefined);
|
||||
const tx = await updateMixnode(profitMargin, fee);
|
||||
setConfirmationDetails({
|
||||
status: 'success',
|
||||
title: 'Profit margin update successful',
|
||||
txUrl: `${urls(network).blockExplorer}/transaction/${tx?.transaction_hash}`,
|
||||
});
|
||||
};
|
||||
|
||||
const handleRedeemReward = async (fee?: FeeDetails) => {
|
||||
setShowModal(undefined);
|
||||
const tx = await redeemRewards(fee);
|
||||
setConfirmationDetails({
|
||||
status: 'success',
|
||||
title: 'Rewards redeemed successfully',
|
||||
txUrl: `${urls(network).blockExplorer}/transaction/${tx?.transaction_hash}`,
|
||||
});
|
||||
};
|
||||
|
||||
const handleBondedMixnodeAction = (action: TBondedMixnodeActions) => {
|
||||
switch (action) {
|
||||
case 'bondMore': {
|
||||
setShowModal('bond-more');
|
||||
break;
|
||||
}
|
||||
case 'unbond': {
|
||||
setShowModal('unbond');
|
||||
break;
|
||||
}
|
||||
case 'redeem': {
|
||||
setShowModal('redeem');
|
||||
break;
|
||||
}
|
||||
case 'nodeSettings': {
|
||||
setShowModal('node-settings');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ mt: 4 }}>
|
||||
{!bondedNode && <Bond disabled={isLoading} onBond={() => setShowModal('bond-mixnode')} />}
|
||||
|
||||
{bondedNode && isMixnode(bondedNode) && (
|
||||
<BondedMixnode
|
||||
mixnode={bondedNode}
|
||||
network={network}
|
||||
onActionSelect={(action) => handleBondedMixnodeAction(action)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{bondedNode && isGateway(bondedNode) && (
|
||||
<BondedGateway gateway={bondedNode} onActionSelect={handleBondedMixnodeAction} network={network} />
|
||||
)}
|
||||
{showModal === 'bond-mixnode' && (
|
||||
<BondMixnodeModal
|
||||
denom={clientDetails?.display_mix_denom || 'nym'}
|
||||
hasVestingTokens={Boolean(originalVesting)}
|
||||
onBondMixnode={handleBondMixnode}
|
||||
onSelectNodeType={() => setShowModal('bond-gateway')}
|
||||
onClose={() => setShowModal(undefined)}
|
||||
onError={handleError}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showModal === 'bond-gateway' && (
|
||||
<BondGatewayModal
|
||||
denom={clientDetails?.display_mix_denom || 'nym'}
|
||||
hasVestingTokens={Boolean(originalVesting)}
|
||||
onBondGateway={handleBondGateway}
|
||||
onSelectNodeType={() => setShowModal('bond-mixnode')}
|
||||
onClose={() => setShowModal(undefined)}
|
||||
onError={handleError}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showModal === 'unbond' && bondedNode && (
|
||||
<UnbondModal
|
||||
node={bondedNode}
|
||||
onClose={() => setShowModal(undefined)}
|
||||
onConfirm={handleUnbond}
|
||||
onError={handleError}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showModal === 'redeem' && bondedNode && isMixnode(bondedNode) && (
|
||||
<RedeemRewardsModal
|
||||
node={bondedNode}
|
||||
onClose={() => setShowModal(undefined)}
|
||||
onConfirm={handleRedeemReward}
|
||||
onError={handleError}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showModal === 'node-settings' && bondedNode && isMixnode(bondedNode) && (
|
||||
<NodeSettings
|
||||
currentPm={bondedNode.profitMargin}
|
||||
isVesting={Boolean(bondedNode.proxy)}
|
||||
onConfirm={handleUpdateProfitMargin}
|
||||
onClose={() => setShowModal(undefined)}
|
||||
onError={handleError}
|
||||
/>
|
||||
)}
|
||||
|
||||
{confirmationDetails && confirmationDetails.status === 'success' && (
|
||||
<ConfirmationDetailsModal
|
||||
title={confirmationDetails.title}
|
||||
subtitle={confirmationDetails.subtitle || 'This operation can take up to one hour to process'}
|
||||
status={confirmationDetails.status}
|
||||
txUrl={confirmationDetails.txUrl}
|
||||
onClose={() => {
|
||||
setConfirmationDetails(undefined);
|
||||
handleCloseModal();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{confirmationDetails && confirmationDetails.status === 'error' && (
|
||||
<ErrorModal open message={confirmationDetails.subtitle} onClose={() => setConfirmationDetails(undefined)} />
|
||||
)}
|
||||
|
||||
{isLoading && <LoadingModal />}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export const BondingPage = () => (
|
||||
<BondingContextProvider>
|
||||
<Bonding />
|
||||
</BondingContextProvider>
|
||||
);
|
||||
>>>>>>> develop
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { FC, useContext, useEffect, useState } from 'react';
|
||||
import { Box, Button, Paper, Stack, Typography } from '@mui/material';
|
||||
import { Theme, useTheme } from '@mui/material/styles';
|
||||
import { Box, Button, Paper, Stack, Typography, CircularProgress } from '@mui/material';
|
||||
import { useTheme, Theme } from '@mui/material/styles';
|
||||
import { DecCoin, decimalToFloatApproximation, DelegationWithEverything, FeeDetails } from '@nymproject/types';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { AppContext, urls } from 'src/context/main';
|
||||
@@ -10,7 +10,7 @@ import { Console } from 'src/utils/console';
|
||||
import { OverSaturatedBlockerModal } from 'src/components/Delegation/DelegateBlocker';
|
||||
import { getSpendableCoins, userBalance } from 'src/requests';
|
||||
import { RewardsSummary } from '../../components/Rewards/RewardsSummary';
|
||||
import { DelegationContextProvider, useDelegationContext } from '../../context/delegations';
|
||||
import { DelegationContextProvider, useDelegationContext, TDelegations } from '../../context/delegations';
|
||||
import { RewardsContextProvider, useRewardsContext } from '../../context/rewards';
|
||||
import { DelegateModal } from '../../components/Delegation/DelegateModal';
|
||||
import { UndelegateModal } from '../../components/Delegation/UndelegateModal';
|
||||
@@ -268,41 +268,90 @@ export const Delegation: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => {
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Paper elevation={0} sx={{ p: 3, mt: 4 }}>
|
||||
<Stack spacing={5}>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="h6" lineHeight={1.334} fontWeight={600}>
|
||||
Delegations
|
||||
</Typography>
|
||||
const delegationsComponent = (delegations: TDelegations | undefined) => {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Box sx={{ width: '100%', display: 'flex', justifyContent: 'center' }}>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
);
|
||||
} else if (Boolean(delegations?.length)) {
|
||||
return (
|
||||
<DelegationList
|
||||
explorerUrl={urls(network).networkExplorer}
|
||||
isLoading={isLoading}
|
||||
items={delegations}
|
||||
onItemActionClick={handleDelegationItemActionClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'flex-end' }}>
|
||||
<Box marginRight={3} width={1}>
|
||||
<Typography variant="body2">
|
||||
Checkout the{' '}
|
||||
<Link
|
||||
href={`${urls(network).networkExplorer}/network-components/mixnodes/`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
text="Network Explorer"
|
||||
fontSize={14}
|
||||
text="list of mixnodes"
|
||||
fontWeight={theme.palette.mode === 'light' ? 400 : 600}
|
||||
noIcon
|
||||
/>
|
||||
/>{' '}
|
||||
for performance and other parameters to help make delegation decisions.
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
Hint: In Nym explorer use <b>advanced filters</b> to precisely define what node you are looking for.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
disableElevation
|
||||
onClick={() => setShowNewDelegationModal(true)}
|
||||
sx={{ py: 1.5, px: 5, color: 'primary.contrastText' }}
|
||||
>
|
||||
Delegate
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Paper elevation={0} sx={{ p: 3, mt: 4 }}>
|
||||
<Stack spacing={3}>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="h6" lineHeight={1.334} fontWeight={600}>
|
||||
Delegations
|
||||
</Typography>
|
||||
{!!delegations?.length && (
|
||||
<Link
|
||||
href={`${urls(network).networkExplorer}/network-components/mixnodes/`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
text="Network Explorer"
|
||||
fontSize={14}
|
||||
fontWeight={theme.palette.mode === 'light' ? 400 : 600}
|
||||
noIcon
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="end">
|
||||
<RewardsSummary isLoading={isLoading} totalDelegation={totalDelegations} totalRewards={totalRewards} />
|
||||
<Button
|
||||
variant="contained"
|
||||
disableElevation
|
||||
onClick={() => setShowNewDelegationModal(true)}
|
||||
sx={{ py: 1.5, px: 5, color: 'primary.contrastText' }}
|
||||
>
|
||||
Delegate
|
||||
</Button>
|
||||
</Box>
|
||||
<DelegationList
|
||||
explorerUrl={urls(network).networkExplorer}
|
||||
isLoading={isLoading}
|
||||
items={delegations}
|
||||
onItemActionClick={handleDelegationItemActionClick}
|
||||
/>
|
||||
|
||||
{!!delegations?.length && (
|
||||
<Box display="flex" justifyContent="space-between" alignItems="end">
|
||||
<RewardsSummary isLoading={isLoading} totalDelegation={totalDelegations} totalRewards={totalRewards} />
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
disableElevation
|
||||
onClick={() => setShowNewDelegationModal(true)}
|
||||
sx={{ py: 1.5, px: 5, color: 'primary.contrastText' }}
|
||||
>
|
||||
Delegate
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
{delegationsComponent(delegations)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
|
||||
@@ -4,11 +4,7 @@ import { ApplicationLayout } from 'src/layouts';
|
||||
import { Terminal } from 'src/pages/terminal';
|
||||
import { Send } from 'src/components/Send';
|
||||
import { Receive } from '../components/Receive';
|
||||
<<<<<<< HEAD
|
||||
import { Balance, InternalDocs, Unbond, DelegationPage, Admin, BondingPage, NodeSettingsPage } from '../pages';
|
||||
=======
|
||||
import { Balance, InternalDocs, DelegationPage, Admin, BondingPage } from '../pages';
|
||||
>>>>>>> develop
|
||||
import { Balance, InternalDocs, DelegationPage, Admin, BondingPage, NodeSettingsPage } from '../pages';
|
||||
|
||||
export const AppRoutes = () => (
|
||||
<ApplicationLayout>
|
||||
@@ -18,11 +14,7 @@ export const AppRoutes = () => (
|
||||
<Routes>
|
||||
<Route path="/balance" element={<Balance />} />
|
||||
<Route path="/bonding" element={<BondingPage />} />
|
||||
<<<<<<< HEAD
|
||||
<Route path="/bonding/node-settings" element={<NodeSettingsPage />} />
|
||||
<Route path="/unbond" element={<Unbond />} />
|
||||
=======
|
||||
>>>>>>> develop
|
||||
<Route path="/delegation" element={<DelegationPage />} />
|
||||
<Route path="/docs" element={<InternalDocs />} />
|
||||
<Route path="/admin" element={<Admin />} />
|
||||
|
||||
Vendored
+3
@@ -69,6 +69,9 @@ declare module '@mui/material/styles' {
|
||||
hover: {
|
||||
background: string;
|
||||
};
|
||||
modal: {
|
||||
border: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,6 +61,9 @@ const darkMode: NymPaletteVariant = {
|
||||
hover: {
|
||||
background: '#36393E',
|
||||
},
|
||||
modal: {
|
||||
border: '#484d53',
|
||||
},
|
||||
};
|
||||
|
||||
const lightMode: NymPaletteVariant = {
|
||||
@@ -89,6 +92,9 @@ const lightMode: NymPaletteVariant = {
|
||||
hover: {
|
||||
background: '#F9F9F9',
|
||||
},
|
||||
modal: {
|
||||
border: 'transparent',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,6 @@ use mixnet_contract_common::{
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::rngs::OsRng;
|
||||
use std::collections::HashSet;
|
||||
use std::process;
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
use validator_client::nymd::SigningNymdClient;
|
||||
@@ -314,20 +313,10 @@ impl RewardedSetUpdater {
|
||||
|
||||
async fn wait_until_epoch_end(&mut self, shutdown: &mut ShutdownListener) -> Option<Interval> {
|
||||
const POLL_INTERVAL: Duration = Duration::from_secs(120);
|
||||
const MAXIMUM_ATTEMPTS: usize = 5;
|
||||
let mut failed_attempts = 0;
|
||||
|
||||
loop {
|
||||
let current_interval = match self.current_interval_details().await {
|
||||
Err(err) => {
|
||||
failed_attempts += 1;
|
||||
if failed_attempts == MAXIMUM_ATTEMPTS {
|
||||
error!(
|
||||
"failed to obtain epoch information {} times in a row. Existing now",
|
||||
MAXIMUM_ATTEMPTS
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
error!("failed to obtain information about the current interval - {}. Going to retry in {}s", err, POLL_INTERVAL.as_secs());
|
||||
tokio::select! {
|
||||
_ = sleep(POLL_INTERVAL) => {
|
||||
@@ -342,8 +331,6 @@ impl RewardedSetUpdater {
|
||||
Ok(interval) => interval,
|
||||
};
|
||||
|
||||
failed_attempts = 0;
|
||||
|
||||
if current_interval.is_current_epoch_over {
|
||||
return Some(current_interval.interval);
|
||||
} else {
|
||||
@@ -378,14 +365,7 @@ impl RewardedSetUpdater {
|
||||
) -> Result<(), RewardingError> {
|
||||
self.validator_cache.wait_for_initial_values().await;
|
||||
|
||||
const MAX_FAILURES: usize = 10;
|
||||
let mut consecutive_failures = 0;
|
||||
while !shutdown.is_shutdown() {
|
||||
if consecutive_failures == MAX_FAILURES {
|
||||
error!("We have failed to perform epoch operations {} times in a row. The validator API will shutdown now", MAX_FAILURES);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let interval_details = match self.wait_until_epoch_end(&mut shutdown).await {
|
||||
// received a shutdown
|
||||
None => return Ok(()),
|
||||
@@ -397,10 +377,7 @@ impl RewardedSetUpdater {
|
||||
}
|
||||
if let Err(err) = self.perform_epoch_operations(interval_details).await {
|
||||
error!("failed to perform epoch operations - {}", err);
|
||||
consecutive_failures += 1;
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
} else {
|
||||
consecutive_failures = 0;
|
||||
sleep(Duration::from_secs(30)).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user