prepare token switching

This commit is contained in:
Matthias Kretschmann 2024-03-30 19:50:51 +00:00
parent 2498e5e5cb
commit 894009fe69
Signed by: m
GPG Key ID: 606EEEF3C479A91F
16 changed files with 196 additions and 96 deletions

View File

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

View File

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

View File

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

View File

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

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

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

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

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

View File

@ -1 +1,2 @@
export * from './FormAmount'
export * from './types'

View File

@ -0,0 +1 @@
export type Token = 'ocean' | 'fet' | 'agix' | 'usd' | undefined

View File

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

View File

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

View File

@ -4,7 +4,7 @@
padding: 1.25rem 1.5rem;
}
.results h3 {
.title {
margin-bottom: 1.5rem;
font-size: 1.1rem;
min-height: 52px;

View File

@ -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
View File

@ -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",

View File

@ -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",