Add account balances
This commit is contained in:
@@ -1,10 +1,46 @@
|
||||
import { NYM_ACCOUNT_ADDRESS } from "@/app/api/urls";
|
||||
import ExplorerCard from "@/components/cards/ExplorerCard";
|
||||
import { NYM_ACCOUNT_ADDRESS, NYM_PRICES_API } from "@/app/api/urls";
|
||||
import { AccountBalancesCard } from "@/components/accountPageComponents/AccountBalancesCard";
|
||||
import { AccountInfoCard } from "@/components/accountPageComponents/AccountInfoCard";
|
||||
import { ContentLayout } from "@/components/contentLayout/ContentLayout";
|
||||
import SectionHeading from "@/components/headings/SectionHeading";
|
||||
import ExplorerListItem from "@/components/list/ListItem";
|
||||
import ExplorerButtonGroup from "@/components/toggleButton/ToggleButton";
|
||||
import { Box, Grid2, Stack } from "@mui/material";
|
||||
import { Box, Grid2 } from "@mui/material";
|
||||
|
||||
interface IRewardDetails {
|
||||
amount_staked: IAmountDetails;
|
||||
node_id: number;
|
||||
node_still_fully_bonded: boolean;
|
||||
rewards: IAmountDetails;
|
||||
}
|
||||
|
||||
interface IAmountDetails {
|
||||
denom: string;
|
||||
amount: string;
|
||||
}
|
||||
|
||||
interface IDelegationDetails {
|
||||
node_id: number;
|
||||
delegated: IAmountDetails;
|
||||
height: number;
|
||||
proxy: null | string;
|
||||
}
|
||||
|
||||
interface ITotalDetails {
|
||||
amount: string;
|
||||
denom: string;
|
||||
}
|
||||
|
||||
export interface IAccountInfo {
|
||||
accumulated_rewards: IRewardDetails[];
|
||||
address: string;
|
||||
balances: IAmountDetails[];
|
||||
claimable_rewards: IAmountDetails;
|
||||
delegations: IDelegationDetails[];
|
||||
operator_rewards: null | any;
|
||||
total_delegations: ITotalDetails;
|
||||
total_value: ITotalDetails;
|
||||
vesting_account: null | any;
|
||||
}
|
||||
|
||||
export default async function Account({
|
||||
params,
|
||||
@@ -12,7 +48,7 @@ export default async function Account({
|
||||
params: Promise<{ address: string }>;
|
||||
}) {
|
||||
// const address = (await params).address;
|
||||
const address = "n17hllefp8rn3ayk23rsgp7agtxkv56mazv3w637";
|
||||
const address = "n1z0msxu8c098umdhnthpr2ac3ck2n3an97dm8pn";
|
||||
|
||||
const nymAccountAddress = `${NYM_ACCOUNT_ADDRESS}${address}`;
|
||||
const accountData = await fetch(nymAccountAddress, {
|
||||
@@ -23,12 +59,25 @@ export default async function Account({
|
||||
next: { revalidate: 60 },
|
||||
// refresh event list cache at given interval
|
||||
});
|
||||
const nymAccountData = await accountData.json();
|
||||
const nymAccountData: IAccountInfo = await accountData.json();
|
||||
|
||||
if (!nymAccountData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const nymPrice = await fetch(NYM_PRICES_API, {
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
next: { revalidate: 60 },
|
||||
// refresh event list cache at given interval
|
||||
});
|
||||
|
||||
const nymPriceData = await nymPrice.json();
|
||||
|
||||
console.log("nymPriceData :>> ", nymPriceData);
|
||||
|
||||
console.log("nymAccountData :>> ", nymAccountData);
|
||||
return (
|
||||
<ContentLayout>
|
||||
@@ -51,33 +100,13 @@ export default async function Account({
|
||||
</Box>
|
||||
</Grid2>
|
||||
<Grid2 size={4}>
|
||||
<ExplorerCard label="Action" sx={{ height: "100%" }}>
|
||||
<div />
|
||||
</ExplorerCard>
|
||||
<AccountInfoCard accountInfo={nymAccountData} />
|
||||
</Grid2>
|
||||
<Grid2 size={8}>
|
||||
<ExplorerCard label="Basic info">
|
||||
<Stack gap={1}>
|
||||
<ExplorerListItem
|
||||
divider
|
||||
label="NYM Address"
|
||||
value="0x1234567890"
|
||||
/>
|
||||
<ExplorerListItem
|
||||
divider
|
||||
label="Identity Key"
|
||||
value="0x1234567890"
|
||||
/>
|
||||
<ExplorerListItem
|
||||
row
|
||||
divider
|
||||
label="Node bonded"
|
||||
value="24/11/2024"
|
||||
/>
|
||||
<ExplorerListItem row divider label="Nr. of stakes" value="56" />
|
||||
<ExplorerListItem row label="Self bonded" value="10,000 NYM" />
|
||||
</Stack>
|
||||
</ExplorerCard>
|
||||
<AccountBalancesCard
|
||||
accountInfo={nymAccountData}
|
||||
nymPrice={nymPriceData.usd}
|
||||
/>
|
||||
</Grid2>
|
||||
</Grid2>
|
||||
</ContentLayout>
|
||||
|
||||
@@ -29,3 +29,6 @@ export const NYM_NODE_BONDED =
|
||||
|
||||
export const NYM_ACCOUNT_ADDRESS =
|
||||
"https://explorer.nymtech.net/api/v1/tmp/unstable/account/";
|
||||
|
||||
export const NYM_PRICES_API =
|
||||
"https://canary-nym-vpn-chain-payment-watcher.nymte.ch/v1/price/average";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { IAccountStatsCardProps } from "@/components/cards/AccountStatsCard";
|
||||
import type { IAccountBalancesTableProps } from "@/components/cards/AccountBalancesTable";
|
||||
import type { ContentCardProps } from "@/components/cards/MonoCard";
|
||||
|
||||
const explorerCard: ContentCardProps = {
|
||||
@@ -78,7 +78,7 @@ const explorerCard: ContentCardProps = {
|
||||
},
|
||||
};
|
||||
|
||||
const accountStatsCard: IAccountStatsCardProps = {
|
||||
const accountStatsCard: IAccountBalancesTableProps = {
|
||||
overTitle: "Total value",
|
||||
priceTitle: 1990.0174,
|
||||
rows: [
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
"use client";
|
||||
import type { IAccountInfo } from "@/app/account/[id]/page";
|
||||
import ExplorerCard from "../cards/ExplorerCard";
|
||||
|
||||
import { runTurboTracing } from "next/dist/build/swc/generated-native";
|
||||
import { AccountBalancesTable } from "../cards/AccountBalancesTable";
|
||||
|
||||
export interface IAccontStatsRowProps {
|
||||
type: string;
|
||||
allocation: number;
|
||||
amount: number;
|
||||
value: number;
|
||||
history?: { type: string; amount: number }[];
|
||||
isLastRow?: boolean;
|
||||
progressBarColor?: string;
|
||||
}
|
||||
|
||||
interface IAccountBalancesCardProps {
|
||||
accountInfo: IAccountInfo;
|
||||
nymPrice: number;
|
||||
}
|
||||
|
||||
const getNymsFormated = (unyms: number) => {
|
||||
const balance = unyms / 1000000;
|
||||
return balance;
|
||||
};
|
||||
const getPriceInUSD = (unyms: number, usdPrice: number) => {
|
||||
const balanceInUSD = (unyms / 1000000) * usdPrice;
|
||||
const balanceFormated = Number(balanceInUSD.toFixed(2));
|
||||
return balanceFormated;
|
||||
};
|
||||
|
||||
const getAllocation = (unyms: number, totalUnyms: number): number => {
|
||||
const allocationPercentage = (unyms * 100) / totalUnyms;
|
||||
return Number(allocationPercentage.toFixed(2));
|
||||
};
|
||||
|
||||
export const AccountBalancesCard = (props: IAccountBalancesCardProps) => {
|
||||
const { accountInfo, nymPrice } = props;
|
||||
|
||||
const totalBalanceUSD = getPriceInUSD(
|
||||
Number(accountInfo.total_value.amount),
|
||||
nymPrice,
|
||||
);
|
||||
|
||||
const spendableNYM = getNymsFormated(Number(accountInfo.balances[0].amount));
|
||||
const spendableUSD = getPriceInUSD(
|
||||
Number(accountInfo.balances[0].amount),
|
||||
nymPrice,
|
||||
);
|
||||
const spendableAllocation = getAllocation(
|
||||
Number(accountInfo.balances[0].amount),
|
||||
Number(accountInfo.total_value.amount),
|
||||
);
|
||||
|
||||
const delegationsNYM = getNymsFormated(
|
||||
Number(accountInfo.total_delegations.amount),
|
||||
);
|
||||
const delegationsUSD = getPriceInUSD(
|
||||
Number(accountInfo.total_delegations.amount),
|
||||
nymPrice,
|
||||
);
|
||||
const delegationsAllocation = getAllocation(
|
||||
Number(accountInfo.total_delegations.amount),
|
||||
Number(accountInfo.total_value.amount),
|
||||
);
|
||||
|
||||
const claimableNYM = getNymsFormated(
|
||||
Number(accountInfo.claimable_rewards.amount),
|
||||
);
|
||||
const claimableUSD = getPriceInUSD(
|
||||
Number(accountInfo.claimable_rewards.amount),
|
||||
nymPrice,
|
||||
);
|
||||
const claimableAllocation = getAllocation(
|
||||
Number(accountInfo.claimable_rewards.amount),
|
||||
Number(accountInfo.total_value.amount),
|
||||
);
|
||||
|
||||
const tableRows = [
|
||||
{
|
||||
type: "Spendable",
|
||||
allocation: spendableAllocation,
|
||||
amount: spendableNYM,
|
||||
value: spendableUSD,
|
||||
},
|
||||
{
|
||||
type: "Delegated",
|
||||
allocation: delegationsAllocation,
|
||||
amount: delegationsNYM,
|
||||
value: delegationsUSD,
|
||||
history: [
|
||||
{ type: "Liquid", amount: 6900 },
|
||||
{ type: "Locked", amount: 6900 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "Claimable",
|
||||
allocation: claimableAllocation,
|
||||
amount: claimableNYM,
|
||||
value: claimableUSD,
|
||||
history: [
|
||||
{ type: "Unlocked", amount: 6900 },
|
||||
{ type: "Staking rewards", amount: 6900 },
|
||||
{ type: "Operator comission", amount: 6900 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "Self bonded",
|
||||
allocation: 15.53,
|
||||
amount: 12800,
|
||||
value: 1200,
|
||||
},
|
||||
{
|
||||
type: "Locked",
|
||||
allocation: 15.53,
|
||||
amount: 12800,
|
||||
value: 1200,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<ExplorerCard
|
||||
label="Total value"
|
||||
title={`$ ${totalBalanceUSD}`}
|
||||
sx={{ height: "100%" }}
|
||||
>
|
||||
<AccountBalancesTable rows={tableRows} />
|
||||
</ExplorerCard>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
"use client";
|
||||
import type { IAccountInfo } from "@/app/account/[id]/page";
|
||||
import { Box, Stack, Typography } from "@mui/material";
|
||||
import ExplorerCard from "../cards/ExplorerCard";
|
||||
import CopyToClipboard from "../copyToClipboard/CopyToClipboard";
|
||||
import ExplorerListItem from "../list/ListItem";
|
||||
import { CardQRCode } from "../qrCode/QrCode";
|
||||
|
||||
interface IAccountInfoCardProps {
|
||||
accountInfo: IAccountInfo;
|
||||
}
|
||||
|
||||
export const AccountInfoCard = (props: IAccountInfoCardProps) => {
|
||||
const { accountInfo } = props;
|
||||
|
||||
const balance = Number(accountInfo.balances[0].amount) / 1000000;
|
||||
const balanceFormated = `${balance} NYM`;
|
||||
|
||||
return (
|
||||
<ExplorerCard
|
||||
label="Address"
|
||||
title={balanceFormated}
|
||||
sx={{ height: "100%" }}
|
||||
>
|
||||
<Stack gap={5}>
|
||||
<Box display={"flex"} justifyContent={"flex-start"}>
|
||||
<CardQRCode url={accountInfo.address} />
|
||||
</Box>
|
||||
|
||||
<ExplorerListItem
|
||||
label="Address"
|
||||
value={
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={0.1}
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
width="100%"
|
||||
>
|
||||
<Typography variant="body4">{accountInfo.address}</Typography>
|
||||
<CopyToClipboard text={accountInfo.address} />
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
</Stack>
|
||||
</ExplorerCard>
|
||||
);
|
||||
};
|
||||
+61
-80
@@ -235,14 +235,12 @@ const Row = (props: IAccontStatsRowProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
export interface IAccountStatsCardProps {
|
||||
export interface IAccountBalancesTableProps {
|
||||
rows: Array<IAccontStatsRowProps>;
|
||||
overTitle?: string;
|
||||
priceTitle?: number;
|
||||
}
|
||||
|
||||
export const AccountStatsCard = (props: IAccountStatsCardProps) => {
|
||||
const { rows, overTitle, priceTitle } = props;
|
||||
export const AccountBalancesTable = (props: IAccountBalancesTableProps) => {
|
||||
const { rows } = props;
|
||||
const tablet = useMediaQuery(TABLET_WIDTH);
|
||||
const progressBarPercentages = () => {
|
||||
return rows.map((row) => row.allocation);
|
||||
@@ -262,80 +260,63 @@ export const AccountStatsCard = (props: IAccountStatsCardProps) => {
|
||||
const progressValues = getProgressValues();
|
||||
|
||||
return (
|
||||
<Card sx={{ height: "100%", borderRadius: "unset", padding: 1 }}>
|
||||
<CardContent>
|
||||
{overTitle && (
|
||||
<Typography
|
||||
mb={3}
|
||||
textTransform={"uppercase"}
|
||||
variant="h5"
|
||||
sx={{ color: "pine.600", letterSpacing: 0.7 }}
|
||||
>
|
||||
{overTitle}
|
||||
</Typography>
|
||||
)}
|
||||
{priceTitle && (
|
||||
<Typography variant="h3" sx={{ color: "pine.950" }} mb={3}>
|
||||
${priceTitle}
|
||||
</Typography>
|
||||
)}
|
||||
{!tablet && <MultiSegmentProgressBar values={progressValues} />}
|
||||
<TableContainer>
|
||||
<Table aria-label="collapsible table" sx={{ marginBottom: 3 }}>
|
||||
<TableHead>
|
||||
{tablet ? (
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography variant="subtitle2" sx={{ color: "pine.600" }}>
|
||||
Type
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography variant="subtitle2" sx={{ color: "pine.600" }}>
|
||||
Allocation
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography variant="subtitle2" sx={{ color: "pine.600" }}>
|
||||
Amount
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography variant="subtitle2" sx={{ color: "pine.600" }}>
|
||||
Value
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography variant="subtitle2" sx={{ color: "pine.600" }}>
|
||||
Type
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography variant="subtitle2" sx={{ color: "pine.600" }}>
|
||||
Amount / Value
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
)}
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rows.map((row, i) => (
|
||||
<Row
|
||||
key={row.type}
|
||||
{...row}
|
||||
isLastRow={i === rows.length - 1}
|
||||
progressBarColor={progressBarColours[i]}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Box>
|
||||
{!tablet && <MultiSegmentProgressBar values={progressValues} />}
|
||||
<TableContainer>
|
||||
<Table aria-label="collapsible table" sx={{ marginBottom: 3 }}>
|
||||
<TableHead>
|
||||
{tablet ? (
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography variant="subtitle2" sx={{ color: "pine.600" }}>
|
||||
Type
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography variant="subtitle2" sx={{ color: "pine.600" }}>
|
||||
Allocation
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography variant="subtitle2" sx={{ color: "pine.600" }}>
|
||||
Amount
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography variant="subtitle2" sx={{ color: "pine.600" }}>
|
||||
Value
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography variant="subtitle2" sx={{ color: "pine.600" }}>
|
||||
Type
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography variant="subtitle2" sx={{ color: "pine.600" }}>
|
||||
Amount / Value
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
)}
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rows.map((row, i) => (
|
||||
<Row
|
||||
key={row.type}
|
||||
{...row}
|
||||
isLastRow={i === rows.length - 1}
|
||||
progressBarColor={progressBarColours[i]}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
UpDownPriceIndicator,
|
||||
} from "../price/UpDownPriceIndicator";
|
||||
import type { IDynamicProgressBarProps } from "../progressBars/EpochProgressBar";
|
||||
import { CardQRCode, type ICardQRCodeProps } from "../qrCode/QrCode";
|
||||
import { StarRating } from "../starRating";
|
||||
|
||||
interface ICardTitlePriceProps {
|
||||
@@ -126,26 +127,6 @@ const CardCopyAddress = (props: ICardCopyAddressProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
interface ICardQRCodeProps {
|
||||
url: string;
|
||||
}
|
||||
|
||||
const CardQRCode = (props: ICardQRCodeProps) => {
|
||||
const { url } = props;
|
||||
return (
|
||||
<Box display={"flex"} justifyContent={"flex-start"}>
|
||||
<Box
|
||||
padding={1}
|
||||
border={"1px solid #C3D7D7"}
|
||||
display={"block"}
|
||||
width={"unset"}
|
||||
>
|
||||
<QRCodeCanvas value={url} />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
interface ICardRatingsProps {
|
||||
ratings: Array<{ title: string; numberOfStars: number }>;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { IBondInfo, INodeDescription } from "@/app/api";
|
||||
import { Stack } from "@mui/material";
|
||||
import { Stack, Typography } from "@mui/material";
|
||||
import { format } from "date-fns";
|
||||
import ExplorerCard from "../cards/ExplorerCard";
|
||||
import CopyToClipboard from "../copyToClipboard/CopyToClipboard";
|
||||
import ExplorerListItem from "../list/ListItem";
|
||||
|
||||
interface IBasicInfoCardProps {
|
||||
@@ -25,12 +26,40 @@ export const BasicInfoCard = (props: IBasicInfoCardProps) => {
|
||||
<ExplorerListItem
|
||||
divider
|
||||
label="NYM Address"
|
||||
value={bondInfo.bond_information.owner}
|
||||
value={
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={0.1}
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
width="100%"
|
||||
>
|
||||
<Typography variant="body4">
|
||||
{bondInfo.bond_information.owner}
|
||||
</Typography>
|
||||
<CopyToClipboard text={bondInfo.bond_information.owner} />
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
<ExplorerListItem
|
||||
divider
|
||||
label="Identity Key"
|
||||
value={bondInfo.bond_information.node.identity_key}
|
||||
value={
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={0.1}
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
width="100%"
|
||||
>
|
||||
<Typography variant="body4">
|
||||
{bondInfo.bond_information.node.identity_key}
|
||||
</Typography>
|
||||
<CopyToClipboard
|
||||
text={bondInfo.bond_information.node.identity_key}
|
||||
/>
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
<ExplorerListItem row divider label="Node bonded" value={timeBonded} />
|
||||
<ExplorerListItem
|
||||
|
||||
@@ -22,7 +22,6 @@ export const NodeMetricsCard = (props: INodeMetricsCardProps) => {
|
||||
label="Host"
|
||||
value={nodeDescription.description.host_information.ip_address.toString()}
|
||||
/>
|
||||
<ExplorerListItem row divider label="Staker rew." value="10,000 NYM" />
|
||||
<ExplorerListItem row divider label="Version" value="1.1.1.1" />
|
||||
<ExplorerListItem row label="Active set Prob." value="High" />
|
||||
</ExplorerCard>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Box } from "@mui/material";
|
||||
import { QRCodeCanvas } from "qrcode.react";
|
||||
|
||||
export interface ICardQRCodeProps {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export const CardQRCode = (props: ICardQRCodeProps) => {
|
||||
const { url } = props;
|
||||
return (
|
||||
<Box
|
||||
border={"1px solid #C3D7D7"}
|
||||
display={"flex"}
|
||||
justifyContent={"center"}
|
||||
alignItems={"center"}
|
||||
width={144}
|
||||
height={144}
|
||||
>
|
||||
<QRCodeCanvas value={url} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user