1 ASI
diff --git a/features/prices/components/MarketData/index.tsx b/features/prices/components/MarketData/index.tsx
new file mode 100644
index 0000000..9ea4537
--- /dev/null
+++ b/features/prices/components/MarketData/index.tsx
@@ -0,0 +1 @@
+export * from './MarketData'
diff --git a/hooks/index.ts b/features/prices/hooks/index.tsx
similarity index 50%
rename from hooks/index.ts
rename to features/prices/hooks/index.tsx
index 5ec829b..95409a3 100644
--- a/hooks/index.ts
+++ b/features/prices/hooks/index.tsx
@@ -1,2 +1 @@
export * from './use-prices'
-export * from './use-quote'
diff --git a/hooks/use-prices.tsx b/features/prices/hooks/use-prices.tsx
similarity index 91%
rename from hooks/use-prices.tsx
rename to features/prices/hooks/use-prices.tsx
index 0fbaad1..7bea155 100644
--- a/hooks/use-prices.tsx
+++ b/features/prices/hooks/use-prices.tsx
@@ -1,9 +1,18 @@
+'use client'
+
import { tokens } from '@/constants'
import { fetcher, getTokenAddressBySymbol } from '@/lib/utils'
import useSWR from 'swr'
const tokenAddresses = tokens.map((token) => token.address).toString()
+export type Prices = {
+ ocean: number
+ fet: number
+ agix: number
+ asi: number
+}
+
export function usePrices(): {
prices: { ocean: number; fet: number; agix: number; asi: number }
isValidating: boolean
diff --git a/features/prices/index.tsx b/features/prices/index.tsx
new file mode 100644
index 0000000..0203c73
--- /dev/null
+++ b/features/prices/index.tsx
@@ -0,0 +1,2 @@
+export * from './components/MarketData'
+export * from './hooks'
diff --git a/components/Strategies/Buy.tsx b/features/strategies/components/Buy.tsx
similarity index 91%
rename from components/Strategies/Buy.tsx
rename to features/strategies/components/Buy.tsx
index ddce207..4e65691 100644
--- a/components/Strategies/Buy.tsx
+++ b/features/strategies/components/Buy.tsx
@@ -1,13 +1,12 @@
'use client'
-import { ratioOceanToAsi, ratioAgixToAsi, ratioFetToAsi } from '@/constants'
-import { Result } from '@/components/ResultRow'
import { useState } from 'react'
import { useDebounce } from 'use-debounce'
-import stylesShared from './styles.module.css'
-import { usePrices } from '@/hooks'
-import { FormAmount } from '@/components/FormAmount'
+import { ratioOceanToAsi, ratioAgixToAsi, ratioFetToAsi } from '@/constants'
+import { usePrices } from '@/features/prices'
import { getTokenBySymbol } from '@/lib/utils'
+import { FormAmount, Result } from '@/features/strategies/components'
+import stylesShared from '@/features/strategies/styles/shared.module.css'
export function Buy() {
const { prices, isValidating, isLoading } = usePrices()
diff --git a/components/FormAmount/FormAmount.module.css b/features/strategies/components/FormAmount/FormAmount.module.css
similarity index 100%
rename from components/FormAmount/FormAmount.module.css
rename to features/strategies/components/FormAmount/FormAmount.module.css
diff --git a/features/strategies/components/FormAmount/FormAmount.tsx b/features/strategies/components/FormAmount/FormAmount.tsx
new file mode 100644
index 0000000..a8b8e09
--- /dev/null
+++ b/features/strategies/components/FormAmount/FormAmount.tsx
@@ -0,0 +1,64 @@
+import styles from './FormAmount.module.css'
+import { Dispatch, SetStateAction } from 'react'
+import { TokenSymbol } from '@/types'
+import { Select, Input } from '@/components'
+
+export function FormAmount({
+ amount,
+ setAmount,
+ token,
+ setToken,
+ isFiat
+}: {
+ amount: number
+ setAmount: Dispatch>
+ token: TokenSymbol | string
+ setToken?: Dispatch>
+ isFiat?: boolean
+}) {
+ function handleAmountChange(e: React.ChangeEvent) {
+ const { value } = e.target
+
+ if (value === '') {
+ setAmount(0)
+ } else {
+ setAmount(Number(value))
+ }
+ }
+
+ function handleTokenChange(e: React.ChangeEvent) {
+ if (!setToken) return
+ setToken(e.target.value as TokenSymbol)
+ }
+
+ const options = isFiat
+ ? [{ value: 'USD', label: 'USD' }]
+ : [
+ { value: 'OCEAN', label: 'OCEAN' },
+ { value: 'FET', label: 'FET' },
+ { value: 'AGIX', label: 'AGIX' }
+ ]
+
+ return (
+
+ )
+}
diff --git a/components/FormAmount/index.tsx b/features/strategies/components/FormAmount/index.tsx
similarity index 100%
rename from components/FormAmount/index.tsx
rename to features/strategies/components/FormAmount/index.tsx
diff --git a/features/strategies/components/FormMarket/FormMarket.module.css b/features/strategies/components/FormMarket/FormMarket.module.css
new file mode 100644
index 0000000..738f394
--- /dev/null
+++ b/features/strategies/components/FormMarket/FormMarket.module.css
@@ -0,0 +1,8 @@
+.form {
+ display: inline-flex;
+ border: 1px solid rgba(var(--foreground-rgb), 0.15);
+ border-radius: var(--border-radius);
+ overflow: hidden;
+ margin: -0.15em 0.25rem 0 0.25rem;
+ font-size: 0.9em;
+}
diff --git a/features/strategies/components/FormMarket/FormMarket.tsx b/features/strategies/components/FormMarket/FormMarket.tsx
new file mode 100644
index 0000000..875d98f
--- /dev/null
+++ b/features/strategies/components/FormMarket/FormMarket.tsx
@@ -0,0 +1,27 @@
+import styles from './FormMarket.module.css'
+import { Dispatch, SetStateAction } from 'react'
+import { Select } from '@/components'
+import { type Market } from '@/features/strategies'
+
+export function FormMarket({
+ market,
+ setMarket
+}: {
+ market: Market
+ setMarket: Dispatch>
+}) {
+ const options = [
+ { value: 'market', label: 'All Markets' },
+ { value: 'uniswap-v3', label: 'Uniswap v3' }
+ ]
+ return (
+
+ )
+}
diff --git a/features/strategies/components/FormMarket/index.tsx b/features/strategies/components/FormMarket/index.tsx
new file mode 100644
index 0000000..02153f2
--- /dev/null
+++ b/features/strategies/components/FormMarket/index.tsx
@@ -0,0 +1 @@
+export * from './FormMarket'
diff --git a/components/ResultRow/ResultRow.module.css b/features/strategies/components/Result/Result.module.css
similarity index 100%
rename from components/ResultRow/ResultRow.module.css
rename to features/strategies/components/Result/Result.module.css
diff --git a/components/ResultRow/ResultRow.tsx b/features/strategies/components/Result/Result.tsx
similarity index 94%
rename from components/ResultRow/ResultRow.tsx
rename to features/strategies/components/Result/Result.tsx
index f893363..1e75173 100644
--- a/components/ResultRow/ResultRow.tsx
+++ b/features/strategies/components/Result/Result.tsx
@@ -1,7 +1,7 @@
-import styles from './ResultRow.module.css'
+import styles from './Result.module.css'
import { formatNumber } from '@/lib/utils'
import { ArrowRightIcon } from '@radix-ui/react-icons'
-import { TokenLogo } from '../TokenLogo/TokenLogo'
+import { TokenLogo } from '@/components'
import { Token } from '@/types'
type Props = {
diff --git a/features/strategies/components/Result/index.tsx b/features/strategies/components/Result/index.tsx
new file mode 100644
index 0000000..0405368
--- /dev/null
+++ b/features/strategies/components/Result/index.tsx
@@ -0,0 +1 @@
+export * from './Result'
diff --git a/features/strategies/components/Swap/Results.tsx b/features/strategies/components/Swap/Results.tsx
new file mode 100644
index 0000000..d8a7fb1
--- /dev/null
+++ b/features/strategies/components/Swap/Results.tsx
@@ -0,0 +1,81 @@
+import { ratioOceanToAsi, ratioAgixToAsi, ratioFetToAsi } from '@/constants'
+import { getTokenBySymbol } from '@/lib/utils'
+import { type TokenSymbol } from '@/types'
+import { usePrices, type Prices } from '@/features/prices'
+import { type Market, useQuote } from '@/features/strategies'
+import { Result } from '../Result'
+
+export function SwapResults({
+ tokenSymbol,
+ amount,
+ market
+}: {
+ tokenSymbol: TokenSymbol
+ amount: number
+ market: Market
+}) {
+ const isUniswap = market === 'uniswap-v3'
+
+ const {
+ prices,
+ isValidating: isValidatingPrices,
+ isLoading: isLoadingPrices
+ } = usePrices()
+
+ const {
+ amountToOcean: amountToOceanUniswap,
+ amountToAgix: amountToAgixUniswap,
+ amountToFet: amountToFetUniswap,
+ isValidatingToAgix,
+ isLoadingToAgix,
+ isValidatingToFet,
+ isLoadingToFet,
+ isValidatingToOcean,
+ isLoadingToOcean
+ } = useQuote(tokenSymbol, amount, isUniswap)
+
+ const amountInUsd = amount * prices[tokenSymbol.toLowerCase() as keyof Prices]
+ const amountToOcean = amountInUsd / prices.ocean
+ const amountToAgix = amountInUsd / prices.agix
+ const amountToFet = amountInUsd / prices.fet
+
+ return (
+ <>
+
+
+
+
+
+ >
+ )
+}
diff --git a/components/Strategies/Swap/Swap.tsx b/features/strategies/components/Swap/Swap.tsx
similarity index 59%
rename from components/Strategies/Swap/Swap.tsx
rename to features/strategies/components/Swap/Swap.tsx
index 47a5e15..0121a98 100644
--- a/components/Strategies/Swap/Swap.tsx
+++ b/features/strategies/components/Swap/Swap.tsx
@@ -1,16 +1,18 @@
'use client'
-import stylesShared from '../styles.module.css'
import { useState } from 'react'
import { useDebounce } from 'use-debounce'
-import { FormAmount } from '@/components/FormAmount'
import { SwapResults } from './Results'
import { TokenSymbol } from '@/types'
+import { FormAmount, FormMarket } from '@/features/strategies/components'
+import stylesShared from '@/features/strategies/styles/shared.module.css'
+import { type Market } from '@/features/strategies'
export function Swap() {
const [amount, setAmount] = useState(100)
const [debouncedAmount] = useDebounce(amount, 500)
const [tokenSymbol, setTokenSymbol] = useState('OCEAN')
+ const [market, setMarket] = useState('all')
return (
@@ -22,10 +24,15 @@ export function Swap() {
setAmount={setAmount}
setToken={setTokenSymbol}
/>{' '}
- on Uniswap right now gets you:
+ on right now gets
+ you:
-
+
)
}
diff --git a/features/strategies/components/Swap/index.tsx b/features/strategies/components/Swap/index.tsx
new file mode 100644
index 0000000..0fba608
--- /dev/null
+++ b/features/strategies/components/Swap/index.tsx
@@ -0,0 +1 @@
+export * from './Swap'
diff --git a/features/strategies/components/index.tsx b/features/strategies/components/index.tsx
new file mode 100644
index 0000000..f59a09f
--- /dev/null
+++ b/features/strategies/components/index.tsx
@@ -0,0 +1,3 @@
+export * from './FormAmount'
+export * from './FormMarket'
+export * from './Result'
diff --git a/features/strategies/hooks/index.tsx b/features/strategies/hooks/index.tsx
new file mode 100644
index 0000000..1905d1d
--- /dev/null
+++ b/features/strategies/hooks/index.tsx
@@ -0,0 +1 @@
+export * from './use-quote'
diff --git a/features/strategies/hooks/use-quote.ts b/features/strategies/hooks/use-quote.ts
new file mode 100644
index 0000000..91c7700
--- /dev/null
+++ b/features/strategies/hooks/use-quote.ts
@@ -0,0 +1,91 @@
+'use client'
+
+import { TokenSymbol } from '@/types'
+import { getTokenAddressBySymbol, fetcher } from '@/lib/utils'
+import useSWR from 'swr'
+
+const options = {
+ keepPreviousData: true // so loading UI can kick in properly
+}
+
+export function useQuote(
+ tokenSymbol: TokenSymbol,
+ amount: number,
+ shouldFetch: boolean
+) {
+ // -> AGIX
+ const {
+ data: dataSwapToAgix,
+ isValidating: isValidatingToAgix,
+ isLoading: isLoadingToAgix
+ } = useSWR(
+ shouldFetch
+ ? `/api/quote/?tokenIn=${getTokenAddressBySymbol(
+ tokenSymbol
+ )}&tokenOut=${getTokenAddressBySymbol('AGIX')}&amountIn=${amount}`
+ : null,
+ fetcher,
+ options
+ )
+
+ // -> FET
+ const {
+ data: dataSwapToFet,
+ isValidating: isValidatingToFet,
+ isLoading: isLoadingToFet
+ } = useSWR(
+ shouldFetch
+ ? `/api/quote/?tokenIn=${getTokenAddressBySymbol(
+ tokenSymbol
+ )}&tokenOut=${getTokenAddressBySymbol('FET')}&amountIn=${amount}`
+ : null,
+ fetcher,
+ options
+ )
+
+ // -> OCEAN
+ const {
+ data: dataSwapToOcean,
+ isValidating: isValidatingToOcean,
+ isLoading: isLoadingToOcean
+ } = useSWR(
+ shouldFetch
+ ? `/api/quote/?tokenIn=${getTokenAddressBySymbol(
+ tokenSymbol
+ )}&tokenOut=${getTokenAddressBySymbol('OCEAN')}&amountIn=${amount}`
+ : null,
+ fetcher,
+ options
+ )
+
+ const amountToOcean =
+ dataSwapToOcean?.amountOut / Number(`1e${dataSwapToOcean?.decimals}`)
+ const amountToAgix =
+ dataSwapToAgix?.amountOut / Number(`1e${dataSwapToAgix?.decimals}`)
+ const amountToFet =
+ dataSwapToFet?.amountOut / Number(`1e${dataSwapToFet?.decimals}`)
+
+ return shouldFetch
+ ? {
+ amountToOcean,
+ amountToAgix,
+ amountToFet,
+ isValidatingToAgix,
+ isLoadingToAgix,
+ isValidatingToFet,
+ isLoadingToFet,
+ isValidatingToOcean,
+ isLoadingToOcean
+ }
+ : {
+ amountToOcean: undefined,
+ amountToAgix: undefined,
+ amountToFet: undefined,
+ isValidatingToAgix: false,
+ isLoadingToAgix: false,
+ isValidatingToFet: false,
+ isLoadingToFet: false,
+ isValidatingToOcean: false,
+ isLoadingToOcean: false
+ }
+}
diff --git a/features/strategies/index.tsx b/features/strategies/index.tsx
new file mode 100644
index 0000000..22d2f5f
--- /dev/null
+++ b/features/strategies/index.tsx
@@ -0,0 +1,4 @@
+export * from './components/Buy'
+export * from './components/Swap'
+export * from './hooks'
+export * from './types'
diff --git a/components/Strategies/styles.module.css b/features/strategies/styles/shared.module.css
similarity index 92%
rename from components/Strategies/styles.module.css
rename to features/strategies/styles/shared.module.css
index a79bd4d..5701657 100644
--- a/components/Strategies/styles.module.css
+++ b/features/strategies/styles/shared.module.css
@@ -9,4 +9,5 @@
font-size: 1.2rem;
color: rgb(var(--foreground-rgb-highlight));
min-height: 58px;
+ line-height: 1.5;
}
diff --git a/features/strategies/types.ts b/features/strategies/types.ts
new file mode 100644
index 0000000..8b18ffa
--- /dev/null
+++ b/features/strategies/types.ts
@@ -0,0 +1 @@
+export type Market = 'all' | 'uniswap-v3'
diff --git a/hooks/use-quote.ts b/hooks/use-quote.ts
deleted file mode 100644
index 51304b8..0000000
--- a/hooks/use-quote.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-import { TokenSymbol } from '@/types'
-import { getTokenAddressBySymbol, fetcher } from '@/lib/utils'
-import useSWR from 'swr'
-
-const options = {
- keepPreviousData: true // so loading UI can kick in properly
-}
-
-export function useQuote(tokenSymbol: TokenSymbol, amount: number) {
- // -> AGIX
- const {
- data: dataSwapToAgix,
- isValidating: isValidatingToAgix,
- isLoading: isLoadingToAgix
- } = useSWR(
- `/api/quote/?tokenIn=${getTokenAddressBySymbol(
- tokenSymbol
- )}&tokenOut=${getTokenAddressBySymbol('AGIX')}&amountIn=${amount}`,
- fetcher,
- options
- )
-
- // -> FET
- const {
- data: dataSwapToFet,
- isValidating: isValidatingToFet,
- isLoading: isLoadingToFet
- } = useSWR(
- `/api/quote/?tokenIn=${getTokenAddressBySymbol(
- tokenSymbol
- )}&tokenOut=${getTokenAddressBySymbol('FET')}&amountIn=${amount}`,
- fetcher,
- options
- )
-
- // -> OCEAN
- const {
- data: dataSwapToOcean,
- isValidating: isValidatingToOcean,
- isLoading: isLoadingToOcean
- } = useSWR(
- `/api/quote/?tokenIn=${getTokenAddressBySymbol(
- tokenSymbol
- )}&tokenOut=${getTokenAddressBySymbol('OCEAN')}&amountIn=${amount}`,
- fetcher,
- options
- )
-
- const amountToOcean =
- dataSwapToOcean?.amountOut / Number(`1e${dataSwapToOcean?.decimals}`)
- const amountToAgix =
- dataSwapToAgix?.amountOut / Number(`1e${dataSwapToAgix?.decimals}`)
- const amountToFet =
- dataSwapToFet?.amountOut / Number(`1e${dataSwapToFet?.decimals}`)
-
- return {
- amountToOcean,
- amountToAgix,
- amountToFet,
- isValidatingToAgix,
- isLoadingToAgix,
- isValidatingToFet,
- isLoadingToFet,
- isValidatingToOcean,
- isLoadingToOcean
- }
-}