mirror of
https://github.com/kremalicious/asi-calculator.git
synced 2024-12-22 17:33:18 +01:00
refactor
This commit is contained in:
parent
ac386a6ef4
commit
d3f3f8392d
@ -1 +1,2 @@
|
|||||||
ONEINCH_API_KEY=""
|
ONEINCH_API_KEY=""
|
||||||
|
WEB3_API_URL=""
|
46
app/api/prices/route.ts
Normal file
46
app/api/prices/route.ts
Normal file
@ -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'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
:root {
|
:root {
|
||||||
--max-width: 800px;
|
--max-width: 740px;
|
||||||
--border-radius: 0.3rem;
|
--border-radius: 0.3rem;
|
||||||
|
|
||||||
--foreground-rgb: 20, 20, 20;
|
--foreground-rgb: 20, 20, 20;
|
||||||
@ -27,6 +27,8 @@ html,
|
|||||||
body {
|
body {
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
font-family: var(--font-firaCode);
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@ -39,6 +41,9 @@ body {
|
|||||||
rgb(var(--background-start-rgb));
|
rgb(var(--background-start-rgb));
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@ -46,6 +51,11 @@ a {
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html {
|
html {
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
|
@ -2,11 +2,11 @@ import type { Metadata } from 'next'
|
|||||||
import { Fira_Code } from 'next/font/google'
|
import { Fira_Code } from 'next/font/google'
|
||||||
import './globals.css'
|
import './globals.css'
|
||||||
|
|
||||||
const firaCode = Fira_Code({ subsets: ['latin'] })
|
const firaCode = Fira_Code({ subsets: ['latin'], variable: '--font-firaCode' })
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'OCEAN + AGIX + FET = ASI',
|
title: 'ASI Calculator',
|
||||||
description: 'Calculate how much ASI you get for your OCEAN, AGIX, or FET.'
|
description: 'See how much ASI you get for your OCEAN, AGIX, or FET.'
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
.main,
|
.main {
|
||||||
.footer {
|
flex: 1 0 auto;
|
||||||
padding: 2rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.title,
|
.title,
|
||||||
@ -11,16 +10,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
margin-bottom: 0.5rem;
|
font-size: clamp(1.3rem, 10vw, 2.5rem);
|
||||||
font-size: 2rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
font-size: 1.3rem;
|
font-size: clamp(1.1rem, 10vw, 1.75rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
|
flex-shrink: 0;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Prices } from '@/features/Prices'
|
import { Prices } from '@/components/Prices'
|
||||||
import styles from './page.module.css'
|
import styles from './page.module.css'
|
||||||
import { metadata } from './layout'
|
import { metadata } from './layout'
|
||||||
|
|
||||||
|
44
components/Content/Content.module.css
Normal file
44
components/Content/Content.module.css
Normal file
@ -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;
|
||||||
|
}
|
41
components/Content/Content.tsx
Normal file
41
components/Content/Content.tsx
Normal file
@ -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 (
|
||||||
|
<div className={styles.content}>
|
||||||
|
<p>
|
||||||
|
Calculations are based on the{' '}
|
||||||
|
<a href="https://blog.oceanprotocol.com/ocean-protocol-is-joining-the-superintelligence-alliance-767c82693f24#3c8e">
|
||||||
|
fixed ASI exchange rate
|
||||||
|
</a>
|
||||||
|
, the fluctuating fiat values fetched from <a href="">Coingecko</a>, and
|
||||||
|
token swap quotes from <a href="https://1inch.io">1inch</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul className={styles.calculationBase}>
|
||||||
|
<li>
|
||||||
|
1 OCEAN = {ratioOceanToAsi} ASI
|
||||||
|
<span className={styles.label}>fixed</span>
|
||||||
|
<br />= ${prices.ocean}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
1 AGIX = {ratioAgixToAsi} ASI
|
||||||
|
<span className={styles.label}>fixed</span>
|
||||||
|
<br />= ${prices.agix}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
1 Fet = {ratioFetToAsi} ASI
|
||||||
|
<span className={styles.label}>fixed</span>
|
||||||
|
<br />= ${prices.asi}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
1
components/Content/index.tsx
Normal file
1
components/Content/index.tsx
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './Content'
|
@ -1,14 +1,9 @@
|
|||||||
.header {
|
|
||||||
max-width: var(--max-width);
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid {
|
.grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 2rem;
|
gap: 2rem;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||||||
max-width: calc(var(--max-width) * 2);
|
max-width: calc(var(--max-width) * 1.5);
|
||||||
margin: 2rem auto;
|
margin: 3rem auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.results {
|
.results {
|
@ -13,16 +13,15 @@ import {
|
|||||||
} from '@/constants'
|
} from '@/constants'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { FormAmount } from './FormAmount'
|
import { FormAmount } from './FormAmount'
|
||||||
import { fetcher } from '../utils'
|
import { fetcher, formatNumber } from '@/utils'
|
||||||
|
import { Content } from '@/components/Content'
|
||||||
|
|
||||||
export function Prices() {
|
export function Prices() {
|
||||||
const [amountSwap, setAmountSwap] = useState(100)
|
const [amountSwap, setAmountSwap] = useState(100)
|
||||||
const [debouncedAmountSwap] = useDebounce(amountSwap, 500)
|
const [debouncedAmountSwap] = useDebounce(amountSwap, 500)
|
||||||
|
|
||||||
console.log(debouncedAmountSwap)
|
|
||||||
|
|
||||||
const { data: dataPrices } = useSWR(
|
const { data: dataPrices } = useSWR(
|
||||||
`https://web3.kremalicious.com/api/prices/?tokens=${tokens.toString()}`,
|
`/api/prices/?tokens=${tokens.toString()}`,
|
||||||
fetcher
|
fetcher
|
||||||
)
|
)
|
||||||
const { data: dataSwapOceanToAgix } = useSWR(
|
const { data: dataSwapOceanToAgix } = useSWR(
|
||||||
@ -46,19 +45,9 @@ export function Prices() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.header}>
|
|
||||||
<p>
|
|
||||||
1 OCEAN = {ratioOceanToAsi} ASI (fixed) = ${priceOcean}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
1 AGIX = {ratioAgixToAsi} ASI (fixed) = ${priceAgix}
|
|
||||||
</p>
|
|
||||||
<p>1 Fet = 1 ASI (fixed) = ${priceAsi}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
<div className={styles.results}>
|
<div className={styles.results}>
|
||||||
<h3>${exampleBuyInUsd} right now gets you:</h3>
|
<h3>Buying with ${exampleBuyInUsd} right now gets you:</h3>
|
||||||
<Result
|
<Result
|
||||||
symbol="OCEAN"
|
symbol="OCEAN"
|
||||||
amount={exampleBuyInUsd / priceOcean}
|
amount={exampleBuyInUsd / priceOcean}
|
||||||
@ -85,9 +74,9 @@ export function Prices() {
|
|||||||
|
|
||||||
<div className={styles.results}>
|
<div className={styles.results}>
|
||||||
<h3>
|
<h3>
|
||||||
<FormAmount amount={amountSwap} setAmount={setAmountSwap} /> OCEAN
|
Swapping{' '}
|
||||||
(${(debouncedAmountSwap * priceOcean).toFixed(2)}) right now gets
|
<FormAmount amount={amountSwap} setAmount={setAmountSwap} /> OCEAN (
|
||||||
you:
|
{formatNumber(debouncedAmountSwap * priceOcean, 'USD')}) gets you:
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<Result
|
<Result
|
||||||
@ -135,6 +124,7 @@ export function Prices() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Content prices={{ ocean: priceOcean, agix: priceAgix, asi: priceAsi }} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -1 +0,0 @@
|
|||||||
export * from './components/Prices'
|
|
Loading…
Reference in New Issue
Block a user