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 { .calculationBase {
border-top: 1px solid rgba(var(--foreground-rgb), 0.2); border-top: 1px solid rgba(var(--foreground-rgb), 0.2);
border-left: 1px solid rgba(var(--foreground-rgb), 0.2); border-left: 1px solid rgba(var(--foreground-rgb), 0.2);
border-bottom: none; border-bottom: none;
display: grid; 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); border-radius: var(--border-radius);
margin-top: 1.5rem; margin-top: 1.5rem;
} }
@ -25,3 +14,14 @@
padding: 1rem; padding: 1rem;
font-size: 0.8rem; 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() { export function Content() {
return ( return (
<div className={styles.content}> <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> <p>
All displayed values should be seen as estimates. Except for the{' '} 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"> <a href="https://blog.oceanprotocol.com/ocean-protocol-is-joining-the-superintelligence-alliance-767c82693f24#3c8e">
fixed ASI exchange rate fixed ASI exchange rate
</a> </a>
, the fiat values fetched from{' '} , all other values are constantly changing based on market conditions.
<a href="https://coingecko.com">Coingecko</a> and the quotes from{' '}
<a href="https://uniswap.org">Uniswap</a> v3 swap routes are constantly
changing.
</p> </p>
<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 investment once the actual ASI swap mechanism is released. Use at your
own risk. own risk.
</p> </p>

View File

@ -1,37 +1,6 @@
.form { .form {
display: inline-block; display: inline-flex;
} border: 1px solid rgba(var(--foreground-rgb), 0.15);
.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);
border-radius: var(--border-radius); border-radius: var(--border-radius);
} overflow: hidden;
.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

@ -1,20 +1,26 @@
import { InputAmount } from '@/components/FormAmount/Inputs/InputAmount'
import { InputToken } from './Inputs/InputToken'
import styles from './FormAmount.module.css' import styles from './FormAmount.module.css'
import { Dispatch, SetStateAction } from 'react'
import { Token } from './types'
export function FormAmount({ export function FormAmount({
amount, amount,
setAmount setAmount,
token,
setToken,
isFiat
}: { }: {
amount: number amount: number
setAmount: (amount: number) => void setAmount: Dispatch<SetStateAction<number>>
token: Token
setToken?: Dispatch<SetStateAction<Token>>
isFiat?: boolean
}) { }) {
return ( return (
<form className={styles.form}> <form className={styles.form}>
<input <InputAmount amount={amount} setAmount={setAmount} />
className={styles.input} <InputToken token={token} setToken={setToken} isFiat={isFiat} />
type="text"
value={amount}
onChange={(e) => setAmount(Number(e.target.value))}
/>
</form> </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 './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' 'use client'
import { ratioOceanToAsi, ratioAgixToAsi, ratioFetToAsi } from '@/constants' import { ratioOceanToAsi, ratioAgixToAsi, ratioFetToAsi } from '@/constants'
import { FormAmount } from '../FormAmount'
import { Result } from '../ResultRow' import { Result } from '../ResultRow'
import { useState } from 'react' import { useState } from 'react'
import { useDebounce } from 'use-debounce' import { useDebounce } from 'use-debounce'
import styles from './styles.module.css' import stylesShared from './styles.module.css'
import { usePrices } from '@/hooks' import { usePrices } from '@/hooks'
import { FormAmount } from '@/components/FormAmount'
export function Buy() { export function Buy() {
const prices = usePrices() const prices = usePrices()
const [amountBuy, setAmountBuy] = useState(100) const [amount, setAmount] = useState(100)
const [debouncedAmountBuy] = useDebounce(amountBuy, 500) const [debouncedAmount] = useDebounce(amount, 500)
return ( return (
<div className={styles.results}> <div className={stylesShared.results}>
<h3> <h3 className={stylesShared.title}>
Buying with $ Buying with{' '}
<FormAmount amount={amountBuy} setAmount={setAmountBuy} /> right now <FormAmount amount={amount} setAmount={setAmount} token="usd" isFiat />{' '}
gets you: right now gets you:
</h3> </h3>
<Result <Result
tokenSymbol="OCEAN" tokenSymbol="OCEAN"
tokenAddress="0x967da4048cd07ab37855c090aaf366e4ce1b9f48" tokenAddress="0x967da4048cd07ab37855c090aaf366e4ce1b9f48"
amount={prices.ocean ? debouncedAmountBuy / prices.ocean : 0} amount={prices.ocean ? debouncedAmount / prices.ocean : 0}
amountAsi={ amountAsi={
prices.ocean prices.ocean ? (debouncedAmount / prices.ocean) * ratioOceanToAsi : 0
? (debouncedAmountBuy / prices.ocean) * ratioOceanToAsi
: 0
} }
amountFiat={ amountFiat={
prices.ocean prices.ocean
? (debouncedAmountBuy / prices.ocean) * ratioOceanToAsi * prices.asi ? (debouncedAmount / prices.ocean) * ratioOceanToAsi * prices.asi
: 0 : 0
} }
/> />
<Result <Result
tokenSymbol="AGIX" tokenSymbol="AGIX"
tokenAddress="0x5b7533812759b45c2b44c19e320ba2cd2681b542" tokenAddress="0x5b7533812759b45c2b44c19e320ba2cd2681b542"
amount={prices.agix ? debouncedAmountBuy / prices.agix : 0} amount={prices.agix ? debouncedAmount / prices.agix : 0}
amountAsi={ amountAsi={
prices.agix ? (debouncedAmountBuy / prices.agix) * ratioAgixToAsi : 0 prices.agix ? (debouncedAmount / prices.agix) * ratioAgixToAsi : 0
} }
amountFiat={ amountFiat={
prices.agix prices.agix
? (debouncedAmountBuy / prices.agix) * ratioAgixToAsi * prices.asi ? (debouncedAmount / prices.agix) * ratioAgixToAsi * prices.asi
: 0 : 0
} }
/> />
<Result <Result
tokenSymbol="FET" tokenSymbol="FET"
tokenAddress="0xaea46a60368a7bd060eec7df8cba43b7ef41ad85" tokenAddress="0xaea46a60368a7bd060eec7df8cba43b7ef41ad85"
amount={prices.fet ? debouncedAmountBuy / prices.fet : 0} amount={prices.fet ? debouncedAmount / prices.fet : 0}
amountAsi={ amountAsi={
prices.fet ? (debouncedAmountBuy / prices.fet) * ratioFetToAsi : 0 prices.fet ? (debouncedAmount / prices.fet) * ratioFetToAsi : 0
} }
amountFiat={ amountFiat={
prices.fet prices.fet
? (debouncedAmountBuy / prices.fet) * ratioFetToAsi * prices.asi ? (debouncedAmount / prices.fet) * ratioFetToAsi * prices.asi
: 0 : 0
} }
/> />

View File

@ -7,43 +7,50 @@ import {
tokens tokens
} from '@/constants' } from '@/constants'
import { fetcher, formatNumber } from '@/utils' import { fetcher, formatNumber } from '@/utils'
import { FormAmount } from '../FormAmount' import { Result } from '@/components/ResultRow'
import { Result } from '../ResultRow' import stylesShared from './styles.module.css'
import styles from './styles.module.css'
import { useState } from 'react' import { useState } from 'react'
import useSWR from 'swr' import useSWR from 'swr'
import { useDebounce } from 'use-debounce' import { useDebounce } from 'use-debounce'
import { usePrices } from '@/hooks' import { usePrices } from '@/hooks'
import { FormAmount, type Token } from '@/components/FormAmount'
export function Swap() { export function Swap() {
const prices = usePrices() const prices = usePrices()
const [amountSwap, setAmountSwap] = useState(100) const [amount, setAmount] = useState(100)
const [debouncedAmountSwap] = useDebounce(amountSwap, 500) const [debouncedAmount] = useDebounce(amount, 500)
const [token, setToken] = useState<Token>('ocean')
const { data: dataSwapOceanToAgix } = useSWR( 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 fetcher
) )
const { data: dataSwapOceanToFet } = useSWR( 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 fetcher
) )
return ( return (
<div className={styles.results}> <div className={stylesShared.results}>
<h3> <h3 className={stylesShared.title}>
Swapping <FormAmount amount={amountSwap} setAmount={setAmountSwap} />{' '} Swapping{' '}
OCEAN ({formatNumber(debouncedAmountSwap * prices.ocean, 'USD')}) right <FormAmount
now gets you: amount={amount}
token={token}
setAmount={setAmount}
// setToken={setToken}
/>{' '}
({formatNumber(amount * prices[token || 'ocean'], 'USD')}) right now
gets you:
</h3> </h3>
<Result <Result
tokenSymbol="OCEAN" tokenSymbol="OCEAN"
tokenAddress="0x967da4048cd07ab37855c090aaf366e4ce1b9f48" tokenAddress="0x967da4048cd07ab37855c090aaf366e4ce1b9f48"
amount={debouncedAmountSwap} amount={debouncedAmount}
amountAsi={debouncedAmountSwap * ratioOceanToAsi} amountAsi={debouncedAmount * ratioOceanToAsi}
amountFiat={debouncedAmountSwap * ratioOceanToAsi * prices.asi} amountFiat={debouncedAmount * ratioOceanToAsi * prices.asi}
/> />
<Result <Result

View File

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

View File

@ -4,7 +4,7 @@ import { tokens } from '@/constants'
import { fetcher } from '@/utils' import { fetcher } from '@/utils'
import useSWR from 'swr' import useSWR from 'swr'
export function usePrices() { export function usePrices(): { [key: string]: number } {
const { data: dataPrices } = useSWR( const { data: dataPrices } = useSWR(
`/api/prices/?tokens=${tokens.toString()}`, `/api/prices/?tokens=${tokens.toString()}`,
fetcher fetcher

9
package-lock.json generated
View File

@ -9,6 +9,7 @@
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@coingecko/cryptoformat": "^0.8.1", "@coingecko/cryptoformat": "^0.8.1",
"@radix-ui/react-icons": "^1.3.0",
"next": "14.1.4", "next": "14.1.4",
"react": "^18", "react": "^18",
"react-dom": "^18", "react-dom": "^18",
@ -380,6 +381,14 @@
"node": ">=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": { "node_modules/@rushstack/eslint-patch": {
"version": "1.10.1", "version": "1.10.1",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.1.tgz", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.1.tgz",

View File

@ -10,6 +10,7 @@
}, },
"dependencies": { "dependencies": {
"@coingecko/cryptoformat": "^0.8.1", "@coingecko/cryptoformat": "^0.8.1",
"@radix-ui/react-icons": "^1.3.0",
"next": "14.1.4", "next": "14.1.4",
"react": "^18", "react": "^18",
"react-dom": "^18", "react-dom": "^18",