diff --git a/nym-wallet/src/context/bonding.tsx b/nym-wallet/src/context/bonding.tsx index cf5ffa3114..d7a584e533 100644 --- a/nym-wallet/src/context/bonding.tsx +++ b/nym-wallet/src/context/bonding.tsx @@ -1,4 +1,4 @@ -import { TransactionExecuteResult } from '@nymproject/types'; +import { MajorCurrencyAmount, TransactionExecuteResult } from '@nymproject/types'; import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import type { Network } from 'src/types'; import { TBondGatewayArgs, TBondMixNodeArgs } from 'src/types'; @@ -15,12 +15,12 @@ import { export interface BondedMixnode { key: string; ip: string; - stake: number; - bond: number; + stake: MajorCurrencyAmount; + bond: MajorCurrencyAmount; stakeSaturation: number; profitMargin: number; - nodeRewards: number; - operatorRewards: number; + nodeRewards: MajorCurrencyAmount; + operatorRewards: MajorCurrencyAmount; delegators: number; } @@ -28,7 +28,7 @@ export interface BondedMixnode { export interface BondedGateway { key: string; ip: string; - bond: number; + bond: MajorCurrencyAmount; location?: string; // TODO not yet available, only available in Network Explorer API } @@ -89,7 +89,17 @@ export const BondingContextProvider = ({ }; const refresh = useCallback(async () => { - const bounded = null; + const bounded: BondedMixnode = { + bond: { denom: 'NYM', amount: '1234' }, + key: 'B2Xx4haarLWMajX8w259oHjtRZsC7nHwagbWrJNiA3QC', + delegators: 123, + ip: '1.2.34.5', + nodeRewards: { denom: 'NYM', amount: '123' }, + operatorRewards: { denom: 'NYM', amount: '12' }, + profitMargin: 10, + stake: { denom: 'NYM', amount: '99' }, + stakeSaturation: 99, + }; // TODO fetch bondedMixnode and bondedGatway via tauri dedicated requests /* try { bounded = await fetchBondingData(); @@ -181,6 +191,7 @@ export const BondingContextProvider = ({ isLoading, error, bondMixnode, + bondedMixnode, bondGateway, unbondMixnode, unbondGateway, diff --git a/nym-wallet/src/context/mocks/bonding.tsx b/nym-wallet/src/context/mocks/bonding.tsx index 72d7e9927c..0fddce84a7 100644 --- a/nym-wallet/src/context/mocks/bonding.tsx +++ b/nym-wallet/src/context/mocks/bonding.tsx @@ -6,22 +6,22 @@ import { mockSleep } from './utils'; const SLEEP_MS = 1000; -const bondedMixnodeMock = { +const bondedMixnodeMock: BondedMixnode = { key: '7mjM2fYbtN6kxMwp1TrmQ4VwPks3URR5pBgWPWhzT98F', ip: '112.43.234.56', - stake: 35847.221, - bond: 12576.32745, + stake: { denom: 'NYM', amount: '1234' }, + bond: { denom: 'NYM', amount: '1234' }, stakeSaturation: 95, profitMargin: 15, - nodeRewards: 12576.32745, - operatorRewards: 12576.32, + nodeRewards: { denom: 'NYM', amount: '1234' }, + operatorRewards: { denom: 'NYM', amount: '1234' }, delegators: 5423, }; -const bondedGatewayMock = { +const bondedGatewayMock: BondedGateway = { key: 'WayM2fYbtN6kxMwp1TrmQ4VwPks3URR5pBgWPWhzT98F', ip: '112.43.234.57', - bond: 12576.32745, + bond: { denom: 'NYM', amount: '1234' }, }; const TxResultMock: TransactionExecuteResult = { diff --git a/nym-wallet/src/pages/bonding/BondingCard.tsx b/nym-wallet/src/pages/bonding/BondingCard.tsx index acae090c76..e685704047 100644 --- a/nym-wallet/src/pages/bonding/BondingCard.tsx +++ b/nym-wallet/src/pages/bonding/BondingCard.tsx @@ -1,15 +1,17 @@ import React, { useContext, useEffect, useReducer } from 'react'; import { Box, Button, Typography } from '@mui/material'; +import { Link } from '@nymproject/react/link/Link'; import { Gateway, MajorCurrencyAmount, MixNode } from '@nymproject/types'; import { NymCard } from '../../components'; import { NodeIdentityModal } from './NodeIdentityModal'; import { ACTIONTYPE, AmountData, BondState, FormStep, NodeData, NodeType } from './types'; import { AmountModal } from './AmountModal'; -import { AppContext } from '../../context'; +import { AppContext, urls } from '../../context'; import { SummaryModal } from './SummaryModal'; import { bond, vestingBond } from '../../requests'; import { TBondArgs } from '../../types'; -import { SimpleModal } from '../../components/Modals/SimpleModal'; +import { Console } from '../../utils/console'; +import { SimpleDialog } from './SimpleDialog'; const initialState: BondState = { showModal: false, @@ -51,7 +53,7 @@ export const BondingCard = () => { const { formStep, showModal } = state; console.log(state); - const { userBalance, clientDetails } = useContext(AppContext); + const { userBalance, clientDetails, network } = useContext(AppContext); useEffect(() => { dispatch({ type: 'reset' }); @@ -101,11 +103,17 @@ export const BondingCard = () => { dispatch({ type: 'set_tx', payload: tx }); dispatch({ type: 'next_step' }); }) - .catch(() => { + .catch((e: any) => { + Console.error('Failed to bond', e); // TODO do something }); }; + const onConfirm = () => { + dispatch({ type: 'close_modal' }); + dispatch({ type: 'reset' }); + }; + return ( { /> )} {formStep === 4 && showModal && ( - { - dispatch({ type: 'close_modal' }); - dispatch({ type: 'reset' }); - }} - header="Bonding successful" - okLabel="Done" + onClose={onConfirm} + title="Bonding successful" + confirmText="Done" > - Link to transaction on network explorer - + + View on blockchain + + )} ); diff --git a/nym-wallet/src/pages/bonding/BoundedMixnodeCard.tsx b/nym-wallet/src/pages/bonding/BoundedMixnodeCard.tsx deleted file mode 100644 index ba4f9ecc7d..0000000000 --- a/nym-wallet/src/pages/bonding/BoundedMixnodeCard.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import { Alert, Button, Grid, Link, Typography } from '@mui/material'; -import { NymCard } from '../../components'; - -export const BondedMixnodeCard = () => ( - - - bonded mixnode data table - - -); diff --git a/nym-wallet/src/pages/bonding/BoundedGatewayCard.tsx b/nym-wallet/src/pages/bonding/GatewayCard.tsx similarity index 86% rename from nym-wallet/src/pages/bonding/BoundedGatewayCard.tsx rename to nym-wallet/src/pages/bonding/GatewayCard.tsx index 26c6cd1dca..08794d4f82 100644 --- a/nym-wallet/src/pages/bonding/BoundedGatewayCard.tsx +++ b/nym-wallet/src/pages/bonding/GatewayCard.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Grid } from '@mui/material'; import { NymCard } from '../../components'; -export const BondedGatewayCard = () => ( +export const GatewayCard = () => ( bonded gateway data table diff --git a/nym-wallet/src/pages/bonding/MixnodeCard.tsx b/nym-wallet/src/pages/bonding/MixnodeCard.tsx new file mode 100644 index 0000000000..ba38302f6a --- /dev/null +++ b/nym-wallet/src/pages/bonding/MixnodeCard.tsx @@ -0,0 +1,110 @@ +import React, { useMemo } from 'react'; +import { IconButton, Typography } from '@mui/material'; +import { MoreVert } from '@mui/icons-material'; +import { Link } from '@nymproject/react/link/Link'; +import { NymCard } from '../../components'; +import { BondedMixnode } from '../../context'; +import { Cell, Header, NodeTable } from './NodeTable'; + +const headers: Header[] = [ + { + header: 'Stake', + id: 'stake', + sx: { pl: 0 }, + }, + { + header: 'Bond', + id: 'bond', + }, + { + header: 'Stake saturation', + id: 'stake-saturation', + tooltip: 'TODO', + }, + { + header: 'PM', + id: 'profit-margin', + tooltip: + 'The percentage of the node rewards that you as the node operator will take before the rest of the reward is shared between you and the delegators.', + }, + { + header: 'Node rewards', + id: 'node-rewards', + tooltip: 'This is the total rewards for this node in this epoch including delegates and the operators share.', + }, + { + header: 'Operator rewards', + id: 'operator-rewards', + tooltip: + 'This is your (operator) new rewards including the PM and cost. You can compound your rewards manually every epoch or unbond your node to redeem them.', + }, + { + header: 'No. delegators', + id: 'delegators', + }, + { + id: 'menu-button', + size: 'small', + sx: { pr: 0 }, + }, +]; + +export const MixnodeCard = ({ mixnode }: { mixnode: BondedMixnode }) => { + const { stake, bond, stakeSaturation, profitMargin, nodeRewards, operatorRewards, delegators } = mixnode; + const cells: Cell[] = useMemo( + () => [ + { + cell: `${stake.amount} ${stake.denom}`, + id: 'stake-cell', + sx: { pl: 0 }, + }, + { + cell: `${bond.amount} ${bond.denom}`, + id: 'bond-cell', + }, + { + cell: `${stakeSaturation}%`, + id: 'stake-saturation-cell', + }, + { + cell: `${profitMargin}%`, + id: 'pm-cell', + }, + { + cell: `${nodeRewards.amount} ${nodeRewards.denom}`, + id: 'node-rewards-cell', + }, + { + cell: `${operatorRewards.amount} ${operatorRewards.denom}`, + id: 'operator-rewards-cell', + }, + { + cell: delegators, + id: 'delegators-cell', + }, + { + cell: ( + + + + ), + id: 'menu-button-cell', + align: 'center', + size: 'small', + sx: { pr: 0 }, + }, + ], + [mixnode], + ); + return ( + + + + Check more stats of your node on the{' '} + + explorer + + + + ); +}; diff --git a/nym-wallet/src/pages/bonding/NodeTable.tsx b/nym-wallet/src/pages/bonding/NodeTable.tsx new file mode 100644 index 0000000000..4dcc45fe29 --- /dev/null +++ b/nym-wallet/src/pages/bonding/NodeTable.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { + Box, + Stack, + SxProps, + Table, + TableBody, + TableCell as MUITableCell, + TableContainer, + TableHead, + TableRow, + Tooltip, + Typography, +} from '@mui/material'; +import { InfoOutlined } from '@mui/icons-material'; + +export interface TableCell { + children: React.ReactNode; + color?: string; + align?: 'center' | 'inherit' | 'justify' | 'left' | 'right'; + size?: 'small' | 'medium'; + sx?: SxProps; +} + +export type TableHeader = TableCell & { tooltip?: React.ReactNode }; + +const CellHeader = ({ children, tooltip, sx, size, align, color }: TableHeader) => ( + + {tooltip ? ( + + + + {children} + + + ) : ( + {children} + )} + +); + +const CellValue = ({ children, align, size, color, sx }: TableCell) => ( + + {children} + +); + +export type Header = Omit & { header?: React.ReactNode; id: string }; +export type Cell = Omit & { cell: React.ReactNode; id: string }; + +export interface TableProps { + headers: Header[]; + cells: Cell[]; +} + +export const NodeTable = ({ headers, cells }: TableProps) => ( + + + + + {headers.map(({ header, id, tooltip, sx }) => ( + + {header} + + ))} + + + + + {cells.map(({ cell, id, align, size, color, sx }) => ( + + {cell} + + ))} + + +
+
+); diff --git a/nym-wallet/src/pages/bonding/SimpleDialog.tsx b/nym-wallet/src/pages/bonding/SimpleDialog.tsx new file mode 100644 index 0000000000..44175cbc62 --- /dev/null +++ b/nym-wallet/src/pages/bonding/SimpleDialog.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material'; + +interface Props { + open: boolean; + onClose: () => void; + children?: React.ReactNode; + title: string; + confirmText: string; +} + +export const SimpleDialog = ({ open, onClose, children, title, confirmText }: Props) => ( + + {title} + {children} + + + + +); diff --git a/nym-wallet/src/pages/bonding/SummaryModal.tsx b/nym-wallet/src/pages/bonding/SummaryModal.tsx index c236b57637..88a0b506ac 100644 --- a/nym-wallet/src/pages/bonding/SummaryModal.tsx +++ b/nym-wallet/src/pages/bonding/SummaryModal.tsx @@ -34,18 +34,18 @@ export const SummaryModal = ({ open, onClose, onSubmit, header, buttonText, iden return ( - Identity Key - {identityKey} + Identity Key + {identityKey} - - Amount - {`${amount.amount} ${amount.denom}`} + + Amount + {`${amount.amount} ${amount.denom}`} - - Fee for this operation - {fee} + + Fee for this operation + {fee} ); diff --git a/nym-wallet/src/pages/bonding/index.tsx b/nym-wallet/src/pages/bonding/index.tsx index f4109ec65e..8a6746a471 100644 --- a/nym-wallet/src/pages/bonding/index.tsx +++ b/nym-wallet/src/pages/bonding/index.tsx @@ -4,8 +4,8 @@ import { Box } from '@mui/material'; import { useBondingContext, BondingContextProvider } from '../../context'; import { PageLayout } from '../../layouts'; import { BondingCard } from './BondingCard'; -import { BondedMixnodeCard } from './BoundedMixnodeCard'; -import { BondedGatewayCard } from './BoundedGatewayCard'; +import { MixnodeCard } from './MixnodeCard'; +import { GatewayCard } from './GatewayCard'; import { EnumRequestStatus } from '../../components'; import { useCheckOwnership } from '../../hooks/useCheckOwnership'; @@ -26,13 +26,7 @@ const Bonding = () => { return ( - {!bondedMixnode && - !bondedGateway && - status === EnumRequestStatus.initial && - !ownership.hasOwnership && - !isLoading && } - {bondedMixnode && } - {bondedGateway && } + {bondedMixnode && } );