diff --git a/.env.example b/.env.example index 0a3e03d..4a4032e 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ -ONEINCH_API_KEY="" \ No newline at end of file +ONEINCH_API_KEY="" +WEB3_API_URL="" \ No newline at end of file diff --git a/app/api/prices/route.ts b/app/api/prices/route.ts new file mode 100644 index 0000000..798f0e3 --- /dev/null +++ b/app/api/prices/route.ts @@ -0,0 +1,46 @@ +import { type NextRequest } from 'next/server' + +export const runtime = 'edge' + +const apiUrl = process.env.WEB3_API_URL + +const config: RequestInit = { + headers: { + 'content-type': 'application/json' + }, + method: 'GET', + next: { revalidate: 60 } +} + +export async function GET(request: NextRequest) { + const searchParams = request?.nextUrl?.searchParams + const tokens = searchParams?.get('tokens') + + if (!tokens) { + return Response.json(null, { status: 400 }) + } + + const url = `${apiUrl}/prices/?tokens=${tokens}` + let data + let status + + try { + const res = await fetch(url, config) + const json = await res.json() + + data = json + status = res.status + } catch (error: unknown) { + console.error((error as Error).message) + data = null + status = 500 + } + + return new Response(JSON.stringify(data), { + status, + headers: { + 'Content-Type': 'application/json', + 'Cache-Control': 'public, s-maxage=10' + } + }) +} diff --git a/app/globals.css b/app/globals.css index 698772a..4eba5d2 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,5 +1,5 @@ :root { - --max-width: 800px; + --max-width: 740px; --border-radius: 0.3rem; --foreground-rgb: 20, 20, 20; @@ -27,6 +27,8 @@ html, body { max-width: 100vw; overflow-x: hidden; + font-family: var(--font-firaCode); + text-rendering: optimizeLegibility; } body { @@ -39,6 +41,9 @@ body { rgb(var(--background-start-rgb)); line-height: 1.5; min-height: 100vh; + display: flex; + flex-direction: column; + padding: 2rem; } a { @@ -46,6 +51,11 @@ a { text-decoration: none; } +ul { + list-style: none; + padding: 0; +} + @media (prefers-color-scheme: dark) { html { color-scheme: dark; diff --git a/app/layout.tsx b/app/layout.tsx index 4a00b66..9993651 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -2,11 +2,11 @@ import type { Metadata } from 'next' import { Fira_Code } from 'next/font/google' import './globals.css' -const firaCode = Fira_Code({ subsets: ['latin'] }) +const firaCode = Fira_Code({ subsets: ['latin'], variable: '--font-firaCode' }) export const metadata: Metadata = { - title: 'OCEAN + AGIX + FET = ASI', - description: 'Calculate how much ASI you get for your OCEAN, AGIX, or FET.' + title: 'ASI Calculator', + description: 'See how much ASI you get for your OCEAN, AGIX, or FET.' } export default function RootLayout({ diff --git a/app/page.module.css b/app/page.module.css index 3ab6606..6734b36 100644 --- a/app/page.module.css +++ b/app/page.module.css @@ -1,6 +1,5 @@ -.main, -.footer { - padding: 2rem; +.main { + flex: 1 0 auto; } .title, @@ -11,16 +10,16 @@ } .title { - margin-bottom: 0.5rem; - font-size: 2rem; + font-size: clamp(1.3rem, 10vw, 2.5rem); } .description { margin-bottom: 2rem; - font-size: 1.3rem; + font-size: clamp(1.1rem, 10vw, 1.75rem); } .footer { margin-top: 2rem; + flex-shrink: 0; font-size: 0.8rem; } diff --git a/app/page.tsx b/app/page.tsx index c7f3d18..abbca06 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,4 +1,4 @@ -import { Prices } from '@/features/Prices' +import { Prices } from '@/components/Prices' import styles from './page.module.css' import { metadata } from './layout' diff --git a/components/Content/Content.module.css b/components/Content/Content.module.css new file mode 100644 index 0000000..93e5ffc --- /dev/null +++ b/components/Content/Content.module.css @@ -0,0 +1,44 @@ +.content { + max-width: var(--max-width); + margin: auto; +} + +.content p { + margin-bottom: 1rem; +} + +.content a { + text-decoration: underline; +} + +.label { + font-size: 0.7rem; + color: rgba(var(--foreground-rgb), 0.6); + border: 1px solid rgba(var(--foreground-rgb), 0.2); + border-radius: var(--border-radius); + padding: 0.1rem 0.3rem; + margin-left: 0.2rem; + margin-right: 0.2rem; + vertical-align: middle; +} + +.calculationBase { + border: 1px solid rgba(var(--foreground-rgb), 0.2); + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + border-radius: var(--border-radius); +} + +.calculationBase li { + border-bottom: 1px solid rgba(var(--foreground-rgb), 0.2); + padding: 1rem; + font-size: 0.8rem; +} + +.calculationBase li:first-child { + border-right: 1px solid rgba(var(--foreground-rgb), 0.2); +} + +.calculationBase li:last-child { + border-bottom: none; +} diff --git a/components/Content/Content.tsx b/components/Content/Content.tsx new file mode 100644 index 0000000..50d17f3 --- /dev/null +++ b/components/Content/Content.tsx @@ -0,0 +1,41 @@ +import { ratioOceanToAsi, ratioAgixToAsi, ratioFetToAsi } from '@/constants' +import styles from './Content.module.css' + +type Props = { + prices: { + [key: string]: number + } +} + +export function Content({ prices }: Props) { + return ( +
+

+ Calculations are based on the{' '} + + fixed ASI exchange rate + + , the fluctuating fiat values fetched from Coingecko, and + token swap quotes from 1inch. +

+ + +
+ ) +} diff --git a/components/Content/index.tsx b/components/Content/index.tsx new file mode 100644 index 0000000..aa90efe --- /dev/null +++ b/components/Content/index.tsx @@ -0,0 +1 @@ +export * from './Content' diff --git a/features/Prices/components/FormAmount.module.css b/components/FormAmount.module.css similarity index 100% rename from features/Prices/components/FormAmount.module.css rename to components/FormAmount.module.css diff --git a/features/Prices/components/FormAmount.tsx b/components/FormAmount.tsx similarity index 100% rename from features/Prices/components/FormAmount.tsx rename to components/FormAmount.tsx diff --git a/features/Prices/components/Prices.module.css b/components/Prices.module.css similarity index 70% rename from features/Prices/components/Prices.module.css rename to components/Prices.module.css index eec4cac..617733d 100644 --- a/features/Prices/components/Prices.module.css +++ b/components/Prices.module.css @@ -1,14 +1,9 @@ -.header { - max-width: var(--max-width); - margin: auto; -} - .grid { display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); - max-width: calc(var(--max-width) * 2); - margin: 2rem auto; + max-width: calc(var(--max-width) * 1.5); + margin: 3rem auto; } .results { diff --git a/features/Prices/components/Prices.tsx b/components/Prices.tsx similarity index 85% rename from features/Prices/components/Prices.tsx rename to components/Prices.tsx index 8f8569e..938d53b 100644 --- a/features/Prices/components/Prices.tsx +++ b/components/Prices.tsx @@ -13,16 +13,15 @@ import { } from '@/constants' import { useState } from 'react' import { FormAmount } from './FormAmount' -import { fetcher } from '../utils' +import { fetcher, formatNumber } from '@/utils' +import { Content } from '@/components/Content' export function Prices() { const [amountSwap, setAmountSwap] = useState(100) const [debouncedAmountSwap] = useDebounce(amountSwap, 500) - console.log(debouncedAmountSwap) - const { data: dataPrices } = useSWR( - `https://web3.kremalicious.com/api/prices/?tokens=${tokens.toString()}`, + `/api/prices/?tokens=${tokens.toString()}`, fetcher ) const { data: dataSwapOceanToAgix } = useSWR( @@ -46,19 +45,9 @@ export function Prices() { return ( <> -
-

- 1 OCEAN = {ratioOceanToAsi} ASI (fixed) = ${priceOcean} -

-

- 1 AGIX = {ratioAgixToAsi} ASI (fixed) = ${priceAgix} -

-

1 Fet = 1 ASI (fixed) = ${priceAsi}

-
-
-

${exampleBuyInUsd} right now gets you:

+

Buying with ${exampleBuyInUsd} right now gets you:

- OCEAN - (${(debouncedAmountSwap * priceOcean).toFixed(2)}) right now gets - you: + Swapping{' '} + OCEAN ( + {formatNumber(debouncedAmountSwap * priceOcean, 'USD')}) gets you:

+ ) } diff --git a/features/Prices/components/Result.module.css b/components/Result.module.css similarity index 100% rename from features/Prices/components/Result.module.css rename to components/Result.module.css diff --git a/features/Prices/components/Result.tsx b/components/Result.tsx similarity index 100% rename from features/Prices/components/Result.tsx rename to components/Result.tsx diff --git a/features/Prices/index.tsx b/features/Prices/index.tsx deleted file mode 100644 index 3d5ff57..0000000 --- a/features/Prices/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export * from './components/Prices' diff --git a/features/Prices/utils.ts b/utils/index.ts similarity index 100% rename from features/Prices/utils.ts rename to utils/index.ts