Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions packages/lib/modules/pool/actions/create/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
151 changes: 151 additions & 0 deletions packages/lib/modules/pool/actions/create/modal/SimilarPoolsModal.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Modal isCentered isOpen={isOpen} onClose={onClose} size="xl">
<SuccessOverlay />
<ModalContent bg="background.level1">
<ModalBody padding="lg">
<VStack spacing="md">
<BalAlert
content="You can still create this pool, but you'll fragment liquidity making your pool less profitable (on top of additional set up gas fees)."
status="warning"
title={`Similar pools already exist on ${getChainShortName(network)} (${PROJECT_CONFIG.projectName})`}
/>

{similarPools?.slice(0, 3).map(pool => (
<Card key={pool.address} position="relative" variant="modalSubSection">
<VStack spacing="md">
<Link
as={NextLink}
href={getPoolPath({
chain: pool.chain,
id: pool.address,
type: pool.type,
protocolVersion: pool.protocolVersion,
})}
position="absolute"
rel="noopener noreferrer"
right="sm"
target="_blank"
top="sm"
zIndex={1}
>
<Box bg="background.level4" color="font.link" p="sm" rounded="full" shadow="md">
<ArrowUpRight size={16} />
</Box>
</Link>
<HStack w="full">
<Text>{pool.symbol}</Text>
</HStack>
<HStack flexWrap="wrap" gap="sm" width="full">
<NetworkIcon chain={network} size={9} withPadding={false} />

{pool.poolTokens.map(token => (
<Box flexShrink={0} key={token.address}>
<Card p="sm" rounded="full" variant="subSection" width="fit-content">
<HStack>
<TokenIcon
address={token.address}
alt={token.symbol}
chain={network}
size={20}
/>

<Text fontWeight="bold">{token.symbol}</Text>

{token.weight && (
<Text fontSize="sm">{fNum('weight', token.weight)}</Text>
)}
</HStack>
</Card>
</Box>
))}
</HStack>
<HStack spacing="sm" w="full">
<Text color="font.secondary" fontSize="sm">
Type: {pool.type.toLowerCase()}
</Text>

<Text color="font.secondary">•</Text>

<Text color="font.secondary" fontSize="sm">
TVL: {fNum('fiat', pool.dynamicData.totalLiquidity)}
</Text>

<Text color="font.secondary">•</Text>

<Text color="font.secondary" fontSize="sm">
Swap fees: {fNum('feePercent', pool.dynamicData.swapFee)}
</Text>
</HStack>
</VStack>
</Card>
))}

<HStack display="grid" gap="md" gridTemplateColumns="1fr 1fr" mt="sm" w="full">
<Button
onClick={() => {
poolCreationForm.setValue('hasAcceptedSimilarPoolsWarning', true)
onClose()
}}
size="lg"
variant="secondary"
>
Continue anyway
</Button>

<Button
onClick={() => {
resetPoolCreationForm()
onClose()
}}
size="lg"
variant="tertiary"
>
Reset and start over
</Button>
</HStack>
</VStack>
</ModalBody>
</ModalContent>
</Modal>
)
}
Original file line number Diff line number Diff line change
@@ -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,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -13,13 +14,16 @@ export function PoolDetailsStep() {
const isDisabled = !isPoolCreationFormValid || !isReClammFormValid

return (
<Box as="form" style={{ width: '100%' }}>
<VStack align="start" spacing="xl" w="full">
{isReClamm && <ReClammConfiguration />}
<PoolDetails />
<PoolSettings />
<PoolCreationFormAction disabled={isDisabled} />
</VStack>
</Box>
<>
<Box as="form" style={{ width: '100%' }}>
<VStack align="start" spacing="xl" w="full">
{isReClamm && <ReClammConfiguration />}
<PoolDetails />
<PoolSettings />
<PoolCreationFormAction disabled={isDisabled} />
</VStack>
</Box>
<SimilarPoolsModal />
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export function ChoosePoolTokens() {
})

setSelectedTokenIndex(null)
poolCreationForm.setValue('hasAcceptedSimilarPoolsWarning', false)
if (isReClamm) reClammConfigForm.resetToInitial()
}

Expand Down
1 change: 1 addition & 0 deletions packages/lib/modules/pool/actions/create/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export type PoolCreationForm = {
disableUnbalancedLiquidity: boolean
hasAcceptedTokenWeightsRisk: boolean
hasAcceptedPoolCreationRisk: boolean
hasAcceptedSimilarPoolsWarning: boolean
}

export type ReClammConfig = {
Expand Down
9 changes: 7 additions & 2 deletions packages/lib/shared/components/icons/NetworkIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<NetworkConfigProps | undefined>(undefined)
const { iconPath, shortName } = getNetworkConfig(chain)

const imageSize = Number(size) * 2 + 8
const imageSize = Number(size) * (withPadding ? 2 : 3) + 8

useEffect(() => {
if (shortName && iconPath) {
Expand Down
Loading