mirror of
https://github.com/kremalicious/asi-calculator.git
synced 2024-12-22 09:23:16 +01:00
prepare token switching
This commit is contained in:
parent
2498e5e5cb
commit
894009fe69
@ -1,20 +1,9 @@
|
||||
.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-top: 1px solid rgba(var(--foreground-rgb), 0.2);
|
||||
border-left: 1px solid rgba(var(--foreground-rgb), 0.2);
|
||||
border-bottom: none;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
border-radius: var(--border-radius);
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
@ -25,3 +14,14 @@
|
||||
padding: 1rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
@ -3,18 +3,22 @@ import styles from './Content.module.css'
|
||||
export function Content() {
|
||||
return (
|
||||
<div className={styles.content}>
|
||||
<p>
|
||||
The fiat values are fetched from{' '}
|
||||
<a href="https://coingecko.com">Coingecko</a>, and the token swap
|
||||
estimations directly from <a href="https://uniswap.org">Uniswap</a> v3
|
||||
swap routes.
|
||||
</p>
|
||||
<p>
|
||||
All displayed values should be seen as estimates. Except for the{' '}
|
||||
<a href="https://blog.oceanprotocol.com/ocean-protocol-is-joining-the-superintelligence-alliance-767c82693f24#3c8e">
|
||||
fixed ASI exchange rate
|
||||
</a>
|
||||
, the fiat values fetched from{' '}
|
||||
<a href="https://coingecko.com">Coingecko</a> and the quotes from{' '}
|
||||
<a href="https://uniswap.org">Uniswap</a> v3 swap routes are constantly
|
||||
changing.
|
||||
, all other values are constantly changing based on market conditions.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
There is no guarantee displayed values reflect the value of your
|
||||
There is no guarantee the displayed values reflect the value of your
|
||||
investment once the actual ASI swap mechanism is released. Use at your
|
||||
own risk.
|
||||
</p>
|
||||
|
@ -1,37 +1,6 @@
|
||||
.form {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.input {
|
||||
all: unset;
|
||||
width: 70px;
|
||||
padding-left: 0.2rem;
|
||||
padding-right: 0.2rem;
|
||||
text-align: center;
|
||||
background-color: rgba(var(--background-end-rgb), 0.4);
|
||||
display: inline-flex;
|
||||
border: 1px solid rgba(var(--foreground-rgb), 0.15);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.input:hover {
|
||||
background-color: rgba(var(--background-end-rgb), 0.5);
|
||||
}
|
||||
|
||||
.input:focus-within {
|
||||
outline: none;
|
||||
background-color: rgba(var(--background-end-rgb), 0.9);
|
||||
color: rgb(var(--foreground-rgb-highlight));
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.input {
|
||||
background-color: rgba(var(--foreground-rgb), 0.15);
|
||||
}
|
||||
|
||||
.input:hover {
|
||||
background-color: rgba(var(--foreground-rgb), 0.2);
|
||||
}
|
||||
|
||||
.input:focus-within {
|
||||
background-color: rgba(var(--foreground-rgb), 0.3);
|
||||
}
|
||||
overflow: hidden;
|
||||
}
|
||||
|
@ -1,20 +1,26 @@
|
||||
import { InputAmount } from '@/components/FormAmount/Inputs/InputAmount'
|
||||
import { InputToken } from './Inputs/InputToken'
|
||||
import styles from './FormAmount.module.css'
|
||||
import { Dispatch, SetStateAction } from 'react'
|
||||
import { Token } from './types'
|
||||
|
||||
export function FormAmount({
|
||||
amount,
|
||||
setAmount
|
||||
setAmount,
|
||||
token,
|
||||
setToken,
|
||||
isFiat
|
||||
}: {
|
||||
amount: number
|
||||
setAmount: (amount: number) => void
|
||||
setAmount: Dispatch<SetStateAction<number>>
|
||||
token: Token
|
||||
setToken?: Dispatch<SetStateAction<Token>>
|
||||
isFiat?: boolean
|
||||
}) {
|
||||
return (
|
||||
<form className={styles.form}>
|
||||
<input
|
||||
className={styles.input}
|
||||
type="text"
|
||||
value={amount}
|
||||
onChange={(e) => setAmount(Number(e.target.value))}
|
||||
/>
|
||||
<InputAmount amount={amount} setAmount={setAmount} />
|
||||
<InputToken token={token} setToken={setToken} isFiat={isFiat} />
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
32
components/FormAmount/Inputs/InputAmount.module.css
Normal file
32
components/FormAmount/Inputs/InputAmount.module.css
Normal file
@ -0,0 +1,32 @@
|
||||
.input {
|
||||
all: unset;
|
||||
width: 70px;
|
||||
padding-left: 0.2rem;
|
||||
padding-right: 0.2rem;
|
||||
text-align: center;
|
||||
background-color: rgba(var(--background-end-rgb), 0.4);
|
||||
}
|
||||
|
||||
.input:hover {
|
||||
background-color: rgba(var(--background-end-rgb), 0.5);
|
||||
}
|
||||
|
||||
.input:focus-within {
|
||||
outline: none;
|
||||
background-color: rgba(var(--background-end-rgb), 0.9);
|
||||
color: rgb(var(--foreground-rgb-highlight));
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.input {
|
||||
background-color: rgba(var(--foreground-rgb), 0.15);
|
||||
}
|
||||
|
||||
.input:hover {
|
||||
background-color: rgba(var(--foreground-rgb), 0.2);
|
||||
}
|
||||
|
||||
.input:focus-within {
|
||||
background-color: rgba(var(--foreground-rgb), 0.3);
|
||||
}
|
||||
}
|
19
components/FormAmount/Inputs/InputAmount.tsx
Normal file
19
components/FormAmount/Inputs/InputAmount.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { Dispatch, SetStateAction } from 'react'
|
||||
import styles from './InputAmount.module.css'
|
||||
|
||||
export function InputAmount({
|
||||
amount,
|
||||
setAmount
|
||||
}: {
|
||||
amount: number
|
||||
setAmount: Dispatch<SetStateAction<number>>
|
||||
}) {
|
||||
return (
|
||||
<input
|
||||
className={styles.input}
|
||||
type="text"
|
||||
value={amount}
|
||||
onChange={(e) => setAmount(Number(e.target.value))}
|
||||
/>
|
||||
)
|
||||
}
|
17
components/FormAmount/Inputs/InputToken.module.css
Normal file
17
components/FormAmount/Inputs/InputToken.module.css
Normal file
@ -0,0 +1,17 @@
|
||||
.select {
|
||||
display: inline-block;
|
||||
all: unset;
|
||||
padding: 0 0.75rem;
|
||||
padding-right: 1.25rem;
|
||||
}
|
||||
|
||||
.selectWrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
right: 0.175rem;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
}
|
36
components/FormAmount/Inputs/InputToken.tsx
Normal file
36
components/FormAmount/Inputs/InputToken.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { Dispatch, SetStateAction } from 'react'
|
||||
import styles from './InputToken.module.css'
|
||||
import { CaretDownIcon } from '@radix-ui/react-icons'
|
||||
import { Token } from '../types'
|
||||
|
||||
export function InputToken({
|
||||
token,
|
||||
setToken,
|
||||
isFiat
|
||||
}: {
|
||||
token: Token
|
||||
isFiat?: boolean
|
||||
setToken?: Dispatch<SetStateAction<Token>>
|
||||
}) {
|
||||
return (
|
||||
<span className={styles.selectWrapper}>
|
||||
<select
|
||||
className={styles.select}
|
||||
onChange={(e) => (setToken ? setToken(e.target.value as Token) : null)}
|
||||
value={token}
|
||||
disabled={!setToken}
|
||||
>
|
||||
{isFiat ? (
|
||||
<option value="usd">USD</option>
|
||||
) : (
|
||||
<>
|
||||
<option value="ocean">OCEAN</option>
|
||||
<option value="fet">FET</option>
|
||||
<option value="agix">AGIX</option>
|
||||
</>
|
||||
)}
|
||||
</select>
|
||||
{setToken ? <CaretDownIcon className={styles.icon} /> : null}
|
||||
</span>
|
||||
)
|
||||
}
|
@ -1 +1,2 @@
|
||||
export * from './FormAmount'
|
||||
export * from './types'
|
||||
|
1
components/FormAmount/types.ts
Normal file
1
components/FormAmount/types.ts
Normal file
@ -0,0 +1 @@
|
||||
export type Token = 'ocean' | 'fet' | 'agix' | 'usd' | undefined
|
@ -1,63 +1,61 @@
|
||||
'use client'
|
||||
|
||||
import { ratioOceanToAsi, ratioAgixToAsi, ratioFetToAsi } from '@/constants'
|
||||
import { FormAmount } from '../FormAmount'
|
||||
import { Result } from '../ResultRow'
|
||||
import { useState } from 'react'
|
||||
import { useDebounce } from 'use-debounce'
|
||||
import styles from './styles.module.css'
|
||||
import stylesShared from './styles.module.css'
|
||||
import { usePrices } from '@/hooks'
|
||||
import { FormAmount } from '@/components/FormAmount'
|
||||
|
||||
export function Buy() {
|
||||
const prices = usePrices()
|
||||
const [amountBuy, setAmountBuy] = useState(100)
|
||||
const [debouncedAmountBuy] = useDebounce(amountBuy, 500)
|
||||
const [amount, setAmount] = useState(100)
|
||||
const [debouncedAmount] = useDebounce(amount, 500)
|
||||
|
||||
return (
|
||||
<div className={styles.results}>
|
||||
<h3>
|
||||
Buying with $
|
||||
<FormAmount amount={amountBuy} setAmount={setAmountBuy} /> right now
|
||||
gets you:
|
||||
<div className={stylesShared.results}>
|
||||
<h3 className={stylesShared.title}>
|
||||
Buying with{' '}
|
||||
<FormAmount amount={amount} setAmount={setAmount} token="usd" isFiat />{' '}
|
||||
right now gets you:
|
||||
</h3>
|
||||
<Result
|
||||
tokenSymbol="OCEAN"
|
||||
tokenAddress="0x967da4048cd07ab37855c090aaf366e4ce1b9f48"
|
||||
amount={prices.ocean ? debouncedAmountBuy / prices.ocean : 0}
|
||||
amount={prices.ocean ? debouncedAmount / prices.ocean : 0}
|
||||
amountAsi={
|
||||
prices.ocean
|
||||
? (debouncedAmountBuy / prices.ocean) * ratioOceanToAsi
|
||||
: 0
|
||||
prices.ocean ? (debouncedAmount / prices.ocean) * ratioOceanToAsi : 0
|
||||
}
|
||||
amountFiat={
|
||||
prices.ocean
|
||||
? (debouncedAmountBuy / prices.ocean) * ratioOceanToAsi * prices.asi
|
||||
? (debouncedAmount / prices.ocean) * ratioOceanToAsi * prices.asi
|
||||
: 0
|
||||
}
|
||||
/>
|
||||
<Result
|
||||
tokenSymbol="AGIX"
|
||||
tokenAddress="0x5b7533812759b45c2b44c19e320ba2cd2681b542"
|
||||
amount={prices.agix ? debouncedAmountBuy / prices.agix : 0}
|
||||
amount={prices.agix ? debouncedAmount / prices.agix : 0}
|
||||
amountAsi={
|
||||
prices.agix ? (debouncedAmountBuy / prices.agix) * ratioAgixToAsi : 0
|
||||
prices.agix ? (debouncedAmount / prices.agix) * ratioAgixToAsi : 0
|
||||
}
|
||||
amountFiat={
|
||||
prices.agix
|
||||
? (debouncedAmountBuy / prices.agix) * ratioAgixToAsi * prices.asi
|
||||
? (debouncedAmount / prices.agix) * ratioAgixToAsi * prices.asi
|
||||
: 0
|
||||
}
|
||||
/>
|
||||
<Result
|
||||
tokenSymbol="FET"
|
||||
tokenAddress="0xaea46a60368a7bd060eec7df8cba43b7ef41ad85"
|
||||
amount={prices.fet ? debouncedAmountBuy / prices.fet : 0}
|
||||
amount={prices.fet ? debouncedAmount / prices.fet : 0}
|
||||
amountAsi={
|
||||
prices.fet ? (debouncedAmountBuy / prices.fet) * ratioFetToAsi : 0
|
||||
prices.fet ? (debouncedAmount / prices.fet) * ratioFetToAsi : 0
|
||||
}
|
||||
amountFiat={
|
||||
prices.fet
|
||||
? (debouncedAmountBuy / prices.fet) * ratioFetToAsi * prices.asi
|
||||
? (debouncedAmount / prices.fet) * ratioFetToAsi * prices.asi
|
||||
: 0
|
||||
}
|
||||
/>
|
||||
|
@ -7,43 +7,50 @@ import {
|
||||
tokens
|
||||
} from '@/constants'
|
||||
import { fetcher, formatNumber } from '@/utils'
|
||||
import { FormAmount } from '../FormAmount'
|
||||
import { Result } from '../ResultRow'
|
||||
import styles from './styles.module.css'
|
||||
import { Result } from '@/components/ResultRow'
|
||||
import stylesShared from './styles.module.css'
|
||||
import { useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { useDebounce } from 'use-debounce'
|
||||
import { usePrices } from '@/hooks'
|
||||
import { FormAmount, type Token } from '@/components/FormAmount'
|
||||
|
||||
export function Swap() {
|
||||
const prices = usePrices()
|
||||
const [amountSwap, setAmountSwap] = useState(100)
|
||||
const [debouncedAmountSwap] = useDebounce(amountSwap, 500)
|
||||
const [amount, setAmount] = useState(100)
|
||||
const [debouncedAmount] = useDebounce(amount, 500)
|
||||
const [token, setToken] = useState<Token>('ocean')
|
||||
|
||||
const { data: dataSwapOceanToAgix } = useSWR(
|
||||
`/api/quote/?tokenIn=${tokens[0]}&tokenOut=${tokens[2]}&amountIn=${debouncedAmountSwap}`,
|
||||
`/api/quote/?tokenIn=${tokens[0]}&tokenOut=${tokens[2]}&amountIn=${debouncedAmount}`,
|
||||
fetcher
|
||||
)
|
||||
|
||||
const { data: dataSwapOceanToFet } = useSWR(
|
||||
`/api/quote/?tokenIn=${tokens[0]}&tokenOut=${tokens[1]}&amountIn=${debouncedAmountSwap}`,
|
||||
`/api/quote/?tokenIn=${tokens[0]}&tokenOut=${tokens[1]}&amountIn=${debouncedAmount}`,
|
||||
fetcher
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={styles.results}>
|
||||
<h3>
|
||||
Swapping <FormAmount amount={amountSwap} setAmount={setAmountSwap} />{' '}
|
||||
OCEAN ({formatNumber(debouncedAmountSwap * prices.ocean, 'USD')}) right
|
||||
now gets you:
|
||||
<div className={stylesShared.results}>
|
||||
<h3 className={stylesShared.title}>
|
||||
Swapping{' '}
|
||||
<FormAmount
|
||||
amount={amount}
|
||||
token={token}
|
||||
setAmount={setAmount}
|
||||
// setToken={setToken}
|
||||
/>{' '}
|
||||
({formatNumber(amount * prices[token || 'ocean'], 'USD')}) right now
|
||||
gets you:
|
||||
</h3>
|
||||
|
||||
<Result
|
||||
tokenSymbol="OCEAN"
|
||||
tokenAddress="0x967da4048cd07ab37855c090aaf366e4ce1b9f48"
|
||||
amount={debouncedAmountSwap}
|
||||
amountAsi={debouncedAmountSwap * ratioOceanToAsi}
|
||||
amountFiat={debouncedAmountSwap * ratioOceanToAsi * prices.asi}
|
||||
amount={debouncedAmount}
|
||||
amountAsi={debouncedAmount * ratioOceanToAsi}
|
||||
amountFiat={debouncedAmount * ratioOceanToAsi * prices.asi}
|
||||
/>
|
||||
|
||||
<Result
|
||||
|
@ -4,7 +4,7 @@
|
||||
padding: 1.25rem 1.5rem;
|
||||
}
|
||||
|
||||
.results h3 {
|
||||
.title {
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 1.1rem;
|
||||
min-height: 52px;
|
||||
|
@ -4,7 +4,7 @@ import { tokens } from '@/constants'
|
||||
import { fetcher } from '@/utils'
|
||||
import useSWR from 'swr'
|
||||
|
||||
export function usePrices() {
|
||||
export function usePrices(): { [key: string]: number } {
|
||||
const { data: dataPrices } = useSWR(
|
||||
`/api/prices/?tokens=${tokens.toString()}`,
|
||||
fetcher
|
||||
|
9
package-lock.json
generated
9
package-lock.json
generated
@ -9,6 +9,7 @@
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@coingecko/cryptoformat": "^0.8.1",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"next": "14.1.4",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
@ -380,6 +381,14 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-icons": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz",
|
||||
"integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.x || ^17.x || ^18.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@rushstack/eslint-patch": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.1.tgz",
|
||||
|
@ -10,6 +10,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@coingecko/cryptoformat": "^0.8.1",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"next": "14.1.4",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
|
Loading…
Reference in New Issue
Block a user