Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"singleQuote": true,
"useTabs": true,
"tabWidth": 2,
"printWidth": 120,
"printWidth": 100,
"trailingComma": "none",
"endOfLine": "auto"
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
"format": "prettier --write ."
"format": "prettier --write ."
},
"dependencies": {
"@bbachain/buffer-layout": "^1.0.0",
Expand Down
106 changes: 106 additions & 0 deletions public/BBA_logo_wrapping.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
108 changes: 108 additions & 0 deletions public/WBBA_logo_wrapping.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
204 changes: 154 additions & 50 deletions src/app/(walletConnected)/liquidity-pools/create-pool/page.tsx

Large diffs are not rendered by default.

81 changes: 48 additions & 33 deletions src/app/(walletConnected)/swap/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,7 @@ import {
import { TTokenProps } from '@/features/swap/types'
import { getCoinGeckoId } from '@/features/swap/utils'
import { cn } from '@/lib/utils'

const initialBaseTokenProps: TTokenProps = {
name: 'BBA Coin',
symbol: 'BBA',
address: NATIVE_MINT.toBase58(),
logoURI: '/bba_logo.svg',
decimals: 9,
tags: ['native']
}

const initialQuoteTokenProps: TTokenProps = {
name: 'Tether USD',
symbol: 'USDT',
address: 'C5CpKwRY2Q5kPYhx78XimCg2eRT3YUgPFAoocFF7Vgf',
logoURI: 'https://assets.coingecko.com/coins/images/325/small/Tether.png',
decimals: 6,
tags: ['stablecoin']
}
import StaticTokens from '@/staticData/tokens'

export default function Swap() {
/**
Expand All @@ -61,8 +44,8 @@ export default function Swap() {
const searchParams = useSearchParams()
const router = useRouter()
const [amountIn, setAmountIn] = useState<string>('')
const [fromTokenProps, setFromTokenProps] = useState<TTokenProps>(initialBaseTokenProps)
const [toTokenProps, setToTokenProps] = useState<TTokenProps>(initialQuoteTokenProps)
const [fromTokenProps, setFromTokenProps] = useState<TTokenProps>(StaticTokens[1])
const [toTokenProps, setToTokenProps] = useState<TTokenProps>(StaticTokens[2])
const [isTokenDialogOpen, setIsTokenDialogOpen] = useState<boolean>(false)
const [maxSlippage, setMaxSlippage] = useState<number>(0.5)
const [timeLimit, setTimeLimit] = useState<string>('0')
Expand Down Expand Up @@ -151,8 +134,12 @@ export default function Swap() {
const getMintBBalance = useGetUserBalanceByMint({ mintAddress: toTokenProps.address })

// Get token prices (fallback to external API if needed)
const getMintATokenPrice = useGetCoinGeckoTokenPrice({ coinGeckoId: getCoinGeckoId(fromTokenProps.address) })
const getMintBTokenPrice = useGetCoinGeckoTokenPrice({ coinGeckoId: getCoinGeckoId(toTokenProps.address) })
const getMintATokenPrice = useGetCoinGeckoTokenPrice({
coinGeckoId: getCoinGeckoId(fromTokenProps.address)
})
const getMintBTokenPrice = useGetCoinGeckoTokenPrice({
coinGeckoId: getCoinGeckoId(toTokenProps.address)
})

// Swap execution
const executeSwapMutation = useExecuteSwap()
Expand All @@ -172,7 +159,14 @@ export default function Swap() {
canSwapLoading: canSwapQuery.isLoading,
route: swapRouteQuery.data
})
}, [amountIn, fromTokenProps.symbol, toTokenProps.symbol, swapQuoteQuery, canSwapQuery, swapRouteQuery])
}, [
amountIn,
fromTokenProps.symbol,
toTokenProps.symbol,
swapQuoteQuery,
canSwapQuery,
swapRouteQuery
])

// Computed values
const swapQuote = swapQuoteQuery.data
Expand All @@ -199,7 +193,11 @@ export default function Swap() {
const isAmountPositive = REGEX.POSITIVE_DECIMAL.test(amountIn) && userInputAmount > 0
const hasValidTokenPair = fromTokenProps.address !== toTokenProps.address
const isValid =
!isBaseTokenBalanceNotEnough && isAmountPositive && hasValidTokenPair && canSwapQuery.data === true && swapQuote
!isBaseTokenBalanceNotEnough &&
isAmountPositive &&
hasValidTokenPair &&
canSwapQuery.data === true &&
swapQuote

// Exchange rate and other computed values
const exchangeRate = swapQuote?.exchangeRate
Expand Down Expand Up @@ -258,9 +256,12 @@ export default function Swap() {
poolAddress: swapQuote.poolAddress
})

toast.success(`Swap successful! Received ${result.actualOutputAmount.toFixed(6)} ${toTokenProps.symbol}`, {
duration: 5000
})
toast.success(
`Swap successful! Received ${result.actualOutputAmount.toFixed(6)} ${toTokenProps.symbol}`,
{
duration: 5000
}
)

// Reset form
setAmountIn('')
Expand All @@ -274,7 +275,9 @@ export default function Swap() {

return (
<div className="px-[15px] flex flex-col items-center lg:space-y-14 md:space-y-9 space-y-3">
<h1 className="text-center md:text-[55px] leading-tight text-xl font-bold text-main-black">Swap Assets</h1>
<h1 className="text-center md:text-[55px] leading-tight text-xl font-bold text-main-black">
Swap Assets
</h1>

<Card className="md:w-[550px] w-full border-hover-green border-[1px] rounded-[16px] md:p-9 p-3 drop-shadow-lg">
<CardHeader className="text-center flex flex-row items-center justify-between space-y-0 p-0 md:pb-[18px] pb-3">
Expand Down Expand Up @@ -303,6 +306,7 @@ export default function Swap() {
setInputAmount={handleInputChange}
/>
<SwapItem
noCheckBalance
type="to"
tokenProps={toTokenProps}
balance={mintBBalance / Math.pow(10, toTokenProps.decimals)}
Expand Down Expand Up @@ -343,14 +347,18 @@ export default function Swap() {
{/* Error state */}
{swapQuoteQuery.error && (
<p className="text-xs text-red-500">
Error: {swapQuoteQuery.error instanceof Error ? swapQuoteQuery.error.message : 'Failed to get quote'}
Error:{' '}
{swapQuoteQuery.error instanceof Error
? swapQuoteQuery.error.message
: 'Failed to get quote'}
</p>
)}

{/* Pool information */}
{swapQuote && (
<p className="text-xs text-dark-grey">
Pool TVL: <span className="text-main-black">${swapQuote.poolTvl.toLocaleString()}</span>
Pool TVL:{' '}
<span className="text-main-black">${swapQuote.poolTvl.toLocaleString()}</span>
</p>
)}

Expand All @@ -363,7 +371,9 @@ export default function Swap() {

{/* Warning for no liquidity */}
{canSwapQuery.data === false && (
<p className="text-xs text-red-500">No liquidity pool available for this token pair</p>
<p className="text-xs text-red-500">
No liquidity pool available for this token pair
</p>
)}

{/* Input validation warnings */}
Expand Down Expand Up @@ -391,7 +401,10 @@ export default function Swap() {
className={cn(
'text-main-black',
swapQuote?.priceImpact && swapQuote.priceImpact > 5 && 'text-red-500',
swapQuote?.priceImpact && swapQuote.priceImpact > 1 && swapQuote.priceImpact <= 5 && 'text-yellow-500'
swapQuote?.priceImpact &&
swapQuote.priceImpact > 1 &&
swapQuote.priceImpact <= 5 &&
'text-yellow-500'
)}
>
{priceImpact}
Expand All @@ -416,7 +429,9 @@ export default function Swap() {
!isValid && 'hover:cursor-not-allowed'
)}
>
{(isSwapQuoteLoading || executeSwapMutation.isPending) && <Loader2 className="animate-spin mr-2" />}
{(isSwapQuoteLoading || executeSwapMutation.isPending) && (
<Loader2 className="animate-spin mr-2" />
)}
{executeSwapMutation.isPending
? 'Swapping...'
: isSwapQuoteLoading
Expand Down
124 changes: 124 additions & 0 deletions src/app/(walletConnected)/wrapping/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
'use client'

import { useEffect, useState } from 'react'
import toast from 'react-hot-toast'

import { balanceFormater } from '@/components/common/WalletButton'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import WrapBalanceItem from '@/features/wrapping/components/WrapBalanceItem'
import WrapContent from '@/features/wrapping/components/WrapContentCard'
import { useGetWBBABalance, useUnwrapBBA, useWrapBBA } from '@/features/wrapping/services'
import { useGetBalance } from '@/services/wallet'
import StaticTokens from '@/staticData/tokens'

export default function Wrapping() {
const wrapBBAMutation = useWrapBBA()
const unwrapWBBAMutation = useUnwrapBBA()
const getBBABalance = useGetBalance()
const getWBBABalance = useGetWBBABalance()
const BBABalance = balanceFormater(getBBABalance.data ?? 0)
const WBBABalance = (getWBBABalance.data?.balance ?? 0) / Math.pow(10, StaticTokens[0].decimals)
const [inputAmount, setInputAmount] = useState<string>('')

const isAmountPositive = Number(inputAmount) >= 0

const generalInvalid = inputAmount === '' || Number(inputAmount) <= 0 || !isAmountPositive
const isWrapInvalid = generalInvalid || Number(inputAmount) > BBABalance
const isUnwrapInvalid = generalInvalid || Number(inputAmount) > WBBABalance

const onWrapBBA = () => wrapBBAMutation.mutate({ amount: Number(inputAmount) })
const onUnwrapWBBA = () => unwrapWBBAMutation.mutate({ amount: Number(inputAmount) })

useEffect(() => {
if (wrapBBAMutation.isSuccess && wrapBBAMutation.data) {
setInputAmount('')
toast.success(wrapBBAMutation.data.message)
}
}, [wrapBBAMutation.data, wrapBBAMutation.isSuccess])

useEffect(() => {
if (unwrapWBBAMutation.isSuccess && unwrapWBBAMutation.data) {
setInputAmount('')
toast.success(unwrapWBBAMutation.data.message)
}
}, [unwrapWBBAMutation.data, unwrapWBBAMutation.isSuccess])

return (
<div className="xl:w-5/6 md:w-11/12 mx-auto md:px-0 px-[15px] flex flex-col md:space-y-14 space-y-6">
<section className="flex flex-col text-center space-y-2">
<h2 className="font-bold lg:text-[45px] md:text-3xl text-xl text-main-black">
BBA Wrapping
</h2>
<h5 className="font-normal lg:text-lg md:text-sm text-xs text-dark-grey">
Easily convert between BBA and WBBA. Use WBBA for swaps and liquidity pools.
</h5>
</section>
<section className="flex justify-between md:flex-row md:space-x-6 flex-col md:space-y-0 space-y-3 w-full items-center">
<WrapBalanceItem
type="BBA"
balance={BBABalance ?? 0}
isLoading={getBBABalance.isLoading || getBBABalance.isRefetching}
/>
<WrapBalanceItem
type="WBBA"
balance={WBBABalance ?? 0}
isLoading={getWBBABalance.isLoading || getWBBABalance.isRefetching}
/>
</section>
<section className="border border-main-green rounded-[12px] md:py-8 md:px-6 px-3 py-3">
<Tabs defaultValue="wrap" onValueChange={() => setInputAmount('')}>
<TabsList className="flex md:w-80 w-full mb-[18px] rounded-md bg-light-green p-0">
<TabsTrigger
value="wrap"
className="flex-1 w-full rounded-md px-6 py-2 text-sm font-normal
data-[state=active]:bg-main-green
hover:bg-main-green
data-[state=active]:text-main-white
data-[state=inactive]:text-light-grey
hover:!text-main-white"
>
Wrap
</TabsTrigger>
<TabsTrigger
value="unwrap"
className="flex-1 w-full rounded-md px-6 py-2 text-sm font-normal
data-[state=active]:bg-main-green
hover:bg-main-green
data-[state=active]:text-main-white
data-[state=inactive]:text-light-grey
hover:!text-main-white"
>
Unwrap
</TabsTrigger>
</TabsList>
<TabsContent value="wrap">
<WrapContent
base="BBA"
target="WBBA"
baseBalance={BBABalance ?? 0}
targetBalance={WBBABalance ?? 0}
inputAmount={inputAmount}
setInputAmount={setInputAmount}
isInvalid={isWrapInvalid}
isLoading={wrapBBAMutation.isPending}
onAction={onWrapBBA}
/>
</TabsContent>
<TabsContent value="unwrap">
<WrapContent
base="WBBA"
target="BBA"
baseBalance={WBBABalance ?? 0}
targetBalance={BBABalance ?? 0}
inputAmount={inputAmount}
setInputAmount={setInputAmount}
isInvalid={isUnwrapInvalid}
isLoading={unwrapWBBAMutation.isPending}
onAction={onUnwrapWBBA}
/>
</TabsContent>
</Tabs>
</section>
</div>
)
}
2 changes: 1 addition & 1 deletion src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ body {
--secondary-light-yellow: 35 54% 19%;
--secondary-light-blue: 213 42% 18%;
--secondary-hover-green: 110 75% 61%;
--secondary-dark-grey: 0 0% 100%;
--secondary-dark-grey: 0 1% 64%;
--secondary-transparent-green: hsla(110, 50%, 9%, 0.36);
--secondary-strokes: 0 0% 100%;
--box: 0 0% 20%;
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/WalletButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { useIsMobile } from '@/hooks/isMobile'
import { useGetBalance } from '@/services/wallet'
import { useWalletListDialog } from '@/stores/walletDialog'

const balanceFormater = (balance: number) => Math.round((balance / BBA_DALTON_UNIT) * 100000) / 100000
export const balanceFormater = (balance: number) => Math.round((balance / BBA_DALTON_UNIT) * 100000) / 100000

function BalanceValue() {
const getBalanceQuery = useGetBalance()
Expand Down
16 changes: 13 additions & 3 deletions src/components/layout/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ import CustomWalletButton from '../common/WalletButton'
import { useCluster } from '../providers/ClusterProvider'
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '../ui/accordion'
import { Button } from '../ui/button'
import { Drawer, DrawerClose, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '../ui/drawer'
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerHeader,
DrawerTitle,
DrawerTrigger
} from '../ui/drawer'
import {
NavigationMenu,
NavigationMenuContent,
Expand Down Expand Up @@ -56,7 +63,10 @@ function MobileMenuDrawer() {
<RxHamburgerMenu />
</Button>
</DrawerTrigger>
<DrawerContent aria-describedby={undefined} className="h-screen rounded-none top-0 mt-0 right-0 left-auto w-4/6">
<DrawerContent
aria-describedby={undefined}
className="h-screen rounded-none top-0 mt-0 right-0 left-auto w-4/6"
>
<DrawerHeader className="p-0 m-0">
<DrawerTitle></DrawerTitle>
<DrawerClose asChild>
Expand Down Expand Up @@ -154,7 +164,7 @@ export default function Navbar() {
useEffect(() => setMounted(true), [])

return (
<nav className="2xl:px-24 md:px-20 py-3.5 flex items-center justify-between px-4 fixed !bg-main-white z-50 w-full">
<nav className="2xl:px-24 xl:px-10 md:px-20 py-3.5 flex items-center justify-between px-4 fixed !bg-main-white z-50 w-full">
<div className="flex items-center md:space-x-5 lg:space-x-[35px]">
<Link href="/">
<ThemeImage
Expand Down
9 changes: 8 additions & 1 deletion src/constants/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,20 @@ const POOL_SERVICE_KEY = {
DEPOSIT_LIQUIDITY: 'deposit-liquidity'
} as const

const WRAPPING_SERVICE_KEY = {
GET_WBBA_BALANCE: 'get-wbba-balance',
WRAP_BBA: 'wrap-bba',
UNWRAP_WBBA: 'unwrap-wbba'
} as const

const SERVICES_KEY = {
GLOBAL: GLOBAL_SERVICE_KEY,
WALLET: WALLET_SERVICE_KEY,
TOKEN: TOKEN_SERVICE_KEY,
NFT: NFT_SERVICE_KEY,
SWAP: SWAP_SERVICE_KEY,
POOL: POOL_SERVICE_KEY
POOL: POOL_SERVICE_KEY,
WRAPPING: WRAPPING_SERVICE_KEY
} as const

export default SERVICES_KEY
Loading