Skip to content

Commit

Permalink
feat(tangle-dapp): Add liquid staking unstaking card (#2415)
Browse files Browse the repository at this point in the history
Co-authored-by: vutuanlinh2k2 <[email protected]>
  • Loading branch information
yurixander and vutuanlinh2k2 authored Jul 22, 2024
1 parent 60b225a commit 3f7904b
Show file tree
Hide file tree
Showing 70 changed files with 2,472 additions and 726 deletions.
5 changes: 0 additions & 5 deletions .yarnrc

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default function useAmountToTransfer() {
? parseUnits(
formatBn(amount, decimals, {
includeCommas: false,
fractionLength: undefined,
fractionMaxLength: undefined,
}),
decimals,
).toString()
Expand Down
36 changes: 7 additions & 29 deletions apps/tangle-dapp/app/liquid-staking/[tokenSymbol]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
'use client';

import { notFound } from 'next/navigation';
import { FC } from 'react';

import LiquidStakingCard from '../../../components/LiquidStaking/LiquidStakingCard';
import TokenInfoCard from '../../../components/LiquidStaking/TokenInfoCard';
import { LIQUID_STAKING_TOKEN_PREFIX } from '../../../constants/liquidStaking';
import LiquidStakeAndUnstakeCards from '../../../components/LiquidStaking/LiquidStakeAndUnstakeCards';
import UnstakeRequestsTable from '../../../components/LiquidStaking/UnstakeRequestsTable';
import isLiquidStakingToken from '../../../utils/liquidStaking/isLiquidStakingToken';

type Props = {
Expand All @@ -17,33 +18,10 @@ const LiquidStakingTokenPage: FC<Props> = ({ params: { tokenSymbol } }) => {
}

return (
<div className="grid grid-cols-2 gap-12">
<TokenInfoCard
stakingInfo={{
title: 'Staking',
tooltip: `Total staked ${tokenSymbol}`,
value: '-',
}}
availableInfo={{
title: 'Available',
tooltip: `Available ${LIQUID_STAKING_TOKEN_PREFIX}${tokenSymbol}`,
value: '98.00',
valueTooltip: `Available ${LIQUID_STAKING_TOKEN_PREFIX}${tokenSymbol}`,
}}
unstakingInfo={{
title: 'Unstaking',
tooltip: `Total unstaking ${tokenSymbol} in progress`,
value: '-',
}}
apyInfo={{
title: 'APY',
tooltip: 'APY (Annual Percentage Yield) %',
value: '-',
}}
tokenSymbol={tokenSymbol}
/>
<div className="flex flex-wrap gap-12 items-start">
<LiquidStakeAndUnstakeCards />

<LiquidStakingCard />
<UnstakeRequestsTable />
</div>
);
};
Expand Down
59 changes: 25 additions & 34 deletions apps/tangle-dapp/app/liquid-staking/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,11 @@ import { FC } from 'react';
import { GlassCard } from '../../components';
import LiquidStakingTokenItem from '../../components/LiquidStaking/LiquidStakingTokenItem';
import StatItem from '../../components/LiquidStaking/StatItem';
import { LS_CHAIN_TO_TOKEN, TVS_TOOLTIP } from '../../constants/liquidStaking';
import entriesOf from '../../utils/entriesOf';
import { LIQUID_STAKING_CHAINS } from '../../constants/liquidStaking';

const LiquidStakingPage: FC = () => {
return (
<div className="flex flex-col">
<Typography variant="h4" fw="bold">
Overview
</Typography>

<div className="flex flex-col gap-10">
<GlassCard className="flex flex-row items-center justify-between w-full overflow-x-auto">
<div className="flex flex-col gap-2">
<Typography variant="h5" fw="bold">
Expand All @@ -22,44 +17,40 @@ const LiquidStakingPage: FC = () => {

<Typography variant="body1" fw="normal">
Get Liquid Staking Tokens (LSTs) to earn & unleash restaking on
Tangle via delegation.
Tangle Mainnet via delegation.
</Typography>
</div>

<div className="flex gap-6">
<div className="flex gap-6 h-full">
<StatItem title="$123.01" subtitle="My Total Staking" />

<StatItem title="$123,412.01" subtitle="TVS" tooltip={TVS_TOOLTIP} />

<StatItem title="3.12 %" subtitle="Est. Daily Rewards" />
</div>
</GlassCard>

<GlassCard className="space-y-4">
<Typography variant="h5" fw="bold">
<div className="flex flex-col gap-4">
<Typography variant="h4" fw="bold">
Liquid Staking Tokens
</Typography>

<div className="overflow-x-auto">
<div className="flex flex-col gap-4 min-w-[750px]">
{entriesOf(LS_CHAIN_TO_TOKEN).map(([chain, token]) => {
return (
<LiquidStakingTokenItem
key={chain}
chain={chain}
title={`Tangle ${chain}`}
tokenSymbol={token}
// TODO: Using dummy values.
annualPercentageYield={0.23456}
// TODO: Can't pass non-plain objects as props to Client components from Server components (this page). For now, passing in as a string then creating BN instance inside the component.
totalStaked="100000000"
totalValueStaked={220_000_123}
/>
);
})}
<GlassCard className="space-y-4">
<div className="overflow-x-auto">
<div className="flex flex-col gap-4 min-w-[750px]">
{LIQUID_STAKING_CHAINS.map((chain) => {
return (
<LiquidStakingTokenItem
key={chain.id}
chainId={chain.id}
title={`Tangle ${chain.name}`}
tokenSymbol={chain.token}
// TODO: Can't pass non-plain objects as props to Client components from Server components (this page). For now, passing in as a string then creating BN instance inside the component.
totalStaked="100000000"
totalValueStaked={220_000_123}
/>
);
})}
</div>
</div>
</div>
</GlassCard>
</GlassCard>
</div>
</div>
);
};
Expand Down
37 changes: 37 additions & 0 deletions apps/tangle-dapp/components/LiquidStaking/AddressLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { HexString } from '@polkadot/util/types';
import { ExternalLinkLine } from '@webb-tools/icons';
import { shortenString, Typography } from '@webb-tools/webb-ui-components';
import { FC, useCallback } from 'react';

import { AnySubstrateAddress } from '../../types/utils';

export type AddressLinkProps = {
address: AnySubstrateAddress | HexString;
};

const AddressLink: FC<AddressLinkProps> = ({ address }) => {
// TODO: Determine href.
const href = '#';

// Stop propagation to prevent a parent modal (if any) from closing.
const handleClick = useCallback((event: any) => {
event.stopPropagation();
}, []);

return (
<a
href={href}
target="_blank"
onClick={handleClick}
className="flex gap-1 items-center justify-start hover:underline"
>
<Typography variant="body1" fw="normal" className="dark:text-mono-0">
{shortenString(address, 6)}
</Typography>

<ExternalLinkLine className="dark:fill-mono-0" />
</a>
);
};

export default AddressLink;
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { CheckboxCircleFill, TimeFillIcon, UndoIcon } from '@webb-tools/icons';
import { Button, Typography } from '@webb-tools/webb-ui-components';
import { FC, ReactElement } from 'react';

import GlassCard from '../GlassCard';

const AvailableWithdrawCard: FC = () => {
return (
<GlassCard className="flex rounded-xl gap-2">
<div className="flex justify-between">
<div className="flex flex-col gap-1">
<Typography variant="body1" fw="semibold">
Available
</Typography>

<Typography variant="h5" fw="bold">
0.0 DOT
</Typography>
</div>

<div className="flex gap-1 items-center justify-center">
<Button variant="utility" size="sm">
Withdraw
</Button>

{/* TODO: Need a tooltip for this, since it's only an icon. */}
<Button variant="utility" size="sm">
<UndoIcon className="dark:fill-blue-40" />
</Button>
</div>
</div>

<hr className="border-mono-0 dark:border-mono-160" />

<div className="flex justify-between items-end">
<div className="flex flex-col gap-1">
<Typography variant="body1" fw="semibold">
My requests
</Typography>

<div className="flex gap-2">
<RequestItem
icon={<CheckboxCircleFill className="fill-green-50" />}
text="0"
/>

<RequestItem
icon={<TimeFillIcon className="fill-blue-50" />}
text="1"
/>
</div>
</div>

<div className="flex items-center justify-center gap-1">
<TimeFillIcon className="dark:fill-mono-0" />

<Typography variant="body1" fw="bold" className="dark:text-mono-0">
1.00 tgDOT
</Typography>
</div>
</div>
</GlassCard>
);
};

type RequestItemProps = {
icon: ReactElement;
text: string;
};

/** @internal */
const RequestItem: FC<RequestItemProps> = ({ icon, text }) => {
return (
<div className="flex items-center justify-center gap-1">
{icon}

<Typography className="dark:text-mono-0" variant="body1" fw="bold">
{text}
</Typography>
</div>
);
};

export default AvailableWithdrawCard;
74 changes: 74 additions & 0 deletions apps/tangle-dapp/components/LiquidStaking/CancelUnstakeModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { CloseCircleLineIcon } from '@webb-tools/icons';
import {
Button,
Modal,
ModalContent,
ModalFooter,
ModalHeader,
Typography,
} from '@webb-tools/webb-ui-components';
import { FC, useCallback } from 'react';

import ExternalLink from './ExternalLink';
import ModalIcon from './ModalIcon';
import { UnstakeRequestItem } from './UnstakeRequestsTable';

export type CancelUnstakeModalProps = {
isOpen: boolean;
unstakeRequest: UnstakeRequestItem;
onClose: () => void;
};

const CancelUnstakeModal: FC<CancelUnstakeModalProps> = ({
isOpen,
// TODO: Make use of the unstake request data, which is relevant for the link's href.
unstakeRequest: _unstakeRequest,
onClose,
}) => {
const handleConfirm = useCallback(() => {
// TODO: Set button as loading, disable ability to close modal, and proceed to execute the unstake cancellation via its corresponding extrinsic call.
}, []);

return (
<Modal open>
<ModalContent
isCenter
isOpen={isOpen}
className="w-full max-w-[calc(100vw-40px)] md:max-w-[500px] rounded-2xl bg-mono-0 dark:bg-mono-180"
>
<ModalHeader titleVariant="h4" onClose={onClose}>
Cancel Unstake
</ModalHeader>

<div className="flex flex-col items-center justify-center gap-2 p-9">
<ModalIcon Icon={CloseCircleLineIcon} />

<Typography
className="dark:text-mono-0 text-center"
variant="body1"
fw="normal"
>
Are you sure you want to cancel your unstaking request? By
cancelling, your tokens will remain staked and continue earning
rewards.
</Typography>

{/* TODO: External link's href. */}
<ExternalLink href="#">Learn More</ExternalLink>
</div>

<ModalFooter className="flex gap-1 px-8 py-6 space-y-0">
<Button onClick={onClose} isFullWidth variant="secondary">
Cancel
</Button>

<Button onClick={handleConfirm} isFullWidth variant="primary">
Confirm
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
};

export default CancelUnstakeModal;
Loading

0 comments on commit 3f7904b

Please sign in to comment.