diff --git a/.vscode/settings.json b/.vscode/settings.json index 44a73ec3a..ea1afb54e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,5 +3,8 @@ { "mode": "auto" } - ] + ], + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } } diff --git a/apps/beets-frontend-v3/app/(app)/swap/[[...slug]]/layout.tsx b/apps/beets-frontend-v3/app/(app)/swap/[[...slug]]/layout.tsx index 7f4cf9ffc..573d6f529 100644 --- a/apps/beets-frontend-v3/app/(app)/swap/[[...slug]]/layout.tsx +++ b/apps/beets-frontend-v3/app/(app)/swap/[[...slug]]/layout.tsx @@ -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' @@ -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 ( @@ -33,7 +36,7 @@ export default function SwapLayout({ params: { slug }, children }: Props) { - {children} + {children} diff --git a/apps/frontend-v3/app/(app)/pools/[chain]/[variant]/[id]/stake/page.tsx b/apps/frontend-v3/app/(app)/pools/[chain]/[variant]/[id]/stake/page.tsx index 29cc70532..cbb41e245 100644 --- a/apps/frontend-v3/app/(app)/pools/[chain]/[variant]/[id]/stake/page.tsx +++ b/apps/frontend-v3/app/(app)/pools/[chain]/[variant]/[id]/stake/page.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react-hooks/exhaustive-deps */ 'use client' import { PoolActionsLayout } from '@repo/lib/modules/pool/actions/PoolActionsLayout' diff --git a/apps/frontend-v3/app/(app)/pools/[chain]/[variant]/[id]/swap/[[...txHash]]/page.tsx b/apps/frontend-v3/app/(app)/pools/[chain]/[variant]/[id]/swap/[[...txHash]]/page.tsx new file mode 100644 index 000000000..adedf1f6d --- /dev/null +++ b/apps/frontend-v3/app/(app)/pools/[chain]/[variant]/[id]/swap/[[...txHash]]/page.tsx @@ -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 ( + + {isLoading ? null : ( + + + + )} + + ) +} diff --git a/apps/frontend-v3/app/(app)/swap/[[...slug]]/layout.tsx b/apps/frontend-v3/app/(app)/swap/[[...slug]]/layout.tsx index 7f4cf9ffc..27c3157ad 100644 --- a/apps/frontend-v3/app/(app)/swap/[[...slug]]/layout.tsx +++ b/apps/frontend-v3/app/(app)/swap/[[...slug]]/layout.tsx @@ -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 ( - - - - - - {children} - - - - - + {children} ) } diff --git a/packages/lib/modules/pool/PoolDetail/PoolHeader/PoolAdvancedOptions.tsx b/packages/lib/modules/pool/PoolDetail/PoolHeader/PoolAdvancedOptions.tsx new file mode 100644 index 000000000..adb92fd39 --- /dev/null +++ b/packages/lib/modules/pool/PoolDetail/PoolHeader/PoolAdvancedOptions.tsx @@ -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 ( + setIsPopoverOpen(true)} + onClose={() => setIsPopoverOpen(false)} + placement="bottom-end" + > + + + + + + + + + + {isPopoverOpen && ( + + + + + Swap tokens directly via this pool + + + + )} + + + + + + ) +} diff --git a/packages/lib/modules/pool/PoolDetail/PoolHeader/PoolHeader.tsx b/packages/lib/modules/pool/PoolDetail/PoolHeader/PoolHeader.tsx index 37c7571a5..4bb40667b 100644 --- a/packages/lib/modules/pool/PoolDetail/PoolHeader/PoolHeader.tsx +++ b/packages/lib/modules/pool/PoolDetail/PoolHeader/PoolHeader.tsx @@ -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' @@ -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() @@ -55,14 +56,22 @@ export function PoolHeader() { - + + + + {/* + Will be enabled when pool swaps handler is implemented: + + */} + + + } + /> + + + + {pool.name} (swap route) + + + + ) +} diff --git a/packages/lib/modules/swap/SwapForm.tsx b/packages/lib/modules/swap/SwapForm.tsx index 4a0d526f7..e8b660a13 100644 --- a/packages/lib/modules/swap/SwapForm.tsx +++ b/packages/lib/modules/swap/SwapForm.tsx @@ -38,8 +38,17 @@ import { useUserAccount } from '../web3/UserAccountProvider' import { ConnectWallet } from '../web3/ConnectWallet' import { SafeAppAlert } from '@repo/lib/shared/components/alerts/SafeAppAlert' import { useTokens } from '../tokens/TokensProvider' +import { useIsPoolSwapUrl } from './useIsPoolSwapUrl' +import { CompactTokenSelectModal } from '../tokens/TokenSelectModal/TokenSelectList/CompactTokenSelectModal' +import { PoolSwapCard } from './PoolSwapCard' +import { isSameAddress } from '@repo/lib/shared/utils/addresses' + +type Props = { + redirectToPoolPage?: () => void // Only used for pool swaps +} +export function SwapForm({ redirectToPoolPage }: Props) { + const isPoolSwapUrl = useIsPoolSwapUrl() -export function SwapForm() { const { tokenIn, tokenOut, @@ -53,6 +62,7 @@ export function SwapForm() { swapAction, swapTxHash, transactionSteps, + isPoolSwap, setSelectedChain, setTokenInAmount, setTokenOutAmount, @@ -94,6 +104,22 @@ export function SwapForm() { } } + function handleTokenSelectForPoolSwap(token: GqlToken) { + if ( + tokens.length === 2 && + tokenSelectKey === 'tokenIn' && + isSameAddress(token.address, tokenOut.address) + ) + return switchTokens() + if ( + tokens.length === 2 && + tokenSelectKey === 'tokenOut' && + isSameAddress(token.address, tokenIn.address) + ) + return switchTokens() + handleTokenSelect(token) + } + function openTokenSelectModal(tokenSelectKey: 'tokenIn' | 'tokenOut') { setTokenSelectKey(tokenSelectKey) tokenSelectDisclosure.onOpen() @@ -107,8 +133,9 @@ export function SwapForm() { if (swapTxHash) { resetSwapAmounts() - replaceUrlPath() transactionSteps.resetTransactionSteps() + if (isPoolSwapUrl) return redirectToPoolPage?.() + replaceUrlPath() } } @@ -124,7 +151,7 @@ export function SwapForm() { > - {capitalize(swapAction)} + {isPoolSwap ? 'Single pool swap' : capitalize(swapAction)}