This commit is contained in:
Matthias Kretschmann 2024-03-30 00:32:46 +00:00
parent ac386a6ef4
commit d3f3f8392d
Signed by: m
GPG Key ID: 606EEEF3C479A91F
17 changed files with 164 additions and 38 deletions

View File

@ -1 +1,2 @@
ONEINCH_API_KEY=""
WEB3_API_URL=""

46
app/api/prices/route.ts Normal file
View 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'
}
})
}

View File

@ -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;

View File

@ -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({

View File

@ -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;
}

View File

@ -1,4 +1,4 @@
import { Prices } from '@/features/Prices'
import { Prices } from '@/components/Prices'
import styles from './page.module.css'
import { metadata } from './layout'

View 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;
}

View 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>
)
}

View File

@ -0,0 +1 @@
export * from './Content'

View File

@ -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 {

View File

@ -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 (
<>
<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.results}>
<h3>${exampleBuyInUsd} right now gets you:</h3>
<h3>Buying with ${exampleBuyInUsd} right now gets you:</h3>
<Result
symbol="OCEAN"
amount={exampleBuyInUsd / priceOcean}
@ -85,9 +74,9 @@ export function Prices() {
<div className={styles.results}>
<h3>
<FormAmount amount={amountSwap} setAmount={setAmountSwap} /> OCEAN
(${(debouncedAmountSwap * priceOcean).toFixed(2)}) right now gets
you:
Swapping{' '}
<FormAmount amount={amountSwap} setAmount={setAmountSwap} /> OCEAN (
{formatNumber(debouncedAmountSwap * priceOcean, 'USD')}) gets you:
</h3>
<Result
@ -135,6 +124,7 @@ export function Prices() {
/>
</div>
</div>
<Content prices={{ ocean: priceOcean, agix: priceAgix, asi: priceAsi }} />
</>
)
}

View File

@ -1 +0,0 @@
export * from './components/Prices'