UI style updates (#106)
* feat: updates on the button styles * env: updated yarn.lock * fix: added account actions * fix: updated the orbs logic * fix: fixed the blur presets * feat: updated the button logic * fix: wallet modal style adjustments * fix: updated close icon * fix: fixed the close button * fix: fix types * fix: fixed the build * tidy: component cleanup * feat: added new AccountDetails component * refactor: propper usage of tailwind * refactor: imports * feat: added pages for all scenarios * fix: fix the loading component * fix: remove loading from default trade * fix: fixed the build * fix: fixed losing the provider on hotplug * tidy: remove unused code * fix: added error messages * add borrow page structure * env: enhanced debugging by restructuring the ENV object * fix: fixed the build * fix: fixed the wording on missing env variables * feat: added button hover (#112) * feat: added button hover * fix: added bg transition to primary buttons * feat: pages refactored (#111) * feat: pages refactored * fix: added loader for AccountNavigation * fix: fixed the wallet store management * fix: get rid of the walletSlice and refactor * fix: added gap to the borrow page * fix: fixed some dependencies * fix: added initClients back * fix: fixed according to feedback --------- Co-authored-by: bwvdhelm <34470358+bobthebuidlr@users.noreply.github.com>
@ -9,5 +9,7 @@ NEXT_PUBLIC_ACCOUNT_NFT=osmo1l8c3g6zy7kjhuh8d2kqyvxkw0myn4puxv0tzcdf9nwxd386r9l7
|
|||||||
NEXT_PUBLIC_ORACLE=osmo1dqz2u3c8rs5e7w5fnchsr2mpzzsxew69wtdy0aq4jsd76w7upmsstqe0s8
|
NEXT_PUBLIC_ORACLE=osmo1dqz2u3c8rs5e7w5fnchsr2mpzzsxew69wtdy0aq4jsd76w7upmsstqe0s8
|
||||||
NEXT_PUBLIC_RED_BANK=osmo1g30recyv8pfy3qd4qn3dn7plc0rn5z68y5gn32j39e96tjhthzxsw3uvvu
|
NEXT_PUBLIC_RED_BANK=osmo1g30recyv8pfy3qd4qn3dn7plc0rn5z68y5gn32j39e96tjhthzxsw3uvvu
|
||||||
NEXT_PUBLIC_CREDIT_MANAGER=osmo12hgn4jec4tftahm7spf7c2aqsqrxzzk50hkq60e89atumyu0zvys7vzxdc
|
NEXT_PUBLIC_CREDIT_MANAGER=osmo12hgn4jec4tftahm7spf7c2aqsqrxzzk50hkq60e89atumyu0zvys7vzxdc
|
||||||
|
NEXT_PUBLIC_INCENTIVES=osmo1zxs8fry3m8j94pqg7h4muunyx86en27cl0xgk76fc839xg2qnn6qtpjs48
|
||||||
NEXT_PUBLIC_ZAPPER=osmo1ua8dwc9v8qjh7n3qf8kg6xvrwjm5yu9xxln7yjvgmrvfzaxvzsuqfcdnjq
|
NEXT_PUBLIC_ZAPPER=osmo1ua8dwc9v8qjh7n3qf8kg6xvrwjm5yu9xxln7yjvgmrvfzaxvzsuqfcdnjq
|
||||||
NEXT_PUBLIC_SWAPPER=osmo1uj6r9tu440wwp2mhtagh48yvmeyeaqt2xa7kdnlhyrqcuthlj4ss7ghg6n
|
NEXT_PUBLIC_SWAPPER=osmo1uj6r9tu440wwp2mhtagh48yvmeyeaqt2xa7kdnlhyrqcuthlj4ss7ghg6n
|
||||||
|
NEXT_PUBLIC_API=http://localhost:3000/api
|
@ -39,7 +39,7 @@
|
|||||||
"recharts": "^2.2.0",
|
"recharts": "^2.2.0",
|
||||||
"sass": "^1.58.3",
|
"sass": "^1.58.3",
|
||||||
"swr": "^2.0.3",
|
"swr": "^2.0.3",
|
||||||
"tailwindcss-border-gradient-radius": "^3.0.1",
|
"tailwind-scrollbar-hide": "^1.1.7",
|
||||||
"use-local-storage-state": "^18.1.1",
|
"use-local-storage-state": "^18.1.1",
|
||||||
"zustand": "^4.1.4"
|
"zustand": "^4.1.4"
|
||||||
},
|
},
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export default function laoding() {
|
|
||||||
return '...isLoading'
|
|
||||||
}
|
|
@ -1,7 +1,5 @@
|
|||||||
import { getBorrowData } from 'utils/api'
|
import BorrowPage from 'components/pages/borrow'
|
||||||
|
|
||||||
export default async function page() {
|
export default async function page({ params }: PageProps) {
|
||||||
const borrowData = await getBorrowData()
|
return <BorrowPage params={params} />
|
||||||
|
|
||||||
return `You are a guest`
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
export default function page() {
|
import CouncilPage from 'components/pages/council'
|
||||||
return `You are a guest`
|
|
||||||
|
export default async function page({ params }: PageProps) {
|
||||||
|
return <CouncilPage params={params} />
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
export default function page() {
|
import EarnPage from 'components/pages/earn'
|
||||||
return `You are a guest`
|
|
||||||
|
export default async function page({ params }: PageProps) {
|
||||||
|
return <EarnPage params={params} />
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import classNames from 'classnames'
|
||||||
|
|
||||||
|
import AccountDetails from 'components/Account/AccountDetails'
|
||||||
import Background from 'components/Background'
|
import Background from 'components/Background'
|
||||||
import FetchPrices from 'components/FetchPrices'
|
import FetchPrices from 'components/FetchPrices'
|
||||||
import { Modals } from 'components/Modals'
|
import { Modals } from 'components/Modals'
|
||||||
@ -9,22 +12,25 @@ import 'styles/globals.scss'
|
|||||||
|
|
||||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<html lang='en'>
|
<html className='m-0 p-0' lang='en'>
|
||||||
<head />
|
<head />
|
||||||
|
<body className='m-0 cursor-default bg-body p-0 font-sans text-white'>
|
||||||
<body>
|
<WalletConnectProvider>
|
||||||
<div className='relative min-h-screen w-full'>
|
<Background />
|
||||||
<WalletConnectProvider>
|
<DesktopNavigation />
|
||||||
<Background />
|
</WalletConnectProvider>
|
||||||
<DesktopNavigation />
|
<FetchPrices />
|
||||||
</WalletConnectProvider>
|
<main
|
||||||
<Modals />
|
className={classNames(
|
||||||
<Toaster />
|
'relative flex justify-center py-6',
|
||||||
<FetchPrices />
|
'lg:mt-[65px] lg:h-[calc(100vh-65px)]',
|
||||||
<main className='relative flex lg:min-h-[calc(100vh-120px)]'>
|
)}
|
||||||
<div className='flex flex-grow flex-col flex-wrap'>{children}</div>
|
>
|
||||||
</main>
|
<div className='flex max-w-content flex-grow flex-col flex-wrap'>{children}</div>
|
||||||
</div>
|
<AccountDetails />
|
||||||
|
</main>
|
||||||
|
<Modals />
|
||||||
|
<Toaster />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
)
|
)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
export default function page() {
|
import TradePage from 'components/pages/trade'
|
||||||
return 'Connect to your wallet'
|
|
||||||
|
export default async function page({ params }: PageProps) {
|
||||||
|
return <TradePage params={params} />
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
export default function page() {
|
import PortfolioPage from 'components/pages/portfolio'
|
||||||
return `You are a guest`
|
|
||||||
|
export default async function page({ params }: PageProps) {
|
||||||
|
return <PortfolioPage params={params} />
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
export default function page() {
|
import TradePage from 'components/pages/trade'
|
||||||
return `You are a guest`
|
|
||||||
|
export default async function page({ params }: PageProps) {
|
||||||
|
return <TradePage params={params} />
|
||||||
}
|
}
|
||||||
|
@ -1,45 +1,5 @@
|
|||||||
import { BorrowTable } from 'components/Borrow/BorrowTable'
|
import BorrowPage from 'components/pages/borrow'
|
||||||
import { Card } from 'components/Card'
|
|
||||||
import { getAccountDebts, getBorrowData } from 'utils/api'
|
|
||||||
import { getMarketAssets } from 'utils/assets'
|
|
||||||
|
|
||||||
export default async function page({ params }: { params: PageParams }) {
|
export default async function page({ params }: PageProps) {
|
||||||
const debtData = await getAccountDebts(params.account)
|
return <BorrowPage params={params} />
|
||||||
const borrowData = await getBorrowData()
|
|
||||||
|
|
||||||
const marketAssets = getMarketAssets()
|
|
||||||
|
|
||||||
const { available, active } = marketAssets.reduce(
|
|
||||||
(prev: { available: BorrowAsset[]; active: BorrowAssetActive[] }, curr) => {
|
|
||||||
const borrow = borrowData.find((borrow) => borrow.denom === curr.denom)
|
|
||||||
if (borrow) {
|
|
||||||
const debt = debtData.find((debt) => debt.denom === curr.denom)
|
|
||||||
if (debt) {
|
|
||||||
prev.active.push({
|
|
||||||
...borrow,
|
|
||||||
debt: debt.amount,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
prev.available.push(borrow)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return prev
|
|
||||||
},
|
|
||||||
{ available: [], active: [] },
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='flex w-full flex-col'>
|
|
||||||
{active.length > 0 && (
|
|
||||||
<Card title='Borrowings'>
|
|
||||||
<BorrowTable data={active} />
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
{available.length > 0 && (
|
|
||||||
<Card title='Available to borrow'>
|
|
||||||
<BorrowTable data={available} />
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export default function loading() {
|
|
||||||
return '...isLoading'
|
|
||||||
}
|
|
@ -1,11 +1,5 @@
|
|||||||
import { Card } from 'components/Card'
|
import CouncilPage from 'components/pages/council'
|
||||||
|
|
||||||
export default function page() {
|
export default async function page({ params }: PageProps) {
|
||||||
return (
|
return <CouncilPage params={params} />
|
||||||
<div className='flex w-full'>
|
|
||||||
<Card title='Council'>
|
|
||||||
<></>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export default function loading() {
|
|
||||||
return '...isLoading'
|
|
||||||
}
|
|
@ -1,12 +1,5 @@
|
|||||||
import { Card } from 'components/Card'
|
import EarnPage from 'components/pages/earn'
|
||||||
import { Text } from 'components/Text'
|
|
||||||
|
|
||||||
export default function page() {
|
export default async function page({ params }: PageProps) {
|
||||||
return (
|
return <EarnPage params={params} />
|
||||||
<div className='flex w-full gap-4'>
|
|
||||||
<Card title='Yield'>
|
|
||||||
<></>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
export default function page() {
|
import TradePage from 'components/pages/trade'
|
||||||
return 'Trade page'
|
|
||||||
|
export default async function page({ params }: PageProps) {
|
||||||
|
return <TradePage params={params} />
|
||||||
}
|
}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
export default function page({ params }: { params: PageParams }) {
|
|
||||||
return 'error!'
|
|
||||||
}
|
|
@ -1,15 +1,5 @@
|
|||||||
import { getCreditAccounts } from 'utils/api'
|
import PortfolioPage from 'components/pages/portfolio'
|
||||||
|
|
||||||
export default async function page({ params }: { params: PageParams }) {
|
export default async function page({ params }: PageProps) {
|
||||||
const creditAccounts = await getCreditAccounts(params.wallet)
|
return <PortfolioPage params={params} />
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='flex w-full items-start gap-4'>
|
|
||||||
<ul>
|
|
||||||
{creditAccounts.map((account: string, index: number) => (
|
|
||||||
<li key={index}>{account}</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export default function loading() {
|
|
||||||
return '...isLoading'
|
|
||||||
}
|
|
@ -1,21 +1,5 @@
|
|||||||
import { Card } from 'components/Card'
|
import TradePage from 'components/pages/trade'
|
||||||
|
|
||||||
export default function page() {
|
export default async function page({ params }: PageProps) {
|
||||||
return (
|
return <TradePage params={params} />
|
||||||
<div className='flex w-full flex-wrap'>
|
|
||||||
<div className='mb-4 flex flex-grow gap-4'>
|
|
||||||
<Card title='TradingView graph' className='flex-1'>
|
|
||||||
<></>
|
|
||||||
</Card>
|
|
||||||
<div className='flex flex-col gap-4'>
|
|
||||||
<Card title='Orderbook module'>
|
|
||||||
<></>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Card title='Order history'>
|
|
||||||
<></>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export default function page() {
|
|
||||||
return `You are a viewer or a user`
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
export default function page() {
|
|
||||||
return `You are a viewer or a user`
|
|
||||||
}
|
|
@ -1,3 +1,5 @@
|
|||||||
export default function page() {
|
import BorrowPage from 'components/pages/borrow'
|
||||||
return `You are a viewer or a user`
|
|
||||||
|
export default async function page({ params }: PageProps) {
|
||||||
|
return <BorrowPage params={params} />
|
||||||
}
|
}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export default function page() {
|
|
||||||
return `You are a viewer or a user`
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
export default function page() {
|
|
||||||
return `You are a viewer or a user`
|
|
||||||
}
|
|
@ -1,3 +1,5 @@
|
|||||||
export default function page() {
|
import CouncilPage from 'components/pages/council'
|
||||||
return `You are a viewer or a user`
|
|
||||||
|
export default async function page({ params }: PageProps) {
|
||||||
|
return <CouncilPage params={params} />
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
export default function page() {
|
import EarnPage from 'components/pages/earn'
|
||||||
return `You are a viewer or a user`
|
|
||||||
|
export default async function page({ params }: PageProps) {
|
||||||
|
return <EarnPage params={params} />
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,3 @@
|
|||||||
import { AccountNavigation } from 'components/Account/AccountNavigation'
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||||
import { getCreditAccounts } from 'utils/api'
|
return children
|
||||||
|
|
||||||
export default async function RootLayout({
|
|
||||||
children,
|
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
params: PageParams
|
|
||||||
}) {
|
|
||||||
const creditAccounts = await getCreditAccounts(params.wallet)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className='relative hidden bg-header lg:block'>
|
|
||||||
<AccountNavigation creditAccounts={creditAccounts} />
|
|
||||||
</div>
|
|
||||||
<main className='p-4'>{children}</main>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
export default function page() {
|
import PortfolioPage from 'components/pages/portfolio'
|
||||||
return `You are a viewer or a user`
|
|
||||||
|
export default async function page({ params }: PageProps) {
|
||||||
|
return <PortfolioPage params={params} />
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
export default function page() {
|
import TradePage from 'components/pages/trade'
|
||||||
return `You are a viewer or a user`
|
|
||||||
|
export default async function page({ params }: PageProps) {
|
||||||
|
return <TradePage params={params} />
|
||||||
}
|
}
|
||||||
|
@ -1,121 +1,40 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
import { Gauge } from 'components/Gauge'
|
||||||
|
import { Heart } from 'components/Icons'
|
||||||
|
import { Text } from 'components/Text'
|
||||||
|
import useParams from 'hooks/useParams'
|
||||||
|
|
||||||
import classNames from 'classnames'
|
export default function AccountDetails() {
|
||||||
import { useEffect, useState } from 'react'
|
const params = useParams()
|
||||||
|
const hasAccount = params.account && !isNaN(Number(params.account))
|
||||||
|
|
||||||
import { AccountManageOverlay } from 'components/Account/AccountManageOverlay'
|
return hasAccount ? (
|
||||||
import { RiskChart } from 'components/Account/RiskChart'
|
<div className='fixed top-[89px] right-4 w-16 rounded-base border border-white/20 bg-white/5 backdrop-blur-sticky'>
|
||||||
import { Button } from 'components/Button'
|
<div className='flex w-full flex-wrap justify-center py-4'>
|
||||||
import { ArrowRightLine, ChevronDown, ChevronLeft } from 'components/Icons'
|
<Gauge tooltip='Health Factor' value={0.2} icon={<Heart />} />
|
||||||
import { LabelValuePair } from 'components/LabelValuePair'
|
<Text size='2xs' className='mt-1 mb-0.5 w-full text-center text-white/50'>
|
||||||
import { PositionsList } from 'components/PositionsList'
|
Health
|
||||||
import { useAccountStats } from 'hooks/data/useAccountStats'
|
</Text>
|
||||||
import { convertFromGwei } from 'utils/formatters'
|
<Text size='xs' className='w-full text-center'>
|
||||||
import { createRiskData } from 'utils/risk'
|
89%
|
||||||
import useStore from 'store'
|
</Text>
|
||||||
import { getBaseAsset, getMarketAssets } from 'utils/assets'
|
|
||||||
|
|
||||||
export const AccountDetails = () => {
|
|
||||||
const enableAnimations = useStore((s) => s.enableAnimations)
|
|
||||||
const selectedAccount = useStore((s) => s.selectedAccount)
|
|
||||||
const isOpen = useStore((s) => s.isOpen)
|
|
||||||
const marketAssets = getMarketAssets()
|
|
||||||
const baseAsset = getBaseAsset()
|
|
||||||
|
|
||||||
const accountStats = useAccountStats()
|
|
||||||
|
|
||||||
const [showManageMenu, setShowManageMenu] = useState(false)
|
|
||||||
const [riskData, setRiskData] = useState<RiskTimePair[]>()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setRiskData(createRiskData(accountStats?.risk ?? 0))
|
|
||||||
}, [accountStats?.risk, selectedAccount])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
'relative flex w-[400px] basis-[400px] flex-wrap content-start border-l border-white/20 bg-header',
|
|
||||||
enableAnimations && 'transition-[margin] duration-1000 ease-in-out',
|
|
||||||
isOpen ? 'mr-0' : '-mr-[400px]',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
useStore.setState({ isOpen: true })
|
|
||||||
}}
|
|
||||||
variant='text'
|
|
||||||
className={classNames(
|
|
||||||
'absolute top-1/2 -left-[20px] w-[21px] -translate-y-1/2 bg-header p-0',
|
|
||||||
'rounded-none rounded-tl-sm rounded-bl-sm',
|
|
||||||
'border border-white/20',
|
|
||||||
enableAnimations && 'transition-[opacity] delay-1000 duration-500 ease-in-out',
|
|
||||||
isOpen ? 'pointer-events-none opacity-0' : 'opacity-100',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={classNames(
|
|
||||||
'flex h-20 px-1 py-6 text-white/40 hover:text-white',
|
|
||||||
enableAnimations && 'transition-[color]',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<ChevronLeft />
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
<div className='relative flex w-full flex-wrap items-center border-b border-white/20'>
|
|
||||||
<Button
|
|
||||||
variant='text'
|
|
||||||
className='flex flex-grow flex-nowrap items-center justify-center p-4 text-center text-white text-xl-caps'
|
|
||||||
onClick={() => setShowManageMenu(!showManageMenu)}
|
|
||||||
>
|
|
||||||
Account {selectedAccount}
|
|
||||||
<span className='ml-2 flex w-4'>
|
|
||||||
<ChevronDown />
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
<div className='flex border-l border-white/20' onClick={() => {}}>
|
|
||||||
<Button
|
|
||||||
variant='text'
|
|
||||||
className={classNames(
|
|
||||||
'w-14 p-4 text-white/40 hover:cursor-pointer hover:text-white',
|
|
||||||
enableAnimations && 'transition-[color]',
|
|
||||||
)}
|
|
||||||
onClick={() => {
|
|
||||||
useStore.setState({ isOpen: false })
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ArrowRightLine />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<AccountManageOverlay
|
|
||||||
className='top-[60px] left-[36px]'
|
|
||||||
show={showManageMenu}
|
|
||||||
setShow={setShowManageMenu}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='flex w-full flex-wrap p-3'>
|
<div className='w-full border border-x-0 border-white/20 py-4'>
|
||||||
<LabelValuePair
|
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50'>
|
||||||
className='mb-2'
|
Leverage
|
||||||
label='Total Position:'
|
</Text>
|
||||||
value={{
|
<Text size='xs' className='w-full text-center'>
|
||||||
format: 'number',
|
4.5x
|
||||||
amount: convertFromGwei(
|
</Text>
|
||||||
accountStats?.totalPosition ?? 0,
|
</div>
|
||||||
baseAsset.denom,
|
<div className='w-full py-4'>
|
||||||
marketAssets,
|
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50'>
|
||||||
),
|
Balance
|
||||||
prefix: '$',
|
</Text>{' '}
|
||||||
}}
|
<Text size='xs' className='w-full text-center'>
|
||||||
/>
|
$300M
|
||||||
<LabelValuePair
|
</Text>
|
||||||
label='Total Liabilities:'
|
|
||||||
value={{
|
|
||||||
format: 'number',
|
|
||||||
amount: convertFromGwei(accountStats?.totalDebt ?? 0, baseAsset.denom, marketAssets),
|
|
||||||
prefix: '$',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
{riskData && <RiskChart data={riskData} />}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
) : null
|
||||||
}
|
}
|
||||||
|
@ -1,94 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import { useRouter } from 'next/navigation'
|
|
||||||
|
|
||||||
import { Button } from 'components/Button'
|
|
||||||
import { Add, ArrowDown, ArrowsLeftRight, ArrowUp, Rubbish } from 'components/Icons'
|
|
||||||
import { Overlay } from 'components/Overlay/Overlay'
|
|
||||||
import { OverlayAction } from 'components/Overlay/OverlayAction'
|
|
||||||
import { Text } from 'components/Text'
|
|
||||||
import useParams from 'hooks/useParams'
|
|
||||||
import useStore from 'store'
|
|
||||||
import { hardcodedFee } from 'utils/contants'
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
className?: string
|
|
||||||
setShow: (show: boolean) => void
|
|
||||||
show: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AccountManageOverlay = ({ className, setShow, show }: Props) => {
|
|
||||||
const router = useRouter()
|
|
||||||
const params = useParams()
|
|
||||||
const createCreditAccount = useStore((s) => s.createCreditAccount)
|
|
||||||
const deleteCreditAccount = useStore((s) => s.deleteCreditAccount)
|
|
||||||
|
|
||||||
async function createAccount() {
|
|
||||||
const newAccountId = await createCreditAccount({ fee: hardcodedFee })
|
|
||||||
router.push(`/wallets/${params.wallet}/accounts/${newAccountId}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteAccountHandler() {
|
|
||||||
const isSuccess = await deleteCreditAccount({ fee: hardcodedFee, accountId: params.account })
|
|
||||||
if (isSuccess) {
|
|
||||||
router.push(`/wallets/${params.wallet}/accounts`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Overlay className={className} show={show} setShow={setShow}>
|
|
||||||
<div className='flex w-[274px] flex-wrap'>
|
|
||||||
<Text size='sm' uppercase={true} className='w-full px-4 pt-4 text-center text-accent-dark'>
|
|
||||||
Manage
|
|
||||||
</Text>
|
|
||||||
<div className='flex w-full justify-between border-b border-b-black/10 p-4'>
|
|
||||||
<Button
|
|
||||||
className='flex w-[115px] items-center justify-center pl-0 pr-2'
|
|
||||||
onClick={() => {
|
|
||||||
useStore.setState({ fundAccountModal: true })
|
|
||||||
setShow(false)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span className='mr-1 w-3'>
|
|
||||||
<ArrowUp />
|
|
||||||
</span>
|
|
||||||
Fund
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
className='flex w-[115px] items-center justify-center pl-0 pr-2'
|
|
||||||
color='secondary'
|
|
||||||
onClick={() => {
|
|
||||||
useStore.setState({ withdrawModal: true })
|
|
||||||
setShow(false)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span className='mr-1 w-3'>
|
|
||||||
<ArrowDown />
|
|
||||||
</span>
|
|
||||||
Withdraw
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className='flex w-full flex-wrap p-4'>
|
|
||||||
<OverlayAction
|
|
||||||
setShow={setShow}
|
|
||||||
text='Create New Account'
|
|
||||||
onClick={createAccount}
|
|
||||||
icon={<Add />}
|
|
||||||
/>
|
|
||||||
<OverlayAction
|
|
||||||
setShow={setShow}
|
|
||||||
text='Close Account'
|
|
||||||
onClick={deleteAccountHandler}
|
|
||||||
icon={<Rubbish />}
|
|
||||||
/>
|
|
||||||
<OverlayAction
|
|
||||||
setShow={setShow}
|
|
||||||
text='Transfer Balance'
|
|
||||||
onClick={() => alert('TODO')}
|
|
||||||
icon={<ArrowsLeftRight />}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Overlay>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,36 +1,38 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import classNames from 'classnames'
|
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
import { AccountManageOverlay } from 'components/Account/AccountManageOverlay'
|
|
||||||
import { Button } from 'components/Button'
|
import { Button } from 'components/Button'
|
||||||
import { ChevronDown } from 'components/Icons'
|
import {
|
||||||
|
Account,
|
||||||
|
Add,
|
||||||
|
ArrowDownLine,
|
||||||
|
ArrowsLeftRight,
|
||||||
|
ArrowUpLine,
|
||||||
|
Rubbish,
|
||||||
|
} from 'components/Icons'
|
||||||
|
import Loading from 'components/Loading'
|
||||||
import { Overlay } from 'components/Overlay/Overlay'
|
import { Overlay } from 'components/Overlay/Overlay'
|
||||||
|
import { Text } from 'components/Text'
|
||||||
import useParams from 'hooks/useParams'
|
import useParams from 'hooks/useParams'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import { hardcodedFee } from 'utils/contants'
|
import { hardcodedFee } from 'utils/contants'
|
||||||
|
|
||||||
const MAX_VISIBLE_CREDIT_ACCOUNTS = 5
|
export const AccountNavigation = () => {
|
||||||
|
|
||||||
interface Props {
|
|
||||||
creditAccounts: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AccountNavigation = (props: Props) => {
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const address = useStore((s) => s.client?.recentWallet.account?.address) || ''
|
|
||||||
const selectedAccount = params.account
|
const selectedAccount = params.account
|
||||||
|
|
||||||
const createCreditAccount = useStore((s) => s.createCreditAccount)
|
const createCreditAccount = useStore((s) => s.createCreditAccount)
|
||||||
|
const deleteCreditAccount = useStore((s) => s.deleteCreditAccount)
|
||||||
|
const creditAccounts = useStore((s) => s.creditAccounts)
|
||||||
|
const address = useStore((s) => s.address)
|
||||||
|
|
||||||
const hasCreditAccounts = !!props.creditAccounts?.length
|
const hasCreditAccounts = !!creditAccounts?.length
|
||||||
const firstCreditAccounts = props.creditAccounts?.slice(0, MAX_VISIBLE_CREDIT_ACCOUNTS) ?? []
|
const accountSelected = !!selectedAccount && !isNaN(Number(selectedAccount))
|
||||||
const restCreditAccounts = props.creditAccounts?.slice(MAX_VISIBLE_CREDIT_ACCOUNTS) ?? []
|
|
||||||
|
|
||||||
const [showManageMenu, setShowManageMenu] = useState(false)
|
const [showMenu, setShowMenu] = useState(false)
|
||||||
const [showMoreMenu, setShowMoreMenu] = useState(false)
|
|
||||||
|
|
||||||
async function createAccountHandler() {
|
async function createAccountHandler() {
|
||||||
const accountId = await createCreditAccount({ fee: hardcodedFee })
|
const accountId = await createCreditAccount({ fee: hardcodedFee })
|
||||||
@ -38,93 +40,128 @@ export const AccountNavigation = (props: Props) => {
|
|||||||
router.push(`/wallets/${params.wallet}/accounts/${accountId}`)
|
router.push(`/wallets/${params.wallet}/accounts/${accountId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
async function deleteAccountHandler() {
|
||||||
<section
|
if (!accountSelected) return
|
||||||
role='navigation'
|
const isSuccess = await deleteCreditAccount({ fee: hardcodedFee, accountId: selectedAccount })
|
||||||
className='flex h-11 w-full items-center gap-6 border-b border-white/20 px-6 text-sm text-white/40'
|
if (isSuccess) {
|
||||||
>
|
router.push(`/wallets/${params.wallet}/accounts`)
|
||||||
<>
|
}
|
||||||
{hasCreditAccounts ? (
|
}
|
||||||
<>
|
|
||||||
{firstCreditAccounts.map((account) => (
|
return !address ? null : (
|
||||||
<Button
|
<>
|
||||||
key={account}
|
{creditAccounts === null ? (
|
||||||
className={classNames(
|
<Loading className='h-8 w-35' />
|
||||||
'cursor-pointer whitespace-nowrap px-4 text-base hover:text-white',
|
) : (
|
||||||
selectedAccount === account ? 'text-white' : 'text-white/40',
|
<>
|
||||||
)}
|
{' '}
|
||||||
variant='text'
|
{hasCreditAccounts ? (
|
||||||
onClick={() => {
|
|
||||||
router.push(`/wallets/${params.wallet}/accounts/${account}/${params.page}`)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Account {account}
|
|
||||||
</Button>
|
|
||||||
))}
|
|
||||||
<div className='relative'>
|
<div className='relative'>
|
||||||
{restCreditAccounts.length > 0 && (
|
<Button
|
||||||
<>
|
variant='solid'
|
||||||
<Button
|
color='tertiary'
|
||||||
className='flex items-center px-3 py-3 text-base hover:text-white'
|
className='flex flex-1 flex-nowrap'
|
||||||
variant='text'
|
icon={<Account />}
|
||||||
onClick={() => setShowMoreMenu(!showMoreMenu)}
|
onClick={() => setShowMenu(!showMenu)}
|
||||||
>
|
hasSubmenu
|
||||||
More
|
>
|
||||||
<span className='ml-1 inline-block w-3'>
|
<span>{accountSelected ? `Account ${selectedAccount}` : 'Select Account'}</span>
|
||||||
<ChevronDown />
|
</Button>
|
||||||
</span>
|
<Overlay className='l-0 mt-2 w-[274px]' show={showMenu} setShow={setShowMenu}>
|
||||||
</Button>
|
{accountSelected && (
|
||||||
<Overlay show={showMoreMenu} setShow={setShowMoreMenu}>
|
<div className='flex w-full flex-wrap'>
|
||||||
<div className='flex w-[120px] flex-wrap p-4'>
|
<Text size='sm' uppercase={true} className='w-full justify-center px-4 pt-4'>
|
||||||
{restCreditAccounts.map((account) => (
|
Manage Account {selectedAccount}
|
||||||
|
</Text>
|
||||||
|
<div className='flex w-full justify-between p-4'>
|
||||||
|
<Button
|
||||||
|
className='flex w-[115px] items-center justify-center pl-0 pr-2'
|
||||||
|
text='Fund'
|
||||||
|
icon={<ArrowUpLine />}
|
||||||
|
onClick={() => {
|
||||||
|
useStore.setState({ fundAccountModal: true })
|
||||||
|
setShowMenu(false)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className='flex w-[115px] items-center justify-center pl-0 pr-2'
|
||||||
|
color='secondary'
|
||||||
|
icon={<ArrowDownLine />}
|
||||||
|
text='Withdraw'
|
||||||
|
onClick={() => {
|
||||||
|
useStore.setState({ withdrawModal: true })
|
||||||
|
setShowMenu(false)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='flex w-full flex-wrap border-t border-t-white/10 p-4'>
|
||||||
|
<Button
|
||||||
|
className='w-full whitespace-nowrap py-2'
|
||||||
|
variant='transparent'
|
||||||
|
color='quaternary'
|
||||||
|
text='Create New Account'
|
||||||
|
onClick={() => {
|
||||||
|
setShowMenu(false)
|
||||||
|
createAccountHandler()
|
||||||
|
}}
|
||||||
|
icon={<Add />}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className='w-full whitespace-nowrap py-2'
|
||||||
|
variant='transparent'
|
||||||
|
color='quaternary'
|
||||||
|
text='Close Account'
|
||||||
|
onClick={() => {
|
||||||
|
setShowMenu(false)
|
||||||
|
deleteAccountHandler()
|
||||||
|
}}
|
||||||
|
icon={<Rubbish />}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className='w-full whitespace-nowrap py-2'
|
||||||
|
variant='transparent'
|
||||||
|
color='quaternary'
|
||||||
|
text='Transfer Balance'
|
||||||
|
onClick={() => {
|
||||||
|
setShowMenu(false)
|
||||||
|
/* TODO: add Transfer Balance Function */
|
||||||
|
}}
|
||||||
|
icon={<ArrowsLeftRight />}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{creditAccounts.length > 1 && (
|
||||||
|
<div className='flex w-full flex-wrap border-t border-t-white/10 p-4'>
|
||||||
|
<Text size='sm' uppercase={true} className='w-full justify-center pb-2'>
|
||||||
|
Select Account
|
||||||
|
</Text>
|
||||||
|
{creditAccounts.map((account) =>
|
||||||
|
selectedAccount === account ? null : (
|
||||||
<Button
|
<Button
|
||||||
key={account}
|
key={account}
|
||||||
variant='text'
|
className='w-full whitespace-nowrap py-2'
|
||||||
className={classNames(
|
variant='transparent'
|
||||||
'w-full whitespace-nowrap py-2 text-sm',
|
color='quaternary'
|
||||||
selectedAccount === account
|
|
||||||
? 'text-secondary'
|
|
||||||
: 'cursor-pointer text-accent-dark hover:text-secondary',
|
|
||||||
)}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowMoreMenu(!showMoreMenu)
|
|
||||||
router.push(`/wallets/${params.wallet}/accounts/${account}`)
|
router.push(`/wallets/${params.wallet}/accounts/${account}`)
|
||||||
|
setShowMenu(!showMenu)
|
||||||
}}
|
}}
|
||||||
>
|
text={`Account ${account}`}
|
||||||
Account {account}
|
/>
|
||||||
</Button>
|
),
|
||||||
))}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Overlay>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className='relative'>
|
|
||||||
<Button
|
|
||||||
className={classNames(
|
|
||||||
'flex items-center px-3 py-3 text-base hover:text-white',
|
|
||||||
showManageMenu ? 'text-white' : 'text-white/40',
|
|
||||||
)}
|
)}
|
||||||
onClick={() => setShowManageMenu(!showManageMenu)}
|
</Overlay>
|
||||||
variant='text'
|
|
||||||
>
|
|
||||||
Manage
|
|
||||||
<span className='ml-1 inline-block w-3'>
|
|
||||||
<ChevronDown />
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<AccountManageOverlay
|
|
||||||
className='-left-[86px]'
|
|
||||||
show={showManageMenu}
|
|
||||||
setShow={setShowManageMenu}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
) : (
|
||||||
) : (
|
<Button onClick={createAccountHandler} icon={<Add />} color='tertiary'>
|
||||||
<>{address ? <Button onClick={createAccountHandler}>Create Account</Button> : ''}</>
|
Create Account
|
||||||
)}
|
</Button>
|
||||||
</>
|
)}
|
||||||
</section>
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import BigNumber from 'bignumber.js'
|
|
||||||
|
|
||||||
import { BorrowCapacity } from 'components/BorrowCapacity'
|
|
||||||
import { Button } from 'components/Button'
|
|
||||||
import { FormattedNumber } from 'components/FormattedNumber'
|
|
||||||
import { Gauge } from 'components/Gauge'
|
|
||||||
import { Text } from 'components/Text'
|
|
||||||
import { useAccountStats } from 'hooks/data/useAccountStats'
|
|
||||||
import { useCreditAccounts } from 'hooks/queries/useCreditAccounts'
|
|
||||||
import { getBaseAsset } from 'utils/assets'
|
|
||||||
import { formatLeverage, formatValue } from 'utils/formatters'
|
|
||||||
|
|
||||||
export const AccountStatus = () => {
|
|
||||||
const baseAsset = getBaseAsset()
|
|
||||||
const accountStats = useAccountStats()
|
|
||||||
const { data: creditAccountsList } = useCreditAccounts()
|
|
||||||
|
|
||||||
const createCreditAccount = () => {
|
|
||||||
console.log('create credit account')
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasCreditAccounts = creditAccountsList && creditAccountsList.length > 0
|
|
||||||
|
|
||||||
if (!hasCreditAccounts) {
|
|
||||||
return (
|
|
||||||
<Button className='my-3 mr-6' onClick={() => createCreditAccount()}>
|
|
||||||
Create Credit Account
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='flex w-[400px] items-center justify-between gap-3 border-l border-l-white/20 px-3 py-3'>
|
|
||||||
{accountStats && (
|
|
||||||
<>
|
|
||||||
<Text size='sm' className='flex flex-grow text-white'>
|
|
||||||
<FormattedNumber
|
|
||||||
amount={BigNumber(accountStats.netWorth)
|
|
||||||
.dividedBy(10 ** baseAsset.decimals)
|
|
||||||
.toNumber()}
|
|
||||||
animate
|
|
||||||
options={{ prefix: '$: ' }}
|
|
||||||
/>
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<Gauge
|
|
||||||
value={accountStats.currentLeverage / accountStats.maxLeverage}
|
|
||||||
label='Lvg'
|
|
||||||
tooltip={
|
|
||||||
<Text size='sm'>
|
|
||||||
Current Leverage: {formatLeverage(accountStats.currentLeverage)}
|
|
||||||
<br />
|
|
||||||
Max Leverage: {formatLeverage(accountStats.maxLeverage)}
|
|
||||||
</Text>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Gauge
|
|
||||||
value={accountStats.risk}
|
|
||||||
label='Risk'
|
|
||||||
tooltip={
|
|
||||||
<Text size='sm'>
|
|
||||||
Current Risk:{' '}
|
|
||||||
{formatValue(accountStats.risk * 100, { minDecimals: 0, suffix: '%' })}
|
|
||||||
</Text>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<BorrowCapacity
|
|
||||||
limit={80}
|
|
||||||
max={100}
|
|
||||||
balance={100 - accountStats.health * 100}
|
|
||||||
barHeight='16px'
|
|
||||||
hideValues={true}
|
|
||||||
showTitle={false}
|
|
||||||
className='w-[140px]'
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -66,7 +66,7 @@ export const FundAccountModal = () => {
|
|||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [marketAssets, balancesData])
|
}, [balancesData, marketAssets, selectedToken])
|
||||||
|
|
||||||
// ---------------
|
// ---------------
|
||||||
// VARIABLES
|
// VARIABLES
|
||||||
@ -130,7 +130,7 @@ export const FundAccountModal = () => {
|
|||||||
your osmosis wallet.
|
your osmosis wallet.
|
||||||
</Text>
|
</Text>
|
||||||
<>
|
<>
|
||||||
<div className='mb-4 rounded-md border border-white/20'>
|
<div className='mb-4 rounded-base border border-white/20'>
|
||||||
<div className='mb-1 flex justify-between border-b border-white/20 p-2'>
|
<div className='mb-1 flex justify-between border-b border-white/20 p-2'>
|
||||||
<Text size='sm' className='text-white'>
|
<Text size='sm' className='text-white'>
|
||||||
Asset:
|
Asset:
|
||||||
|
@ -2,20 +2,16 @@ import { Switch } from '@headlessui/react'
|
|||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import React, { useEffect, useMemo, useState } from 'react'
|
import React, { useEffect, useMemo, useState } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
|
||||||
|
|
||||||
import { BorrowCapacity } from 'components/BorrowCapacity'
|
import { BorrowCapacity } from 'components/BorrowCapacity'
|
||||||
import { convertFromGwei, formatLeverage, formatValue } from 'utils/formatters'
|
|
||||||
import { getTokenDecimals, getTokenSymbol } from 'utils/tokens'
|
|
||||||
import { CircularProgress } from 'components/CircularProgress'
|
|
||||||
import { Button } from 'components/Button'
|
import { Button } from 'components/Button'
|
||||||
import { Text } from 'components/Text'
|
import { CircularProgress } from 'components/CircularProgress'
|
||||||
import { Slider } from 'components/Slider'
|
|
||||||
import { FormattedNumber } from 'components/FormattedNumber'
|
import { FormattedNumber } from 'components/FormattedNumber'
|
||||||
import { Gauge } from 'components/Gauge'
|
import { Gauge } from 'components/Gauge'
|
||||||
import { LabelValuePair } from 'components/LabelValuePair'
|
import { LabelValuePair } from 'components/LabelValuePair'
|
||||||
import { Modal } from 'components/Modal'
|
import { Modal } from 'components/Modal'
|
||||||
import { PositionsList } from 'components/PositionsList'
|
import { Slider } from 'components/Slider'
|
||||||
|
import { Text } from 'components/Text'
|
||||||
import { useAccountStats } from 'hooks/data/useAccountStats'
|
import { useAccountStats } from 'hooks/data/useAccountStats'
|
||||||
import { useCalculateMaxWithdrawAmount } from 'hooks/data/useCalculateMaxWithdrawAmount'
|
import { useCalculateMaxWithdrawAmount } from 'hooks/data/useCalculateMaxWithdrawAmount'
|
||||||
import { useWithdrawFunds } from 'hooks/mutations/useWithdrawFunds'
|
import { useWithdrawFunds } from 'hooks/mutations/useWithdrawFunds'
|
||||||
@ -23,6 +19,8 @@ import { useCreditAccountPositions } from 'hooks/queries/useCreditAccountPositio
|
|||||||
import { useTokenPrices } from 'hooks/queries/useTokenPrices'
|
import { useTokenPrices } from 'hooks/queries/useTokenPrices'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import { getBaseAsset, getMarketAssets } from 'utils/assets'
|
import { getBaseAsset, getMarketAssets } from 'utils/assets'
|
||||||
|
import { convertFromGwei, formatLeverage, formatValue } from 'utils/formatters'
|
||||||
|
import { getTokenDecimals, getTokenSymbol } from 'utils/tokens'
|
||||||
|
|
||||||
export const WithdrawModal = () => {
|
export const WithdrawModal = () => {
|
||||||
// ---------------
|
// ---------------
|
||||||
@ -169,7 +167,7 @@ export const WithdrawModal = () => {
|
|||||||
<div className='flex w-full'>
|
<div className='flex w-full'>
|
||||||
<div className='flex flex-1 flex-col border-r border-white/20'>
|
<div className='flex flex-1 flex-col border-r border-white/20'>
|
||||||
<div className='border-b border-white/20 p-6'>
|
<div className='border-b border-white/20 p-6'>
|
||||||
<div className='mb-4 rounded-md border border-white/20'>
|
<div className='mb-4 rounded-base border border-white/20'>
|
||||||
<div className='mb-1 flex justify-between border-b border-white/20 p-2'>
|
<div className='mb-1 flex justify-between border-b border-white/20 p-2'>
|
||||||
<Text size='sm' className='text-white'>
|
<Text size='sm' className='text-white'>
|
||||||
Asset:
|
Asset:
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { FormattedNumber } from './FormattedNumber'
|
import { FormattedNumber } from 'components/FormattedNumber'
|
||||||
import TitleAndSubCell from './TitleAndSubCell'
|
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
asset: Asset
|
asset: Asset
|
||||||
|
@ -1,5 +1,44 @@
|
|||||||
'use client'
|
import classNames from 'classnames'
|
||||||
|
|
||||||
export default function Background() {
|
export default function Background() {
|
||||||
return <div className='background' />
|
return (
|
||||||
|
<div className='background pointer-events-none fixed inset-0 h-full w-full overflow-hidden bg-body'>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'fixed',
|
||||||
|
'h-[20vw] w-[20vw]',
|
||||||
|
'min-h-[150px] min-w-[150px]',
|
||||||
|
'max-h-[500px] max-w-[500px]',
|
||||||
|
'top-[-10vw] left-[-10vw]',
|
||||||
|
'bg-orb-primary blur-orb-primary ',
|
||||||
|
'translate-x-0 translate-y-0 rounded-full opacity-20',
|
||||||
|
'animate-[float_120s_ease_in_out_infinite_2s]',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'fixed',
|
||||||
|
'h-[40vw] w-[40vw]',
|
||||||
|
'min-h-[400px] min-w-[400px]',
|
||||||
|
'max-h-[1000px] max-w-[1000px]',
|
||||||
|
'bottom-[-10vw] right-[-8vw]',
|
||||||
|
'bg-orb-secondary blur-orb-secondary',
|
||||||
|
'translate-x-0 translate-y-0 rounded-full opacity-30',
|
||||||
|
'animate-[float_150s_bounce_out_infinite_1s]',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'fixed',
|
||||||
|
'h-[25vw] w-[25vw]',
|
||||||
|
'min-h-[120px] min-w-[120px]',
|
||||||
|
'max-h-[600px] max-w-[600px]',
|
||||||
|
'top-[-10vw] right-[-4vw]',
|
||||||
|
'bg-orb-tertiary blur-orb-tertiary ',
|
||||||
|
'translate-x-0 translate-y-0 rounded-full opacity-20',
|
||||||
|
'animate-[float_180s_ease_in_infinite]',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -8,20 +8,19 @@ import {
|
|||||||
SortingState,
|
SortingState,
|
||||||
useReactTable,
|
useReactTable,
|
||||||
} from '@tanstack/react-table'
|
} from '@tanstack/react-table'
|
||||||
|
import classNames from 'classnames'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import classNames from 'classnames'
|
|
||||||
|
|
||||||
|
import AmountAndValue from 'components/AmountAndValue'
|
||||||
import { AssetRow } from 'components/Borrow/AssetRow'
|
import { AssetRow } from 'components/Borrow/AssetRow'
|
||||||
import { ChevronDown, ChevronUp } from 'components/Icons'
|
import { ChevronDown, ChevronUp } from 'components/Icons'
|
||||||
import { getMarketAssets } from 'utils/assets'
|
|
||||||
import { Text } from 'components/Text'
|
import { Text } from 'components/Text'
|
||||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||||
import { FormattedNumber } from 'components/FormattedNumber'
|
import { getMarketAssets } from 'utils/assets'
|
||||||
import AmountAndValue from 'components/AmountAndValue'
|
|
||||||
import { formatPercent } from 'utils/formatters'
|
import { formatPercent } from 'utils/formatters'
|
||||||
|
import AssetExpanded from 'components/Borrow/AssetExpanded'
|
||||||
import AssetExpanded from './AssetExpanded'
|
import Loading from 'components/Loading'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
data: BorrowAsset[] | BorrowAssetActive[]
|
data: BorrowAsset[] | BorrowAssetActive[]
|
||||||
@ -52,11 +51,17 @@ export const BorrowTable = (props: Props) => {
|
|||||||
{
|
{
|
||||||
accessorKey: 'borrowRate',
|
accessorKey: 'borrowRate',
|
||||||
header: 'Borrow Rate',
|
header: 'Borrow Rate',
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => {
|
||||||
<Text className='justify-end' size='sm'>
|
if (row.original.borrowRate === null) {
|
||||||
{formatPercent(row.original.borrowRate)}
|
return <Loading />
|
||||||
</Text>
|
}
|
||||||
),
|
|
||||||
|
return (
|
||||||
|
<Text className='justify-end' size='sm'>
|
||||||
|
{formatPercent(row.original.borrowRate)}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
...((props.data[0] as BorrowAssetActive)?.debt
|
...((props.data[0] as BorrowAssetActive)?.debt
|
||||||
? [
|
? [
|
||||||
@ -82,6 +87,10 @@ export const BorrowTable = (props: Props) => {
|
|||||||
|
|
||||||
if (!asset) return null
|
if (!asset) return null
|
||||||
|
|
||||||
|
if (row.original.liquidity === null) {
|
||||||
|
return <Loading />
|
||||||
|
}
|
||||||
|
|
||||||
return <AmountAndValue asset={asset} amount={row.original.liquidity.amount} />
|
return <AmountAndValue asset={asset} amount={row.original.liquidity.amount} />
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -97,7 +106,7 @@ export const BorrowTable = (props: Props) => {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[],
|
[marketAssets, props.data],
|
||||||
)
|
)
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
|
69
src/components/Borrow/Borrowings.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { Suspense } from 'react'
|
||||||
|
|
||||||
|
import { Card } from 'components/Card'
|
||||||
|
import { getAccountDebts, getBorrowData } from 'utils/api'
|
||||||
|
import { getMarketAssets } from 'utils/assets'
|
||||||
|
|
||||||
|
import { BorrowTable } from './BorrowTable'
|
||||||
|
|
||||||
|
async function Content(props: Props) {
|
||||||
|
const debtData = await getAccountDebts(props.params?.account)
|
||||||
|
const borrowData = await getBorrowData()
|
||||||
|
|
||||||
|
const marketAssets = getMarketAssets()
|
||||||
|
|
||||||
|
function getBorrowAssets() {
|
||||||
|
return marketAssets.reduce(
|
||||||
|
(prev: { available: BorrowAsset[]; active: BorrowAssetActive[] }, curr) => {
|
||||||
|
const borrow = borrowData.find((borrow) => borrow.denom === curr.denom)
|
||||||
|
if (borrow) {
|
||||||
|
const debt = debtData.find((debt) => debt.denom === curr.denom)
|
||||||
|
if (debt) {
|
||||||
|
prev.active.push({
|
||||||
|
...borrow,
|
||||||
|
debt: debt.amount,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
prev.available.push(borrow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prev
|
||||||
|
},
|
||||||
|
{ available: [], active: [] },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { available, active } = getBorrowAssets()
|
||||||
|
|
||||||
|
return <BorrowTable data={props.type === 'active' ? active : available} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function Fallback() {
|
||||||
|
const marketAssets = getMarketAssets()
|
||||||
|
|
||||||
|
const available: BorrowAsset[] = marketAssets.reduce((prev: BorrowAsset[], curr) => {
|
||||||
|
prev.push({ ...curr, borrowRate: null, liquidity: null })
|
||||||
|
|
||||||
|
return prev
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return <BorrowTable data={available} />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BorrowPage(props: Props) {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
className='h-fit w-full'
|
||||||
|
title={props.type === 'active' ? 'Borrowings' : 'Available to borrow'}
|
||||||
|
>
|
||||||
|
<Suspense fallback={<Fallback />}>
|
||||||
|
{/* @ts-expect-error Server Component */}
|
||||||
|
<Content params={props.params} type={props.type} />
|
||||||
|
</Suspense>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props extends PageProps {
|
||||||
|
type: 'active' | 'available'
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
|
import { Modal } from 'components/Modal'
|
||||||
import { Modal } from './Modal'
|
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||||
import TitleAndSubCell from './TitleAndSubCell'
|
|
||||||
|
|
||||||
export default function BorrowModal() {
|
export default function BorrowModal() {
|
||||||
const open = useStore((s) => s.borrowModal)
|
const open = useStore((s) => s.borrowModal)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import React, { LegacyRef, ReactNode } from 'react'
|
import React, { LegacyRef, ReactElement, ReactNode } from 'react'
|
||||||
|
|
||||||
import { CircularProgress } from 'components/CircularProgress'
|
import { CircularProgress } from 'components/CircularProgress'
|
||||||
|
import { ChevronDown } from 'components/Icons'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -13,30 +14,39 @@ interface Props {
|
|||||||
showProgressIndicator?: boolean
|
showProgressIndicator?: boolean
|
||||||
size?: 'small' | 'medium' | 'large'
|
size?: 'small' | 'medium' | 'large'
|
||||||
text?: string | ReactNode
|
text?: string | ReactNode
|
||||||
variant?: 'solid' | 'transparent' | 'round' | 'text'
|
variant?: 'solid' | 'transparent' | 'round'
|
||||||
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void
|
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void
|
||||||
|
icon?: ReactElement
|
||||||
|
iconClassName?: string
|
||||||
|
hasSubmenu?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const buttonColorClasses = {
|
export const buttonColorClasses = {
|
||||||
primary:
|
primary:
|
||||||
'border-none text-white bg-primary hover:bg-primary-highlight active:bg-primary-highlight-10 focus:bg-primary-highlight',
|
'border-none gradient-primary-to-secondary hover:bg-white/20 active:bg-white/40 focus:bg-white/20',
|
||||||
secondary:
|
secondary:
|
||||||
'border-none text-white bg-secondary hover:bg-secondary-highlight active:bg-secondary-highlight-10 focus:bg-secondary-highlight',
|
'border border-white/30 bg-transparent hover:bg-white/20 active:bg-white/40 focus:bg-white/20',
|
||||||
tertiary:
|
tertiary:
|
||||||
'border text-white bg-secondary-dark/60 border-white/60 hover:bg-secondary-dark hover:border-white active:bg-secondary-dark-10 active:border-white focus:bg-secondary-dark focus:border-white',
|
'border border-transparent bg-white/10 hover:bg-white/20 active:bg-white/40 focus:bg-white/20',
|
||||||
quaternary:
|
quaternary:
|
||||||
'border bg-transparent text-white/60 border-transparent hover:text-white hover:border-white active:text-white active:border-white',
|
'bg-transparent text-white/60 border-transparent hover:text-white hover:border-white active:text-white active:border-white',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const buttonBorderClasses =
|
||||||
|
'before:content-[" "] before:absolute before:inset-0 before:rounded-sm before:p-[1px] before:border-glas before:z-[-1]'
|
||||||
|
|
||||||
|
const buttonGradientClasses = [
|
||||||
|
'before:content-[" "] before:absolute before:inset-0 before:rounded-sm before:z-[-1] before:opacity-0',
|
||||||
|
'before:gradient-secondary-to-primary before:transition-opacity before:duration-500 before:ease-in',
|
||||||
|
'hover:before:opacity-100',
|
||||||
|
]
|
||||||
|
|
||||||
const buttonTransparentColorClasses = {
|
const buttonTransparentColorClasses = {
|
||||||
primary:
|
primary: 'border-none hover:text-primary active:text-primary focus:text-primary',
|
||||||
'border-none text-primary hover:text-primary-highlight active:text-primary-highlight focus:text-primary-highlight',
|
secondary: 'border-none hover:text-secondary active:text-secondary focus:text-secondary',
|
||||||
secondary:
|
tertiary: 'border-none hover:text-white/80 active:text-white/80 focus:text-white/80',
|
||||||
'border-none text-secondary hover:text-secondary-highlight active:text-secondary-highlight focus:text-secondary-highlight',
|
|
||||||
tertiary:
|
|
||||||
'text-secondary-dark hover:text-secondary-dark-10 active:text-secondary-dark-10 focus:text-secondary-dark-10',
|
|
||||||
quaternary:
|
quaternary:
|
||||||
'border border-transparent text-white/60 hover:text-white hover:border-white active:text-white active:border-white',
|
'border-none text-white/60 hover:text-white hover:border-white active:text-white active:border-white',
|
||||||
}
|
}
|
||||||
|
|
||||||
const buttonRoundSizeClasses = {
|
const buttonRoundSizeClasses = {
|
||||||
@ -46,16 +56,41 @@ const buttonRoundSizeClasses = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const buttonSizeClasses = {
|
export const buttonSizeClasses = {
|
||||||
small: 'text-sm px-5 py-1.5 min-h-[32px]',
|
small: 'text-sm',
|
||||||
medium: 'text-base px-6 py-2.5 min-h-[40px]',
|
medium: 'text-base',
|
||||||
large: 'text-lg px-6 py-2.5 min-h-[56px]',
|
large: 'text-lg',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const buttonPaddingClasses = {
|
||||||
|
small: 'px-2.5 py-1.5 min-h-[32px]',
|
||||||
|
medium: 'px-3 py-2 min-h-[40px]',
|
||||||
|
large: 'px-3.5 py-2.5 min-h-[56px]',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const buttonVariantClasses = {
|
export const buttonVariantClasses = {
|
||||||
solid: 'text-white',
|
solid: 'rounded-sm text-white shadow-button justify-center group',
|
||||||
transparent: 'bg-transparent p-0',
|
transparent: 'rounded-sm bg-transparent p-0 transition duration-200 ease-in',
|
||||||
round: 'rounded-full p-0',
|
round: 'rounded-full p-0',
|
||||||
text: 'border-none bg-transparent',
|
}
|
||||||
|
|
||||||
|
function glowElement() {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className={classNames(
|
||||||
|
'glow-container z-1 opacity-0 group-hover:animate-glow group-focus:animate-glow',
|
||||||
|
'pointer-events-none absolute inset-0 h-full w-full',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
pathLength='100'
|
||||||
|
strokeLinecap='round'
|
||||||
|
width='100%'
|
||||||
|
height='100%'
|
||||||
|
rx='4'
|
||||||
|
className='absolute glow-line group-hover:glow-hover group-focus:glow-hover'
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Button = React.forwardRef(function Button(
|
export const Button = React.forwardRef(function Button(
|
||||||
@ -70,17 +105,22 @@ export const Button = React.forwardRef(function Button(
|
|||||||
text,
|
text,
|
||||||
variant = 'solid',
|
variant = 'solid',
|
||||||
onClick,
|
onClick,
|
||||||
|
icon,
|
||||||
|
iconClassName,
|
||||||
|
hasSubmenu,
|
||||||
}: Props,
|
}: Props,
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
const buttonClasses = []
|
const buttonClasses = []
|
||||||
const enableAnimations = useStore((s) => s.enableAnimations)
|
const enableAnimations = useStore((s) => s.enableAnimations)
|
||||||
|
const isDisabled = disabled || showProgressIndicator
|
||||||
|
|
||||||
switch (variant) {
|
switch (variant) {
|
||||||
case 'round':
|
case 'round':
|
||||||
buttonClasses.push(
|
buttonClasses.push(
|
||||||
buttonSizeClasses[size],
|
buttonSizeClasses[size],
|
||||||
buttonRoundSizeClasses[size],
|
buttonRoundSizeClasses[size],
|
||||||
|
buttonPaddingClasses[size],
|
||||||
buttonColorClasses[color],
|
buttonColorClasses[color],
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
@ -90,7 +130,11 @@ export const Button = React.forwardRef(function Button(
|
|||||||
break
|
break
|
||||||
|
|
||||||
case 'solid':
|
case 'solid':
|
||||||
buttonClasses.push(buttonSizeClasses[size], buttonColorClasses[color])
|
buttonClasses.push(
|
||||||
|
buttonSizeClasses[size],
|
||||||
|
buttonPaddingClasses[size],
|
||||||
|
buttonColorClasses[color],
|
||||||
|
)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@ -98,10 +142,14 @@ export const Button = React.forwardRef(function Button(
|
|||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'outline-nones cursor-pointer appearance-none break-normal rounded-3xl',
|
'relative z-1 flex items-center',
|
||||||
|
'cursor-pointer appearance-none break-normal outline-none',
|
||||||
|
'text-white transition-all duration-500',
|
||||||
enableAnimations && 'transition-color',
|
enableAnimations && 'transition-color',
|
||||||
buttonClasses,
|
buttonClasses,
|
||||||
buttonVariantClasses[variant],
|
buttonVariantClasses[variant],
|
||||||
|
variant === 'solid' && color === 'tertiary' && buttonBorderClasses,
|
||||||
|
variant === 'solid' && color === 'primary' && buttonGradientClasses,
|
||||||
disabled && 'pointer-events-none opacity-50',
|
disabled && 'pointer-events-none opacity-50',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
@ -109,8 +157,25 @@ export const Button = React.forwardRef(function Button(
|
|||||||
ref={ref as LegacyRef<HTMLButtonElement>}
|
ref={ref as LegacyRef<HTMLButtonElement>}
|
||||||
onClick={disabled ? () => {} : onClick}
|
onClick={disabled ? () => {} : onClick}
|
||||||
>
|
>
|
||||||
{text && !children && !showProgressIndicator && <span>{text}</span>}
|
{icon && !isDisabled && (
|
||||||
{children && !showProgressIndicator && children}
|
<span
|
||||||
|
className={classNames(
|
||||||
|
'flex items-center justify-center',
|
||||||
|
(text || children) && 'mr-2',
|
||||||
|
iconClassName ?? 'h-4 w-4',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{text && !children && !isDisabled && <span>{text}</span>}
|
||||||
|
{children && !isDisabled && children}
|
||||||
|
{hasSubmenu && !isDisabled && (
|
||||||
|
<span className='ml-2 inline-block w-2.5'>
|
||||||
|
<ChevronDown />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{variant === 'solid' && !isDisabled && glowElement()}
|
||||||
{showProgressIndicator && (
|
{showProgressIndicator && (
|
||||||
<CircularProgress size={size === 'small' ? 10 : size === 'medium' ? 12 : 18} />
|
<CircularProgress size={size === 'small' ? 10 : size === 'medium' ? 12 : 18} />
|
||||||
)}
|
)}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { ReactNode } from 'react'
|
import { ReactElement, ReactNode } from 'react'
|
||||||
|
|
||||||
import { Text } from 'components/Text'
|
import { Text } from 'components/Text'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title?: string | ReactElement
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
className?: string
|
className?: string
|
||||||
|
contentClassName?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Card = (props: Props) => {
|
export const Card = (props: Props) => {
|
||||||
@ -14,13 +15,16 @@ export const Card = (props: Props) => {
|
|||||||
<section
|
<section
|
||||||
className={classNames(
|
className={classNames(
|
||||||
props.className,
|
props.className,
|
||||||
'h-fit w-full max-w-full overflow-hidden rounded-md border-[1px] border-white/20',
|
'relative z-1 flex max-w-full flex-col flex-wrap items-start overflow-hidden rounded-base border border-transparent bg-white/5',
|
||||||
|
'before:content-[" "] before:absolute before:inset-0 before:z-[-1] before:rounded-base before:p-[1px] before:border-glas',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Text size='lg' className='bg-white/10 p-4 font-semibold'>
|
{props.title && (
|
||||||
{props.title}
|
<Text size='lg' className='flex w-full items-center bg-white/10 p-4 font-semibold'>
|
||||||
</Text>
|
{props.title}
|
||||||
<div>{props.children}</div>
|
</Text>
|
||||||
|
)}
|
||||||
|
<div className={classNames('w-full', props.contentClassName)}>{props.children}</div>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
|
|
||||||
export const ContainerSecondary = ({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
className?: string
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<div className={`rounded-md bg-[#D8DAEA] px-3 py-2 text-[#585A74] ${className}`}>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
30
src/components/Council/Overview.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { Suspense } from 'react'
|
||||||
|
|
||||||
|
import { Card } from 'components/Card'
|
||||||
|
import Loading from 'components/Loading'
|
||||||
|
import { Text } from 'components/Text'
|
||||||
|
|
||||||
|
async function Content(props: PageProps) {
|
||||||
|
const wallet = props.params.wallet
|
||||||
|
|
||||||
|
return wallet ? (
|
||||||
|
<Text size='sm'>{`Council page for ${wallet}`}</Text>
|
||||||
|
) : (
|
||||||
|
<Text size='sm'>Council view only</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Fallback() {
|
||||||
|
return <Loading className='h-4 w-50' />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Overview(props: PageProps) {
|
||||||
|
return (
|
||||||
|
<Card className='h-fit w-full justify-center' title='Council' contentClassName='px-4 py-6'>
|
||||||
|
<Suspense fallback={<Fallback />}>
|
||||||
|
{/* @ts-expect-error Server Component */}
|
||||||
|
<Content params={props.params} />
|
||||||
|
</Suspense>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
32
src/components/Earn/Overview.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { Suspense } from 'react'
|
||||||
|
|
||||||
|
import { Card } from 'components/Card'
|
||||||
|
import Loading from 'components/Loading'
|
||||||
|
import { Text } from 'components/Text'
|
||||||
|
|
||||||
|
async function Content(props: PageProps) {
|
||||||
|
const wallet = props.params.wallet
|
||||||
|
|
||||||
|
return wallet ? (
|
||||||
|
<Text size='sm'>{`Earn page for ${wallet}`}</Text>
|
||||||
|
) : (
|
||||||
|
<Text size='sm' className='w-full text-center'>
|
||||||
|
You need to be connected to use the earn page
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Fallback() {
|
||||||
|
return <Loading className='h-4 w-50' />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Overview(props: PageProps) {
|
||||||
|
return (
|
||||||
|
<Card className='h-fit w-full justify-center' title='Earn' contentClassName='px-4 py-6'>
|
||||||
|
<Suspense fallback={<Fallback />}>
|
||||||
|
{/* @ts-expect-error Server Component */}
|
||||||
|
<Content params={props.params} />
|
||||||
|
</Suspense>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { ReactNode } from 'react'
|
import { ReactElement, ReactNode } from 'react'
|
||||||
|
|
||||||
import { Tooltip } from 'components/Tooltip'
|
import { Tooltip } from 'components/Tooltip'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
@ -11,81 +11,68 @@ interface Props {
|
|||||||
diameter?: number
|
diameter?: number
|
||||||
value: number
|
value: number
|
||||||
label?: string
|
label?: string
|
||||||
|
icon?: ReactElement
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Gauge = ({
|
export const Gauge = ({
|
||||||
background = '#15161A',
|
background = '#FFFFFF22',
|
||||||
diameter = 40,
|
diameter = 40,
|
||||||
value = 0,
|
value = 0,
|
||||||
label,
|
|
||||||
tooltip,
|
tooltip,
|
||||||
|
icon,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const enableAnimations = useStore((s) => s.enableAnimations)
|
const enableAnimations = useStore((s) => s.enableAnimations)
|
||||||
|
const radius = 16
|
||||||
const percentage = value * 100
|
const percentage = value * 100
|
||||||
const percentageValue = percentage > 100 ? 100 : percentage < 0 ? 0 : percentage
|
const percentageValue = percentage > 100 ? 100 : percentage < 0 ? 0 : percentage
|
||||||
const semiCirclePercentage = percentageValue == -50 ? 0 : Math.abs(percentageValue / 2 - 50)
|
const circlePercent = 100 - percentageValue
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip content={tooltip}>
|
<Tooltip content={tooltip}>
|
||||||
<div
|
<div className={classNames('relative', `w-${diameter / 4} h-${diameter / 4}`)}>
|
||||||
className={classNames(
|
|
||||||
'relative overflow-hidden',
|
|
||||||
`w-${diameter / 4} h-${diameter / 8 + 1}`,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<svg
|
<svg
|
||||||
viewBox='2 -2 28 36'
|
viewBox='2 -2 28 36'
|
||||||
width={diameter}
|
width={diameter}
|
||||||
height={diameter}
|
height={diameter}
|
||||||
style={{ transform: 'rotate(180deg)' }}
|
style={{ transform: 'rotate(-90deg)' }}
|
||||||
className='absolute top-0 left-0'
|
className='absolute top-0 left-0'
|
||||||
>
|
>
|
||||||
<linearGradient id='gradient'>
|
<linearGradient id='gradient'>
|
||||||
<stop stopColor='#C13338' offset='0%'></stop>
|
<stop stopColor='rgba(255, 160, 187)' offset='0%'></stop>
|
||||||
<stop stopColor='#4F3D9F' offset='50%'></stop>
|
<stop stopColor='rgba(186, 8, 189)' offset='50%'></stop>
|
||||||
<stop stopColor='#15BFA9' offset='100%'></stop>
|
<stop stopColor='rgba(255, 160, 187)' offset='100%'></stop>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<circle
|
<circle
|
||||||
fill='none'
|
fill='none'
|
||||||
stroke={background}
|
stroke={background}
|
||||||
strokeWidth={4}
|
strokeWidth={4}
|
||||||
strokeDasharray='50 100'
|
strokeDashoffset='0'
|
||||||
strokeLinecap='round'
|
r={radius}
|
||||||
r='16'
|
cx={radius}
|
||||||
cx='16'
|
cy={radius}
|
||||||
cy='16'
|
|
||||||
shapeRendering='geometricPrecision'
|
shapeRendering='geometricPrecision'
|
||||||
/>
|
/>
|
||||||
<circle
|
<circle
|
||||||
r='16'
|
r={radius}
|
||||||
cx='16'
|
cx={radius}
|
||||||
cy='16'
|
cy={radius}
|
||||||
fill='none'
|
fill='transparent'
|
||||||
strokeLinecap='round'
|
|
||||||
stroke='url(#gradient)'
|
stroke='url(#gradient)'
|
||||||
strokeDasharray='50 100'
|
|
||||||
strokeWidth={5}
|
strokeWidth={5}
|
||||||
|
strokeDashoffset={circlePercent}
|
||||||
|
strokeDasharray='100'
|
||||||
|
pathLength='100'
|
||||||
style={{
|
style={{
|
||||||
strokeDashoffset: semiCirclePercentage,
|
|
||||||
transition: enableAnimations ? 'stroke-dashoffset 1s ease' : 'none',
|
transition: enableAnimations ? 'stroke-dashoffset 1s ease' : 'none',
|
||||||
}}
|
}}
|
||||||
shapeRendering='geometricPrecision'
|
shapeRendering='geometricPrecision'
|
||||||
|
strokeLinecap='round'
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
{label && (
|
{icon && (
|
||||||
<span
|
<div className='absolute inset-0 flex items-center justify-center p-2.5 opacity-30'>
|
||||||
className='text-xs'
|
{icon}
|
||||||
style={{
|
</div>
|
||||||
width: '100%',
|
|
||||||
left: '0',
|
|
||||||
textAlign: 'center',
|
|
||||||
bottom: '-2px',
|
|
||||||
position: 'absolute',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
3
src/components/Icons/Account.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg viewBox="0 0 15 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M14.1666 3.6665H0.833252M0.833252 6.33317H3.1977C3.55783 6.33317 3.73789 6.33317 3.91201 6.36421C4.06655 6.39176 4.21732 6.4374 4.36119 6.50021C4.52328 6.57097 4.6731 6.67085 4.97274 6.87061L5.36043 7.12906C5.66007 7.32883 5.80989 7.42871 5.97198 7.49947C6.11585 7.56227 6.26662 7.60792 6.42116 7.63547C6.59528 7.6665 6.77534 7.6665 7.13547 7.6665H7.86437C8.2245 7.6665 8.40456 7.6665 8.57868 7.63547C8.73322 7.60792 8.88399 7.56227 9.02786 7.49947C9.18995 7.42871 9.33977 7.32883 9.63941 7.12906L10.0271 6.87061C10.3267 6.67085 10.4766 6.57097 10.6386 6.50021C10.7825 6.4374 10.9333 6.39176 11.0878 6.36421C11.2619 6.33317 11.442 6.33317 11.8021 6.33317H14.1666M0.833252 2.79984L0.833252 9.19984C0.833252 9.94657 0.833252 10.3199 0.978577 10.6052C1.10641 10.856 1.31038 11.06 1.56126 11.1878C1.84648 11.3332 2.21985 11.3332 2.96659 11.3332L12.0333 11.3332C12.78 11.3332 13.1534 11.3332 13.4386 11.1878C13.6895 11.06 13.8934 10.856 14.0213 10.6052C14.1666 10.3199 14.1666 9.94657 14.1666 9.19984V2.79984C14.1666 2.0531 14.1666 1.67973 14.0213 1.39452C13.8934 1.14363 13.6895 0.939661 13.4386 0.81183C13.1534 0.666505 12.78 0.666505 12.0333 0.666505L2.96659 0.666504C2.21985 0.666504 1.84648 0.666504 1.56126 0.811828C1.31038 0.93966 1.10641 1.14363 0.978577 1.39452C0.833252 1.67973 0.833252 2.0531 0.833252 2.79984Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
@ -1,5 +1,3 @@
|
|||||||
<svg viewBox='0 0 12 12' xmlns='http://www.w3.org/2000/svg' fill="currentColor">
|
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path d="M7.99992 5.33594V10.6693M5.33325 8.0026H10.6666M14.6666 8.0026C14.6666 11.6845 11.6818 14.6693 7.99992 14.6693C4.31802 14.6693 1.33325 11.6845 1.33325 8.0026C1.33325 4.32071 4.31802 1.33594 7.99992 1.33594C11.6818 1.33594 14.6666 4.32071 14.6666 8.0026Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
d='M10.6667 5.33317H6.66675V1.33317C6.66675 1.15636 6.59651 0.98679 6.47149 0.861766C6.34646 0.736742 6.17689 0.666504 6.00008 0.666504C5.82327 0.666504 5.6537 0.736742 5.52868 0.861766C5.40365 0.98679 5.33341 1.15636 5.33341 1.33317V5.33317H1.33341C1.1566 5.33317 0.987035 5.40341 0.86201 5.52843C0.736986 5.65346 0.666748 5.82303 0.666748 5.99984C0.666748 6.17665 0.736986 6.34622 0.86201 6.47124C0.987035 6.59627 1.1566 6.6665 1.33341 6.6665H5.33341V10.6665C5.33341 10.8433 5.40365 11.0129 5.52868 11.1379C5.6537 11.2629 5.82327 11.3332 6.00008 11.3332C6.17689 11.3332 6.34646 11.2629 6.47149 11.1379C6.59651 11.0129 6.66675 10.8433 6.66675 10.6665V6.6665H10.6667C10.8436 6.6665 11.0131 6.59627 11.1382 6.47124C11.2632 6.34622 11.3334 6.17665 11.3334 5.99984C11.3334 5.82303 11.2632 5.65346 11.1382 5.52843C11.0131 5.40341 10.8436 5.33317 10.6667 5.33317Z'
|
</svg>
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 964 B After Width: | Height: | Size: 417 B |
3
src/components/Icons/ArrowDownLine.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M14.5 14H2.5M12.5 7.33333L8.5 11.3333M8.5 11.3333L4.5 7.33333M8.5 11.3333V2" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 237 B |
3
src/components/Icons/ArrowUpLine.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M14 2H2M12 8.66667L8 4.66667M8 4.66667L4 8.66667M8 4.66667V14" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 223 B |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 178 B |
3
src/components/Icons/Heart.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.99541 2.42388C6.66251 0.8656 4.43981 0.446428 2.76978 1.87334C1.09974 3.30026 0.864625 5.68598 2.17611 7.3736C3.26652 8.77674 6.56649 11.7361 7.64805 12.6939C7.76905 12.801 7.82955 12.8546 7.90012 12.8757C7.96172 12.8941 8.02911 12.8941 8.09071 12.8757C8.16128 12.8546 8.22178 12.801 8.34278 12.6939C9.42433 11.7361 12.7243 8.77674 13.8147 7.3736C15.1262 5.68598 14.9198 3.28525 13.2211 1.87334C11.5223 0.461438 9.32832 0.8656 7.99541 2.42388Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 648 B |
@ -1,12 +1,15 @@
|
|||||||
// @index(['./*.svg'], f => `export { default as ${f.name} } from 'components/Icons/${f.name}.svg'`)
|
// @index(['./*.svg'], f => `export { default as ${f.name} } from 'components/Icons/${f.name}.svg'`)
|
||||||
|
export { default as Account } from 'components/Icons/Account.svg'
|
||||||
export { default as Add } from 'components/Icons/Add.svg'
|
export { default as Add } from 'components/Icons/Add.svg'
|
||||||
export { default as ArrowBack } from 'components/Icons/ArrowBack.svg'
|
export { default as ArrowBack } from 'components/Icons/ArrowBack.svg'
|
||||||
export { default as ArrowDown } from 'components/Icons/ArrowDown.svg'
|
export { default as ArrowDown } from 'components/Icons/ArrowDown.svg'
|
||||||
|
export { default as ArrowDownLine } from 'components/Icons/ArrowDownLine.svg'
|
||||||
export { default as ArrowLeftLine } from 'components/Icons/ArrowLeftLine.svg'
|
export { default as ArrowLeftLine } from 'components/Icons/ArrowLeftLine.svg'
|
||||||
export { default as ArrowRightLine } from 'components/Icons/ArrowRightLine.svg'
|
export { default as ArrowRightLine } from 'components/Icons/ArrowRightLine.svg'
|
||||||
export { default as ArrowsLeftRight } from 'components/Icons/ArrowsLeftRight.svg'
|
export { default as ArrowsLeftRight } from 'components/Icons/ArrowsLeftRight.svg'
|
||||||
export { default as ArrowsUpDown } from 'components/Icons/ArrowsUpDown.svg'
|
export { default as ArrowsUpDown } from 'components/Icons/ArrowsUpDown.svg'
|
||||||
export { default as ArrowUp } from 'components/Icons/ArrowUp.svg'
|
export { default as ArrowUp } from 'components/Icons/ArrowUp.svg'
|
||||||
|
export { default as ArrowUpLine } from 'components/Icons/ArrowUpLine.svg'
|
||||||
export { default as BurgerMenu } from 'components/Icons/BurgerMenu.svg'
|
export { default as BurgerMenu } from 'components/Icons/BurgerMenu.svg'
|
||||||
export { default as Check } from 'components/Icons/Check.svg'
|
export { default as Check } from 'components/Icons/Check.svg'
|
||||||
export { default as ChevronDown } from 'components/Icons/ChevronDown.svg'
|
export { default as ChevronDown } from 'components/Icons/ChevronDown.svg'
|
||||||
@ -22,6 +25,7 @@ export { default as Ellipsis } from 'components/Icons/Ellipsis.svg'
|
|||||||
export { default as ExternalLink } from 'components/Icons/ExternalLink.svg'
|
export { default as ExternalLink } from 'components/Icons/ExternalLink.svg'
|
||||||
export { default as Failed } from 'components/Icons/Failed.svg'
|
export { default as Failed } from 'components/Icons/Failed.svg'
|
||||||
export { default as Github } from 'components/Icons/Github.svg'
|
export { default as Github } from 'components/Icons/Github.svg'
|
||||||
|
export { default as Heart } from 'components/Icons/Heart.svg'
|
||||||
export { default as Info } from 'components/Icons/Info.svg'
|
export { default as Info } from 'components/Icons/Info.svg'
|
||||||
export { default as Logo } from 'components/Icons/Logo.svg'
|
export { default as Logo } from 'components/Icons/Logo.svg'
|
||||||
export { default as MarsProtocol } from 'components/Icons/MarsProtocol.svg'
|
export { default as MarsProtocol } from 'components/Icons/MarsProtocol.svg'
|
||||||
|
@ -3,8 +3,6 @@ import classNames from 'classnames'
|
|||||||
interface Props {
|
interface Props {
|
||||||
className?: string
|
className?: string
|
||||||
count?: number
|
count?: number
|
||||||
height?: number
|
|
||||||
width?: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Loading(props: Props) {
|
export default function Loading(props: Props) {
|
||||||
@ -14,10 +12,8 @@ export default function Loading(props: Props) {
|
|||||||
<div
|
<div
|
||||||
role='status'
|
role='status'
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'animate-pulse rounded-md bg-white/40',
|
'max-w-full animate-pulse rounded-sm bg-white/40',
|
||||||
props.className,
|
props.className ? props.className : 'h-4 w-full',
|
||||||
props.height ? `h-[${props.height}px]` : 'h-[300px]',
|
|
||||||
props.width ? `w-[${props.width}px]` : 'w-full',
|
|
||||||
)}
|
)}
|
||||||
key={i}
|
key={i}
|
||||||
/>
|
/>
|
||||||
|
@ -3,8 +3,7 @@ import { ReactNode } from 'react'
|
|||||||
|
|
||||||
import { Close } from 'components/Icons'
|
import { Close } from 'components/Icons'
|
||||||
import { Text } from 'components/Text'
|
import { Text } from 'components/Text'
|
||||||
|
import { Button } from 'components/Button'
|
||||||
import { Button } from './Button'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
@ -21,17 +20,22 @@ export const Modal = (props: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return props.open ? (
|
return props.open ? (
|
||||||
<div className='fixed top-0 left-0 z-20 h-screen w-screen'>
|
<div className='fixed top-0 left-0 z-40 h-screen w-screen'>
|
||||||
<div className='relative flex h-full w-full items-center justify-center'>
|
<div className='relative flex h-full w-full items-center justify-center'>
|
||||||
<section
|
<section
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'relative z-40 w-[790px] max-w-full rounded-md border-[1px] border-white/20 bg-white/5 p-6 backdrop-blur-3xl ',
|
'relative z-40 w-[790px] max-w-full rounded-base border border-white/20 bg-white/5 p-6 backdrop-blur-3xl',
|
||||||
props.className,
|
props.className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className='flex justify-between pb-6'>
|
<div className='flex justify-between pb-6'>
|
||||||
<Text>{props.title}</Text>
|
<Text>{props.title}</Text>
|
||||||
<Button onClick={onClickAway} text='X' color='tertiary' />
|
<Button
|
||||||
|
onClick={onClickAway}
|
||||||
|
icon={<Close />}
|
||||||
|
iconClassName='h-2 w-2'
|
||||||
|
color='tertiary'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>{props.children ? props.children : props.content}</div>
|
<div>{props.children ? props.children : props.content}</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
import { ConfirmModal } from 'components/Account/ConfirmModal'
|
import { ConfirmModal } from 'components/Account/ConfirmModal'
|
||||||
import { FundAccountModal } from 'components/Account/FundAccountModal'
|
import { FundAccountModal } from 'components/Account/FundAccountModal'
|
||||||
|
import BorrowModal from 'components/BorrowModal'
|
||||||
import BorrowModal from './BorrowModal'
|
|
||||||
|
|
||||||
export const Modals = () => (
|
export const Modals = () => (
|
||||||
<>
|
<>
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { usePathname } from 'next/navigation'
|
import { usePathname } from 'next/navigation'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
|
||||||
|
import { AccountNavigation } from 'components/Account/AccountNavigation'
|
||||||
import { Logo } from 'components/Icons'
|
import { Logo } from 'components/Icons'
|
||||||
import { NavLink } from 'components/Navigation/NavLink'
|
import { NavLink } from 'components/Navigation/NavLink'
|
||||||
import Wallet from 'components/Wallet/Wallet'
|
import Wallet from 'components/Wallet/Wallet'
|
||||||
@ -20,8 +22,14 @@ export default function DesktopNavigation() {
|
|||||||
const pathname = usePathname() || ''
|
const pathname = usePathname() || ''
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='relative hidden bg-header lg:block'>
|
<header
|
||||||
<div className='flex items-center justify-between border-b border-white/20 px-6 py-3'>
|
className={classNames(
|
||||||
|
'fixed top-0 left-0 z-30 hidden w-full',
|
||||||
|
'before:content-[" "] before:absolute before:inset-0 before:z-[-1] before:h-full before:w-full before:rounded-sm before:backdrop-blur-sticky',
|
||||||
|
'lg:block',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className='flex items-center justify-between border-b border-white/20 py-3 pl-6 pr-4'>
|
||||||
<div className='flex flex-grow items-center'>
|
<div className='flex flex-grow items-center'>
|
||||||
<Link href={getRoute(pathname, { page: 'trade' })}>
|
<Link href={getRoute(pathname, { page: 'trade' })}>
|
||||||
<span className='block h-10 w-10'>
|
<span className='block h-10 w-10'>
|
||||||
@ -36,8 +44,11 @@ export default function DesktopNavigation() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Wallet />
|
<div className='flex gap-4'>
|
||||||
|
<AccountNavigation />
|
||||||
|
<Wallet />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</header>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
'use client'
|
import classNames from 'classnames'
|
||||||
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { usePathname } from 'next/navigation'
|
import { usePathname } from 'next/navigation'
|
||||||
import { ReactNode } from 'react'
|
import { ReactNode } from 'react'
|
||||||
import classNames from 'classnames'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
href: string
|
href: string
|
||||||
@ -18,7 +16,7 @@ export const NavLink = ({ href, children }: Props) => {
|
|||||||
<Link
|
<Link
|
||||||
href={href}
|
href={href}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'text-lg-caps hover:text-white active:text-white',
|
'text-sm font-semibold hover:text-white active:text-white',
|
||||||
isActive ? 'pointer-events-none text-white' : 'text-white/60',
|
isActive ? 'pointer-events-none text-white' : 'text-white/60',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@ -18,7 +18,7 @@ export const Overlay = ({ children, content, className, show, setShow }: Props)
|
|||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'max-w-screen absolute z-50 rounded-lg shadow-overlay gradient-popover',
|
'max-w-screen absolute z-50 rounded-sm border border-white/40 shadow-overlay backdrop-blur-sm gradient-popover',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
import classNames from 'classnames'
|
|
||||||
import { ReactNode } from 'react'
|
|
||||||
|
|
||||||
import { Button } from 'components/Button'
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
className?: string
|
|
||||||
icon?: ReactNode
|
|
||||||
onClick: () => void
|
|
||||||
setShow: (show: boolean) => void
|
|
||||||
text: string | ReactNode
|
|
||||||
}
|
|
||||||
|
|
||||||
export const OverlayAction = ({ className, icon, onClick, setShow, text }: Props) => {
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
className={classNames(
|
|
||||||
'flex items-center whitespace-nowrap py-2 text-left text-sm text-accent-dark hover:text-secondary',
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
variant='text'
|
|
||||||
onClick={() => {
|
|
||||||
setShow(false)
|
|
||||||
onClick()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{icon && <span className='mt-[1px] mr-2 flex w-4'>{icon}</span>}
|
|
||||||
{text}
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
70
src/components/Portfolio/AccountOverview.tsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { Suspense } from 'react'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
|
||||||
|
import { Card } from 'components/Card'
|
||||||
|
import Loading from 'components/Loading'
|
||||||
|
import { Text } from 'components/Text'
|
||||||
|
import { getCreditAccounts } from 'utils/api'
|
||||||
|
|
||||||
|
async function Content(props: PageProps) {
|
||||||
|
const wallet = props.params.wallet
|
||||||
|
const currentAccount = props.params.account
|
||||||
|
const hasAccount = !isNaN(Number(currentAccount))
|
||||||
|
const creditAccounts = await getCreditAccounts(wallet)
|
||||||
|
|
||||||
|
return wallet ? (
|
||||||
|
<div className={classNames('grid grid-cols-1 gap-4', 'md:grid-cols-2', 'lg:grid-cols-3')}>
|
||||||
|
{creditAccounts.map((account: string, index: number) => (
|
||||||
|
<Card
|
||||||
|
className='h-fit w-full'
|
||||||
|
title={`Account ${account}`}
|
||||||
|
key={index}
|
||||||
|
contentClassName='px-4 py-6'
|
||||||
|
>
|
||||||
|
{hasAccount && currentAccount === account ? (
|
||||||
|
<Text size='sm'>Current Account</Text>
|
||||||
|
) : (
|
||||||
|
<Text size='sm'>Account details</Text>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Card className='h-fit w-full justify-center' title='Portfolio' contentClassName='px-4 py-6'>
|
||||||
|
<Text size='sm' className='w-full text-center'>
|
||||||
|
You need to be connected to view the porfolio page
|
||||||
|
</Text>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Fallback() {
|
||||||
|
const cardCount = 3
|
||||||
|
return (
|
||||||
|
<div className={classNames('grid grid-cols-1 gap-4', 'md:grid-cols-2', 'lg:grid-cols-3')}>
|
||||||
|
{Array.from({ length: cardCount }, (_, i) => (
|
||||||
|
<Card
|
||||||
|
key={i}
|
||||||
|
className='h-fit w-full'
|
||||||
|
title={
|
||||||
|
<>
|
||||||
|
Account <Loading className='ml-2 h-4 w-8' />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
contentClassName='px-4 py-6'
|
||||||
|
>
|
||||||
|
<Loading className='h-4 w-50' />
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AccountOverview(props: PageProps) {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<Fallback />}>
|
||||||
|
{/* @ts-expect-error Server Component */}
|
||||||
|
<Content params={props.params} />
|
||||||
|
</Suspense>
|
||||||
|
)
|
||||||
|
}
|
@ -3,19 +3,18 @@ import BigNumber from 'bignumber.js'
|
|||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import React, { useMemo, useState } from 'react'
|
import React, { useMemo, useState } from 'react'
|
||||||
import { NumericFormat } from 'react-number-format'
|
import { NumericFormat } from 'react-number-format'
|
||||||
import { toast } from 'react-toastify'
|
|
||||||
|
|
||||||
import { Button } from 'components/Button'
|
import { Button } from 'components/Button'
|
||||||
|
import { Card } from 'components/Card'
|
||||||
import { CircularProgress } from 'components/CircularProgress'
|
import { CircularProgress } from 'components/CircularProgress'
|
||||||
import { ContainerSecondary } from 'components/ContainerSecondary'
|
|
||||||
import { Slider } from 'components/Slider'
|
import { Slider } from 'components/Slider'
|
||||||
import { useRepayFunds } from 'hooks/mutations/useRepayFunds'
|
import { useRepayFunds } from 'hooks/mutations/useRepayFunds'
|
||||||
import { useAllBalances } from 'hooks/queries/useAllBalances'
|
import { useAllBalances } from 'hooks/queries/useAllBalances'
|
||||||
import { useCreditAccountPositions } from 'hooks/queries/useCreditAccountPositions'
|
import { useCreditAccountPositions } from 'hooks/queries/useCreditAccountPositions'
|
||||||
import { useTokenPrices } from 'hooks/queries/useTokenPrices'
|
import { useTokenPrices } from 'hooks/queries/useTokenPrices'
|
||||||
import { getTokenDecimals, getTokenSymbol } from 'utils/tokens'
|
|
||||||
import { getMarketAssets } from 'utils/assets'
|
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
|
import { getMarketAssets } from 'utils/assets'
|
||||||
|
import { getTokenDecimals, getTokenSymbol } from 'utils/tokens'
|
||||||
|
|
||||||
// 0.001% buffer / slippage to avoid repay action from not fully repaying the debt amount
|
// 0.001% buffer / slippage to avoid repay action from not fully repaying the debt amount
|
||||||
const REPAY_BUFFER = 1.00001
|
const REPAY_BUFFER = 1.00001
|
||||||
@ -137,7 +136,7 @@ export const RepayModal = ({ show, onClose, tokenDenom }: Props) => {
|
|||||||
Repay {tokenSymbol}
|
Repay {tokenSymbol}
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
<div className='mb-4 flex flex-col gap-2 text-sm'>
|
<div className='mb-4 flex flex-col gap-2 text-sm'>
|
||||||
<ContainerSecondary>
|
<Card>
|
||||||
<p className='mb-7'>
|
<p className='mb-7'>
|
||||||
In wallet: {walletAmount.toLocaleString()} {tokenSymbol}
|
In wallet: {walletAmount.toLocaleString()} {tokenSymbol}
|
||||||
</p>
|
</p>
|
||||||
@ -176,7 +175,7 @@ export const RepayModal = ({ show, onClose, tokenDenom }: Props) => {
|
|||||||
}}
|
}}
|
||||||
onMaxClick={() => setAmount(maxValue)}
|
onMaxClick={() => setAmount(maxValue)}
|
||||||
/>
|
/>
|
||||||
</ContainerSecondary>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
className='mt-auto w-full'
|
className='mt-auto w-full'
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import * as RadixSlider from '@radix-ui/react-slider'
|
import * as RadixSlider from '@radix-ui/react-slider'
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
className?: string
|
className?: string
|
||||||
@ -27,7 +26,7 @@ export const Slider = ({ className, value, onChange, onMaxClick }: Props) => {
|
|||||||
</RadixSlider.Thumb>
|
</RadixSlider.Thumb>
|
||||||
</RadixSlider.Root>
|
</RadixSlider.Root>
|
||||||
<button
|
<button
|
||||||
className='ml-4 rounded-md bg-blue-600 py-1 px-2 text-xs font-semibold text-white'
|
className='ml-4 rounded-base bg-blue-600 py-1 px-2 text-xs font-semibold text-white'
|
||||||
onClick={onMaxClick}
|
onClick={onMaxClick}
|
||||||
>
|
>
|
||||||
MAX
|
MAX
|
||||||
|
@ -29,7 +29,6 @@ export const Text = ({
|
|||||||
<HtmlElement
|
<HtmlElement
|
||||||
className={classNames(
|
className={classNames(
|
||||||
className,
|
className,
|
||||||
'flex items-center',
|
|
||||||
uppercase ? `text-${sizeClass}-caps` : `text-${sizeClass}`,
|
uppercase ? `text-${sizeClass}-caps` : `text-${sizeClass}`,
|
||||||
monospace && 'number',
|
monospace && 'number',
|
||||||
)}
|
)}
|
||||||
|
@ -13,11 +13,9 @@ interface Props extends React.HTMLProps<HTMLAnchorElement> {
|
|||||||
|
|
||||||
const colorClasses = {
|
const colorClasses = {
|
||||||
primary:
|
primary:
|
||||||
'text-primary hover:text-primary-highlight active:text-primary-highlight-10 focus:text-primary-highlight',
|
'text-primary hover:text-secondary active:text-secondary/90 focus:text-text-secondary/90',
|
||||||
secondary:
|
secondary: 'text-secondary hover:text-primary active:text-primary/90 focus:text-text-primary/90',
|
||||||
'text-secondary hover:text-secondary-highlight active:text-secondary-highlight-10 focus:text-secondary-highlight',
|
tertiary: 'text-white/80 hover:text-white active:text-white/90 focus:text-text-white/90',
|
||||||
tertiary:
|
|
||||||
'text-secondary-dark/60 hover:text-secondary-dark active:text-secondary-dark-10 focus:text-secondary-dark',
|
|
||||||
quaternary: 'hover:text-white active:text-white',
|
quaternary: 'hover:text-white active:text-white',
|
||||||
}
|
}
|
||||||
const textSizeClasses = {
|
const textSizeClasses = {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
import { toast as createToast, Slide, ToastContainer } from 'react-toastify'
|
import { toast as createToast, Slide, ToastContainer } from 'react-toastify'
|
||||||
|
|
||||||
|
import { Check, Warning } from 'components/Icons'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
|
|
||||||
export default function Toaster() {
|
export default function Toaster() {
|
||||||
@ -11,9 +12,23 @@ export default function Toaster() {
|
|||||||
|
|
||||||
if (toast) {
|
if (toast) {
|
||||||
if (toast.isError) {
|
if (toast.isError) {
|
||||||
createToast.error(toast.message)
|
createToast.error(toast.message, {
|
||||||
|
progressClassName: 'bg-loss',
|
||||||
|
icon: (
|
||||||
|
<span className='h-4 w-4'>
|
||||||
|
<Warning />
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
createToast.success(toast.message)
|
createToast.success(toast.message, {
|
||||||
|
progressClassName: 'bg-profit',
|
||||||
|
icon: (
|
||||||
|
<span className='h-4 w-4'>
|
||||||
|
<Check />
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
useStore.setState({ toast: null })
|
useStore.setState({ toast: null })
|
||||||
router.refresh()
|
router.refresh()
|
||||||
@ -21,11 +36,14 @@ export default function Toaster() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ToastContainer
|
<ToastContainer
|
||||||
autoClose={3000}
|
autoClose={5000}
|
||||||
closeButton={false}
|
closeButton={false}
|
||||||
position='bottom-right'
|
position='bottom-right'
|
||||||
newestOnTop
|
newestOnTop
|
||||||
transition={enableAnimations ? Slide : undefined}
|
transition={enableAnimations ? Slide : undefined}
|
||||||
|
className='w-[280px] p-0'
|
||||||
|
toastClassName='z-50 text-xs rounded-sm border border-white/40 shadow-overlay backdrop-blur-sm gradient-popover px-2 py-4'
|
||||||
|
bodyClassName='p-0 text-white m-0'
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ export const Tooltip = ({
|
|||||||
render={(attrs) => {
|
render={(attrs) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='max-w-[320px] rounded-lg px-4 py-2 shadow-tooltip gradient-tooltip'
|
className='max-w-[320px] rounded-lg px-4 py-2 text-xs shadow-tooltip gradient-tooltip'
|
||||||
{...attrs}
|
{...attrs}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
|
32
src/components/Trade/OrderBook.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { Suspense } from 'react'
|
||||||
|
|
||||||
|
import { Card } from 'components/Card'
|
||||||
|
import Loading from 'components/Loading'
|
||||||
|
import { Text } from 'components/Text'
|
||||||
|
|
||||||
|
async function Content(props: PageProps) {
|
||||||
|
const wallet = props.params.wallet
|
||||||
|
|
||||||
|
return wallet ? (
|
||||||
|
<Text size='sm'>{`Order book for ${wallet}`}</Text>
|
||||||
|
) : (
|
||||||
|
<Text size='sm' className='w-full text-center'>
|
||||||
|
You need to be connected to see the order book
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Fallback() {
|
||||||
|
return <Loading className='h-4 w-50' />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function OrderBook(props: PageProps) {
|
||||||
|
return (
|
||||||
|
<Card className='col-span-3' title='Order Book' contentClassName='px-4 py-6'>
|
||||||
|
<Suspense fallback={<Fallback />}>
|
||||||
|
{/* @ts-expect-error Server Component */}
|
||||||
|
<Content params={props.params} />
|
||||||
|
</Suspense>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
38
src/components/Trade/Trade.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { Suspense } from 'react'
|
||||||
|
|
||||||
|
import { Card } from 'components/Card'
|
||||||
|
import Loading from 'components/Loading'
|
||||||
|
import { Text } from 'components/Text'
|
||||||
|
|
||||||
|
async function Content(props: PageProps) {
|
||||||
|
const wallet = props.params.wallet
|
||||||
|
const currentAccount = props.params.account
|
||||||
|
const hasAccount = !isNaN(Number(currentAccount))
|
||||||
|
|
||||||
|
return wallet ? (
|
||||||
|
<>
|
||||||
|
{hasAccount ? (
|
||||||
|
<Text size='sm'>{`Trade with Account ${currentAccount}`}</Text>
|
||||||
|
) : (
|
||||||
|
<Text size='sm'>Select an Account to trade</Text>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Text size='sm'>You need to be connected to trade</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Fallback() {
|
||||||
|
return <Loading className='h-4 w-50' />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Trade(props: PageProps) {
|
||||||
|
return (
|
||||||
|
<Card className='h-full w-full' title='Trade Module' contentClassName='px-4 py-6'>
|
||||||
|
<Suspense fallback={<Fallback />}>
|
||||||
|
{/* @ts-expect-error Server Component */}
|
||||||
|
<Content params={props.params} />
|
||||||
|
</Suspense>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
24
src/components/Trade/TradingView.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Suspense } from 'react'
|
||||||
|
|
||||||
|
import { Card } from 'components/Card'
|
||||||
|
import Loading from 'components/Loading'
|
||||||
|
import { Text } from 'components/Text'
|
||||||
|
|
||||||
|
async function Content(props: PageProps) {
|
||||||
|
return <Text size='sm'>Chart view</Text>
|
||||||
|
}
|
||||||
|
|
||||||
|
function Fallback() {
|
||||||
|
return <Loading className='h-4 w-50' />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TradingView(props: PageProps) {
|
||||||
|
return (
|
||||||
|
<Card className='col-span-2 h-full' title='Trading View' contentClassName='px-4 py-6'>
|
||||||
|
<Suspense fallback={<Fallback />}>
|
||||||
|
{/* @ts-expect-error Server Component */}
|
||||||
|
<Content params={props.params} />
|
||||||
|
</Suspense>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
import { useWalletManager, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
|
import { useWalletManager, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
|
||||||
import { ReactNode } from 'react'
|
import { ReactNode } from 'react'
|
||||||
|
|
||||||
|
import { Button } from 'components/Button'
|
||||||
import { CircularProgress } from 'components/CircularProgress'
|
import { CircularProgress } from 'components/CircularProgress'
|
||||||
import { Wallet } from 'components/Icons'
|
import { Wallet } from 'components/Icons'
|
||||||
|
|
||||||
@ -15,24 +18,21 @@ export default function ConnectButton(props: Props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='relative'>
|
<div className='relative'>
|
||||||
<button
|
<Button
|
||||||
|
variant='solid'
|
||||||
|
color='tertiary'
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
className='flex h-[31px] min-w-[186px] flex-1 flex-nowrap content-center items-center justify-center rounded-2xl border border-white/60 bg-black/10 px-4 pt-0.5 text-white text-2xs-caps hover:border-white hover:bg-white/60'
|
|
||||||
onClick={connect}
|
onClick={connect}
|
||||||
|
icon={<Wallet />}
|
||||||
>
|
>
|
||||||
{props.status === WalletConnectionStatus.Connecting ? (
|
{props.status === WalletConnectionStatus.Connecting ? (
|
||||||
<span className='flex justify-center'>
|
<span className='flex justify-center'>
|
||||||
<CircularProgress size={16} />
|
<CircularProgress size={16} />
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<span>{props.textOverride || 'Connect Wallet'}</span>
|
||||||
<span className='flex h-4 w-4 items-center justify-center'>
|
|
||||||
<Wallet />
|
|
||||||
</span>
|
|
||||||
<span className='ml-2 mt-0.5'>{props.textOverride || 'Connect Wallet'}</span>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
import { Coin } from '@cosmjs/stargate'
|
import { Coin } from '@cosmjs/stargate'
|
||||||
import {
|
import {
|
||||||
ChainInfoID,
|
ChainInfoID,
|
||||||
@ -18,9 +20,9 @@ import { Check, Copy, ExternalLink, Osmo } from 'components/Icons'
|
|||||||
import { Overlay } from 'components/Overlay/Overlay'
|
import { Overlay } from 'components/Overlay/Overlay'
|
||||||
import { Text } from 'components/Text'
|
import { Text } from 'components/Text'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
|
import { getWalletBalances } from 'utils/api'
|
||||||
import { getBaseAsset } from 'utils/assets'
|
import { getBaseAsset } from 'utils/assets'
|
||||||
import { formatValue, truncate } from 'utils/formatters'
|
import { formatValue, truncate } from 'utils/formatters'
|
||||||
import { getWalletBalances } from 'utils/api'
|
|
||||||
|
|
||||||
export default function ConnectedButton() {
|
export default function ConnectedButton() {
|
||||||
// ---------------
|
// ---------------
|
||||||
@ -30,7 +32,6 @@ export default function ConnectedButton() {
|
|||||||
const { disconnect: terminate } = useWalletManager()
|
const { disconnect: terminate } = useWalletManager()
|
||||||
const address = useStore((s) => s.client?.recentWallet.account?.address)
|
const address = useStore((s) => s.client?.recentWallet.account?.address)
|
||||||
const network = useStore((s) => s.client?.recentWallet.network)
|
const network = useStore((s) => s.client?.recentWallet.network)
|
||||||
const name = useStore((s) => s.name)
|
|
||||||
const baseAsset = getBaseAsset()
|
const baseAsset = getBaseAsset()
|
||||||
|
|
||||||
const { data, isLoading } = useSWR(address, getWalletBalances)
|
const { data, isLoading } = useSWR(address, getWalletBalances)
|
||||||
@ -73,7 +74,7 @@ export default function ConnectedButton() {
|
|||||||
<div className={'relative'}>
|
<div className={'relative'}>
|
||||||
{network?.chainId !== ChainInfoID.Osmosis1 && (
|
{network?.chainId !== ChainInfoID.Osmosis1 && (
|
||||||
<Text
|
<Text
|
||||||
className='absolute -right-2 -top-2.5 rounded-lg bg-secondary-highlight p-0.5 px-2'
|
className='absolute -right-2 -top-2.5 z-10 rounded-lg p-0.5 px-2 gradient-primary-to-secondary'
|
||||||
size='3xs'
|
size='3xs'
|
||||||
uppercase
|
uppercase
|
||||||
>
|
>
|
||||||
@ -81,24 +82,19 @@ export default function ConnectedButton() {
|
|||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<button
|
<Button
|
||||||
className={classNames(
|
variant='solid'
|
||||||
'flex h-[31px] flex-1 flex-nowrap content-center items-center justify-center rounded-2xl border border-white/60 bg-secondary-dark/70 px-4 py-0 text-sm text-white ',
|
color='tertiary'
|
||||||
'hover:border-white hover:bg-secondary-dark',
|
icon={<Osmo />}
|
||||||
'active:border-white active:bg-secondary-dark-10',
|
|
||||||
)}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowDetails(!showDetails)
|
setShowDetails(!showDetails)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className='flex h-4 w-4 items-center justify-center'>
|
<span>{truncate(address, [2, 4])}</span>
|
||||||
<Osmo />
|
|
||||||
</span>
|
|
||||||
<span className='ml-2'>{name ? name : truncate(address, [2, 4])}</span>
|
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'number relative ml-2 flex h-full items-center pl-2',
|
'relative ml-2 flex h-full items-center pl-2 number',
|
||||||
'before:content-[" "] before:absolute before:top-1.5 before:bottom-1.5 before:left-0 before:h-[calc(100%-12px)] before:border-l before:border-white',
|
'before:content-[" "] before:absolute before:top-0.5 before:bottom-1.5 before:left-0 before:h-[calc(100%-4px)] before:border-l before:border-white/20',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
@ -107,18 +103,18 @@ export default function ConnectedButton() {
|
|||||||
`${formatValue(walletAmount, { suffix: baseAsset.symbol })}`
|
`${formatValue(walletAmount, { suffix: baseAsset.symbol })}`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</Button>
|
||||||
<Overlay className='right-0 mt-2' show={showDetails} setShow={setShowDetails}>
|
<Overlay className='right-0 mt-2' show={showDetails} setShow={setShowDetails}>
|
||||||
<div className='flex w-[420px] flex-wrap p-6'>
|
<div className='flex w-[440px] flex-wrap p-6'>
|
||||||
<div className='flex-0 mb-4 flex w-full flex-nowrap items-start'>
|
<div className='flex-0 mb-4 flex w-full flex-nowrap items-start'>
|
||||||
<div className='flex w-auto flex-1'>
|
<div className='flex w-auto flex-1'>
|
||||||
<div className='mr-2 flex h-[31px] items-end pb-0.5 text-secondary-dark text-base-caps'>
|
<div className='mr-2 flex h-[31px] items-end pb-0.5 text-base-caps'>
|
||||||
{baseAsset.denom}
|
{baseAsset.denom}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex-0 flex flex-wrap justify-end'>
|
<div className='flex-0 flex flex-wrap justify-end'>
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
animate
|
animate
|
||||||
className='flex items-end text-2xl text-secondary-dark'
|
className='flex items-end text-2xl '
|
||||||
amount={walletAmount}
|
amount={walletAmount}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -128,44 +124,44 @@ export default function ConnectedButton() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex w-full flex-wrap'>
|
<div className='flex w-full flex-wrap'>
|
||||||
<Text uppercase className='mb-1 break-all text-secondary-dark/80'>
|
<Text uppercase className='/80 mb-1 break-all'>
|
||||||
{name ? `‘${name}’` : 'Your Address'}
|
{'Your Address'}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Text
|
<Text size='sm' className='mb-1 hidden break-all font-bold md:block'>
|
||||||
size='sm'
|
|
||||||
className='mb-1 hidden break-all font-bold text-secondary-dark md:block'
|
|
||||||
>
|
|
||||||
{address}
|
{address}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size='sm' className='mb-1 break-all font-bold text-secondary-dark md:hidden'>
|
<Text size='sm' className='mb-1 break-all font-bold md:hidden'>
|
||||||
{truncate(address, [14, 14])}
|
{truncate(address, [14, 14])}
|
||||||
</Text>
|
</Text>
|
||||||
<div className='flex w-full pt-1'>
|
<div className='flex w-full pt-1'>
|
||||||
<button
|
<Button
|
||||||
className='mr-10 flex w-auto appearance-none items-center border-none py-2 text-secondary-dark opacity-70 hover:opacity-100'
|
icon={<Copy />}
|
||||||
|
variant='transparent'
|
||||||
|
className='mr-10 flex w-auto py-2'
|
||||||
|
color='quaternary'
|
||||||
onClick={setCopied}
|
onClick={setCopied}
|
||||||
>
|
>
|
||||||
<span className='mr-1 w-4'>
|
|
||||||
<Copy />
|
|
||||||
</span>
|
|
||||||
{isCopied ? (
|
{isCopied ? (
|
||||||
<Text size='sm'>
|
<Text size='sm'>
|
||||||
Copied <Check />
|
Copied{' '}
|
||||||
|
<span className='ml-1 w-4'>
|
||||||
|
<Check />
|
||||||
|
</span>
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<Text size='sm'>Copy Address</Text>
|
<Text size='sm'>Copy Address</Text>
|
||||||
)}
|
)}
|
||||||
</button>
|
</Button>
|
||||||
<button
|
<Button
|
||||||
className='flex w-auto appearance-none items-center border-none py-2 text-secondary-dark opacity-70 hover:opacity-100'
|
icon={<ExternalLink />}
|
||||||
|
variant='transparent'
|
||||||
|
className='flex w-auto py-2'
|
||||||
|
color='quaternary'
|
||||||
onClick={viewOnFinder}
|
onClick={viewOnFinder}
|
||||||
>
|
>
|
||||||
<span className='mr-1 w-4'>
|
|
||||||
<ExternalLink />
|
|
||||||
</span>
|
|
||||||
<Text size='sm'>View on {explorerName}</Text>
|
<Text size='sm'>View on {explorerName}</Text>
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,42 +6,46 @@ import {
|
|||||||
useWalletManager,
|
useWalletManager,
|
||||||
WalletConnectionStatus,
|
WalletConnectionStatus,
|
||||||
} from '@marsprotocol/wallet-connector'
|
} from '@marsprotocol/wallet-connector'
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
import ConnectButton from 'components/Wallet/ConnectButton'
|
import ConnectButton from 'components/Wallet/ConnectButton'
|
||||||
import ConnectedButton from 'components/Wallet/ConnectedButton'
|
import ConnectedButton from 'components/Wallet/ConnectedButton'
|
||||||
import useParams from 'hooks/useParams'
|
import useParams from 'hooks/useParams'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
|
import { getCreditAccounts } from 'utils/api'
|
||||||
|
|
||||||
export default function Wallet() {
|
export default function Wallet() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
|
|
||||||
const { status } = useWalletManager()
|
const { status } = useWalletManager()
|
||||||
const [isConnected, setIsConnected] = useState(false)
|
|
||||||
const { recentWallet, simulate, sign, broadcast } = useWallet()
|
const { recentWallet, simulate, sign, broadcast } = useWallet()
|
||||||
const client = useStore((s) => s.client)
|
const client = useStore((s) => s.client)
|
||||||
|
const address = useStore((s) => s.address)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const connectedStatus = status === WalletConnectionStatus.Connected
|
const isConnected = status === WalletConnectionStatus.Connected
|
||||||
if (connectedStatus === isConnected) return
|
|
||||||
setIsConnected(connectedStatus)
|
|
||||||
}, [status, isConnected])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useStore.setState({ status })
|
||||||
if (!isConnected && !params.wallet) {
|
useStore.setState(
|
||||||
router.push('/')
|
isConnected
|
||||||
return
|
? {
|
||||||
|
address: recentWallet?.account.address,
|
||||||
|
}
|
||||||
|
: { address: undefined, creditAccounts: null, client: undefined },
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!isConnected || !recentWallet) return
|
||||||
|
|
||||||
|
const fetchCreditAccounts = async () => {
|
||||||
|
if (!recentWallet?.account.address) return
|
||||||
|
const walletCreditAccounts = await getCreditAccounts(recentWallet?.account.address)
|
||||||
|
useStore.setState({ creditAccounts: walletCreditAccounts })
|
||||||
}
|
}
|
||||||
|
|
||||||
const address = client?.recentWallet.account.address
|
fetchCreditAccounts()
|
||||||
if (!address || address === params.wallet) return
|
|
||||||
|
|
||||||
router.push(`/wallets/${client.recentWallet.account.address}`)
|
|
||||||
}, [client, params, isConnected])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!recentWallet) return
|
|
||||||
if (!client) {
|
if (!client) {
|
||||||
const getCosmWasmClient = async () => {
|
const getCosmWasmClient = async () => {
|
||||||
const cosmClient = await getClient(recentWallet.network.rpc)
|
const cosmClient = await getClient(recentWallet.network.rpc)
|
||||||
@ -57,9 +61,11 @@ export default function Wallet() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getCosmWasmClient()
|
getCosmWasmClient()
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}, [simulate, sign, recentWallet, broadcast])
|
|
||||||
return isConnected ? <ConnectedButton /> : <ConnectButton status={status} />
|
if (!address || address === params.wallet) return
|
||||||
|
router.push(`/wallets/${address}`)
|
||||||
|
}, [address, broadcast, client, params, recentWallet, router, simulate, sign, status])
|
||||||
|
|
||||||
|
return address ? <ConnectedButton /> : <ConnectButton status={status} />
|
||||||
}
|
}
|
||||||
|
@ -3,25 +3,27 @@
|
|||||||
import { WalletManagerProvider } from '@marsprotocol/wallet-connector'
|
import { WalletManagerProvider } from '@marsprotocol/wallet-connector'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
|
|
||||||
|
import { Button } from 'components/Button'
|
||||||
import { CircularProgress } from 'components/CircularProgress'
|
import { CircularProgress } from 'components/CircularProgress'
|
||||||
import { CHAIN_ID, ENV_MISSING_MESSAGE, URL_REST, URL_RPC, WALLETS } from 'constants/env'
|
import { Close } from 'components/Icons'
|
||||||
|
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WalletConnectProvider: FC<Props> = ({ children }) => {
|
export const WalletConnectProvider: FC<Props> = ({ children }) => {
|
||||||
if (!CHAIN_ID || !URL_REST || !URL_RPC || !WALLETS) {
|
if (!ENV.CHAIN_ID || !ENV.URL_REST || !ENV.URL_RPC || !ENV.WALLETS) {
|
||||||
console.error(ENV_MISSING_MESSAGE)
|
console.error(ENV_MISSING_MESSAGE)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const chainInfoOverrides = {
|
const chainInfoOverrides = {
|
||||||
rpc: URL_RPC,
|
rpc: ENV.URL_RPC,
|
||||||
rest: URL_REST,
|
rest: ENV.URL_REST,
|
||||||
chainID: CHAIN_ID,
|
chainID: ENV.CHAIN_ID,
|
||||||
}
|
}
|
||||||
const enabledWallets: string[] = WALLETS
|
const enabledWallets: string[] = ENV.WALLETS
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WalletManagerProvider
|
<WalletManagerProvider
|
||||||
@ -30,6 +32,21 @@ export const WalletConnectProvider: FC<Props> = ({ children }) => {
|
|||||||
defaultChainId={chainInfoOverrides.chainID}
|
defaultChainId={chainInfoOverrides.chainID}
|
||||||
enabledWallets={enabledWallets}
|
enabledWallets={enabledWallets}
|
||||||
persistent
|
persistent
|
||||||
|
classNames={{
|
||||||
|
modalContent:
|
||||||
|
'relative z-50 w-[460px] max-w-full rounded-base border border-white/20 bg-white/5 p-6 pb-4 backdrop-blur-3xl flex flex-wrap',
|
||||||
|
modalOverlay:
|
||||||
|
'fixed inset-0 bg-black/60 w-full h-full z-40 flex items-center justify-center cursor-pointer m-0 backdrop-blur-sm',
|
||||||
|
modalHeader: 'text-lg text-white mb-4 flex-grow',
|
||||||
|
modalCloseButton: 'inline-block',
|
||||||
|
walletList: 'w-full',
|
||||||
|
wallet:
|
||||||
|
'flex bg-transparent p-2 w-full rounded-sm cursor-pointer transition duration-500 ease-in mb-2 hover:bg-primary',
|
||||||
|
walletImage: 'w-10 h-10',
|
||||||
|
walletName: 'w-full text-lg',
|
||||||
|
walletDescription: 'w-full text-xs text-white/60 break-all',
|
||||||
|
}}
|
||||||
|
closeIcon={<Button icon={<Close />} iconClassName='h-2 w-2' color='tertiary' />}
|
||||||
renderLoader={() => (
|
renderLoader={() => (
|
||||||
<div>
|
<div>
|
||||||
<CircularProgress size={30} />
|
<CircularProgress size={30} />
|
||||||
|
14
src/components/pages/borrow.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import Borrowings from 'components/Borrow/Borrowings'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
params: PageParams
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BorrowPage(props: Props) {
|
||||||
|
return (
|
||||||
|
<div className='flex w-full flex-col gap-4'>
|
||||||
|
<Borrowings params={props.params} type='active' />
|
||||||
|
<Borrowings params={props.params} type='available' />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
9
src/components/pages/council.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import Overview from 'components/Council/Overview'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
params: PageParams
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CouncilPage(props: Props) {
|
||||||
|
return <Overview params={props.params} />
|
||||||
|
}
|
9
src/components/pages/earn.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import Overview from 'components/Earn/Overview'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
params: PageParams
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EarnPage(props: Props) {
|
||||||
|
return <Overview params={props.params} />
|
||||||
|
}
|
9
src/components/pages/portfolio.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import AccountOverview from 'components/Portfolio/AccountOverview'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
params: PageParams
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PortfolioPage(props: Props) {
|
||||||
|
return <AccountOverview params={props.params} />
|
||||||
|
}
|
17
src/components/pages/trade.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import OrderBook from 'components/Trade/OrderBook'
|
||||||
|
import Trade from 'components/Trade/Trade'
|
||||||
|
import TradingView from 'components/Trade/TradingView'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
params: PageParams
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TradePage(props: Props) {
|
||||||
|
return (
|
||||||
|
<div className='grid grid-flow-row grid-cols-3 grid-rows-2 gap-4'>
|
||||||
|
<TradingView params={props.params} />
|
||||||
|
<Trade params={props.params} />
|
||||||
|
<OrderBook params={props.params} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -1,18 +1,46 @@
|
|||||||
export const ADDRESS_ACCOUNT_NFT = process.env.NEXT_PUBLIC_ACCOUNT_NFT
|
interface EnvironmentVariables {
|
||||||
export const ADDRESS_CREDIT_MANAGER = process.env.NEXT_PUBLIC_CREDIT_MANAGER
|
ADDRESS_ACCOUNT_NFT: string | undefined
|
||||||
export const ADDRESS_INCENTIVES = process.env.NEXT_PUBLIC_INCENTIVES
|
ADDRESS_CREDIT_MANAGER: string | undefined
|
||||||
export const ADDRESS_ORACLE = process.env.NEXT_PUBLIC_ORACLE
|
ADDRESS_INCENTIVES: string | undefined
|
||||||
export const ADDRESS_RED_BANK = process.env.NEXT_PUBLIC_RED_BANK
|
ADDRESS_ORACLE: string | undefined
|
||||||
export const ADDRESS_SWAPPER = process.env.NEXT_PUBLIC_SWAPPER
|
ADDRESS_RED_BANK: string | undefined
|
||||||
export const ADDRESS_ZAPPER = process.env.NEXT_PUBLIC_ZAPPER
|
ADDRESS_SWAPPER: string | undefined
|
||||||
|
ADDRESS_ZAPPER: string | undefined
|
||||||
|
CHAIN_ID: string | undefined
|
||||||
|
NETWORK: string | undefined
|
||||||
|
URL_GQL: string | undefined
|
||||||
|
URL_REST: string | undefined
|
||||||
|
URL_RPC: string | undefined
|
||||||
|
URL_API: string | undefined
|
||||||
|
WALLETS: string[] | undefined
|
||||||
|
}
|
||||||
|
|
||||||
export const CHAIN_ID = process.env.NEXT_PUBLIC_CHAIN_ID
|
export const ENV: EnvironmentVariables = {
|
||||||
export const NETWORK = process.env.NEXT_PUBLIC_NETWORK
|
ADDRESS_ACCOUNT_NFT: process.env.NEXT_PUBLIC_ACCOUNT_NFT,
|
||||||
export const IS_TESTNET = NETWORK !== 'mainnet'
|
ADDRESS_CREDIT_MANAGER: process.env.NEXT_PUBLIC_CREDIT_MANAGER,
|
||||||
export const URL_GQL = process.env.NEXT_PUBLIC_GQL
|
ADDRESS_INCENTIVES: process.env.NEXT_PUBLIC_INCENTIVES,
|
||||||
export const URL_REST = process.env.NEXT_PUBLIC_REST
|
ADDRESS_ORACLE: process.env.NEXT_PUBLIC_ORACLE,
|
||||||
export const URL_RPC = process.env.NEXT_PUBLIC_RPC
|
ADDRESS_RED_BANK: process.env.NEXT_PUBLIC_RED_BANK,
|
||||||
export const URL_API = process.env.NEXT_PUBLIC_API
|
ADDRESS_SWAPPER: process.env.NEXT_PUBLIC_SWAPPER,
|
||||||
export const WALLETS = process.env.NEXT_PUBLIC_WALLETS?.split(',') ?? []
|
ADDRESS_ZAPPER: process.env.NEXT_PUBLIC_ZAPPER,
|
||||||
|
CHAIN_ID: process.env.NEXT_PUBLIC_CHAIN_ID,
|
||||||
|
NETWORK: process.env.NEXT_PUBLIC_NETWORK,
|
||||||
|
URL_GQL: process.env.NEXT_PUBLIC_GQL,
|
||||||
|
URL_REST: process.env.NEXT_PUBLIC_REST,
|
||||||
|
URL_RPC: process.env.NEXT_PUBLIC_RPC,
|
||||||
|
URL_API: process.env.NEXT_PUBLIC_API,
|
||||||
|
WALLETS: process.env.NEXT_PUBLIC_WALLETS?.split(','),
|
||||||
|
}
|
||||||
|
|
||||||
export const ENV_MISSING_MESSAGE = 'Environment variable missing'
|
export const IS_TESTNET = ENV.NETWORK !== 'mainnet'
|
||||||
|
|
||||||
|
export const ENV_MISSING_MESSAGE = () => {
|
||||||
|
const missing: string[] = []
|
||||||
|
Object.keys(ENV).forEach((key) => {
|
||||||
|
if (!ENV[key as keyof EnvironmentVariables]) {
|
||||||
|
missing.push(key)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return `Environment variable(s) missing for: ${missing.join(', ')}`
|
||||||
|
}
|
||||||
|
@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query'
|
|||||||
import request, { gql } from 'graphql-request'
|
import request, { gql } from 'graphql-request'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
import { URL_GQL } from 'constants/env'
|
import { ENV } from 'constants/env'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import { queryKeys } from 'types/query-keys-factory'
|
import { queryKeys } from 'types/query-keys-factory'
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ export const useAllBalances = () => {
|
|||||||
queryKeys.allBalances(address ?? ''),
|
queryKeys.allBalances(address ?? ''),
|
||||||
async () => {
|
async () => {
|
||||||
return await request(
|
return await request(
|
||||||
URL_GQL!,
|
ENV.URL_GQL!,
|
||||||
gql`
|
gql`
|
||||||
query UserBalanceQuery {
|
query UserBalanceQuery {
|
||||||
balance: bank {
|
balance: bank {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
|
||||||
import { ADDRESS_CREDIT_MANAGER } from 'constants/env'
|
import { ENV } from 'constants/env'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import { queryKeys } from 'types/query-keys-factory'
|
import { queryKeys } from 'types/query-keys-factory'
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ const queryMsg = {
|
|||||||
|
|
||||||
export const useAllowedCoins = () => {
|
export const useAllowedCoins = () => {
|
||||||
const client = useStore((s) => s.signingClient)
|
const client = useStore((s) => s.signingClient)
|
||||||
const creditManagerAddress = ADDRESS_CREDIT_MANAGER
|
const creditManagerAddress = ENV.ADDRESS_CREDIT_MANAGER
|
||||||
|
|
||||||
const result = useQuery<Result>(
|
const result = useQuery<Result>(
|
||||||
queryKeys.allowedCoins(),
|
queryKeys.allowedCoins(),
|
||||||
|
@ -2,7 +2,7 @@ import { Coin } from '@cosmjs/stargate'
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
import { ADDRESS_CREDIT_MANAGER } from 'constants/env'
|
import { ENV } from 'constants/env'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import { queryKeys } from 'types/query-keys-factory'
|
import { queryKeys } from 'types/query-keys-factory'
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ interface Result {
|
|||||||
export const useCreditAccountPositions = (accountId: string) => {
|
export const useCreditAccountPositions = (accountId: string) => {
|
||||||
const address = useStore((s) => s.address)
|
const address = useStore((s) => s.address)
|
||||||
const client = useStore((s) => s.signingClient)
|
const client = useStore((s) => s.signingClient)
|
||||||
const creditManagerAddress = ADDRESS_CREDIT_MANAGER
|
const creditManagerAddress = ENV.ADDRESS_CREDIT_MANAGER
|
||||||
|
|
||||||
const result = useQuery<Result>(
|
const result = useQuery<Result>(
|
||||||
queryKeys.creditAccountsPositions(accountId),
|
queryKeys.creditAccountsPositions(accountId),
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
import { ADDRESS_ACCOUNT_NFT } from 'constants/env'
|
import { ENV } from 'constants/env'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import { queryKeys } from 'types/query-keys-factory'
|
import { queryKeys } from 'types/query-keys-factory'
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ export const useCreditAccounts = () => {
|
|||||||
const address = useStore((s) => s.address)
|
const address = useStore((s) => s.address)
|
||||||
const client = useStore((s) => s.signingClient)
|
const client = useStore((s) => s.signingClient)
|
||||||
const selectedAccount = useStore((s) => s.selectedAccount)
|
const selectedAccount = useStore((s) => s.selectedAccount)
|
||||||
const accountNftAddress = ADDRESS_ACCOUNT_NFT
|
const accountNftAddress = ENV.ADDRESS_ACCOUNT_NFT
|
||||||
const setSelectedAccount = (account: string) => {
|
const setSelectedAccount = (account: string) => {
|
||||||
useStore.setState({ selectedAccount: account })
|
useStore.setState({ selectedAccount: account })
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query'
|
|||||||
import request, { gql } from 'graphql-request'
|
import request, { gql } from 'graphql-request'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
import { ADDRESS_RED_BANK, URL_GQL } from 'constants/env'
|
import { ENV } from 'constants/env'
|
||||||
import { queryKeys } from 'types/query-keys-factory'
|
import { queryKeys } from 'types/query-keys-factory'
|
||||||
|
|
||||||
interface Result {
|
interface Result {
|
||||||
@ -13,12 +13,12 @@ interface Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useRedbankBalances = () => {
|
export const useRedbankBalances = () => {
|
||||||
const redBankAddress = ADDRESS_RED_BANK
|
const redBankAddress = ENV.ADDRESS_RED_BANK
|
||||||
const result = useQuery<Result>(
|
const result = useQuery<Result>(
|
||||||
queryKeys.redbankBalances(),
|
queryKeys.redbankBalances(),
|
||||||
async () => {
|
async () => {
|
||||||
return await request(
|
return await request(
|
||||||
URL_GQL!,
|
ENV.URL_GQL!,
|
||||||
gql`
|
gql`
|
||||||
query RedbankBalances {
|
query RedbankBalances {
|
||||||
bank {
|
bank {
|
||||||
|
@ -2,11 +2,15 @@ import { useQuery } from '@tanstack/react-query'
|
|||||||
import { gql, request } from 'graphql-request'
|
import { gql, request } from 'graphql-request'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
import { ADDRESS_ORACLE, URL_GQL } from 'constants/env'
|
import { ENV } from 'constants/env'
|
||||||
import { queryKeys } from 'types/query-keys-factory'
|
import { queryKeys } from 'types/query-keys-factory'
|
||||||
import { getMarketAssets } from 'utils/assets'
|
import { getMarketAssets } from 'utils/assets'
|
||||||
|
|
||||||
const fetchTokenPrices = async (hiveUrl: string, marketAssets: Asset[], oracleAddress: string) => {
|
const fetchTokenPrices = async (
|
||||||
|
hiveUrl: string,
|
||||||
|
marketAssets: Asset[],
|
||||||
|
oracleAddress: string,
|
||||||
|
): Promise<TokenPricesResult> => {
|
||||||
return request(
|
return request(
|
||||||
hiveUrl,
|
hiveUrl,
|
||||||
gql`
|
gql`
|
||||||
@ -32,7 +36,7 @@ export const useTokenPrices = () => {
|
|||||||
const marketAssets = getMarketAssets()
|
const marketAssets = getMarketAssets()
|
||||||
const result = useQuery<TokenPricesResult>(
|
const result = useQuery<TokenPricesResult>(
|
||||||
queryKeys.tokenPrices(),
|
queryKeys.tokenPrices(),
|
||||||
async () => await fetchTokenPrices(URL_GQL!, marketAssets, ADDRESS_ORACLE || ''),
|
async () => await fetchTokenPrices(ENV.URL_GQL!, marketAssets, ENV.ADDRESS_ORACLE || ''),
|
||||||
{
|
{
|
||||||
refetchInterval: 30000,
|
refetchInterval: 30000,
|
||||||
staleTime: Infinity,
|
staleTime: Infinity,
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
||||||
import { ADDRESS_CREDIT_MANAGER, ENV_MISSING_MESSAGE, URL_API, URL_RPC } from 'constants/env'
|
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!URL_RPC || !ADDRESS_CREDIT_MANAGER || !URL_API) {
|
if (!ENV.URL_RPC || !ENV.ADDRESS_CREDIT_MANAGER || !ENV.URL_API) {
|
||||||
return res.status(404).json(ENV_MISSING_MESSAGE)
|
return res.status(404).json(ENV_MISSING_MESSAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountId = req.query.id
|
const accountId = req.query.id
|
||||||
|
|
||||||
const account = await (await fetch(`${URL_API}/accounts/${accountId}`)).json()
|
const account = await (await fetch(`${ENV.URL_API}/accounts/${accountId}`)).json()
|
||||||
|
|
||||||
if (account) {
|
if (account) {
|
||||||
return res.status(200).json([{ denom: 'uosmo', amount: '123876' }])
|
return res.status(200).json([{ denom: 'uosmo', amount: '123876' }])
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
||||||
import { ADDRESS_CREDIT_MANAGER, ENV_MISSING_MESSAGE, URL_API, URL_RPC } from 'constants/env'
|
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!URL_RPC || !ADDRESS_CREDIT_MANAGER || !URL_API) {
|
if (!ENV.URL_RPC || !ENV.ADDRESS_CREDIT_MANAGER || !ENV.URL_API) {
|
||||||
return res.status(404).json(ENV_MISSING_MESSAGE)
|
return res.status(404).json(ENV_MISSING_MESSAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountId = req.query.id
|
const accountId = req.query.id
|
||||||
|
|
||||||
const account = await (await fetch(`${URL_API}/accounts/${accountId}`)).json()
|
const account = await (await fetch(`${ENV.URL_API}/accounts/${accountId}`)).json()
|
||||||
|
|
||||||
if (account) {
|
if (account) {
|
||||||
return res.status(200).json(account.deposits)
|
return res.status(200).json(account.deposits)
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
|
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
||||||
import { ADDRESS_CREDIT_MANAGER, ENV_MISSING_MESSAGE, URL_RPC } from 'constants/env'
|
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!URL_RPC || !ADDRESS_CREDIT_MANAGER) {
|
if (!ENV.URL_RPC || !ENV.ADDRESS_CREDIT_MANAGER) {
|
||||||
return res.status(404).json(ENV_MISSING_MESSAGE)
|
return res.status(404).json(ENV_MISSING_MESSAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountId = req.query.id
|
const accountId = req.query.id
|
||||||
|
|
||||||
const client = await CosmWasmClient.connect(URL_RPC)
|
const client = await CosmWasmClient.connect(ENV.URL_RPC)
|
||||||
|
|
||||||
const data = await client.queryContractSmart(ADDRESS_CREDIT_MANAGER, {
|
const data = await client.queryContractSmart(ENV.ADDRESS_CREDIT_MANAGER, {
|
||||||
positions: {
|
positions: {
|
||||||
account_id: accountId,
|
account_id: accountId,
|
||||||
},
|
},
|
||||||
|
@ -2,20 +2,20 @@ import { Coin } from '@cosmjs/stargate'
|
|||||||
import { gql, request as gqlRequest } from 'graphql-request'
|
import { gql, request as gqlRequest } from 'graphql-request'
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
||||||
import { ADDRESS_RED_BANK, ENV_MISSING_MESSAGE, URL_GQL } from 'constants/env'
|
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!URL_GQL || !ADDRESS_RED_BANK) {
|
if (!ENV.URL_GQL || !ENV.ADDRESS_RED_BANK) {
|
||||||
return res.status(404).json(ENV_MISSING_MESSAGE)
|
return res.status(404).json(ENV_MISSING_MESSAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await gqlRequest<Result>(
|
const result = await gqlRequest<Result>(
|
||||||
URL_GQL,
|
ENV.URL_GQL,
|
||||||
gql`
|
gql`
|
||||||
query RedbankBalances {
|
query RedbankBalances {
|
||||||
bank {
|
bank {
|
||||||
balance(
|
balance(
|
||||||
address: "${ADDRESS_RED_BANK}"
|
address: "${ENV.ADDRESS_RED_BANK}"
|
||||||
) {
|
) {
|
||||||
amount
|
amount
|
||||||
denom
|
denom
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
|
||||||
import { Coin } from '@cosmjs/stargate'
|
import { Coin } from '@cosmjs/stargate'
|
||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
||||||
import { ENV_MISSING_MESSAGE, URL_API } from 'constants/env'
|
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
||||||
import { getMarketAssets } from 'utils/assets'
|
import { getMarketAssets } from 'utils/assets'
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!URL_API) {
|
if (!ENV.URL_API) {
|
||||||
return res.status(404).json(ENV_MISSING_MESSAGE)
|
return res.status(404).json(ENV_MISSING_MESSAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
const marketAssets = getMarketAssets()
|
const marketAssets = getMarketAssets()
|
||||||
const $liquidity = fetch(`${URL_API}/markets/liquidity`)
|
const $liquidity = fetch(`${ENV.URL_API}/markets/liquidity`)
|
||||||
const $markets = fetch(`${URL_API}/markets`)
|
const $markets = fetch(`${ENV.URL_API}/markets`)
|
||||||
const $prices = fetch(`${URL_API}/prices`)
|
const $prices = fetch(`${ENV.URL_API}/prices`)
|
||||||
|
|
||||||
const borrow: BorrowAsset[] = await Promise.all([$liquidity, $markets, $prices]).then(
|
const borrow: BorrowAsset[] = await Promise.all([$liquidity, $markets, $prices]).then(
|
||||||
async ([$liquidity, $markets, $prices]) => {
|
async ([$liquidity, $markets, $prices]) => {
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { gql, request as gqlRequest } from 'graphql-request'
|
import { gql, request as gqlRequest } from 'graphql-request'
|
||||||
|
|
||||||
import { ADDRESS_RED_BANK, ENV_MISSING_MESSAGE, URL_API, URL_GQL } from 'constants/env'
|
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
||||||
import { getContractQuery } from 'utils/query'
|
import { getContractQuery } from 'utils/query'
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!URL_API || !ADDRESS_RED_BANK || !URL_GQL) {
|
if (!ENV.URL_API || !ENV.ADDRESS_RED_BANK || !ENV.URL_GQL) {
|
||||||
return res.status(404).json(ENV_MISSING_MESSAGE)
|
return res.status(404).json(ENV_MISSING_MESSAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
const markets: Market[] = await (await fetch(`${URL_API}/markets`)).json()
|
const markets: Market[] = await (await fetch(`${ENV.URL_API}/markets`)).json()
|
||||||
|
|
||||||
let query = ''
|
let query = ''
|
||||||
|
|
||||||
markets.forEach((asset: any) => {
|
markets.forEach((asset: any) => {
|
||||||
query += getContractQuery(
|
query += getContractQuery(
|
||||||
asset.denom,
|
asset.denom,
|
||||||
ADDRESS_RED_BANK || '',
|
ENV.ADDRESS_RED_BANK || '',
|
||||||
`
|
`
|
||||||
{
|
{
|
||||||
underlying_debt_amount: {
|
underlying_debt_amount: {
|
||||||
@ -28,7 +28,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
})
|
})
|
||||||
|
|
||||||
const result = await gqlRequest<DebtsQuery>(
|
const result = await gqlRequest<DebtsQuery>(
|
||||||
URL_GQL,
|
ENV.URL_GQL,
|
||||||
gql`
|
gql`
|
||||||
query RedbankBalances {
|
query RedbankBalances {
|
||||||
debts: wasm {
|
debts: wasm {
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { gql, request as gqlRequest } from 'graphql-request'
|
import { gql, request as gqlRequest } from 'graphql-request'
|
||||||
|
|
||||||
import { ADDRESS_RED_BANK, ENV_MISSING_MESSAGE, URL_API, URL_GQL, URL_RPC } from 'constants/env'
|
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
||||||
import { getContractQuery } from 'utils/query'
|
import { getContractQuery } from 'utils/query'
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!URL_RPC || !ADDRESS_RED_BANK || !URL_GQL || !URL_API) {
|
if (!ENV.URL_RPC || !ENV.ADDRESS_RED_BANK || !ENV.URL_GQL || !ENV.URL_API) {
|
||||||
return res.status(404).json(ENV_MISSING_MESSAGE)
|
return res.status(404).json(ENV_MISSING_MESSAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
const markets = await (await fetch(`${URL_API}/markets`)).json()
|
const markets = await (await fetch(`${ENV.URL_API}/markets`)).json()
|
||||||
|
|
||||||
let query = ''
|
let query = ''
|
||||||
|
|
||||||
markets.forEach((asset: any) => {
|
markets.forEach((asset: any) => {
|
||||||
query += getContractQuery(
|
query += getContractQuery(
|
||||||
asset.denom,
|
asset.denom,
|
||||||
ADDRESS_RED_BANK || '',
|
ENV.ADDRESS_RED_BANK || '',
|
||||||
`
|
`
|
||||||
{
|
{
|
||||||
underlying_liquidity_amount: {
|
underlying_liquidity_amount: {
|
||||||
@ -28,7 +28,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
})
|
})
|
||||||
|
|
||||||
const result = await gqlRequest<DepositsQuery>(
|
const result = await gqlRequest<DepositsQuery>(
|
||||||
URL_GQL,
|
ENV.URL_GQL,
|
||||||
gql`
|
gql`
|
||||||
query RedbankBalances {
|
query RedbankBalances {
|
||||||
deposits: wasm {
|
deposits: wasm {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { gql, request as gqlRequest } from 'graphql-request'
|
import { gql, request as gqlRequest } from 'graphql-request'
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
||||||
import { ADDRESS_INCENTIVES, ADDRESS_RED_BANK, ENV_MISSING_MESSAGE, URL_GQL } from 'constants/env'
|
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
||||||
import { getMarketAssets } from 'utils/assets'
|
import { getMarketAssets } from 'utils/assets'
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!URL_GQL || !ADDRESS_RED_BANK || !ADDRESS_INCENTIVES) {
|
if (!ENV.URL_GQL || !ENV.ADDRESS_RED_BANK || !ENV.ADDRESS_INCENTIVES) {
|
||||||
return res.status(404).json(ENV_MISSING_MESSAGE)
|
return res.status(404).json(ENV_MISSING_MESSAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14,13 +14,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
const marketQueries = marketAssets.map(
|
const marketQueries = marketAssets.map(
|
||||||
(asset: Asset) =>
|
(asset: Asset) =>
|
||||||
`${asset.denom}: contractQuery(
|
`${asset.denom}: contractQuery(
|
||||||
contractAddress: "${ADDRESS_RED_BANK}"
|
contractAddress: "${ENV.ADDRESS_RED_BANK}"
|
||||||
query: { market: { denom: "${asset.denom}" } }
|
query: { market: { denom: "${asset.denom}" } }
|
||||||
)`,
|
)`,
|
||||||
)
|
)
|
||||||
|
|
||||||
const result = await gqlRequest<RedBankData>(
|
const result = await gqlRequest<RedBankData>(
|
||||||
URL_GQL,
|
ENV.URL_GQL,
|
||||||
gql`
|
gql`
|
||||||
query RedbankQuery {
|
query RedbankQuery {
|
||||||
rbwasmkey: wasm {
|
rbwasmkey: wasm {
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { Coin } from '@cosmjs/stargate'
|
import { Coin } from '@cosmjs/stargate'
|
||||||
|
|
||||||
import { ENV_MISSING_MESSAGE, URL_API } from 'constants/env'
|
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!URL_API) {
|
if (!ENV.URL_API) {
|
||||||
return res.status(404).json(ENV_MISSING_MESSAGE)
|
return res.status(404).json(ENV_MISSING_MESSAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
const $deposits = fetch(`${URL_API}/markets/deposits`)
|
const $deposits = fetch(`${ENV.URL_API}/markets/deposits`)
|
||||||
const $debts = fetch(`${URL_API}/markets/debts`)
|
const $debts = fetch(`${ENV.URL_API}/markets/debts`)
|
||||||
|
|
||||||
const liquidity: Coin[] = await Promise.all([$deposits, $debts]).then(
|
const liquidity: Coin[] = await Promise.all([$deposits, $debts]).then(
|
||||||
async ([$deposits, $debts]) => {
|
async ([$deposits, $debts]) => {
|
||||||
|
@ -2,24 +2,24 @@ import { gql, request as gqlRequest } from 'graphql-request'
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { Coin } from '@cosmjs/stargate'
|
import { Coin } from '@cosmjs/stargate'
|
||||||
|
|
||||||
import { ADDRESS_ORACLE, ENV_MISSING_MESSAGE, URL_GQL } from 'constants/env'
|
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
||||||
import { getMarketAssets } from 'utils/assets'
|
import { getMarketAssets } from 'utils/assets'
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!URL_GQL || !ADDRESS_ORACLE) {
|
if (!ENV.URL_GQL || !ENV.ADDRESS_ORACLE) {
|
||||||
return res.status(404).json(ENV_MISSING_MESSAGE)
|
return res.status(404).json(ENV_MISSING_MESSAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
const marketAssets = getMarketAssets()
|
const marketAssets = getMarketAssets()
|
||||||
|
|
||||||
const result = await gqlRequest<TokenPricesResult>(
|
const result = await gqlRequest<TokenPricesResult>(
|
||||||
URL_GQL,
|
ENV.URL_GQL,
|
||||||
gql`
|
gql`
|
||||||
query PriceOracle {
|
query PriceOracle {
|
||||||
prices: wasm {
|
prices: wasm {
|
||||||
${marketAssets.map((asset) => {
|
${marketAssets.map((asset) => {
|
||||||
return `${asset.symbol}: contractQuery(
|
return `${asset.symbol}: contractQuery(
|
||||||
contractAddress: "${ADDRESS_ORACLE}"
|
contractAddress: "${ENV.ADDRESS_ORACLE}"
|
||||||
query: {
|
query: {
|
||||||
price: {
|
price: {
|
||||||
denom: "${asset.denom}"
|
denom: "${asset.denom}"
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
|
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
||||||
import { ADDRESS_CREDIT_MANAGER, ENV_MISSING_MESSAGE, URL_RPC } from 'constants/env'
|
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!URL_RPC || !ADDRESS_CREDIT_MANAGER) {
|
if (!ENV.URL_RPC || !ENV.ADDRESS_CREDIT_MANAGER) {
|
||||||
return res.status(404).json(ENV_MISSING_MESSAGE)
|
return res.status(404).json(ENV_MISSING_MESSAGE)
|
||||||
}
|
}
|
||||||
const client = await CosmWasmClient.connect(URL_RPC)
|
const client = await CosmWasmClient.connect(ENV.URL_RPC)
|
||||||
|
|
||||||
const data = await client.queryContractSmart(ADDRESS_CREDIT_MANAGER, {
|
const data = await client.queryContractSmart(ENV.ADDRESS_CREDIT_MANAGER, {
|
||||||
vaults_info: { limit: 5, start_after: undefined },
|
vaults_info: { limit: 5, start_after: undefined },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
|
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
||||||
import { ADDRESS_ACCOUNT_NFT, ENV_MISSING_MESSAGE, URL_RPC } from 'constants/env'
|
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!URL_RPC || !ADDRESS_ACCOUNT_NFT) {
|
if (!ENV.URL_RPC || !ENV.ADDRESS_ACCOUNT_NFT) {
|
||||||
return res.status(404).json(ENV_MISSING_MESSAGE)
|
return res.status(404).json(ENV_MISSING_MESSAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
const address = req.query.address
|
const address = req.query.address
|
||||||
|
|
||||||
const client = await CosmWasmClient.connect(URL_RPC)
|
const client = await CosmWasmClient.connect(ENV.URL_RPC)
|
||||||
|
|
||||||
const data = await client.queryContractSmart(ADDRESS_ACCOUNT_NFT, {
|
const data = await client.queryContractSmart(ENV.ADDRESS_ACCOUNT_NFT, {
|
||||||
tokens: {
|
tokens: {
|
||||||
owner: address,
|
owner: address,
|
||||||
},
|
},
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
||||||
import { ENV_MISSING_MESSAGE, URL_REST } from 'constants/env'
|
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!URL_REST) {
|
if (!ENV.URL_REST) {
|
||||||
return res.status(404).json(ENV_MISSING_MESSAGE)
|
return res.status(404).json(ENV_MISSING_MESSAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
const address = req.query.address
|
const address = req.query.address
|
||||||
const uri = '/cosmos/bank/v1beta1/balances/'
|
const uri = '/cosmos/bank/v1beta1/balances/'
|
||||||
|
|
||||||
const response = await fetch(`${URL_REST}${uri}${address}`)
|
const response = await fetch(`${ENV.URL_REST}${uri}${address}`)
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
|