Skip to content

Commit

Permalink
EVM Staking - Adds wallet balance & total staked info container (#1843)
Browse files Browse the repository at this point in the history
  • Loading branch information
devpavan04 authored Nov 22, 2023
1 parent 39c16f3 commit 6913366
Show file tree
Hide file tree
Showing 28 changed files with 1,623 additions and 1,250 deletions.
2 changes: 1 addition & 1 deletion apps/tangle-dapp/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default function RootLayout({
}) {
return (
<html lang="en" suppressHydrationWarning>
<body className="flex h-screen bg-body">
<body>
<Providers>
<Layout>{children}</Layout>
</Providers>
Expand Down
5 changes: 5 additions & 0 deletions apps/tangle-dapp/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Typography } from '@webb-tools/webb-ui-components';
import {
HeaderChipsContainer,
KeyMetricsTableContainer,
NominatorStatsContainer,
ValidatorTablesContainer,
} from '../containers';

Expand All @@ -21,6 +22,10 @@ export default async function Index() {
<KeyMetricsTableContainer />
</div>

<div className="mt-12">
<NominatorStatsContainer />
</div>

<div className="mt-12">
<ValidatorTablesContainer />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { Typography } from '@webb-tools/webb-ui-components/typography/Typography';

import type { MetricReturnType } from '../../types';
import { MetricReturnType } from '../../types';
import getRoundedDownNumberWith2Decimals from '../../utils/getRoundedDownNumberWith2Decimals';
import dataHooks, { defaultHook } from './dataHooks';
import type { MetricItemProps } from './types';
Expand Down
2 changes: 1 addition & 1 deletion apps/tangle-dapp/components/KeyMetricItem/dataHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
useValidatorsCountSubscription,
useWaitingCountSubscription,
} from '../../data';
import type { MetricReturnType } from '../../types';
import { MetricReturnType } from '../../types';

const dataHooks: {
[key: string]: (defaultValue?: MetricReturnType) => MetricReturnType;
Expand Down
64 changes: 64 additions & 0 deletions apps/tangle-dapp/components/StatsMetricItem/StatsMetricItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { SkeletonLoader, Typography } from '@webb-tools/webb-ui-components';
import type { FC } from 'react';
import { Suspense } from 'react';
import { twMerge } from 'tailwind-merge';

import {
getRoundedDownNumberWith2Decimals,
splitTokenValueAndSymbol,
} from '../../utils';
import { InfoIconWithTooltip } from '..';
import { StatsMetricItemProps } from './types';

export const StatsMetricItem: FC<StatsMetricItemProps> = ({
title,
tooltip,
className,
...restProps
}) => {
return (
<div className={twMerge('flex flex-col gap-4', className)}>
<Suspense fallback={<SkeletonLoader size="lg" />}>
<StatsMetricItemValue {...restProps} />
</Suspense>

<div className="flex items-center gap-0.5">
<Typography variant="body1" className="text-mono-140 dark:text-mono-40">
{title}
</Typography>
{tooltip && <InfoIconWithTooltip content={tooltip} />}
</div>
</div>
);
};

/** @internal */
const StatsMetricItemValue = async (
props: Omit<StatsMetricItemProps, 'tooltip' | 'title'>
) => {
const { dataFetcher, address } = props;

const value = await dataFetcher(address);

const { value: value_, symbol } = splitTokenValueAndSymbol(String(value));

return (
<div className="flex gap-2 items-center">
<Typography
variant="h4"
fw="bold"
className="text-mono-200 dark:text-mono-0"
>
{getRoundedDownNumberWith2Decimals(value_)}
</Typography>

<Typography
variant="label"
fw="medium"
className="text-mono-140 dark:text-mono-40"
>
{symbol ? symbol : 'tTNT'}
</Typography>
</div>
);
};
1 change: 1 addition & 0 deletions apps/tangle-dapp/components/StatsMetricItem/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './StatsMetricItem';
9 changes: 9 additions & 0 deletions apps/tangle-dapp/components/StatsMetricItem/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { StatsMetricReturnType } from '../../types';

export interface StatsMetricItemProps {
title: string;
tooltip?: string;
dataFetcher: (address: string) => Promise<StatsMetricReturnType>;
address: string;
className?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const columns = [
columnHelper.accessor('minimumStake', {
header: () => <HeaderCell title="Minimum stake" className="justify-end" />,
cell: (props) => (
<StringCell value={`${props.getValue()} TTNT`} className="text-right" />
<StringCell value={`${props.getValue()} tTNT`} className="text-right" />
),
}),
];
Expand Down
1 change: 1 addition & 0 deletions apps/tangle-dapp/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export * from './InfoIconWithTooltip';
export * from './KeyMetricItem';
export * from './sideBar';
export * from './skeleton';
export * from './StatsMetricItem';
export * from './ValidatorTable';
export * from './WalletDropdown';
13 changes: 13 additions & 0 deletions apps/tangle-dapp/constants/evm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { chainsConfig } from '@webb-tools/dapp-config/chains/chain-config';
import { PresetTypedChainId } from '@webb-tools/dapp-types/ChainId';
import { createPublicClient, defineChain, http } from 'viem';

const tangleTestnetConfig = chainsConfig[PresetTypedChainId.TangleTestnet];
delete tangleTestnetConfig.contracts;

const tangleTestnet = defineChain(tangleTestnetConfig);

export const evmClient = createPublicClient({
chain: tangleTestnet,
transport: http(),
});
1 change: 1 addition & 0 deletions apps/tangle-dapp/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './evm';
export * from './polkadot';
2 changes: 1 addition & 1 deletion apps/tangle-dapp/constants/polkadot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { firstValueFrom } from 'rxjs';

const apiPromiseCache = new Map<string, ApiPromise>();

const TOKEN_UNIT = 'TTNT';
const TOKEN_UNIT = 'tTNT';

export const getPolkadotApiPromise = async (
endpoint: string = TANGLE_RPC_ENDPOINT
Expand Down
6 changes: 3 additions & 3 deletions apps/tangle-dapp/containers/Layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ const Layout: FC<PropsWithChildren> = ({ children }) => {
const isSideBarInitiallyExpanded = getSideBarStateFromCookie();

return (
<>
<div className="flex bg-body h-screen">
<SideBar isExpandedAtDefault={isSideBarInitiallyExpanded} />

<main className="flex flex-col justify-between flex-1 h-full overflow-y-auto max-w-[1448px] m-auto px-10">
<main className="flex flex-col justify-between flex-1 h-full max-w-[1448px] m-auto px-10 overflow-y-auto scrollbar-hide">
<div className="flex flex-col justify-between">
<div className="flex items-center justify-between py-6 mb-10">
<div className="flex items-center space-x-4 lg:space-x-0">
Expand All @@ -32,7 +32,7 @@ const Layout: FC<PropsWithChildren> = ({ children }) => {

<Footer isMinimal className="py-8" />
</main>
</>
</div>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
'use client';

import { useWebContext } from '@webb-tools/api-provider-environment';
import { Button, Divider } from '@webb-tools/webb-ui-components';
import {
SOCIAL_URLS_RECORD,
WEBB_TANGLE_DOCS_STAKING_URL,
} from '@webb-tools/webb-ui-components/constants';
import cx from 'classnames';
import Link from 'next/link';
import { useMemo } from 'react';

import { StatsMetricItem } from '../../components';
import { getTokenWalletBalance, getTotalStakedAmount } from '../../data';
import { convertEthereumToSubstrateAddress } from '../../utils';

export const NominatorStatsContainer = () => {
const { activeAccount } = useWebContext();

const walletAddress = useMemo(() => {
if (!activeAccount?.address) return '0x0';

return activeAccount.address;
}, [activeAccount?.address]);

const substrateAddress = useMemo(() => {
if (!activeAccount?.address) return undefined;

return convertEthereumToSubstrateAddress(activeAccount.address);
}, [activeAccount?.address]);

return (
<div className="flex flex-col md:flex-row gap-4 w-full">
<div
className={cx(
'w-full rounded-2xl overflow-hidden h-[204px] p-4',
'bg-glass dark:bg-glass_dark',
'border-2 border-mono-0 dark:border-mono-160'
)}
>
<StatsMetricItem
title="Available tTNT in Wallet"
dataFetcher={() => getTokenWalletBalance(walletAddress)}
address={walletAddress}
className=""
/>

<Divider className="my-6 bg-mono-0 dark:bg-mono-160" />

<Button variant="utility" className="w-full" isDisabled>
Delegate
</Button>
</div>

<div
className={cx(
'w-full rounded-2xl overflow-hidden h-[204px] p-4',
'bg-glass dark:bg-glass_dark',
'border-2 border-mono-0 dark:border-mono-160'
)}
>
<StatsMetricItem
title="Total Staked tTNT"
tooltip="Total Staked tTNT."
dataFetcher={() => getTotalStakedAmount(substrateAddress)}
address={substrateAddress ?? ''}
className=""
/>

<Divider className="my-6 bg-mono-0 dark:bg-mono-160" />

<div className="flex items-center gap-2">
<Link href={WEBB_TANGLE_DOCS_STAKING_URL} target="_blank">
<Button variant="utility" className="w-full">
Learn More
</Button>
</Link>

<Link href={SOCIAL_URLS_RECORD.discord} target="_blank">
<Button variant="utility" className="w-full">
Join Community
</Button>
</Link>
</div>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './NominatorStatsContainer';
1 change: 1 addition & 0 deletions apps/tangle-dapp/containers/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './HeaderChipsContainer';
export * from './KeyMetricsTableContainer';
export { Layout } from './Layout';
export * from './NominatorStatsContainer';
export * from './ValidatorTablesContainer';
export * from './WalletAndChainContainer';
export * from './WalletModalContainer';
27 changes: 27 additions & 0 deletions apps/tangle-dapp/data/NominatorStats/getTokenWalletBalance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import ensureHex from '@webb-tools/dapp-config/utils/ensureHex';
import { formatEther } from 'viem';

import { evmClient } from '../../constants';
import { StatsMetricReturnType } from '../../types';

export const getTokenWalletBalance = async (
address: string
): Promise<StatsMetricReturnType> => {
if (!address || address === '0x0') {
return NaN;
}

try {
const balance = await evmClient.getBalance({
address: ensureHex(address),
});

const walletBalance = formatEther(balance);

return Number(walletBalance) ?? NaN;
} catch (e) {
console.error(e);

return NaN;
}
};
27 changes: 27 additions & 0 deletions apps/tangle-dapp/data/NominatorStats/getTotalStakedAmount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { u128 } from '@polkadot/types';

import { formatTokenBalance, getPolkadotApiPromise } from '../../constants';
import { StatsMetricReturnType } from '../../types';

export const getTotalStakedAmount = async (
address?: string
): Promise<StatsMetricReturnType> => {
const api = await getPolkadotApiPromise();

if (!api || !address) return NaN;

try {
const data = await api.query.staking.ledger(address);
const ledger = data.unwrapOrDefault();

const totalStaked = new u128(api.registry, ledger.total.toString());

const availableTokenBalance = await formatTokenBalance(totalStaked);

return availableTokenBalance ?? NaN;
} catch (e) {
console.error(e);

return NaN;
}
};
2 changes: 2 additions & 0 deletions apps/tangle-dapp/data/NominatorStats/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './getTokenWalletBalance';
export * from './getTotalStakedAmount';
1 change: 1 addition & 0 deletions apps/tangle-dapp/data/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './HeaderChips';
export * from './NominatorStats';
export * from './TopLevelStats';
export * from './ValidatorTables';
36 changes: 35 additions & 1 deletion apps/tangle-dapp/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
const plugin = require('tailwindcss/plugin');
const { join } = require('path');

const preset = require('@webb-tools/tailwind-preset');
Expand Down Expand Up @@ -28,5 +29,38 @@ module.exports = {
},
},
},
plugins: [],
plugins: [
plugin(function ({ addUtilities }) {
addUtilities(
{
'.scrollbar-hide': {
/* IE and Edge */
'-ms-overflow-style': 'none',

/* Firefox */
'scrollbar-width': 'none',

/* Safari and Chrome */
'&::-webkit-scrollbar': {
display: 'none',
},
},

'.scrollbar-default': {
/* IE and Edge */
'-ms-overflow-style': 'auto',

/* Firefox */
'scrollbar-width': 'auto',

/* Safari and Chrome */
'&::-webkit-scrollbar': {
display: 'block',
},
},
},
['responsive']
);
}),
],
};
Loading

0 comments on commit 6913366

Please sign in to comment.