diff --git a/apps/tangle-dapp/app/liquid-staking/page.tsx b/apps/tangle-dapp/app/liquid-staking/page.tsx
index d48aea5a2..391308b04 100644
--- a/apps/tangle-dapp/app/liquid-staking/page.tsx
+++ b/apps/tangle-dapp/app/liquid-staking/page.tsx
@@ -1,6 +1,7 @@
'use client';
import {
+ AddLineIcon,
CoinIcon,
EditLine,
Search,
@@ -8,6 +9,7 @@ import {
WaterDropletIcon,
} from '@webb-tools/icons';
import {
+ Button,
TabContent,
TabsList as WebbTabsList,
TabsRoot,
@@ -15,7 +17,7 @@ import {
TANGLE_DOCS_LIQUID_STAKING_URL,
Typography,
} from '@webb-tools/webb-ui-components';
-import { FC, useEffect } from 'react';
+import { FC, useEffect, useState } from 'react';
import LsStakeCard from '../../components/LiquidStaking/stakeAndUnstake/LsStakeCard';
import LsUnstakeCard from '../../components/LiquidStaking/stakeAndUnstake/LsUnstakeCard';
@@ -24,6 +26,7 @@ import OnboardingModal from '../../components/OnboardingModal/OnboardingModal';
import StatItem from '../../components/StatItem';
import { OnboardingPageKey } from '../../constants';
import { LsSearchParamKey } from '../../constants/liquidStaking/types';
+import LsCreatePoolModal from '../../containers/LsCreatePoolModal';
import LsMyProtocolsTable from '../../containers/LsMyProtocolsTable';
import { LsProtocolsTable } from '../../containers/LsPoolsTable';
import useNetworkStore from '../../context/useNetworkStore';
@@ -61,6 +64,7 @@ const LiquidStakingPage: FC = () => {
const { network } = useNetworkStore();
const { switchNetwork } = useNetworkSwitcher();
+ const [isCreatePoolModalOpen, setIsCreatePoolModalOpen] = useState(false);
const lsTangleNetwork = getLsTangleNetwork(lsNetworkId);
@@ -87,6 +91,11 @@ const LiquidStakingPage: FC = () => {
return (
+
+
{
-
+
@@ -192,6 +197,20 @@ const LiquidStakingPage: FC = () => {
);
})}
+
+ {/**
+ * TODO: Check what's the min. amount required to create a new pool. If the free balance doesn't meet the min, disable the button and show a tooltip with the reason.
+ */}
+
{/* Tabs Content */}
diff --git a/apps/tangle-dapp/components/AddressInput/AddressInput.tsx b/apps/tangle-dapp/components/AddressInput/AddressInput.tsx
index d1e403072..38ab6c1b6 100644
--- a/apps/tangle-dapp/components/AddressInput/AddressInput.tsx
+++ b/apps/tangle-dapp/components/AddressInput/AddressInput.tsx
@@ -17,6 +17,7 @@ export type AddressInputProps = {
id: string;
title: string;
placeholder?: string;
+ tooltip?: string;
type: AddressType;
showPasteButton?: boolean;
value: string;
@@ -29,6 +30,7 @@ export type AddressInputProps = {
const AddressInput: FC = ({
id,
title,
+ tooltip,
placeholder,
type,
value,
@@ -104,6 +106,7 @@ const AddressInput: FC = ({
= ({
setAmount,
min = null,
max = null,
- decimals = TANGLE_TOKEN_DECIMALS, // Default to the Tangle token decimals.
+ // Default to the Tangle token decimals.
+ decimals = TANGLE_TOKEN_DECIMALS,
minErrorMessage,
maxErrorMessage,
showMaxAction = true,
diff --git a/apps/tangle-dapp/components/LiquidStaking/ExternalLink.tsx b/apps/tangle-dapp/components/LiquidStaking/ExternalLink.tsx
index 6faf77969..c7b229aed 100644
--- a/apps/tangle-dapp/components/LiquidStaking/ExternalLink.tsx
+++ b/apps/tangle-dapp/components/LiquidStaking/ExternalLink.tsx
@@ -21,9 +21,7 @@ const ExternalLink: FC = ({
target="_blank"
size="sm"
variant="link"
- rightIcon={
-
- }
+ rightIcon={}
>
{children}
diff --git a/apps/tangle-dapp/components/LiquidStaking/LsMyPoolsTable.tsx b/apps/tangle-dapp/components/LiquidStaking/LsMyPoolsTable.tsx
index f4c91e85e..4dfab053a 100644
--- a/apps/tangle-dapp/components/LiquidStaking/LsMyPoolsTable.tsx
+++ b/apps/tangle-dapp/components/LiquidStaking/LsMyPoolsTable.tsx
@@ -243,6 +243,7 @@ const LsMyPoolsTable: FC = ({ pools, isShown }) => {
return (
= ({
title,
subtitle,
tooltip,
- largeSubtitle = false,
removeBorder = false,
}) => {
const className = cx('flex flex-col items-start justify-center px-3', {
@@ -26,13 +24,13 @@ const StatItem: FC = ({
return (
-
+
{title}
diff --git a/apps/tangle-dapp/constants/index.ts b/apps/tangle-dapp/constants/index.ts
index 2b6cb6ad0..fdc2fc7c8 100644
--- a/apps/tangle-dapp/constants/index.ts
+++ b/apps/tangle-dapp/constants/index.ts
@@ -63,6 +63,7 @@ export enum TxName {
LS_LIQUIFIER_WITHDRAW = 'liquifier withdraw',
LS_TANGLE_POOL_JOIN = 'join liquid staking pool',
LS_TANGLE_POOL_UNBOND = 'unbond from liquid staking pool',
+ LS_TANGLE_POOL_CREATE = 'create liquid staking pool',
}
export const PAYMENT_DESTINATION_OPTIONS: StakingRewardsDestinationDisplayText[] =
diff --git a/apps/tangle-dapp/containers/LsCreatePoolModal.tsx b/apps/tangle-dapp/containers/LsCreatePoolModal.tsx
new file mode 100644
index 000000000..d175787fd
--- /dev/null
+++ b/apps/tangle-dapp/containers/LsCreatePoolModal.tsx
@@ -0,0 +1,203 @@
+import { BN } from '@polkadot/util';
+import { TANGLE_TOKEN_DECIMALS } from '@webb-tools/dapp-config';
+import {
+ Alert,
+ Button,
+ Input,
+ Modal,
+ ModalContent,
+ ModalFooter,
+ ModalHeader,
+ TANGLE_DOCS_LS_CREATE_POOL_URL,
+} from '@webb-tools/webb-ui-components';
+import assert from 'assert';
+import { FC, useCallback, useState } from 'react';
+
+import AddressInput, {
+ AddressType,
+} from '../components/AddressInput/AddressInput';
+import AmountInput from '../components/AmountInput/AmountInput';
+import { LsNetworkId, LsProtocolId } from '../constants/liquidStaking/types';
+import useBalances from '../data/balances/useBalances';
+import useLsCreatePoolTx from '../data/liquidStaking/tangle/useLsCreatePoolTx';
+import { useLsStore } from '../data/liquidStaking/useLsStore';
+import useInputAmount from '../hooks/useInputAmount';
+import useSubstrateAddress from '../hooks/useSubstrateAddress';
+import { TxStatus } from '../hooks/useSubstrateTx';
+import { SubstrateAddress } from '../types/utils';
+import getLsNetwork from '../utils/liquidStaking/getLsNetwork';
+import getLsProtocolDef from '../utils/liquidStaking/getLsProtocolDef';
+
+export type LsCreatePoolModalProps = {
+ isOpen: boolean;
+ setIsOpen: (isOpen: boolean) => void;
+};
+
+const LsCreatePoolModal: FC = ({
+ isOpen,
+ setIsOpen,
+}) => {
+ const activeSubstrateAddress = useSubstrateAddress();
+ // TODO: Use form validation for the properties/inputs.
+ const [name, setName] = useState('');
+ const [rootAddress, setRootAddress] = useState(activeSubstrateAddress);
+ const { free: freeBalance } = useBalances();
+
+ const [nominatorAddress, setNominatorAddress] =
+ useState(activeSubstrateAddress);
+
+ const [bouncerAddress, setBouncerAddress] = useState(
+ activeSubstrateAddress,
+ );
+
+ const [initialBondAmount, setInitialBondAmount] = useState(null);
+ const [lsProtocolId, setLsProtocolId] = useState(null);
+ const { lsNetworkId } = useLsStore();
+
+ const lsProtocol =
+ lsProtocolId === null ? null : getLsProtocolDef(lsProtocolId);
+
+ const lsNetwork = getLsNetwork(lsNetworkId);
+
+ const { displayAmount, errorMessage } = useInputAmount({
+ amount: initialBondAmount,
+ setAmount: setInitialBondAmount,
+ // Default to TNT's decimals if the protocol hasn't been selected
+ // yet.
+ decimals: lsProtocol?.decimals ?? TANGLE_TOKEN_DECIMALS,
+ });
+
+ // TODO: Also add Restaking Parachain when its non-testnet version is available.
+ const isLiveNetwork = lsNetworkId === LsNetworkId.TANGLE_MAINNET;
+
+ const { execute, status } = useLsCreatePoolTx();
+
+ const handleCreatePoolClick = useCallback(async () => {
+ // TODO: Add form validation, then remove this check.
+ if (
+ initialBondAmount === null ||
+ rootAddress === null ||
+ nominatorAddress === null ||
+ bouncerAddress === null
+ ) {
+ return;
+ }
+
+ assert(
+ execute !== null,
+ 'Button should have been disabled if execute is null.',
+ );
+
+ await execute({
+ name,
+ initialBondAmount,
+ rootAddress,
+ nominatorAddress,
+ bouncerAddress,
+ });
+ }, [
+ bouncerAddress,
+ execute,
+ initialBondAmount,
+ name,
+ nominatorAddress,
+ rootAddress,
+ ]);
+
+ return (
+
+
+ setIsOpen(false)}>
+ Create a Liquid Staking Pool
+
+
+
+
+ {/**
+ * In case that a testnet is selected, it's helpful to let the users
+ * know that the pool will be created on the testnet, and that
+ * it won't be accessible on other networks.
+ */}
+ {!isLiveNetwork && (
+
+ )}
+
+
+
+ {/** TODO: Protocol selection dropdown. */}
+
+
+
+
+
+
+
+
+