Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tangle-dapp): Add Tangle mainnet & testnet networks to liquid staking #2544

Merged
merged 36 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
abecde9
feat(tangle-dapp): Create table foundations
yurixander Sep 3, 2024
8a60c8a
Merge branch 'develop' into yuri/list-parachain-pools-in-table
yurixander Sep 3, 2024
68cb876
refactor(tangle-dapp): Remove `useTokenPrice` hook
yurixander Sep 3, 2024
09df2ab
feat(tangle-dapp): Add missing columns
yurixander Sep 5, 2024
3e782b7
Merge branch 'develop' into yuri/list-parachain-pools-in-table
yurixander Sep 5, 2024
2d46984
refactor(tangle-dapp): Show corresponding table
yurixander Sep 6, 2024
9834217
feat(tangle-dapp): Create `useLsPools` hook
yurixander Sep 7, 2024
25dd1d6
Merge branch 'develop' into yuri/list-parachain-pools-in-table
yurixander Sep 7, 2024
4e5f8d1
feat(tangle-dapp): Create LS pools data fetching hooks
yurixander Sep 8, 2024
45618d1
Merge branch 'yuri/list-parachain-pools-in-table' of https://github.c…
yurixander Sep 8, 2024
51ec94e
fix(tangle-dapp): Fix errors
yurixander Sep 10, 2024
2e8cda2
feat(tangle-dapp): Derive LS pool owner's stake & TVL
yurixander Sep 12, 2024
c8d04c8
Merge branch 'develop' into yuri/list-parachain-pools-in-table
yurixander Sep 12, 2024
373db83
feat(tangle-dapp): Implement `useLsPoolCompoundApys` hook
yurixander Sep 12, 2024
c94356b
Merge branch 'yuri/list-parachain-pools-in-table' of https://github.c…
yurixander Sep 12, 2024
f40d0db
feat(tangle-dapp): Use `useLsPoolCompoundApys` in `useLsPools`
yurixander Sep 13, 2024
d4482bc
ci(tangle-dapp): Bump Tangle Substrate types package
yurixander Sep 13, 2024
eeaf084
ci(tangle-dapp): Fix lint
yurixander Sep 13, 2024
edef04a
feat(tangle-dapp): Handle unsupported networks
yurixander Sep 13, 2024
e3e3f7c
fix(tangle-dapp): Check for LS pool support
yurixander Sep 13, 2024
490d90f
feat(tangle-dapp): Add empty & loading states
yurixander Sep 14, 2024
46be2de
refactor(tangle-dapp): Resolve some TODOs
yurixander Sep 16, 2024
d564cb5
fix(tangle-dapp): Handle edge case where root role is `None`
yurixander Sep 16, 2024
31834b1
docs(tangle-dapp): Resolve more TODOs
yurixander Sep 16, 2024
7a495fd
feat(tangle-dapp): Add initial Tangle mainnet network
yurixander Sep 17, 2024
5fef25f
feat(tangle-dapp): Add Tangle testnet to LS networks
yurixander Sep 23, 2024
fc6ace6
Merge branch 'develop' into yuri/tangle-mainnet-ls-network
yurixander Sep 23, 2024
fb89adc
feat(tangle-dapp): Add Tangle local to LS networks
yurixander Sep 23, 2024
0dfecdb
feat(tangle-dapp): Create `useLsPoolJoinTx` hook
yurixander Sep 23, 2024
25d41e9
refactor(tangle-dapp): Remove `FeedbackBanner` component
yurixander Sep 23, 2024
154989b
Merge branch 'develop' into yuri/tangle-mainnet-ls-network
yurixander Sep 25, 2024
ebb915e
fix(tangle-dapp): Fix Tangle networks id issue
yurixander Sep 25, 2024
7779488
feat(tangle-dapp): Create `useLsPoolUnbondTx` hook
yurixander Sep 26, 2024
941fb7c
ci(tangle-dapp): Fix lint errors
yurixander Sep 27, 2024
4669f02
fix(tangle-dapp): Use `switchNetwork` instead of `setNetwork`
yurixander Sep 27, 2024
8f56acf
fix(tangle-dapp): Fix network mismatch on load bug
yurixander Sep 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions apps/tangle-dapp/app/liquid-staking/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
'use client';

import { FC } from 'react';
import { FC, useEffect } from 'react';

import { LsValidatorTable } from '../../components/LiquidStaking/LsValidatorTable';
import LsStakeCard from '../../components/LiquidStaking/stakeAndUnstake/LsStakeCard';
import LsUnstakeCard from '../../components/LiquidStaking/stakeAndUnstake/LsUnstakeCard';
import UnstakeRequestsTable from '../../components/LiquidStaking/unstakeRequestsTable/UnstakeRequestsTable';
import { LsSearchParamKey } from '../../constants/liquidStaking/types';
import LsPoolsTable from '../../containers/LsPoolsTable';
import useNetworkStore from '../../context/useNetworkStore';
import { useLsStore } from '../../data/liquidStaking/useLsStore';
import useNetworkSwitcher from '../../hooks/useNetworkSwitcher';
import useSearchParamState from '../../hooks/useSearchParamState';
import getLsTangleNetwork from '../../utils/liquidStaking/getLsTangleNetwork';
import isLsParachainChainId from '../../utils/liquidStaking/isLsParachainChainId';
import TabListItem from '../restake/TabListItem';
import TabsList from '../restake/TabsList';
Expand All @@ -28,10 +31,22 @@ const LiquidStakingTokenPage: FC = () => {
value ? SearchParamAction.STAKE : SearchParamAction.UNSTAKE,
});

const { selectedProtocolId } = useLsStore();
const { selectedProtocolId, selectedNetworkId } = useLsStore();
const { network } = useNetworkStore();
const { switchNetwork } = useNetworkSwitcher();

const lsTangleNetwork = getLsTangleNetwork(selectedNetworkId);
const isParachainChain = isLsParachainChainId(selectedProtocolId);

// Sync the network with the selected liquid staking network on load.
// It might differ initially if the user navigates to the page and
// the active network differs from the default liquid staking network.
useEffect(() => {
if (lsTangleNetwork !== null && lsTangleNetwork.id !== network.id) {
switchNetwork(lsTangleNetwork, false);
}
}, [lsTangleNetwork, network.id, selectedNetworkId, switchNetwork]);

return (
<div className="flex flex-wrap gap-12">
<div className="flex flex-col gap-4 w-full min-w-[450px] max-w-[600px]">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,21 @@ import { FC } from 'react';
import { twMerge } from 'tailwind-merge';

import { LS_DERIVATIVE_TOKEN_PREFIX } from '../../../constants/liquidStaking/constants';
import { LsProtocolId, LsToken } from '../../../constants/liquidStaking/types';
import { LsToken } from '../../../constants/liquidStaking/types';
import { ExchangeRateType } from '../../../data/liquidStaking/useLsExchangeRate';
import useLsExchangeRate from '../../../data/liquidStaking/useLsExchangeRate';
import DetailItem from './DetailItem';

export type ExchangeRateDetailItemProps = {
type: ExchangeRateType;
token: LsToken;
protocolId: LsProtocolId;
};

const ExchangeRateDetailItem: FC<ExchangeRateDetailItemProps> = ({
type,
token,
protocolId,
}) => {
const { exchangeRate, isRefreshing } = useLsExchangeRate(type, protocolId);
const { exchangeRate, isRefreshing } = useLsExchangeRate(type);

const exchangeRateElement =
exchangeRate instanceof Error ? (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BN, BN_ZERO } from '@polkadot/util';
import { FC, useMemo } from 'react';

import { EMPTY_VALUE_PLACEHOLDER } from '../../../constants';
import { LsProtocolId } from '../../../constants/liquidStaking/types';
import formatBn from '../../../utils/formatBn';
import getLsProtocolDef from '../../../utils/liquidStaking/getLsProtocolDef';
Expand Down Expand Up @@ -36,6 +37,8 @@ const FeeDetailItem: FC<FeeDetailItemProps> = ({
// Propagate error or loading state.
if (!(feeAmount instanceof BN)) {
return feeAmount;
} else if (feeAmount.isZero()) {
return EMPTY_VALUE_PLACEHOLDER;
}

const formattedAmount = formatBn(feeAmount, protocol.decimals, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,29 @@ import { twMerge } from 'tailwind-merge';

import { EMPTY_VALUE_PLACEHOLDER } from '../../../constants';
import { LS_DERIVATIVE_TOKEN_PREFIX } from '../../../constants/liquidStaking/constants';
import {
LsNetworkId,
LsProtocolId,
} from '../../../constants/liquidStaking/types';
import { LsNetworkId } from '../../../constants/liquidStaking/types';
import { useLsStore } from '../../../data/liquidStaking/useLsStore';
import formatBn from '../../../utils/formatBn';
import getLsProtocolDef from '../../../utils/liquidStaking/getLsProtocolDef';
import useLsAgnosticBalance from './useLsAgnosticBalance';

export type LsAgnosticBalanceProps = {
isNative?: boolean;
protocolId: LsProtocolId;
tooltip?: string;
onlyShowTooltipWhenBalanceIsSet?: boolean;
onClick?: () => void;
};

const LsAgnosticBalance: FC<LsAgnosticBalanceProps> = ({
isNative = true,
protocolId,
tooltip,
onlyShowTooltipWhenBalanceIsSet = true,
onClick,
}) => {
const [isHovering, setIsHovering] = useState(false);
const { balance, isRefreshing } = useLsAgnosticBalance(isNative, protocolId);
const protocol = getLsProtocolDef(protocolId);
const { balance, isRefreshing } = useLsAgnosticBalance(isNative);
const { selectedProtocolId } = useLsStore();
const protocol = getLsProtocolDef(selectedProtocolId);

// Special case for liquid tokens on the `TgToken.sol` contract.
// See: https://github.com/webb-tools/tnt-core/blob/1f371959884352e7af68e6091c5bb330fcaa58b8/src/lst/liquidtoken/TgToken.sol#L26
Expand All @@ -58,7 +55,6 @@ const LsAgnosticBalance: FC<LsAgnosticBalanceProps> = ({
}

const formattedBalance = formatBn(balance, decimals, {
fractionMaxLength: undefined,
includeCommas: true,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
Input,
Typography,
} from '@webb-tools/webb-ui-components';
import React, { FC, useCallback, useMemo } from 'react';
import React, { FC, useCallback, useEffect, useMemo } from 'react';
import { z } from 'zod';

import { LS_DERIVATIVE_TOKEN_PREFIX } from '../../../constants/liquidStaking/constants';
Expand All @@ -22,11 +22,12 @@ import {
LsProtocolId,
LsSearchParamKey,
} from '../../../constants/liquidStaking/types';
import useMintTx from '../../../data/liquidStaking/parachain/useMintTx';
import useLsPoolJoinTx from '../../../data/liquidStaking/tangle/useLsPoolJoinTx';
import useLsExchangeRate, {
ExchangeRateType,
} from '../../../data/liquidStaking/useLsExchangeRate';
import { useLsStore } from '../../../data/liquidStaking/useLsStore';
import useMintTx from '../../../data/liquidStaking/useMintTx';
import useLiquifierDeposit from '../../../data/liquifier/useLiquifierDeposit';
import useActiveAccountAddress from '../../../hooks/useActiveAccountAddress';
import useSearchParamState from '../../../hooks/useSearchParamState';
Expand All @@ -40,6 +41,7 @@ import LsFeeWarning from './LsFeeWarning';
import LsInput from './LsInput';
import TotalDetailItem from './TotalDetailItem';
import UnstakePeriodDetailItem from './UnstakePeriodDetailItem';
import useLsChangeNetwork from './useLsChangeNetwork';
import useLsSpendingLimits from './useLsSpendingLimits';

const LsStakeCard: FC = () => {
Expand All @@ -54,10 +56,15 @@ const LsStakeCard: FC = () => {
selectedProtocolId,
setSelectedProtocolId,
selectedNetworkId,
setSelectedNetworkId,
selectedPoolId,
} = useLsStore();

const { execute: executeMintTx, status: mintTxStatus } = useMintTx();
const { execute: executeTanglePoolJoinTx, status: tanglePoolJoinTxStatus } =
useLsPoolJoinTx();

const { execute: executeParachainMintTx, status: parachainMintTxStatus } =
useMintTx();

const performLiquifierDeposit = useLiquifierDeposit();
const activeAccountAddress = useActiveAccountAddress();

Expand All @@ -67,6 +74,12 @@ const LsStakeCard: FC = () => {
);

const selectedProtocol = getLsProtocolDef(selectedProtocolId);
const tryChangeNetwork = useLsChangeNetwork();

const isTangleNetwork =
selectedNetworkId === LsNetworkId.TANGLE_LOCAL ||
selectedNetworkId === LsNetworkId.TANGLE_MAINNET ||
selectedNetworkId === LsNetworkId.TANGLE_TESTNET;

// TODO: Not loading the correct protocol for: '?amount=123000000000000000000&protocol=7&network=1&action=stake'. When network=1, it switches to protocol=5 on load. Could this be because the protocol is reset to its default once the network is switched?
useSearchParamSync({
Expand All @@ -82,16 +95,13 @@ const LsStakeCard: FC = () => {
value: selectedNetworkId,
parse: (value) => z.nativeEnum(LsNetworkId).parse(parseInt(value)),
stringify: (value) => value.toString(),
setValue: setSelectedNetworkId,
setValue: tryChangeNetwork,
});

const {
exchangeRate: exchangeRateOrError,
isRefreshing: isRefreshingExchangeRate,
} = useLsExchangeRate(
ExchangeRateType.NativeToDerivative,
selectedProtocolId,
);
} = useLsExchangeRate(ExchangeRateType.NativeToDerivative);

// TODO: Properly handle the error state.
const exchangeRate =
Expand All @@ -105,9 +115,9 @@ const LsStakeCard: FC = () => {

if (
selectedProtocol.networkId === LsNetworkId.TANGLE_RESTAKING_PARACHAIN &&
executeMintTx !== null
executeParachainMintTx !== null
) {
executeMintTx({
executeParachainMintTx({
amount: fromAmount,
currency: selectedProtocol.currency,
});
Expand All @@ -116,8 +126,25 @@ const LsStakeCard: FC = () => {
performLiquifierDeposit !== null
) {
await performLiquifierDeposit(selectedProtocol.id, fromAmount);
} else if (
isTangleNetwork &&
executeTanglePoolJoinTx !== null &&
selectedPoolId !== null
) {
executeTanglePoolJoinTx({
amount: fromAmount,
poolId: selectedPoolId,
});
}
}, [executeMintTx, fromAmount, performLiquifierDeposit, selectedProtocol]);
}, [
executeParachainMintTx,
executeTanglePoolJoinTx,
fromAmount,
isTangleNetwork,
performLiquifierDeposit,
selectedProtocol,
selectedPoolId,
]);

const toAmount = useMemo(() => {
if (fromAmount === null || exchangeRate === null) {
Expand All @@ -130,13 +157,15 @@ const LsStakeCard: FC = () => {
const canCallStake =
(fromAmount !== null &&
selectedProtocol.networkId === LsNetworkId.TANGLE_RESTAKING_PARACHAIN &&
executeMintTx !== null) ||
executeParachainMintTx !== null) ||
(selectedProtocol.networkId === LsNetworkId.ETHEREUM_MAINNET_LIQUIFIER &&
performLiquifierDeposit !== null);
performLiquifierDeposit !== null) ||
(isTangleNetwork &&
executeTanglePoolJoinTx !== null &&
selectedPoolId !== null);

const walletBalance = (
<LsAgnosticBalance
protocolId={selectedProtocolId}
tooltip="Click to use all available balance"
onClick={() => {
if (maxSpendable !== null) {
Expand All @@ -146,6 +175,11 @@ const LsStakeCard: FC = () => {
/>
);

// Reset the input amount when the network changes.
useEffect(() => {
setFromAmount(null);
}, [setFromAmount, selectedNetworkId]);

return (
<>
<LsInput
Expand All @@ -161,7 +195,7 @@ const LsStakeCard: FC = () => {
setProtocolId={setSelectedProtocolId}
minAmount={minSpendable ?? undefined}
maxAmount={maxSpendable ?? undefined}
setNetworkId={setSelectedNetworkId}
setNetworkId={tryChangeNetwork}
/>

<ArrowDownIcon className="dark:fill-mono-0 self-center w-7 h-7" />
Expand All @@ -184,7 +218,6 @@ const LsStakeCard: FC = () => {
<UnstakePeriodDetailItem protocolId={selectedProtocolId} />

<ExchangeRateDetailItem
protocolId={selectedProtocolId}
token={selectedProtocol.token}
type={ExchangeRateType.NativeToDerivative}
/>
Expand Down Expand Up @@ -213,7 +246,10 @@ const LsStakeCard: FC = () => {
fromAmount === null ||
fromAmount.isZero()
}
isLoading={mintTxStatus === TxStatus.PROCESSING}
isLoading={
parachainMintTxStatus === TxStatus.PROCESSING ||
tanglePoolJoinTxStatus === TxStatus.PROCESSING
}
loadingText="Processing"
onClick={handleStakeClick}
isFullWidth
Expand Down
Loading
Loading