diff --git a/packages/lib/modules/pool/actions/create/constants.ts b/packages/lib/modules/pool/actions/create/constants.ts index 537aa937a..c2498b9b1 100644 --- a/packages/lib/modules/pool/actions/create/constants.ts +++ b/packages/lib/modules/pool/actions/create/constants.ts @@ -101,6 +101,7 @@ export const INITIAL_POOL_CREATION_FORM: PoolCreationForm = { disableUnbalancedLiquidity: false, hasAcceptedTokenWeightsRisk: false, hasAcceptedPoolCreationRisk: false, + hasAcceptedSimilarPoolsWarning: false, } export const INITIAL_RECLAMM_CONFIG: ReClammConfig = { diff --git a/packages/lib/modules/pool/actions/create/modal/SimilarPoolsModal.tsx b/packages/lib/modules/pool/actions/create/modal/SimilarPoolsModal.tsx new file mode 100644 index 000000000..aafde6035 --- /dev/null +++ b/packages/lib/modules/pool/actions/create/modal/SimilarPoolsModal.tsx @@ -0,0 +1,151 @@ +import { useCheckForSimilarPools } from './useCheckForSimilarPools' +import { BalAlert } from '@repo/lib/shared/components/alerts/BalAlert' +import { getChainShortName } from '@repo/lib/config/app.config' +import { usePoolCreationForm } from '../PoolCreationFormProvider' +import { PROJECT_CONFIG } from '@repo/lib/config/getProjectConfig' +import { + Modal, + ModalContent, + ModalBody, + VStack, + Button, + HStack, + useDisclosure, + Card, + Text, + Box, +} from '@chakra-ui/react' +import { SuccessOverlay } from '@repo/lib/shared/components/modals/SuccessOverlay' +import { useEffect } from 'react' +import { TokenIcon } from '@repo/lib/modules/tokens/TokenIcon' +import { fNum } from '@repo/lib/shared/utils/numbers' +import { NetworkIcon } from '@repo/lib/shared/components/icons/NetworkIcon' +import { Link } from '@chakra-ui/react' +import { ArrowUpRight } from 'react-feather' +import { getPoolPath } from '@repo/lib/modules/pool/pool.utils' +import NextLink from 'next/link' + +export function SimilarPoolsModal() { + const { isOpen, onOpen, onClose } = useDisclosure() + + const { poolCreationForm, resetPoolCreationForm } = usePoolCreationForm() + const { network, hasAcceptedSimilarPoolsWarning } = poolCreationForm.watch() + const { similarPools } = useCheckForSimilarPools() + + useEffect(() => { + if (!hasAcceptedSimilarPoolsWarning && similarPools && similarPools.length > 0) { + onOpen() + } + }, [hasAcceptedSimilarPoolsWarning, similarPools]) + + return ( + + + + + + + + {similarPools?.slice(0, 3).map(pool => ( + + + + + + + + + {pool.symbol} + + + + + {pool.poolTokens.map(token => ( + + + + + + {token.symbol} + + {token.weight && ( + {fNum('weight', token.weight)} + )} + + + + ))} + + + + Type: {pool.type.toLowerCase()} + + + + + + TVL: {fNum('fiat', pool.dynamicData.totalLiquidity)} + + + + + + Swap fees: {fNum('feePercent', pool.dynamicData.swapFee)} + + + + + ))} + + + + + + + + + + + ) +} diff --git a/packages/lib/modules/pool/actions/create/modal/useCheckForSimilarPools.ts b/packages/lib/modules/pool/actions/create/modal/useCheckForSimilarPools.ts new file mode 100644 index 000000000..ebe8fe923 --- /dev/null +++ b/packages/lib/modules/pool/actions/create/modal/useCheckForSimilarPools.ts @@ -0,0 +1,46 @@ +import { useQuery } from '@apollo/client' +import { + GetPoolsDocument, + GqlPoolOrderBy, + GqlPoolOrderDirection, +} from '@repo/lib/shared/services/api/generated/graphql' +import { usePoolCreationForm } from '../PoolCreationFormProvider' +import { getGqlPoolType } from '../helpers' + +export function useCheckForSimilarPools() { + const { poolCreationForm, isWeightedPool } = usePoolCreationForm() + const { network, poolType, poolTokens } = poolCreationForm.watch() + + const { data, loading, error } = useQuery(GetPoolsDocument, { + variables: { + orderBy: GqlPoolOrderBy.TotalLiquidity, + orderDirection: GqlPoolOrderDirection.Desc, + where: { + chainIn: [network], + poolTypeIn: [getGqlPoolType(poolType)], + tokensIn: poolTokens.map(({ address }) => address!), + protocolVersionIn: [3], + }, + }, + skip: !network || !poolType || !poolTokens?.every(token => token.address), + }) + + const similarPools = data?.pools.filter(pool => { + const sameNumberOfTokens = pool.poolTokens.length === poolTokens.length + const sameWeights = + !isWeightedPool || + pool.poolTokens.every( + token => + poolTokens.find(poolToken => poolToken.address === token.address)?.weight === + (Number(token.weight) * 100).toString() + ) + + return sameNumberOfTokens && sameWeights + }) + + return { + similarPools, + isLoadingSimilarPools: loading, + errorFetchingSimilarPools: error, + } +} diff --git a/packages/lib/modules/pool/actions/create/steps/details/PoolDetailsStep.tsx b/packages/lib/modules/pool/actions/create/steps/details/PoolDetailsStep.tsx index 93e96ac94..fee4d11d2 100644 --- a/packages/lib/modules/pool/actions/create/steps/details/PoolDetailsStep.tsx +++ b/packages/lib/modules/pool/actions/create/steps/details/PoolDetailsStep.tsx @@ -4,6 +4,7 @@ import { usePoolCreationForm } from '../../PoolCreationFormProvider' import { PoolDetails } from './PoolDetails' import { PoolSettings } from './PoolSettings' import { ReClammConfiguration } from './ReClammConfiguration' +import { SimilarPoolsModal } from '../../modal/SimilarPoolsModal' export function PoolDetailsStep() { const { poolCreationForm, isReClamm } = usePoolCreationForm() @@ -13,13 +14,16 @@ export function PoolDetailsStep() { const isDisabled = !isPoolCreationFormValid || !isReClammFormValid return ( - - - {isReClamm && } - - - - - + <> + + + {isReClamm && } + + + + + + + ) } diff --git a/packages/lib/modules/pool/actions/create/steps/tokens/ChoosePoolTokens.tsx b/packages/lib/modules/pool/actions/create/steps/tokens/ChoosePoolTokens.tsx index eb6a0a800..1a64afdcb 100644 --- a/packages/lib/modules/pool/actions/create/steps/tokens/ChoosePoolTokens.tsx +++ b/packages/lib/modules/pool/actions/create/steps/tokens/ChoosePoolTokens.tsx @@ -77,6 +77,7 @@ export function ChoosePoolTokens() { }) setSelectedTokenIndex(null) + poolCreationForm.setValue('hasAcceptedSimilarPoolsWarning', false) if (isReClamm) reClammConfigForm.resetToInitial() } diff --git a/packages/lib/modules/pool/actions/create/types.ts b/packages/lib/modules/pool/actions/create/types.ts index 00df3de79..7ef2bd648 100644 --- a/packages/lib/modules/pool/actions/create/types.ts +++ b/packages/lib/modules/pool/actions/create/types.ts @@ -49,6 +49,7 @@ export type PoolCreationForm = { disableUnbalancedLiquidity: boolean hasAcceptedTokenWeightsRisk: boolean hasAcceptedPoolCreationRisk: boolean + hasAcceptedSimilarPoolsWarning: boolean } export type ReClammConfig = { diff --git a/packages/lib/shared/components/icons/NetworkIcon.tsx b/packages/lib/shared/components/icons/NetworkIcon.tsx index 850c44ffb..b0541b075 100644 --- a/packages/lib/shared/components/icons/NetworkIcon.tsx +++ b/packages/lib/shared/components/icons/NetworkIcon.tsx @@ -9,11 +9,16 @@ type NetworkConfigProps = { shortName: string } -export function NetworkIcon({ chain, size = 12, ...rest }: { chain: GqlChain } & SquareProps) { +export function NetworkIcon({ + chain, + size = 12, + withPadding = true, + ...rest +}: { chain: GqlChain; withPadding?: boolean } & SquareProps) { const [networkConfig, setNetworkConfig] = useState(undefined) const { iconPath, shortName } = getNetworkConfig(chain) - const imageSize = Number(size) * 2 + 8 + const imageSize = Number(size) * (withPadding ? 2 : 3) + 8 useEffect(() => { if (shortName && iconPath) {