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
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.
> Calculate how much ASI you get for your OCEAN, AGIX, or FET.

View File

@ -1,15 +1,17 @@
:root {
--max-width: 620px;
--border-radius: 12px;
--max-width: 800px;
--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-end-rgb: 255, 255, 255;
}
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--foreground-rgb: 220, 220, 220;
--foreground-rgb-highlight: 255, 255, 255;
--background-start-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 = {
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({

View File

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

View File

@ -1,11 +1,13 @@
import { Prices } from '@/features/Prices'
import styles from './page.module.css'
import { metadata } from './layout'
export default function Home() {
return (
<>
<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 />
</main>
<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 {
margin-top: 2rem;
border: 1px solid rgba(var(--foreground-rgb), 0.2);
border-radius: var(--border-radius);
padding: 1.25rem 1.5rem;
}
.results h3 {
margin-bottom: 1rem;
font-size: 1.1rem;
}

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import { formatCurrency } from '@coingecko/cryptoformat'
import styles from './Result.module.css'
import { formatNumber } from '../utils'
type Props = {
symbol: string
@ -8,22 +8,14 @@ type Props = {
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) {
return (
<div className={styles.result}>
<p>{formatNumber(amount, symbol)}</p>
<p>
{formatPrice(amount, symbol)} {' '}
<strong title={`${amountAsi}`}>{formatPrice(amountAsi, 'ASI')}</strong>
</p>
<p>
= <strong>{formatPrice(amountFiat, 'USD')}</strong>
{' '}
<strong title={`${amountAsi}`}>{formatNumber(amountAsi, 'ASI')}</strong>{' '}
= <strong>{formatNumber(amountFiat, 'USD')}</strong>
</p>
</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",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ocean-fetch-price-difference",
"name": "asi-calculator",
"version": "0.1.0",
"dependencies": {
"@coingecko/cryptoformat": "^0.8.1",
"next": "14.1.4",
"react": "^18",
"react-dom": "^18",
"swr": "^2.2.5"
"swr": "^2.2.5",
"use-debounce": "^10.0.0"
},
"devDependencies": {
"@types/node": "^20",
@ -4075,6 +4076,17 @@
"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": {
"version": "1.2.0",
"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",
"private": true,
"scripts": {
@ -13,7 +13,8 @@
"next": "14.1.4",
"react": "^18",
"react-dom": "^18",
"swr": "^2.2.5"
"swr": "^2.2.5",
"use-debounce": "^10.0.0"
},
"devDependencies": {
"@types/node": "^20",