This commit is contained in:
Matthias Kretschmann 2024-03-29 21:54:07 +00:00
parent a9c5c20bc2
commit ac386a6ef4
Signed by: m
GPG Key ID: 606EEEF3C479A91F
14 changed files with 219 additions and 144 deletions

View File

@ -1,36 +1,3 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). # asi-calculator
## Getting Started > Calculate how much ASI you get for your OCEAN, AGIX, or FET.
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

View File

@ -1,15 +1,17 @@
:root { :root {
--max-width: 620px; --max-width: 800px;
--border-radius: 12px; --border-radius: 0.3rem;
--foreground-rgb: 0, 0, 0; --foreground-rgb: 20, 20, 20;
--foreground-rgb-highlight: 0, 0, 0;
--background-start-rgb: 214, 219, 220; --background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255; --background-end-rgb: 255, 255, 255;
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
:root { :root {
--foreground-rgb: 255, 255, 255; --foreground-rgb: 220, 220, 220;
--foreground-rgb-highlight: 255, 255, 255;
--background-start-rgb: 0, 0, 0; --background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0; --background-end-rgb: 0, 0, 0;
} }

View File

@ -6,7 +6,7 @@ const firaCode = Fira_Code({ subsets: ['latin'] })
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'OCEAN + AGIX + FET = ASI', title: 'OCEAN + AGIX + FET = ASI',
description: 'Calculate how much ASI you get for your OCEAN, AGIX, or FET' description: 'Calculate how much ASI you get for your OCEAN, AGIX, or FET.'
} }
export default function RootLayout({ export default function RootLayout({

View File

@ -1,12 +1,23 @@
.main, .main,
.footer { .footer {
padding: 2rem; padding: 2rem;
}
.title,
.description {
color: rgb(var(--foreground-rgb-highlight));
max-width: var(--max-width); max-width: var(--max-width);
margin: auto; margin: auto;
} }
.title { .title {
margin-bottom: 0.5rem;
font-size: 2rem;
}
.description {
margin-bottom: 2rem; margin-bottom: 2rem;
font-size: 1.3rem;
} }
.footer { .footer {

View File

@ -1,11 +1,13 @@
import { Prices } from '@/features/Prices' import { Prices } from '@/features/Prices'
import styles from './page.module.css' import styles from './page.module.css'
import { metadata } from './layout'
export default function Home() { export default function Home() {
return ( return (
<> <>
<main className={styles.main}> <main className={styles.main}>
<h1 className={styles.title}>Should I Buy OCEAN or AGIX or FET?</h1> <h1 className={styles.title}>{`${metadata.title}`}</h1>
<p className={styles.description}>{`${metadata.description}`}</p>
<Prices /> <Prices />
</main> </main>
<footer className={styles.footer}>Send to krema.eth</footer> <footer className={styles.footer}>Send to krema.eth</footer>

View File

@ -0,0 +1,23 @@
.form {
display: inline-block;
}
.input {
all: unset;
width: auto;
padding-left: 0.2rem;
padding-right: 0.2rem;
text-align: center;
background-color: rgba(var(--foreground-rgb), 0.15);
border-radius: var(--border-radius);
}
.input:hover {
background-color: rgba(var(--foreground-rgb), 0.2);
}
.input:focus-within {
outline: none;
background-color: rgba(var(--foreground-rgb), 0.3);
color: rgb(var(--foreground-rgb-highlight));
}

View File

@ -0,0 +1,21 @@
import styles from './FormAmount.module.css'
export function FormAmount({
amount,
setAmount
}: {
amount: number
setAmount: (amount: number) => void
}) {
return (
<form className={styles.form}>
<input
className={styles.input}
type="text"
value={amount}
onChange={(e) => setAmount(Number(e.target.value))}
size={1}
/>
</form>
)
}

View File

@ -1,7 +1,23 @@
.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;
}
.results { .results {
margin-top: 2rem; border: 1px solid rgba(var(--foreground-rgb), 0.2);
border-radius: var(--border-radius);
padding: 1.25rem 1.5rem;
} }
.results h3 { .results h3 {
margin-bottom: 1rem; margin-bottom: 1rem;
font-size: 1.1rem;
} }

View File

@ -1,6 +1,7 @@
'use client' 'use client'
import useSWR from 'swr' import useSWR from 'swr'
import { useDebounce } from 'use-debounce'
import styles from './Prices.module.css' import styles from './Prices.module.css'
import { Result } from './Result' import { Result } from './Result'
import { import {
@ -10,28 +11,30 @@ import {
exampleBuyInUsd, exampleBuyInUsd,
ratioFetToAsi ratioFetToAsi
} from '@/constants' } from '@/constants'
import { useState } from 'react'
const fetcher = async (url: string) => { import { FormAmount } from './FormAmount'
const res = await fetch(url) import { fetcher } from '../utils'
if (!res.ok) throw new Error('Failed to fetch')
return await res.json()
}
export function Prices() { export function Prices() {
const [amountSwap, setAmountSwap] = useState(100)
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()}`, `https://web3.kremalicious.com/api/prices/?tokens=${tokens.toString()}`,
fetcher fetcher
) )
const { data: dataSwapOceanToAgix } = useSWR( const { data: dataSwapOceanToAgix } = useSWR(
`/api/quote/?src=${tokens[0]}&dst=${tokens[2]}&amount=${ `/api/quote/?src=${tokens[0]}&dst=${tokens[2]}&amount=${
exampleBuyInUsd * 1e18 debouncedAmountSwap * 1e18
}`, }`,
fetcher fetcher
) )
const { data: dataSwapOceanToFet } = useSWR( const { data: dataSwapOceanToFet } = useSWR(
`/api/quote/?src=${tokens[0]}&dst=${tokens[1]}&amount=${ `/api/quote/?src=${tokens[0]}&dst=${tokens[1]}&amount=${
exampleBuyInUsd * 1e18 debouncedAmountSwap * 1e18
}`, }`,
fetcher fetcher
) )
@ -43,87 +46,94 @@ export function Prices() {
return ( return (
<> <>
<p> <div className={styles.header}>
1 OCEAN = {ratioOceanToAsi} ASI (fixed) = ${priceOcean} <p>
</p> 1 OCEAN = {ratioOceanToAsi} ASI (fixed) = ${priceOcean}
<p> </p>
1 AGIX = {ratioAgixToAsi} ASI (fixed) = ${priceAgix} <p>
</p> 1 AGIX = {ratioAgixToAsi} ASI (fixed) = ${priceAgix}
<p>1 Fet = 1 ASI (fixed) = ${priceAsi}</p> </p>
<p>1 Fet = 1 ASI (fixed) = ${priceAsi}</p>
<div className={styles.results}>
<h3>Buying with ${exampleBuyInUsd} right now gets you:</h3>
<Result
symbol="OCEAN"
amount={exampleBuyInUsd / priceOcean}
amountAsi={(exampleBuyInUsd / priceOcean) * ratioOceanToAsi}
amountFiat={
(exampleBuyInUsd / priceOcean) * ratioOceanToAsi * priceAsi
}
/>
<Result
symbol="AGIX"
amount={exampleBuyInUsd / priceAgix}
amountAsi={(exampleBuyInUsd / priceAgix) * ratioAgixToAsi}
amountFiat={(exampleBuyInUsd / priceAgix) * ratioAgixToAsi * priceAsi}
/>
<Result
symbol="FET"
amount={exampleBuyInUsd / priceFet}
amountAsi={(exampleBuyInUsd / priceFet) * ratioFetToAsi}
amountFiat={(exampleBuyInUsd / priceFet) * ratioFetToAsi * priceAsi}
/>
</div> </div>
<div className={styles.results}> <div className={styles.grid}>
<h3> <div className={styles.results}>
Swapping 100 OCEAN (${(100 * priceOcean).toFixed(2)}) right now gets <h3>${exampleBuyInUsd} right now gets you:</h3>
you: <Result
</h3> symbol="OCEAN"
amount={exampleBuyInUsd / priceOcean}
amountAsi={(exampleBuyInUsd / priceOcean) * ratioOceanToAsi}
amountFiat={
(exampleBuyInUsd / priceOcean) * ratioOceanToAsi * priceAsi
}
/>
<Result
symbol="AGIX"
amount={exampleBuyInUsd / priceAgix}
amountAsi={(exampleBuyInUsd / priceAgix) * ratioAgixToAsi}
amountFiat={
(exampleBuyInUsd / priceAgix) * ratioAgixToAsi * priceAsi
}
/>
<Result
symbol="FET"
amount={exampleBuyInUsd / priceFet}
amountAsi={(exampleBuyInUsd / priceFet) * ratioFetToAsi}
amountFiat={(exampleBuyInUsd / priceFet) * ratioFetToAsi * priceAsi}
/>
</div>
<Result <div className={styles.results}>
symbol="OCEAN" <h3>
amount={100} <FormAmount amount={amountSwap} setAmount={setAmountSwap} /> OCEAN
amountAsi={100 * ratioOceanToAsi} (${(debouncedAmountSwap * priceOcean).toFixed(2)}) right now gets
amountFiat={100 * ratioOceanToAsi * priceAsi} you:
/> </h3>
<Result <Result
symbol="AGIX" symbol="OCEAN"
amount={ amount={debouncedAmountSwap}
dataSwapOceanToAgix?.dstAmount / amountAsi={debouncedAmountSwap * ratioOceanToAsi}
Number(`1e${dataSwapOceanToAgix?.dstToken?.decimals}`) || 0 amountFiat={debouncedAmountSwap * ratioOceanToAsi * priceAsi}
} />
amountAsi={
(dataSwapOceanToAgix?.dstAmount /
Number(`1e${dataSwapOceanToAgix?.dstToken?.decimals}`) || 0) *
ratioAgixToAsi
}
amountFiat={
(dataSwapOceanToAgix?.dstAmount /
Number(`1e${dataSwapOceanToAgix?.dstToken?.decimals}`) || 0) *
ratioAgixToAsi *
priceAsi
}
/>
<Result <Result
symbol="FET" symbol="AGIX"
amount={ amount={
dataSwapOceanToFet?.dstAmount / dataSwapOceanToAgix?.dstAmount /
Number(`1e${dataSwapOceanToFet?.dstToken?.decimals}`) || 0 Number(`1e${dataSwapOceanToAgix?.dstToken?.decimals}`) || 0
} }
amountAsi={ amountAsi={
(dataSwapOceanToFet?.dstAmount / (dataSwapOceanToAgix?.dstAmount /
Number(`1e${dataSwapOceanToFet?.dstToken?.decimals}`) || 0) * Number(`1e${dataSwapOceanToAgix?.dstToken?.decimals}`) || 0) *
ratioFetToAsi ratioAgixToAsi
} }
amountFiat={ amountFiat={
(dataSwapOceanToFet?.dstAmount / (dataSwapOceanToAgix?.dstAmount /
Number(`1e${dataSwapOceanToFet?.dstToken?.decimals}`) || 0) * Number(`1e${dataSwapOceanToAgix?.dstToken?.decimals}`) || 0) *
priceAsi ratioAgixToAsi *
} priceAsi
/> }
/>
<Result
symbol="FET"
amount={
dataSwapOceanToFet?.dstAmount /
Number(`1e${dataSwapOceanToFet?.dstToken?.decimals}`) || 0
}
amountAsi={
(dataSwapOceanToFet?.dstAmount /
Number(`1e${dataSwapOceanToFet?.dstToken?.decimals}`) || 0) *
ratioFetToAsi
}
amountFiat={
(dataSwapOceanToFet?.dstAmount /
Number(`1e${dataSwapOceanToFet?.dstToken?.decimals}`) || 0) *
priceAsi
}
/>
</div>
</div> </div>
</> </>
) )

View File

@ -2,5 +2,9 @@
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.result:last-child {
margin-bottom: 0;
}
.amount { .amount {
} }

View File

@ -1,5 +1,5 @@
import { formatCurrency } from '@coingecko/cryptoformat'
import styles from './Result.module.css' import styles from './Result.module.css'
import { formatNumber } from '../utils'
type Props = { type Props = {
symbol: string symbol: string
@ -8,22 +8,14 @@ type Props = {
amountFiat: number amountFiat: number
} }
function formatPrice(price: number, currency: string) {
return formatCurrency(price, currency, 'en', false, {
decimalPlaces: 3,
significantFigures: 5
})
}
export function Result({ symbol, amount, amountAsi, amountFiat }: Props) { export function Result({ symbol, amount, amountAsi, amountFiat }: Props) {
return ( return (
<div className={styles.result}> <div className={styles.result}>
<p>{formatNumber(amount, symbol)}</p>
<p> <p>
{formatPrice(amount, symbol)} {' '} {' '}
<strong title={`${amountAsi}`}>{formatPrice(amountAsi, 'ASI')}</strong> <strong title={`${amountAsi}`}>{formatNumber(amountAsi, 'ASI')}</strong>{' '}
</p> = <strong>{formatNumber(amountFiat, 'USD')}</strong>
<p>
= <strong>{formatPrice(amountFiat, 'USD')}</strong>
</p> </p>
</div> </div>
) )

14
features/Prices/utils.ts Normal file
View File

@ -0,0 +1,14 @@
import { formatCurrency } from '@coingecko/cryptoformat'
export function formatNumber(price: number, currency: string) {
return formatCurrency(price, currency, 'en', false, {
decimalPlaces: 3,
significantFigures: 5
})
}
export async function fetcher(url: string) {
const res = await fetch(url)
if (!res.ok) throw new Error('Failed to fetch')
return await res.json()
}

18
package-lock.json generated
View File

@ -1,18 +1,19 @@
{ {
"name": "ocean-fetch-price-difference", "name": "asi-calculator",
"version": "0.1.0", "version": "0.1.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ocean-fetch-price-difference", "name": "asi-calculator",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@coingecko/cryptoformat": "^0.8.1", "@coingecko/cryptoformat": "^0.8.1",
"next": "14.1.4", "next": "14.1.4",
"react": "^18", "react": "^18",
"react-dom": "^18", "react-dom": "^18",
"swr": "^2.2.5" "swr": "^2.2.5",
"use-debounce": "^10.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20", "@types/node": "^20",
@ -4075,6 +4076,17 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/use-debounce": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.0.0.tgz",
"integrity": "sha512-XRjvlvCB46bah9IBXVnq/ACP2lxqXyZj0D9hj4K5OzNroMDpTEBg8Anuh1/UfRTRs7pLhQ+RiNxxwZu9+MVl1A==",
"engines": {
"node": ">= 16.0.0"
},
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/use-sync-external-store": { "node_modules/use-sync-external-store": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",

View File

@ -1,5 +1,5 @@
{ {
"name": "ocean-fetch-price-difference", "name": "asi-calculator",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -13,7 +13,8 @@
"next": "14.1.4", "next": "14.1.4",
"react": "^18", "react": "^18",
"react-dom": "^18", "react-dom": "^18",
"swr": "^2.2.5" "swr": "^2.2.5",
"use-debounce": "^10.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20", "@types/node": "^20",