diff --git a/app/not-found.tsx b/app/not-found.tsx new file mode 100644 index 00000000..2180c156 --- /dev/null +++ b/app/not-found.tsx @@ -0,0 +1 @@ +export { NotFoundPage as default } from '@/pages/not-found'; diff --git a/app/page.tsx b/app/page.tsx deleted file mode 100644 index a444596c..00000000 --- a/app/page.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { redirect } from 'next/navigation'; -import { PagePath } from '@/shared/const/pages'; - -export default function RedirectPage() { - redirect(PagePath.Trade); -} diff --git a/app/trade/page.ts b/app/trade/page.ts deleted file mode 100644 index 52c11093..00000000 --- a/app/trade/page.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { RedirectToPair } from '@/pages/trade/redirect.tsx'; - -export default RedirectToPair; diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 00000000..5d3460f4 --- /dev/null +++ b/middleware.ts @@ -0,0 +1,6 @@ +// Redirects "/" and "/trade" paths to "/trade/:primary/:numeraire" +export const config = { + matcher: ['/', '/trade'], +}; + +export { tradeMiddleware as middleware } from '@/pages/trade/index.server'; diff --git a/src/pages/not-found/index.ts b/src/pages/not-found/index.ts new file mode 100644 index 00000000..6278dd6b --- /dev/null +++ b/src/pages/not-found/index.ts @@ -0,0 +1 @@ +export { NotFoundPage } from './page'; diff --git a/src/pages/not-found/link.tsx b/src/pages/not-found/link.tsx new file mode 100644 index 00000000..5ae95879 --- /dev/null +++ b/src/pages/not-found/link.tsx @@ -0,0 +1,15 @@ +'use client'; + +import Link from 'next/link'; +import { PagePath } from '@/shared/const/pages'; +import { Button } from '@penumbra-zone/ui/Button'; + +export const GoBackLink = () => { + return ( +
+ + + +
+ ); +}; diff --git a/src/pages/not-found/page.tsx b/src/pages/not-found/page.tsx new file mode 100644 index 00000000..a1491161 --- /dev/null +++ b/src/pages/not-found/page.tsx @@ -0,0 +1,21 @@ +import { XCircle } from 'lucide-react'; +import { Text } from '@penumbra-zone/ui/Text'; +import { PenumbraWaves } from '@/pages/explore/ui/waves'; +import { GoBackLink } from './link'; + +export const NotFoundPage = () => { + return ( +
+ + +
+
+ + Page not found +
+ + +
+
+ ); +}; diff --git a/src/pages/trade/api/middleware.ts b/src/pages/trade/api/middleware.ts new file mode 100644 index 00000000..fb691243 --- /dev/null +++ b/src/pages/trade/api/middleware.ts @@ -0,0 +1,23 @@ +import { NextResponse, NextRequest } from 'next/server'; +import { ChainRegistryClient } from '@penumbra-labs/registry'; +import { getClientSideEnv } from '@/shared/api/env/getClientSideEnv'; +import { assetPatterns } from '@penumbra-zone/types/assets'; + +export const tradeMiddleware = async (request: NextRequest) => { + const { PENUMBRA_CHAIN_ID } = getClientSideEnv(); + + const chainRegistryClient = new ChainRegistryClient(); + const registry = await chainRegistryClient.remote.get(PENUMBRA_CHAIN_ID); + const allAssets = registry + .getAllAssets() + .filter(m => !assetPatterns.delegationToken.matches(m.display)) + .toSorted((a, b) => Number(b.priorityScore - a.priorityScore)); + + const baseAsset = allAssets[0]?.symbol; + const quoteAsset = allAssets[1]?.symbol; + if (!baseAsset || !quoteAsset) { + return NextResponse.redirect(new URL('not-found', request.url)); + } + + return NextResponse.redirect(new URL(`/trade/${baseAsset}/${quoteAsset}`, request.url)); +}; diff --git a/src/pages/trade/index.server.ts b/src/pages/trade/index.server.ts new file mode 100644 index 00000000..44636ea9 --- /dev/null +++ b/src/pages/trade/index.server.ts @@ -0,0 +1 @@ +export { tradeMiddleware } from './api/middleware'; diff --git a/src/pages/trade/redirect.tsx b/src/pages/trade/redirect.tsx deleted file mode 100644 index e036a58b..00000000 --- a/src/pages/trade/redirect.tsx +++ /dev/null @@ -1,41 +0,0 @@ -'use client'; - -import { redirect } from 'next/navigation'; -import { ChainRegistryClient } from '@penumbra-labs/registry'; -import { envQueryFn } from '@/shared/api/env/env.ts'; -import { useQuery } from '@tanstack/react-query'; -import { assetPatterns } from '@penumbra-zone/types/assets'; - -const redirectSymbolsQueryFn = async () => { - const { PENUMBRA_CHAIN_ID } = await envQueryFn(); - const chainRegistryClient = new ChainRegistryClient(); - const registry = await chainRegistryClient.remote.get(PENUMBRA_CHAIN_ID); - const allAssets = registry - .getAllAssets() - .filter(m => !assetPatterns.delegationToken.matches(m.display)) - .toSorted((a, b) => Number(b.priorityScore - a.priorityScore)); - - const baseAsset = allAssets[0]?.symbol; - const quoteAsset = allAssets[1]?.symbol; - if (!baseAsset || !quoteAsset) { - throw new Error('Could not find symbols in registry'); - } - - return { baseAsset, quoteAsset }; -}; - -export const RedirectToPair = () => { - const { data, isLoading, error } = useQuery({ - queryKey: ['redirectSymbols'], - retry: 1, - queryFn: redirectSymbolsQueryFn, - }); - - if (error) { - return
{String(error)}
; - } else if (isLoading || !data) { - return
Loading...
; - } else { - redirect(`/trade/${data.baseAsset}/${data.quoteAsset}`); - } -};