mirror of
https://github.com/kremalicious/asi-calculator.git
synced 2024-12-22 09:23:16 +01:00
refactor
This commit is contained in:
parent
5d2a805995
commit
70398b26c4
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
--foreground-rgb: 20, 20, 20;
|
--foreground-rgb: 20, 20, 20;
|
||||||
--foreground-rgb-highlight: 0, 0, 0;
|
--foreground-rgb-highlight: 0, 0, 0;
|
||||||
--background-start-rgb: 214, 219, 220;
|
--background-start-rgb: 225, 230, 230;
|
||||||
--background-end-rgb: 255, 255, 255;
|
--background-end-rgb: 255, 255, 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -12,7 +12,7 @@
|
|||||||
:root {
|
:root {
|
||||||
--foreground-rgb: 220, 220, 220;
|
--foreground-rgb: 220, 220, 220;
|
||||||
--foreground-rgb-highlight: 255, 255, 255;
|
--foreground-rgb-highlight: 255, 255, 255;
|
||||||
--background-start-rgb: 0, 0, 0;
|
--background-start-rgb: 25, 25, 25;
|
||||||
--background-end-rgb: 0, 0, 0;
|
--background-end-rgb: 0, 0, 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,14 @@
|
|||||||
font-size: clamp(1.1rem, 10vw, 1.5rem);
|
font-size: clamp(1.1rem, 10vw, 1.5rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 2rem;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||||||
|
max-width: calc(var(--max-width) * 1.5);
|
||||||
|
margin: 3rem auto;
|
||||||
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Prices } from '@/components/Prices'
|
|
||||||
import styles from './page.module.css'
|
import styles from './page.module.css'
|
||||||
import { metadata } from './layout'
|
import { metadata } from './layout'
|
||||||
|
import { Swap, Buy } from '@/components/Strategies'
|
||||||
|
import { Content } from '@/components/Content'
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
@ -10,7 +11,11 @@ export default function Home() {
|
|||||||
<p className={styles.description}>{`${metadata.description}`}</p>
|
<p className={styles.description}>{`${metadata.description}`}</p>
|
||||||
</header>
|
</header>
|
||||||
<main className={styles.main}>
|
<main className={styles.main}>
|
||||||
<Prices />
|
<div className={styles.grid}>
|
||||||
|
<Swap />
|
||||||
|
<Buy />
|
||||||
|
</div>
|
||||||
|
<Content />
|
||||||
</main>
|
</main>
|
||||||
<footer className={styles.footer}>
|
<footer className={styles.footer}>
|
||||||
Send ❤️ and memecoins to krema.eth
|
Send ❤️ and memecoins to krema.eth
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
import { ratioOceanToAsi, ratioAgixToAsi, ratioFetToAsi } from '@/constants'
|
import { ratioOceanToAsi, ratioAgixToAsi, ratioFetToAsi } from '@/constants'
|
||||||
import styles from './Content.module.css'
|
import styles from './Content.module.css'
|
||||||
|
import { usePrices } from '@/hooks'
|
||||||
|
|
||||||
type Props = {
|
export function Content() {
|
||||||
prices: {
|
const prices = usePrices()
|
||||||
[key: string]: number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Content({ prices }: Props) {
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<p>
|
<p>
|
||||||
The 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 <a href="">Coingecko</a> and the token
|
, the fiat values fetched from{' '}
|
||||||
swap quotes from <a href="https://uniswap.org">Uniswap</a> v3 routes are
|
<a href="https://coingecko.com">Coingecko</a> and the token swap quotes
|
||||||
constantly changing.
|
from <a href="https://uniswap.org">Uniswap</a> v3 routes are constantly
|
||||||
|
changing.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
There is no guarantee they reflect the value of your investment once the
|
There is no guarantee they reflect the value of your investment once the
|
||||||
|
1
components/FormAmount/index.tsx
Normal file
1
components/FormAmount/index.tsx
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './FormAmount'
|
@ -1,161 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import useSWR from 'swr'
|
|
||||||
import { useDebounce } from 'use-debounce'
|
|
||||||
import styles from './Prices.module.css'
|
|
||||||
import { Result } from './Result'
|
|
||||||
import {
|
|
||||||
tokens,
|
|
||||||
ratioOceanToAsi,
|
|
||||||
ratioAgixToAsi,
|
|
||||||
ratioFetToAsi
|
|
||||||
} from '@/constants'
|
|
||||||
import { useState } from 'react'
|
|
||||||
import { FormAmount } from './FormAmount'
|
|
||||||
import { fetcher, formatNumber } from '@/utils'
|
|
||||||
import { Content } from '@/components/Content'
|
|
||||||
|
|
||||||
export function Prices() {
|
|
||||||
const [amountSwap, setAmountSwap] = useState(100)
|
|
||||||
const [debouncedAmountSwap] = useDebounce(amountSwap, 500)
|
|
||||||
const [amountBuy, setAmountBuy] = useState(100)
|
|
||||||
const [debouncedAmountBuy] = useDebounce(amountBuy, 500)
|
|
||||||
|
|
||||||
const { data: dataPrices } = useSWR(
|
|
||||||
`/api/prices/?tokens=${tokens.toString()}`,
|
|
||||||
fetcher
|
|
||||||
)
|
|
||||||
|
|
||||||
const { data: dataSwapOceanToAgix } = useSWR(
|
|
||||||
`/api/quote/?tokenIn=${tokens[0]}&tokenOut=${tokens[2]}&amountIn=${debouncedAmountSwap}`,
|
|
||||||
fetcher
|
|
||||||
)
|
|
||||||
|
|
||||||
const { data: dataSwapOceanToFet } = useSWR(
|
|
||||||
`/api/quote/?tokenIn=${tokens[0]}&tokenOut=${tokens[1]}&amountIn=${debouncedAmountSwap}`,
|
|
||||||
fetcher
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceOcean = dataPrices?.[tokens[0]]?.usd || 0
|
|
||||||
const priceFet = dataPrices?.[tokens[1]]?.usd || 0
|
|
||||||
const priceAgix = dataPrices?.[tokens[2]]?.usd || 0
|
|
||||||
const priceAsi = priceFet
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={styles.grid}>
|
|
||||||
<div className={styles.results}>
|
|
||||||
<h3>
|
|
||||||
Swapping{' '}
|
|
||||||
<FormAmount amount={amountSwap} setAmount={setAmountSwap} /> OCEAN (
|
|
||||||
{formatNumber(debouncedAmountSwap * priceOcean, 'USD')}) right now
|
|
||||||
gets you:
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<Result
|
|
||||||
tokenSymbol="OCEAN"
|
|
||||||
tokenAddress="0x967da4048cd07ab37855c090aaf366e4ce1b9f48"
|
|
||||||
amount={debouncedAmountSwap}
|
|
||||||
amountAsi={debouncedAmountSwap * ratioOceanToAsi}
|
|
||||||
amountFiat={debouncedAmountSwap * ratioOceanToAsi * priceAsi}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Result
|
|
||||||
tokenSymbol="AGIX"
|
|
||||||
tokenAddress="0x5b7533812759b45c2b44c19e320ba2cd2681b542"
|
|
||||||
amount={
|
|
||||||
dataSwapOceanToAgix?.amountOut /
|
|
||||||
Number(`1e${dataSwapOceanToAgix?.decimals}`) || 0
|
|
||||||
}
|
|
||||||
amountAsi={
|
|
||||||
(dataSwapOceanToAgix?.amountOut /
|
|
||||||
Number(`1e${dataSwapOceanToAgix?.decimals}`) || 0) *
|
|
||||||
ratioAgixToAsi
|
|
||||||
}
|
|
||||||
amountFiat={
|
|
||||||
(dataSwapOceanToAgix?.amountOut /
|
|
||||||
Number(`1e${dataSwapOceanToAgix?.decimals}`) || 0) *
|
|
||||||
ratioAgixToAsi *
|
|
||||||
priceAsi
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Result
|
|
||||||
tokenSymbol="FET"
|
|
||||||
tokenAddress="0xaea46a60368a7bd060eec7df8cba43b7ef41ad85"
|
|
||||||
amount={
|
|
||||||
dataSwapOceanToFet?.amountOut /
|
|
||||||
Number(`1e${dataSwapOceanToFet?.decimals}`) || 0
|
|
||||||
}
|
|
||||||
amountAsi={
|
|
||||||
(dataSwapOceanToFet?.amountOut /
|
|
||||||
Number(`1e${dataSwapOceanToFet?.decimals}`) || 0) *
|
|
||||||
ratioFetToAsi
|
|
||||||
}
|
|
||||||
amountFiat={
|
|
||||||
(dataSwapOceanToFet?.amountOut /
|
|
||||||
Number(`1e${dataSwapOceanToFet?.decimals}`) || 0) * priceAsi
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.results}>
|
|
||||||
<h3>
|
|
||||||
Buying with $
|
|
||||||
<FormAmount amount={amountBuy} setAmount={setAmountBuy} /> right now
|
|
||||||
gets you:
|
|
||||||
</h3>
|
|
||||||
<Result
|
|
||||||
tokenSymbol="OCEAN"
|
|
||||||
tokenAddress="0x967da4048cd07ab37855c090aaf366e4ce1b9f48"
|
|
||||||
amount={priceOcean ? debouncedAmountBuy / priceOcean : 0}
|
|
||||||
amountAsi={
|
|
||||||
priceOcean
|
|
||||||
? (debouncedAmountBuy / priceOcean) * ratioOceanToAsi
|
|
||||||
: 0
|
|
||||||
}
|
|
||||||
amountFiat={
|
|
||||||
priceOcean
|
|
||||||
? (debouncedAmountBuy / priceOcean) * ratioOceanToAsi * priceAsi
|
|
||||||
: 0
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Result
|
|
||||||
tokenSymbol="AGIX"
|
|
||||||
tokenAddress="0x5b7533812759b45c2b44c19e320ba2cd2681b542"
|
|
||||||
amount={priceAgix ? debouncedAmountBuy / priceAgix : 0}
|
|
||||||
amountAsi={
|
|
||||||
priceAgix ? (debouncedAmountBuy / priceAgix) * ratioAgixToAsi : 0
|
|
||||||
}
|
|
||||||
amountFiat={
|
|
||||||
priceAgix
|
|
||||||
? (debouncedAmountBuy / priceAgix) * ratioAgixToAsi * priceAsi
|
|
||||||
: 0
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Result
|
|
||||||
tokenSymbol="FET"
|
|
||||||
tokenAddress="0xaea46a60368a7bd060eec7df8cba43b7ef41ad85"
|
|
||||||
amount={priceFet ? debouncedAmountBuy / priceFet : 0}
|
|
||||||
amountAsi={
|
|
||||||
priceFet ? (debouncedAmountBuy / priceFet) * ratioFetToAsi : 0
|
|
||||||
}
|
|
||||||
amountFiat={
|
|
||||||
priceFet
|
|
||||||
? (debouncedAmountBuy / priceFet) * ratioFetToAsi * priceAsi
|
|
||||||
: 0
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Content
|
|
||||||
prices={{
|
|
||||||
ocean: priceOcean,
|
|
||||||
agix: priceAgix,
|
|
||||||
asi: priceAsi,
|
|
||||||
fet: priceFet
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
import styles from './Result.module.css'
|
import styles from './ResultRow.module.css'
|
||||||
import { formatNumber } from '../utils'
|
import { formatNumber } from '@/utils'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
1
components/ResultRow/index.tsx
Normal file
1
components/ResultRow/index.tsx
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './ResultRow'
|
66
components/Strategies/Buy.tsx
Normal file
66
components/Strategies/Buy.tsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
'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 { usePrices } from '@/hooks'
|
||||||
|
|
||||||
|
export function Buy() {
|
||||||
|
const prices = usePrices()
|
||||||
|
const [amountBuy, setAmountBuy] = useState(100)
|
||||||
|
const [debouncedAmountBuy] = useDebounce(amountBuy, 500)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.results}>
|
||||||
|
<h3>
|
||||||
|
Buying with $
|
||||||
|
<FormAmount amount={amountBuy} setAmount={setAmountBuy} /> right now
|
||||||
|
gets you:
|
||||||
|
</h3>
|
||||||
|
<Result
|
||||||
|
tokenSymbol="OCEAN"
|
||||||
|
tokenAddress="0x967da4048cd07ab37855c090aaf366e4ce1b9f48"
|
||||||
|
amount={prices.ocean ? debouncedAmountBuy / prices.ocean : 0}
|
||||||
|
amountAsi={
|
||||||
|
prices.ocean
|
||||||
|
? (debouncedAmountBuy / prices.ocean) * ratioOceanToAsi
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
amountFiat={
|
||||||
|
prices.ocean
|
||||||
|
? (debouncedAmountBuy / prices.ocean) * ratioOceanToAsi * prices.asi
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Result
|
||||||
|
tokenSymbol="AGIX"
|
||||||
|
tokenAddress="0x5b7533812759b45c2b44c19e320ba2cd2681b542"
|
||||||
|
amount={prices.agix ? debouncedAmountBuy / prices.agix : 0}
|
||||||
|
amountAsi={
|
||||||
|
prices.agix ? (debouncedAmountBuy / prices.agix) * ratioAgixToAsi : 0
|
||||||
|
}
|
||||||
|
amountFiat={
|
||||||
|
prices.agix
|
||||||
|
? (debouncedAmountBuy / prices.agix) * ratioAgixToAsi * prices.asi
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Result
|
||||||
|
tokenSymbol="FET"
|
||||||
|
tokenAddress="0xaea46a60368a7bd060eec7df8cba43b7ef41ad85"
|
||||||
|
amount={prices.fet ? debouncedAmountBuy / prices.fet : 0}
|
||||||
|
amountAsi={
|
||||||
|
prices.fet ? (debouncedAmountBuy / prices.fet) * ratioFetToAsi : 0
|
||||||
|
}
|
||||||
|
amountFiat={
|
||||||
|
prices.fet
|
||||||
|
? (debouncedAmountBuy / prices.fet) * ratioFetToAsi * prices.asi
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
86
components/Strategies/Swap.tsx
Normal file
86
components/Strategies/Swap.tsx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import {
|
||||||
|
ratioOceanToAsi,
|
||||||
|
ratioAgixToAsi,
|
||||||
|
ratioFetToAsi,
|
||||||
|
tokens
|
||||||
|
} from '@/constants'
|
||||||
|
import { fetcher, formatNumber } from '@/utils'
|
||||||
|
import { FormAmount } from '../FormAmount'
|
||||||
|
import { Result } from '../ResultRow'
|
||||||
|
import styles from './styles.module.css'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import useSWR from 'swr'
|
||||||
|
import { useDebounce } from 'use-debounce'
|
||||||
|
import { usePrices } from '@/hooks'
|
||||||
|
|
||||||
|
export function Swap() {
|
||||||
|
const prices = usePrices()
|
||||||
|
const [amountSwap, setAmountSwap] = useState(100)
|
||||||
|
const [debouncedAmountSwap] = useDebounce(amountSwap, 500)
|
||||||
|
|
||||||
|
const { data: dataSwapOceanToAgix } = useSWR(
|
||||||
|
`/api/quote/?tokenIn=${tokens[0]}&tokenOut=${tokens[2]}&amountIn=${debouncedAmountSwap}`,
|
||||||
|
fetcher
|
||||||
|
)
|
||||||
|
|
||||||
|
const { data: dataSwapOceanToFet } = useSWR(
|
||||||
|
`/api/quote/?tokenIn=${tokens[0]}&tokenOut=${tokens[1]}&amountIn=${debouncedAmountSwap}`,
|
||||||
|
fetcher
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.results}>
|
||||||
|
<h3>
|
||||||
|
Swapping <FormAmount amount={amountSwap} setAmount={setAmountSwap} />{' '}
|
||||||
|
OCEAN ({formatNumber(debouncedAmountSwap * prices.ocean, 'USD')}) right
|
||||||
|
now gets you:
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<Result
|
||||||
|
tokenSymbol="OCEAN"
|
||||||
|
tokenAddress="0x967da4048cd07ab37855c090aaf366e4ce1b9f48"
|
||||||
|
amount={debouncedAmountSwap}
|
||||||
|
amountAsi={debouncedAmountSwap * ratioOceanToAsi}
|
||||||
|
amountFiat={debouncedAmountSwap * ratioOceanToAsi * prices.asi}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Result
|
||||||
|
tokenSymbol="AGIX"
|
||||||
|
tokenAddress="0x5b7533812759b45c2b44c19e320ba2cd2681b542"
|
||||||
|
amount={
|
||||||
|
dataSwapOceanToAgix?.amountOut /
|
||||||
|
Number(`1e${dataSwapOceanToAgix?.decimals}`) || 0
|
||||||
|
}
|
||||||
|
amountAsi={
|
||||||
|
(dataSwapOceanToAgix?.amountOut /
|
||||||
|
Number(`1e${dataSwapOceanToAgix?.decimals}`) || 0) * ratioAgixToAsi
|
||||||
|
}
|
||||||
|
amountFiat={
|
||||||
|
(dataSwapOceanToAgix?.amountOut /
|
||||||
|
Number(`1e${dataSwapOceanToAgix?.decimals}`) || 0) *
|
||||||
|
ratioAgixToAsi *
|
||||||
|
prices.asi
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Result
|
||||||
|
tokenSymbol="FET"
|
||||||
|
tokenAddress="0xaea46a60368a7bd060eec7df8cba43b7ef41ad85"
|
||||||
|
amount={
|
||||||
|
dataSwapOceanToFet?.amountOut /
|
||||||
|
Number(`1e${dataSwapOceanToFet?.decimals}`) || 0
|
||||||
|
}
|
||||||
|
amountAsi={
|
||||||
|
(dataSwapOceanToFet?.amountOut /
|
||||||
|
Number(`1e${dataSwapOceanToFet?.decimals}`) || 0) * ratioFetToAsi
|
||||||
|
}
|
||||||
|
amountFiat={
|
||||||
|
(dataSwapOceanToFet?.amountOut /
|
||||||
|
Number(`1e${dataSwapOceanToFet?.decimals}`) || 0) * prices.asi
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
2
components/Strategies/index.tsx
Normal file
2
components/Strategies/index.tsx
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './Buy'
|
||||||
|
export * from './Swap'
|
@ -1,11 +1,3 @@
|
|||||||
.grid {
|
|
||||||
display: grid;
|
|
||||||
gap: 2rem;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
|
||||||
max-width: calc(var(--max-width) * 1.5);
|
|
||||||
margin: 3rem auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.results {
|
.results {
|
||||||
border: 1px solid rgba(var(--foreground-rgb), 0.2);
|
border: 1px solid rgba(var(--foreground-rgb), 0.2);
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
1
hooks/index.ts
Normal file
1
hooks/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './use-prices'
|
19
hooks/use-prices.tsx
Normal file
19
hooks/use-prices.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { tokens } from '@/constants'
|
||||||
|
import { fetcher } from '@/utils'
|
||||||
|
import useSWR from 'swr'
|
||||||
|
|
||||||
|
export function usePrices() {
|
||||||
|
const { data: dataPrices } = useSWR(
|
||||||
|
`/api/prices/?tokens=${tokens.toString()}`,
|
||||||
|
fetcher
|
||||||
|
)
|
||||||
|
|
||||||
|
const ocean = dataPrices?.[tokens[0]]?.usd || 0
|
||||||
|
const fet = dataPrices?.[tokens[1]]?.usd || 0
|
||||||
|
const agix = dataPrices?.[tokens[2]]?.usd || 0
|
||||||
|
const asi = fet
|
||||||
|
|
||||||
|
return { ocean, fet, agix, asi }
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user