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: swap from pool #78

Merged
merged 13 commits into from
Oct 18, 2024
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
{
"mode": "auto"
}
]
],
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
7 changes: 5 additions & 2 deletions apps/beets-frontend-v3/app/(app)/swap/[[...slug]]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import { ChainSlug, slugToChainMap } from '@repo/lib/modules/pool/pool.utils'
import { SwapProvider } from '@repo/lib/modules/swap/SwapProvider'
import { SwapProviderProps, SwapProvider } from '@repo/lib/modules/swap/SwapProvider'
import { TokenBalancesProvider } from '@repo/lib/modules/tokens/TokenBalancesProvider'
import { TokenInputsValidationProvider } from '@repo/lib/modules/tokens/TokenInputsValidationProvider'
import { useTokens } from '@repo/lib/modules/tokens/TokensProvider'
Expand All @@ -25,6 +25,9 @@ export default function SwapLayout({ params: { slug }, children }: Props) {
? slugToChainMap[pathParams.chain as ChainSlug]
: GqlChain.Mainnet
const initTokens = getTokensByChain(initChain)
const props: SwapProviderProps = {
pathParams,
}

return (
<DefaultPageContainer minH="100vh">
Expand All @@ -33,7 +36,7 @@ export default function SwapLayout({ params: { slug }, children }: Props) {
<TokenInputsValidationProvider>
<TokenBalancesProvider initTokens={initTokens}>
<PriceImpactProvider>
<SwapProvider pathParams={{ ...pathParams }}>{children}</SwapProvider>
<SwapProvider params={props}>{children}</SwapProvider>
</PriceImpactProvider>
</TokenBalancesProvider>
</TokenInputsValidationProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'

import { PoolActionsLayout } from '@repo/lib/modules/pool/actions/PoolActionsLayout'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use client'

import { PoolActionsLayout } from '@repo/lib/modules/pool/actions/PoolActionsLayout'
import { getPoolTokens } from '@repo/lib/modules/pool/pool.helpers'
import { usePoolRedirect } from '@repo/lib/modules/pool/pool.hooks'
import { chainToSlugMap } from '@repo/lib/modules/pool/pool.utils'
import { usePool } from '@repo/lib/modules/pool/PoolProvider'
import { SwapForm } from '@repo/lib/modules/swap/SwapForm'
import SwapLayout from '@repo/lib/modules/swap/SwapLayout'
import { PathParams, SwapProviderProps } from '@repo/lib/modules/swap/SwapProvider'
import { useTokens } from '@repo/lib/modules/tokens/TokensProvider'
import { Hash } from 'viem'

type Props = {
params: { txHash?: string[] }
}
// Page for swapping from a pool page
export default function PoolSwapPage({ params: { txHash } }: Props) {
const { getToken } = useTokens()
const { pool, isLoading } = usePool()
const { redirectToPoolPage } = usePoolRedirect(pool)

const poolTokens = getPoolTokens(pool, getToken)

const maybeTxHash = (txHash?.[0] as Hash) || undefined

const pathParams: PathParams = {
chain: chainToSlugMap[pool.chain],
tokenIn: poolTokens[0].address,
tokenOut: poolTokens[1].address,
urlTxHash: maybeTxHash,
}
const props: SwapProviderProps = { pathParams, isPoolSwap: true, poolTokens }

return (
<PoolActionsLayout>
{isLoading ? null : (
<SwapLayout props={props}>
<SwapForm redirectToPoolPage={redirectToPoolPage} />
</SwapLayout>
)}
</PoolActionsLayout>
)
}
36 changes: 8 additions & 28 deletions apps/frontend-v3/app/(app)/swap/[[...slug]]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,24 @@
'use client'

import { ChainSlug, slugToChainMap } from '@repo/lib/modules/pool/pool.utils'
import { SwapProvider } from '@repo/lib/modules/swap/SwapProvider'
import { TokenBalancesProvider } from '@repo/lib/modules/tokens/TokenBalancesProvider'
import { TokenInputsValidationProvider } from '@repo/lib/modules/tokens/TokenInputsValidationProvider'
import { useTokens } from '@repo/lib/modules/tokens/TokensProvider'
import { TransactionStateProvider } from '@repo/lib/modules/transactions/transaction-steps/TransactionStateProvider'
import { GqlChain } from '@repo/lib/shared/services/api/generated/graphql'
import { PropsWithChildren } from 'react'
import { PriceImpactProvider } from '@repo/lib/modules/price-impact/PriceImpactProvider'
import { DefaultPageContainer } from '@repo/lib/shared/components/containers/DefaultPageContainer'
import { getSwapPathParams } from '@repo/lib/modules/swap/getSwapPathParams'
import { RelayerSignatureProvider } from '@repo/lib/modules/relayer/RelayerSignatureProvider'
import SwapLayout from '../../../../../../packages/lib/modules/swap/SwapLayout'
import { DefaultPageContainer } from '@repo/lib/shared/components/containers/DefaultPageContainer'
import { SwapProviderProps } from '@repo/lib/modules/swap/SwapProvider'

type Props = PropsWithChildren<{
params: { slug?: string[] }
}>

export default function SwapLayout({ params: { slug }, children }: Props) {
export default function Layout({ params: { slug }, children }: Props) {
const pathParams = getSwapPathParams(slug)

const { getTokensByChain } = useTokens()
const initChain = pathParams.chain
? slugToChainMap[pathParams.chain as ChainSlug]
: GqlChain.Mainnet
const initTokens = getTokensByChain(initChain)
const swapProps: SwapProviderProps = {
pathParams,
}

return (
<DefaultPageContainer minH="100vh">
<TransactionStateProvider>
<RelayerSignatureProvider>
<TokenInputsValidationProvider>
<TokenBalancesProvider initTokens={initTokens}>
<PriceImpactProvider>
<SwapProvider pathParams={{ ...pathParams }}>{children}</SwapProvider>
</PriceImpactProvider>
</TokenBalancesProvider>
</TokenInputsValidationProvider>
</RelayerSignatureProvider>
</TransactionStateProvider>
<SwapLayout props={swapProps}> {children} </SwapLayout>
</DefaultPageContainer>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
'use client'

import {
Box,
Button,
HStack,
Link,
Popover,
PopoverArrow,
PopoverBody,
PopoverCloseButton,
PopoverContent,
PopoverTrigger,
VStack,
} from '@chakra-ui/react'
import { SwapIcon } from '@repo/lib/shared/components/icons/SwapIcon'
import { staggeredFadeInUp } from '@repo/lib/shared/utils/animations'
import { AnimatePresence, motion } from 'framer-motion'
import NextLink from 'next/link'
import { usePathname } from 'next/navigation'
import { useState } from 'react'
import { MoreVertical } from 'react-feather'

export function PoolAdvancedOptions() {
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
const pathname = usePathname()

return (
<Popover
isOpen={isPopoverOpen}
onOpen={() => setIsPopoverOpen(true)}
onClose={() => setIsPopoverOpen(false)}
placement="bottom-end"
>
<PopoverTrigger>
<Button variant="tertiary" size="lg" color="grayText">
{<MoreVertical size={16} />}
</Button>
</PopoverTrigger >
<Box zIndex="popover" shadow="2xl" width="max">
<PopoverContent>
<PopoverArrow bg="background.level3" />
<PopoverCloseButton top="sm" />
<PopoverBody p="lg">
<AnimatePresence>
{isPopoverOpen && (
<VStack
align="start"
spacing="xxs"
as={motion.div}
initial="hidden"
animate="show"
exit="exit"
variants={staggeredFadeInUp}
>
<HStack>
<SwapIcon size={24} />
<Link as={NextLink} href={`${pathname}/swap`} prefetch={true} variant="nav">
Swap tokens directly via this pool
</Link>
</HStack>
</VStack>
)}
</AnimatePresence>
</PopoverBody>
</PopoverContent>
</Box>
</Popover>
)
}
27 changes: 18 additions & 9 deletions packages/lib/modules/pool/PoolDetail/PoolHeader/PoolHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Stack, Button, VStack, useDisclosure } from '@chakra-ui/react'
import { Stack, Button, VStack, useDisclosure, HStack } from '@chakra-ui/react'
import { usePathname, useRouter } from 'next/navigation'
import PoolMetaBadges from './PoolMetaBadges'

Expand All @@ -13,6 +13,7 @@ import {
} from '@repo/lib/shared/components/modals/PartnerRedirectModal'
import { useState } from 'react'
import { getXavePoolLink } from '../../pool.utils'
// import { PoolAdvancedOptions } from './PoolAdvancedOptions'

export function PoolHeader() {
const pathname = usePathname()
Expand Down Expand Up @@ -55,14 +56,22 @@ export function PoolHeader() {
<PoolMetaBadges />
<Stack spacing="md" direction={{ base: 'column', md: 'row' }}>
<PoolCategories />
<Button
onClick={handleClick}
variant="primary"
size="lg"
isDisabled={isAddLiquidityBlocked}
>
Add liquidity
</Button>
<HStack spacing="sm">
<Button
onClick={handleClick}
variant="primary"
size="lg"
isDisabled={isAddLiquidityBlocked}
w="full"
>
Add liquidity
</Button>

{/*
Will be enabled when pool swaps handler is implemented:
<PoolAdvancedOptions />
*/}
</HStack>
<PartnerRedirectModal
partner={redirectPartner}
redirectUrl={redirectPartnerUrl}
Expand Down
29 changes: 29 additions & 0 deletions packages/lib/modules/swap/PoolSwapCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { VStack, Card, HStack, Text } from '@chakra-ui/react'
import { BalAlert } from '@repo/lib/shared/components/alerts/BalAlert'
import { BalAlertContent } from '@repo/lib/shared/components/alerts/BalAlertContent'
import { NetworkIcon } from '@repo/lib/shared/components/icons/NetworkIcon'
import { usePool } from '../pool/PoolProvider'

export function PoolSwapCard() {
const { pool } = usePool()
return (
<VStack>
<BalAlert
status="warning"
content={
<BalAlertContent
title="Direct single pool swap (expert option)"
description="This swap routes through a single pool only. Better rates are usually available through the standard swap UI."
forceColumnMode
/>
}
/>
<Card h="full">
<HStack>
<NetworkIcon chain={pool.chain} size={6} />
<Text>{pool.name} (swap route)</Text>
</HStack>
</Card>
</VStack>
)
}
Loading
Loading