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>
This commit is contained in:
Linkie Link 2023-03-08 10:44:39 +01:00 committed by GitHub
parent cbb0700455
commit 21268e5536
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
110 changed files with 2602 additions and 2483 deletions

View File

@ -9,5 +9,7 @@ NEXT_PUBLIC_ACCOUNT_NFT=osmo1l8c3g6zy7kjhuh8d2kqyvxkw0myn4puxv0tzcdf9nwxd386r9l7
NEXT_PUBLIC_ORACLE=osmo1dqz2u3c8rs5e7w5fnchsr2mpzzsxew69wtdy0aq4jsd76w7upmsstqe0s8
NEXT_PUBLIC_RED_BANK=osmo1g30recyv8pfy3qd4qn3dn7plc0rn5z68y5gn32j39e96tjhthzxsw3uvvu
NEXT_PUBLIC_CREDIT_MANAGER=osmo12hgn4jec4tftahm7spf7c2aqsqrxzzk50hkq60e89atumyu0zvys7vzxdc
NEXT_PUBLIC_INCENTIVES=osmo1zxs8fry3m8j94pqg7h4muunyx86en27cl0xgk76fc839xg2qnn6qtpjs48
NEXT_PUBLIC_ZAPPER=osmo1ua8dwc9v8qjh7n3qf8kg6xvrwjm5yu9xxln7yjvgmrvfzaxvzsuqfcdnjq
NEXT_PUBLIC_SWAPPER=osmo1uj6r9tu440wwp2mhtagh48yvmeyeaqt2xa7kdnlhyrqcuthlj4ss7ghg6n
NEXT_PUBLIC_API=http://localhost:3000/api

View File

@ -39,7 +39,7 @@
"recharts": "^2.2.0",
"sass": "^1.58.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",
"zustand": "^4.1.4"
},

View File

@ -1,3 +0,0 @@
export default function laoding() {
return '...isLoading'
}

View File

@ -1,7 +1,5 @@
import { getBorrowData } from 'utils/api'
import BorrowPage from 'components/pages/borrow'
export default async function page() {
const borrowData = await getBorrowData()
return `You are a guest`
export default async function page({ params }: PageProps) {
return <BorrowPage params={params} />
}

View File

@ -1,3 +1,5 @@
export default function page() {
return `You are a guest`
import CouncilPage from 'components/pages/council'
export default async function page({ params }: PageProps) {
return <CouncilPage params={params} />
}

View File

@ -1,3 +1,5 @@
export default function page() {
return `You are a guest`
import EarnPage from 'components/pages/earn'
export default async function page({ params }: PageProps) {
return <EarnPage params={params} />
}

View File

@ -1,3 +1,6 @@
import classNames from 'classnames'
import AccountDetails from 'components/Account/AccountDetails'
import Background from 'components/Background'
import FetchPrices from 'components/FetchPrices'
import { Modals } from 'components/Modals'
@ -9,22 +12,25 @@ import 'styles/globals.scss'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang='en'>
<html className='m-0 p-0' lang='en'>
<head />
<body>
<div className='relative min-h-screen w-full'>
<body className='m-0 cursor-default bg-body p-0 font-sans text-white'>
<WalletConnectProvider>
<Background />
<DesktopNavigation />
</WalletConnectProvider>
<FetchPrices />
<main
className={classNames(
'relative flex justify-center py-6',
'lg:mt-[65px] lg:h-[calc(100vh-65px)]',
)}
>
<div className='flex max-w-content flex-grow flex-col flex-wrap'>{children}</div>
<AccountDetails />
</main>
<Modals />
<Toaster />
<FetchPrices />
<main className='relative flex lg:min-h-[calc(100vh-120px)]'>
<div className='flex flex-grow flex-col flex-wrap'>{children}</div>
</main>
</div>
</body>
</html>
)

View File

@ -1,3 +1,5 @@
export default function page() {
return 'Connect to your wallet'
import TradePage from 'components/pages/trade'
export default async function page({ params }: PageProps) {
return <TradePage params={params} />
}

View File

@ -1,3 +1,5 @@
export default function page() {
return `You are a guest`
import PortfolioPage from 'components/pages/portfolio'
export default async function page({ params }: PageProps) {
return <PortfolioPage params={params} />
}

View File

@ -1,3 +1,5 @@
export default function page() {
return `You are a guest`
import TradePage from 'components/pages/trade'
export default async function page({ params }: PageProps) {
return <TradePage params={params} />
}

View File

@ -1,45 +1,5 @@
import { BorrowTable } from 'components/Borrow/BorrowTable'
import { Card } from 'components/Card'
import { getAccountDebts, getBorrowData } from 'utils/api'
import { getMarketAssets } from 'utils/assets'
import BorrowPage from 'components/pages/borrow'
export default async function page({ params }: { params: PageParams }) {
const debtData = await getAccountDebts(params.account)
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>
)
export default async function page({ params }: PageProps) {
return <BorrowPage params={params} />
}

View File

@ -1,3 +0,0 @@
export default function loading() {
return '...isLoading'
}

View File

@ -1,11 +1,5 @@
import { Card } from 'components/Card'
import CouncilPage from 'components/pages/council'
export default function page() {
return (
<div className='flex w-full'>
<Card title='Council'>
<></>
</Card>
</div>
)
export default async function page({ params }: PageProps) {
return <CouncilPage params={params} />
}

View File

@ -1,3 +0,0 @@
export default function loading() {
return '...isLoading'
}

View File

@ -1,12 +1,5 @@
import { Card } from 'components/Card'
import { Text } from 'components/Text'
import EarnPage from 'components/pages/earn'
export default function page() {
return (
<div className='flex w-full gap-4'>
<Card title='Yield'>
<></>
</Card>
</div>
)
export default async function page({ params }: PageProps) {
return <EarnPage params={params} />
}

View File

@ -1,3 +1,5 @@
export default function page() {
return 'Trade page'
import TradePage from 'components/pages/trade'
export default async function page({ params }: PageProps) {
return <TradePage params={params} />
}

View File

@ -1,5 +0,0 @@
'use client'
export default function page({ params }: { params: PageParams }) {
return 'error!'
}

View File

@ -1,15 +1,5 @@
import { getCreditAccounts } from 'utils/api'
import PortfolioPage from 'components/pages/portfolio'
export default async function page({ params }: { params: PageParams }) {
const creditAccounts = await getCreditAccounts(params.wallet)
return (
<div className='flex w-full items-start gap-4'>
<ul>
{creditAccounts.map((account: string, index: number) => (
<li key={index}>{account}</li>
))}
</ul>
</div>
)
export default async function page({ params }: PageProps) {
return <PortfolioPage params={params} />
}

View File

@ -1,3 +0,0 @@
export default function loading() {
return '...isLoading'
}

View File

@ -1,21 +1,5 @@
import { Card } from 'components/Card'
import TradePage from 'components/pages/trade'
export default function page() {
return (
<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>
)
export default async function page({ params }: PageProps) {
return <TradePage params={params} />
}

View File

@ -1,3 +0,0 @@
export default function page() {
return `You are a viewer or a user`
}

View File

@ -1,3 +0,0 @@
export default function page() {
return `You are a viewer or a user`
}

View File

@ -1,3 +1,5 @@
export default function page() {
return `You are a viewer or a user`
import BorrowPage from 'components/pages/borrow'
export default async function page({ params }: PageProps) {
return <BorrowPage params={params} />
}

View File

@ -1,3 +0,0 @@
export default function page() {
return `You are a viewer or a user`
}

View File

@ -1,3 +0,0 @@
export default function page() {
return `You are a viewer or a user`
}

View File

@ -1,3 +1,5 @@
export default function page() {
return `You are a viewer or a user`
import CouncilPage from 'components/pages/council'
export default async function page({ params }: PageProps) {
return <CouncilPage params={params} />
}

View File

@ -1,3 +1,5 @@
export default function page() {
return `You are a viewer or a user`
import EarnPage from 'components/pages/earn'
export default async function page({ params }: PageProps) {
return <EarnPage params={params} />
}

View File

@ -1,21 +1,3 @@
import { AccountNavigation } from 'components/Account/AccountNavigation'
import { getCreditAccounts } from 'utils/api'
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>
</>
)
export default function RootLayout({ children }: { children: React.ReactNode }) {
return children
}

View File

@ -1,3 +1,5 @@
export default function page() {
return `You are a viewer or a user`
import PortfolioPage from 'components/pages/portfolio'
export default async function page({ params }: PageProps) {
return <PortfolioPage params={params} />
}

View File

@ -1,3 +1,5 @@
export default function page() {
return `You are a viewer or a user`
import TradePage from 'components/pages/trade'
export default async function page({ params }: PageProps) {
return <TradePage params={params} />
}

View File

@ -1,121 +1,40 @@
'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'
import { useEffect, useState } from 'react'
export default function AccountDetails() {
const params = useParams()
const hasAccount = params.account && !isNaN(Number(params.account))
import { AccountManageOverlay } from 'components/Account/AccountManageOverlay'
import { RiskChart } from 'components/Account/RiskChart'
import { Button } from 'components/Button'
import { ArrowRightLine, ChevronDown, ChevronLeft } from 'components/Icons'
import { LabelValuePair } from 'components/LabelValuePair'
import { PositionsList } from 'components/PositionsList'
import { useAccountStats } from 'hooks/data/useAccountStats'
import { convertFromGwei } from 'utils/formatters'
import { createRiskData } from 'utils/risk'
import useStore from 'store'
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>
return hasAccount ? (
<div className='fixed top-[89px] right-4 w-16 rounded-base border border-white/20 bg-white/5 backdrop-blur-sticky'>
<div className='flex w-full flex-wrap justify-center py-4'>
<Gauge tooltip='Health Factor' value={0.2} icon={<Heart />} />
<Text size='2xs' className='mt-1 mb-0.5 w-full text-center text-white/50'>
Health
</Text>
<Text size='xs' className='w-full text-center'>
89%
</Text>
</div>
<AccountManageOverlay
className='top-[60px] left-[36px]'
show={showManageMenu}
setShow={setShowManageMenu}
/>
<div className='w-full border border-x-0 border-white/20 py-4'>
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50'>
Leverage
</Text>
<Text size='xs' className='w-full text-center'>
4.5x
</Text>
</div>
<div className='flex w-full flex-wrap p-3'>
<LabelValuePair
className='mb-2'
label='Total Position:'
value={{
format: 'number',
amount: convertFromGwei(
accountStats?.totalPosition ?? 0,
baseAsset.denom,
marketAssets,
),
prefix: '$',
}}
/>
<LabelValuePair
label='Total Liabilities:'
value={{
format: 'number',
amount: convertFromGwei(accountStats?.totalDebt ?? 0, baseAsset.denom, marketAssets),
prefix: '$',
}}
/>
<div className='w-full py-4'>
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50'>
Balance
</Text>{' '}
<Text size='xs' className='w-full text-center'>
$300M
</Text>
</div>
{riskData && <RiskChart data={riskData} />}
</div>
)
) : null
}

View File

@ -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>
)
}

View File

@ -1,36 +1,38 @@
'use client'
import classNames from 'classnames'
import { useRouter } from 'next/navigation'
import { useState } from 'react'
import { AccountManageOverlay } from 'components/Account/AccountManageOverlay'
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 { Text } from 'components/Text'
import useParams from 'hooks/useParams'
import useStore from 'store'
import { hardcodedFee } from 'utils/contants'
const MAX_VISIBLE_CREDIT_ACCOUNTS = 5
interface Props {
creditAccounts: string[]
}
export const AccountNavigation = (props: Props) => {
export const AccountNavigation = () => {
const router = useRouter()
const params = useParams()
const address = useStore((s) => s.client?.recentWallet.account?.address) || ''
const selectedAccount = params.account
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 firstCreditAccounts = props.creditAccounts?.slice(0, MAX_VISIBLE_CREDIT_ACCOUNTS) ?? []
const restCreditAccounts = props.creditAccounts?.slice(MAX_VISIBLE_CREDIT_ACCOUNTS) ?? []
const hasCreditAccounts = !!creditAccounts?.length
const accountSelected = !!selectedAccount && !isNaN(Number(selectedAccount))
const [showManageMenu, setShowManageMenu] = useState(false)
const [showMoreMenu, setShowMoreMenu] = useState(false)
const [showMenu, setShowMenu] = useState(false)
async function createAccountHandler() {
const accountId = await createCreditAccount({ fee: hardcodedFee })
@ -38,93 +40,128 @@ export const AccountNavigation = (props: Props) => {
router.push(`/wallets/${params.wallet}/accounts/${accountId}`)
}
return (
<section
role='navigation'
className='flex h-11 w-full items-center gap-6 border-b border-white/20 px-6 text-sm text-white/40'
>
<>
{hasCreditAccounts ? (
<>
{firstCreditAccounts.map((account) => (
<Button
key={account}
className={classNames(
'cursor-pointer whitespace-nowrap px-4 text-base hover:text-white',
selectedAccount === account ? 'text-white' : 'text-white/40',
)}
variant='text'
onClick={() => {
router.push(`/wallets/${params.wallet}/accounts/${account}/${params.page}`)
}}
>
Account {account}
</Button>
))}
<div className='relative'>
{restCreditAccounts.length > 0 && (
<>
<Button
className='flex items-center px-3 py-3 text-base hover:text-white'
variant='text'
onClick={() => setShowMoreMenu(!showMoreMenu)}
>
More
<span className='ml-1 inline-block w-3'>
<ChevronDown />
</span>
</Button>
<Overlay show={showMoreMenu} setShow={setShowMoreMenu}>
<div className='flex w-[120px] flex-wrap p-4'>
{restCreditAccounts.map((account) => (
<Button
key={account}
variant='text'
className={classNames(
'w-full whitespace-nowrap py-2 text-sm',
selectedAccount === account
? 'text-secondary'
: 'cursor-pointer text-accent-dark hover:text-secondary',
)}
onClick={() => {
setShowMoreMenu(!showMoreMenu)
router.push(`/wallets/${params.wallet}/accounts/${account}`)
}}
>
Account {account}
</Button>
))}
</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)}
variant='text'
>
Manage
<span className='ml-1 inline-block w-3'>
<ChevronDown />
</span>
</Button>
async function deleteAccountHandler() {
if (!accountSelected) return
const isSuccess = await deleteCreditAccount({ fee: hardcodedFee, accountId: selectedAccount })
if (isSuccess) {
router.push(`/wallets/${params.wallet}/accounts`)
}
}
<AccountManageOverlay
className='-left-[86px]'
show={showManageMenu}
setShow={setShowManageMenu}
return !address ? null : (
<>
{creditAccounts === null ? (
<Loading className='h-8 w-35' />
) : (
<>
{' '}
{hasCreditAccounts ? (
<div className='relative'>
<Button
variant='solid'
color='tertiary'
className='flex flex-1 flex-nowrap'
icon={<Account />}
onClick={() => setShowMenu(!showMenu)}
hasSubmenu
>
<span>{accountSelected ? `Account ${selectedAccount}` : 'Select Account'}</span>
</Button>
<Overlay className='l-0 mt-2 w-[274px]' show={showMenu} setShow={setShowMenu}>
{accountSelected && (
<div className='flex w-full flex-wrap'>
<Text size='sm' uppercase={true} className='w-full justify-center px-4 pt-4'>
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
key={account}
className='w-full whitespace-nowrap py-2'
variant='transparent'
color='quaternary'
onClick={() => {
router.push(`/wallets/${params.wallet}/accounts/${account}`)
setShowMenu(!showMenu)
}}
text={`Account ${account}`}
/>
),
)}
</div>
)}
</Overlay>
</div>
) : (
<>{address ? <Button onClick={createAccountHandler}>Create Account</Button> : ''}</>
<Button onClick={createAccountHandler} icon={<Add />} color='tertiary'>
Create Account
</Button>
)}
</>
)}
</>
</section>
)
}

View File

@ -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>
)
}

View File

@ -66,7 +66,7 @@ export const FundAccountModal = () => {
found = true
}
})
}, [marketAssets, balancesData])
}, [balancesData, marketAssets, selectedToken])
// ---------------
// VARIABLES
@ -130,7 +130,7 @@ export const FundAccountModal = () => {
your osmosis wallet.
</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'>
<Text size='sm' className='text-white'>
Asset:

View File

@ -2,20 +2,16 @@ import { Switch } from '@headlessui/react'
import BigNumber from 'bignumber.js'
import classNames from 'classnames'
import React, { useEffect, useMemo, useState } from 'react'
import { toast } from 'react-toastify'
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 { Text } from 'components/Text'
import { Slider } from 'components/Slider'
import { CircularProgress } from 'components/CircularProgress'
import { FormattedNumber } from 'components/FormattedNumber'
import { Gauge } from 'components/Gauge'
import { LabelValuePair } from 'components/LabelValuePair'
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 { useCalculateMaxWithdrawAmount } from 'hooks/data/useCalculateMaxWithdrawAmount'
import { useWithdrawFunds } from 'hooks/mutations/useWithdrawFunds'
@ -23,6 +19,8 @@ import { useCreditAccountPositions } from 'hooks/queries/useCreditAccountPositio
import { useTokenPrices } from 'hooks/queries/useTokenPrices'
import useStore from 'store'
import { getBaseAsset, getMarketAssets } from 'utils/assets'
import { convertFromGwei, formatLeverage, formatValue } from 'utils/formatters'
import { getTokenDecimals, getTokenSymbol } from 'utils/tokens'
export const WithdrawModal = () => {
// ---------------
@ -169,7 +167,7 @@ export const WithdrawModal = () => {
<div className='flex w-full'>
<div className='flex flex-1 flex-col border-r border-white/20'>
<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'>
<Text size='sm' className='text-white'>
Asset:

View File

@ -1,5 +1,5 @@
import { FormattedNumber } from './FormattedNumber'
import TitleAndSubCell from './TitleAndSubCell'
import { FormattedNumber } from 'components/FormattedNumber'
import TitleAndSubCell from 'components/TitleAndSubCell'
interface Props {
asset: Asset

View File

@ -1,5 +1,44 @@
'use client'
import classNames from 'classnames'
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>
)
}

View File

@ -8,20 +8,19 @@ import {
SortingState,
useReactTable,
} from '@tanstack/react-table'
import classNames from 'classnames'
import Image from 'next/image'
import React from 'react'
import classNames from 'classnames'
import AmountAndValue from 'components/AmountAndValue'
import { AssetRow } from 'components/Borrow/AssetRow'
import { ChevronDown, ChevronUp } from 'components/Icons'
import { getMarketAssets } from 'utils/assets'
import { Text } from 'components/Text'
import TitleAndSubCell from 'components/TitleAndSubCell'
import { FormattedNumber } from 'components/FormattedNumber'
import AmountAndValue from 'components/AmountAndValue'
import { getMarketAssets } from 'utils/assets'
import { formatPercent } from 'utils/formatters'
import AssetExpanded from './AssetExpanded'
import AssetExpanded from 'components/Borrow/AssetExpanded'
import Loading from 'components/Loading'
type Props = {
data: BorrowAsset[] | BorrowAssetActive[]
@ -52,11 +51,17 @@ export const BorrowTable = (props: Props) => {
{
accessorKey: 'borrowRate',
header: 'Borrow Rate',
cell: ({ row }) => (
cell: ({ row }) => {
if (row.original.borrowRate === null) {
return <Loading />
}
return (
<Text className='justify-end' size='sm'>
{formatPercent(row.original.borrowRate)}
</Text>
),
)
},
},
...((props.data[0] as BorrowAssetActive)?.debt
? [
@ -82,6 +87,10 @@ export const BorrowTable = (props: Props) => {
if (!asset) return null
if (row.original.liquidity === null) {
return <Loading />
}
return <AmountAndValue asset={asset} amount={row.original.liquidity.amount} />
},
},
@ -97,7 +106,7 @@ export const BorrowTable = (props: Props) => {
),
},
],
[],
[marketAssets, props.data],
)
const table = useReactTable({

View 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'
}

View File

@ -1,7 +1,6 @@
import useStore from 'store'
import { Modal } from './Modal'
import TitleAndSubCell from './TitleAndSubCell'
import { Modal } from 'components/Modal'
import TitleAndSubCell from 'components/TitleAndSubCell'
export default function BorrowModal() {
const open = useStore((s) => s.borrowModal)

View File

@ -1,7 +1,8 @@
import classNames from 'classnames'
import React, { LegacyRef, ReactNode } from 'react'
import React, { LegacyRef, ReactElement, ReactNode } from 'react'
import { CircularProgress } from 'components/CircularProgress'
import { ChevronDown } from 'components/Icons'
import useStore from 'store'
interface Props {
@ -13,30 +14,39 @@ interface Props {
showProgressIndicator?: boolean
size?: 'small' | 'medium' | 'large'
text?: string | ReactNode
variant?: 'solid' | 'transparent' | 'round' | 'text'
variant?: 'solid' | 'transparent' | 'round'
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void
icon?: ReactElement
iconClassName?: string
hasSubmenu?: boolean
}
export const buttonColorClasses = {
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:
'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:
'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:
'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 = {
primary:
'border-none text-primary hover:text-primary-highlight active:text-primary-highlight focus:text-primary-highlight',
secondary:
'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',
primary: 'border-none hover:text-primary active:text-primary focus:text-primary',
secondary: 'border-none hover:text-secondary active:text-secondary focus:text-secondary',
tertiary: 'border-none hover:text-white/80 active:text-white/80 focus:text-white/80',
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 = {
@ -46,16 +56,41 @@ const buttonRoundSizeClasses = {
}
export const buttonSizeClasses = {
small: 'text-sm px-5 py-1.5 min-h-[32px]',
medium: 'text-base px-6 py-2.5 min-h-[40px]',
large: 'text-lg px-6 py-2.5 min-h-[56px]',
small: 'text-sm',
medium: 'text-base',
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 = {
solid: 'text-white',
transparent: 'bg-transparent p-0',
solid: 'rounded-sm text-white shadow-button justify-center group',
transparent: 'rounded-sm bg-transparent p-0 transition duration-200 ease-in',
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(
@ -70,17 +105,22 @@ export const Button = React.forwardRef(function Button(
text,
variant = 'solid',
onClick,
icon,
iconClassName,
hasSubmenu,
}: Props,
ref,
) {
const buttonClasses = []
const enableAnimations = useStore((s) => s.enableAnimations)
const isDisabled = disabled || showProgressIndicator
switch (variant) {
case 'round':
buttonClasses.push(
buttonSizeClasses[size],
buttonRoundSizeClasses[size],
buttonPaddingClasses[size],
buttonColorClasses[color],
)
break
@ -90,7 +130,11 @@ export const Button = React.forwardRef(function Button(
break
case 'solid':
buttonClasses.push(buttonSizeClasses[size], buttonColorClasses[color])
buttonClasses.push(
buttonSizeClasses[size],
buttonPaddingClasses[size],
buttonColorClasses[color],
)
break
default:
}
@ -98,10 +142,14 @@ export const Button = React.forwardRef(function Button(
return (
<button
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',
buttonClasses,
buttonVariantClasses[variant],
variant === 'solid' && color === 'tertiary' && buttonBorderClasses,
variant === 'solid' && color === 'primary' && buttonGradientClasses,
disabled && 'pointer-events-none opacity-50',
className,
)}
@ -109,8 +157,25 @@ export const Button = React.forwardRef(function Button(
ref={ref as LegacyRef<HTMLButtonElement>}
onClick={disabled ? () => {} : onClick}
>
{text && !children && !showProgressIndicator && <span>{text}</span>}
{children && !showProgressIndicator && children}
{icon && !isDisabled && (
<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 && (
<CircularProgress size={size === 'small' ? 10 : size === 'medium' ? 12 : 18} />
)}

View File

@ -1,12 +1,13 @@
import classNames from 'classnames'
import { ReactNode } from 'react'
import { ReactElement, ReactNode } from 'react'
import { Text } from 'components/Text'
interface Props {
title: string
title?: string | ReactElement
children: ReactNode
className?: string
contentClassName?: string
}
export const Card = (props: Props) => {
@ -14,13 +15,16 @@ export const Card = (props: Props) => {
<section
className={classNames(
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 && (
<Text size='lg' className='flex w-full items-center bg-white/10 p-4 font-semibold'>
{props.title}
</Text>
<div>{props.children}</div>
)}
<div className={classNames('w-full', props.contentClassName)}>{props.children}</div>
</section>
)
}

View File

@ -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>
)
}

View 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>
)
}

View 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>
)
}

View File

@ -1,5 +1,5 @@
import classNames from 'classnames'
import { ReactNode } from 'react'
import { ReactElement, ReactNode } from 'react'
import { Tooltip } from 'components/Tooltip'
import useStore from 'store'
@ -11,81 +11,68 @@ interface Props {
diameter?: number
value: number
label?: string
icon?: ReactElement
}
export const Gauge = ({
background = '#15161A',
background = '#FFFFFF22',
diameter = 40,
value = 0,
label,
tooltip,
icon,
}: Props) => {
const enableAnimations = useStore((s) => s.enableAnimations)
const radius = 16
const percentage = value * 100
const percentageValue = percentage > 100 ? 100 : percentage < 0 ? 0 : percentage
const semiCirclePercentage = percentageValue == -50 ? 0 : Math.abs(percentageValue / 2 - 50)
const circlePercent = 100 - percentageValue
return (
<Tooltip content={tooltip}>
<div
className={classNames(
'relative overflow-hidden',
`w-${diameter / 4} h-${diameter / 8 + 1}`,
)}
>
<div className={classNames('relative', `w-${diameter / 4} h-${diameter / 4}`)}>
<svg
viewBox='2 -2 28 36'
width={diameter}
height={diameter}
style={{ transform: 'rotate(180deg)' }}
style={{ transform: 'rotate(-90deg)' }}
className='absolute top-0 left-0'
>
<linearGradient id='gradient'>
<stop stopColor='#C13338' offset='0%'></stop>
<stop stopColor='#4F3D9F' offset='50%'></stop>
<stop stopColor='#15BFA9' offset='100%'></stop>
<stop stopColor='rgba(255, 160, 187)' offset='0%'></stop>
<stop stopColor='rgba(186, 8, 189)' offset='50%'></stop>
<stop stopColor='rgba(255, 160, 187)' offset='100%'></stop>
</linearGradient>
<circle
fill='none'
stroke={background}
strokeWidth={4}
strokeDasharray='50 100'
strokeLinecap='round'
r='16'
cx='16'
cy='16'
strokeDashoffset='0'
r={radius}
cx={radius}
cy={radius}
shapeRendering='geometricPrecision'
/>
<circle
r='16'
cx='16'
cy='16'
fill='none'
strokeLinecap='round'
r={radius}
cx={radius}
cy={radius}
fill='transparent'
stroke='url(#gradient)'
strokeDasharray='50 100'
strokeWidth={5}
strokeDashoffset={circlePercent}
strokeDasharray='100'
pathLength='100'
style={{
strokeDashoffset: semiCirclePercentage,
transition: enableAnimations ? 'stroke-dashoffset 1s ease' : 'none',
}}
shapeRendering='geometricPrecision'
strokeLinecap='round'
/>
</svg>
{label && (
<span
className='text-xs'
style={{
width: '100%',
left: '0',
textAlign: 'center',
bottom: '-2px',
position: 'absolute',
}}
>
{label}
</span>
{icon && (
<div className='absolute inset-0 flex items-center justify-center p-2.5 opacity-30'>
{icon}
</div>
)}
</div>
</Tooltip>

View 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

View File

@ -1,5 +1,3 @@
<svg viewBox='0 0 12 12' xmlns='http://www.w3.org/2000/svg' fill="currentColor">
<path
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 viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<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"/>
</svg>

Before

Width:  |  Height:  |  Size: 964 B

After

Width:  |  Height:  |  Size: 417 B

View 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

View 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

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 178 B

View 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

View File

@ -1,12 +1,15 @@
// @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 ArrowBack } from 'components/Icons/ArrowBack.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 ArrowRightLine } from 'components/Icons/ArrowRightLine.svg'
export { default as ArrowsLeftRight } from 'components/Icons/ArrowsLeftRight.svg'
export { default as ArrowsUpDown } from 'components/Icons/ArrowsUpDown.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 Check } from 'components/Icons/Check.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 Failed } from 'components/Icons/Failed.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 Logo } from 'components/Icons/Logo.svg'
export { default as MarsProtocol } from 'components/Icons/MarsProtocol.svg'

View File

@ -3,8 +3,6 @@ import classNames from 'classnames'
interface Props {
className?: string
count?: number
height?: number
width?: number
}
export default function Loading(props: Props) {
@ -14,10 +12,8 @@ export default function Loading(props: Props) {
<div
role='status'
className={classNames(
'animate-pulse rounded-md bg-white/40',
props.className,
props.height ? `h-[${props.height}px]` : 'h-[300px]',
props.width ? `w-[${props.width}px]` : 'w-full',
'max-w-full animate-pulse rounded-sm bg-white/40',
props.className ? props.className : 'h-4 w-full',
)}
key={i}
/>

View File

@ -3,8 +3,7 @@ import { ReactNode } from 'react'
import { Close } from 'components/Icons'
import { Text } from 'components/Text'
import { Button } from './Button'
import { Button } from 'components/Button'
interface Props {
title: string
@ -21,17 +20,22 @@ export const Modal = (props: Props) => {
}
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'>
<section
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,
)}
>
<div className='flex justify-between pb-6'>
<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>{props.children ? props.children : props.content}</div>
</section>

View File

@ -2,8 +2,7 @@
import { ConfirmModal } from 'components/Account/ConfirmModal'
import { FundAccountModal } from 'components/Account/FundAccountModal'
import BorrowModal from './BorrowModal'
import BorrowModal from 'components/BorrowModal'
export const Modals = () => (
<>

View File

@ -2,7 +2,9 @@
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import classNames from 'classnames'
import { AccountNavigation } from 'components/Account/AccountNavigation'
import { Logo } from 'components/Icons'
import { NavLink } from 'components/Navigation/NavLink'
import Wallet from 'components/Wallet/Wallet'
@ -20,8 +22,14 @@ export default function DesktopNavigation() {
const pathname = usePathname() || ''
return (
<div className='relative hidden bg-header lg:block'>
<div className='flex items-center justify-between border-b border-white/20 px-6 py-3'>
<header
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'>
<Link href={getRoute(pathname, { page: 'trade' })}>
<span className='block h-10 w-10'>
@ -36,8 +44,11 @@ export default function DesktopNavigation() {
))}
</div>
</div>
<div className='flex gap-4'>
<AccountNavigation />
<Wallet />
</div>
</div>
</header>
)
}

View File

@ -1,9 +1,7 @@
'use client'
import classNames from 'classnames'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { ReactNode } from 'react'
import classNames from 'classnames'
interface Props {
href: string
@ -18,7 +16,7 @@ export const NavLink = ({ href, children }: Props) => {
<Link
href={href}
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',
)}
>

View File

@ -18,7 +18,7 @@ export const Overlay = ({ children, content, className, show, setShow }: Props)
<>
<div
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,
)}
>

View File

@ -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>
)
}

View 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>
)
}

View File

@ -3,19 +3,18 @@ import BigNumber from 'bignumber.js'
import Image from 'next/image'
import React, { useMemo, useState } from 'react'
import { NumericFormat } from 'react-number-format'
import { toast } from 'react-toastify'
import { Button } from 'components/Button'
import { Card } from 'components/Card'
import { CircularProgress } from 'components/CircularProgress'
import { ContainerSecondary } from 'components/ContainerSecondary'
import { Slider } from 'components/Slider'
import { useRepayFunds } from 'hooks/mutations/useRepayFunds'
import { useAllBalances } from 'hooks/queries/useAllBalances'
import { useCreditAccountPositions } from 'hooks/queries/useCreditAccountPositions'
import { useTokenPrices } from 'hooks/queries/useTokenPrices'
import { getTokenDecimals, getTokenSymbol } from 'utils/tokens'
import { getMarketAssets } from 'utils/assets'
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
const REPAY_BUFFER = 1.00001
@ -137,7 +136,7 @@ export const RepayModal = ({ show, onClose, tokenDenom }: Props) => {
Repay {tokenSymbol}
</Dialog.Title>
<div className='mb-4 flex flex-col gap-2 text-sm'>
<ContainerSecondary>
<Card>
<p className='mb-7'>
In wallet: {walletAmount.toLocaleString()} {tokenSymbol}
</p>
@ -176,7 +175,7 @@ export const RepayModal = ({ show, onClose, tokenDenom }: Props) => {
}}
onMaxClick={() => setAmount(maxValue)}
/>
</ContainerSecondary>
</Card>
</div>
<Button
className='mt-auto w-full'

View File

@ -1,5 +1,4 @@
import * as RadixSlider from '@radix-ui/react-slider'
import React from 'react'
type Props = {
className?: string
@ -27,7 +26,7 @@ export const Slider = ({ className, value, onChange, onMaxClick }: Props) => {
</RadixSlider.Thumb>
</RadixSlider.Root>
<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}
>
MAX

View File

@ -29,7 +29,6 @@ export const Text = ({
<HtmlElement
className={classNames(
className,
'flex items-center',
uppercase ? `text-${sizeClass}-caps` : `text-${sizeClass}`,
monospace && 'number',
)}

View File

@ -13,11 +13,9 @@ interface Props extends React.HTMLProps<HTMLAnchorElement> {
const colorClasses = {
primary:
'text-primary hover:text-primary-highlight active:text-primary-highlight-10 focus:text-primary-highlight',
secondary:
'text-secondary hover:text-secondary-highlight active:text-secondary-highlight-10 focus:text-secondary-highlight',
tertiary:
'text-secondary-dark/60 hover:text-secondary-dark active:text-secondary-dark-10 focus:text-secondary-dark',
'text-primary hover:text-secondary active:text-secondary/90 focus:text-text-secondary/90',
secondary: 'text-secondary hover:text-primary active:text-primary/90 focus:text-text-primary/90',
tertiary: 'text-white/80 hover:text-white active:text-white/90 focus:text-text-white/90',
quaternary: 'hover:text-white active:text-white',
}
const textSizeClasses = {

View File

@ -2,6 +2,7 @@
import { useRouter } from 'next/navigation'
import { toast as createToast, Slide, ToastContainer } from 'react-toastify'
import { Check, Warning } from 'components/Icons'
import useStore from 'store'
export default function Toaster() {
@ -11,9 +12,23 @@ export default function Toaster() {
if (toast) {
if (toast.isError) {
createToast.error(toast.message)
createToast.error(toast.message, {
progressClassName: 'bg-loss',
icon: (
<span className='h-4 w-4'>
<Warning />
</span>
),
})
} 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 })
router.refresh()
@ -21,11 +36,14 @@ export default function Toaster() {
return (
<ToastContainer
autoClose={3000}
autoClose={5000}
closeButton={false}
position='bottom-right'
newestOnTop
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'
/>
)
}

View File

@ -33,7 +33,7 @@ export const Tooltip = ({
render={(attrs) => {
return (
<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}
>
{content}

View 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>
)
}

View 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>
)
}

View 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>
)
}

View File

@ -1,6 +1,9 @@
'use client'
import { useWalletManager, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
import { ReactNode } from 'react'
import { Button } from 'components/Button'
import { CircularProgress } from 'components/CircularProgress'
import { Wallet } from 'components/Icons'
@ -15,24 +18,21 @@ export default function ConnectButton(props: Props) {
return (
<div className='relative'>
<button
<Button
variant='solid'
color='tertiary'
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}
icon={<Wallet />}
>
{props.status === WalletConnectionStatus.Connecting ? (
<span className='flex justify-center'>
<CircularProgress size={16} />
</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>
</>
<span>{props.textOverride || 'Connect Wallet'}</span>
)}
</button>
</Button>
</div>
)
}

View File

@ -1,3 +1,5 @@
'use client'
import { Coin } from '@cosmjs/stargate'
import {
ChainInfoID,
@ -18,9 +20,9 @@ import { Check, Copy, ExternalLink, Osmo } from 'components/Icons'
import { Overlay } from 'components/Overlay/Overlay'
import { Text } from 'components/Text'
import useStore from 'store'
import { getWalletBalances } from 'utils/api'
import { getBaseAsset } from 'utils/assets'
import { formatValue, truncate } from 'utils/formatters'
import { getWalletBalances } from 'utils/api'
export default function ConnectedButton() {
// ---------------
@ -30,7 +32,6 @@ export default function ConnectedButton() {
const { disconnect: terminate } = useWalletManager()
const address = useStore((s) => s.client?.recentWallet.account?.address)
const network = useStore((s) => s.client?.recentWallet.network)
const name = useStore((s) => s.name)
const baseAsset = getBaseAsset()
const { data, isLoading } = useSWR(address, getWalletBalances)
@ -73,7 +74,7 @@ export default function ConnectedButton() {
<div className={'relative'}>
{network?.chainId !== ChainInfoID.Osmosis1 && (
<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'
uppercase
>
@ -81,24 +82,19 @@ export default function ConnectedButton() {
</Text>
)}
<button
className={classNames(
'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 ',
'hover:border-white hover:bg-secondary-dark',
'active:border-white active:bg-secondary-dark-10',
)}
<Button
variant='solid'
color='tertiary'
icon={<Osmo />}
onClick={() => {
setShowDetails(!showDetails)
}}
>
<span className='flex h-4 w-4 items-center justify-center'>
<Osmo />
</span>
<span className='ml-2'>{name ? name : truncate(address, [2, 4])}</span>
<span>{truncate(address, [2, 4])}</span>
<div
className={classNames(
'number relative ml-2 flex h-full items-center pl-2',
'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',
'relative ml-2 flex h-full items-center pl-2 number',
'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 ? (
@ -107,18 +103,18 @@ export default function ConnectedButton() {
`${formatValue(walletAmount, { suffix: baseAsset.symbol })}`
)}
</div>
</button>
</Button>
<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 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}
</div>
<div className='flex-0 flex flex-wrap justify-end'>
<FormattedNumber
animate
className='flex items-end text-2xl text-secondary-dark'
className='flex items-end text-2xl '
amount={walletAmount}
/>
</div>
@ -128,44 +124,44 @@ export default function ConnectedButton() {
</div>
</div>
<div className='flex w-full flex-wrap'>
<Text uppercase className='mb-1 break-all text-secondary-dark/80'>
{name ? `${name}` : 'Your Address'}
<Text uppercase className='/80 mb-1 break-all'>
{'Your Address'}
</Text>
<Text
size='sm'
className='mb-1 hidden break-all font-bold text-secondary-dark md:block'
>
<Text size='sm' className='mb-1 hidden break-all font-bold md:block'>
{address}
</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])}
</Text>
<div className='flex w-full pt-1'>
<button
className='mr-10 flex w-auto appearance-none items-center border-none py-2 text-secondary-dark opacity-70 hover:opacity-100'
<Button
icon={<Copy />}
variant='transparent'
className='mr-10 flex w-auto py-2'
color='quaternary'
onClick={setCopied}
>
<span className='mr-1 w-4'>
<Copy />
</span>
{isCopied ? (
<Text size='sm'>
Copied <Check />
Copied{' '}
<span className='ml-1 w-4'>
<Check />
</span>
</Text>
) : (
<Text size='sm'>Copy Address</Text>
)}
</button>
<button
className='flex w-auto appearance-none items-center border-none py-2 text-secondary-dark opacity-70 hover:opacity-100'
</Button>
<Button
icon={<ExternalLink />}
variant='transparent'
className='flex w-auto py-2'
color='quaternary'
onClick={viewOnFinder}
>
<span className='mr-1 w-4'>
<ExternalLink />
</span>
<Text size='sm'>View on {explorerName}</Text>
</button>
</Button>
</div>
</div>
</div>

View File

@ -6,42 +6,46 @@ import {
useWalletManager,
WalletConnectionStatus,
} from '@marsprotocol/wallet-connector'
import { useEffect, useState } from 'react'
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'
import ConnectButton from 'components/Wallet/ConnectButton'
import ConnectedButton from 'components/Wallet/ConnectedButton'
import useParams from 'hooks/useParams'
import useStore from 'store'
import { getCreditAccounts } from 'utils/api'
export default function Wallet() {
const router = useRouter()
const params = useParams()
const { status } = useWalletManager()
const [isConnected, setIsConnected] = useState(false)
const { recentWallet, simulate, sign, broadcast } = useWallet()
const client = useStore((s) => s.client)
const address = useStore((s) => s.address)
useEffect(() => {
const connectedStatus = status === WalletConnectionStatus.Connected
if (connectedStatus === isConnected) return
setIsConnected(connectedStatus)
}, [status, isConnected])
const isConnected = status === WalletConnectionStatus.Connected
useEffect(() => {
if (!isConnected && !params.wallet) {
router.push('/')
return
useStore.setState({ status })
useStore.setState(
isConnected
? {
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
if (!address || address === params.wallet) return
fetchCreditAccounts()
router.push(`/wallets/${client.recentWallet.account.address}`)
}, [client, params, isConnected])
useEffect(() => {
if (!recentWallet) return
if (!client) {
const getCosmWasmClient = async () => {
const cosmClient = await getClient(recentWallet.network.rpc)
@ -57,9 +61,11 @@ export default function Wallet() {
}
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} />
}

View File

@ -3,25 +3,27 @@
import { WalletManagerProvider } from '@marsprotocol/wallet-connector'
import { FC } from 'react'
import { Button } from 'components/Button'
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 = {
children?: React.ReactNode
}
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)
return null
}
const chainInfoOverrides = {
rpc: URL_RPC,
rest: URL_REST,
chainID: CHAIN_ID,
rpc: ENV.URL_RPC,
rest: ENV.URL_REST,
chainID: ENV.CHAIN_ID,
}
const enabledWallets: string[] = WALLETS
const enabledWallets: string[] = ENV.WALLETS
return (
<WalletManagerProvider
@ -30,6 +32,21 @@ export const WalletConnectProvider: FC<Props> = ({ children }) => {
defaultChainId={chainInfoOverrides.chainID}
enabledWallets={enabledWallets}
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={() => (
<div>
<CircularProgress size={30} />

View 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>
)
}

View 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} />
}

View 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} />
}

View 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} />
}

View 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>
)
}

View File

@ -1,18 +1,46 @@
export const ADDRESS_ACCOUNT_NFT = process.env.NEXT_PUBLIC_ACCOUNT_NFT
export const ADDRESS_CREDIT_MANAGER = process.env.NEXT_PUBLIC_CREDIT_MANAGER
export const ADDRESS_INCENTIVES = process.env.NEXT_PUBLIC_INCENTIVES
export const ADDRESS_ORACLE = process.env.NEXT_PUBLIC_ORACLE
export const ADDRESS_RED_BANK = process.env.NEXT_PUBLIC_RED_BANK
export const ADDRESS_SWAPPER = process.env.NEXT_PUBLIC_SWAPPER
export const ADDRESS_ZAPPER = process.env.NEXT_PUBLIC_ZAPPER
interface EnvironmentVariables {
ADDRESS_ACCOUNT_NFT: string | undefined
ADDRESS_CREDIT_MANAGER: string | undefined
ADDRESS_INCENTIVES: string | undefined
ADDRESS_ORACLE: string | undefined
ADDRESS_RED_BANK: string | undefined
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 NETWORK = process.env.NEXT_PUBLIC_NETWORK
export const IS_TESTNET = NETWORK !== 'mainnet'
export const URL_GQL = process.env.NEXT_PUBLIC_GQL
export const URL_REST = process.env.NEXT_PUBLIC_REST
export const URL_RPC = process.env.NEXT_PUBLIC_RPC
export const URL_API = process.env.NEXT_PUBLIC_API
export const WALLETS = process.env.NEXT_PUBLIC_WALLETS?.split(',') ?? []
export const ENV: EnvironmentVariables = {
ADDRESS_ACCOUNT_NFT: process.env.NEXT_PUBLIC_ACCOUNT_NFT,
ADDRESS_CREDIT_MANAGER: process.env.NEXT_PUBLIC_CREDIT_MANAGER,
ADDRESS_INCENTIVES: process.env.NEXT_PUBLIC_INCENTIVES,
ADDRESS_ORACLE: process.env.NEXT_PUBLIC_ORACLE,
ADDRESS_RED_BANK: process.env.NEXT_PUBLIC_RED_BANK,
ADDRESS_SWAPPER: process.env.NEXT_PUBLIC_SWAPPER,
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(', ')}`
}

View File

@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query'
import request, { gql } from 'graphql-request'
import { useMemo } from 'react'
import { URL_GQL } from 'constants/env'
import { ENV } from 'constants/env'
import useStore from 'store'
import { queryKeys } from 'types/query-keys-factory'
@ -20,7 +20,7 @@ export const useAllBalances = () => {
queryKeys.allBalances(address ?? ''),
async () => {
return await request(
URL_GQL!,
ENV.URL_GQL!,
gql`
query UserBalanceQuery {
balance: bank {

View File

@ -1,6 +1,6 @@
import { useQuery } from '@tanstack/react-query'
import { ADDRESS_CREDIT_MANAGER } from 'constants/env'
import { ENV } from 'constants/env'
import useStore from 'store'
import { queryKeys } from 'types/query-keys-factory'
@ -12,7 +12,7 @@ const queryMsg = {
export const useAllowedCoins = () => {
const client = useStore((s) => s.signingClient)
const creditManagerAddress = ADDRESS_CREDIT_MANAGER
const creditManagerAddress = ENV.ADDRESS_CREDIT_MANAGER
const result = useQuery<Result>(
queryKeys.allowedCoins(),

View File

@ -2,7 +2,7 @@ import { Coin } from '@cosmjs/stargate'
import { useQuery } from '@tanstack/react-query'
import { useMemo } from 'react'
import { ADDRESS_CREDIT_MANAGER } from 'constants/env'
import { ENV } from 'constants/env'
import useStore from 'store'
import { queryKeys } from 'types/query-keys-factory'
@ -27,7 +27,7 @@ interface Result {
export const useCreditAccountPositions = (accountId: string) => {
const address = useStore((s) => s.address)
const client = useStore((s) => s.signingClient)
const creditManagerAddress = ADDRESS_CREDIT_MANAGER
const creditManagerAddress = ENV.ADDRESS_CREDIT_MANAGER
const result = useQuery<Result>(
queryKeys.creditAccountsPositions(accountId),

View File

@ -3,7 +3,7 @@
import { useQuery } from '@tanstack/react-query'
import { useMemo } from 'react'
import { ADDRESS_ACCOUNT_NFT } from 'constants/env'
import { ENV } from 'constants/env'
import useStore from 'store'
import { queryKeys } from 'types/query-keys-factory'
@ -15,7 +15,7 @@ export const useCreditAccounts = () => {
const address = useStore((s) => s.address)
const client = useStore((s) => s.signingClient)
const selectedAccount = useStore((s) => s.selectedAccount)
const accountNftAddress = ADDRESS_ACCOUNT_NFT
const accountNftAddress = ENV.ADDRESS_ACCOUNT_NFT
const setSelectedAccount = (account: string) => {
useStore.setState({ selectedAccount: account })
}

View File

@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query'
import request, { gql } from 'graphql-request'
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'
interface Result {
@ -13,12 +13,12 @@ interface Result {
}
export const useRedbankBalances = () => {
const redBankAddress = ADDRESS_RED_BANK
const redBankAddress = ENV.ADDRESS_RED_BANK
const result = useQuery<Result>(
queryKeys.redbankBalances(),
async () => {
return await request(
URL_GQL!,
ENV.URL_GQL!,
gql`
query RedbankBalances {
bank {

View File

@ -2,11 +2,15 @@ import { useQuery } from '@tanstack/react-query'
import { gql, request } from 'graphql-request'
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 { 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(
hiveUrl,
gql`
@ -32,7 +36,7 @@ export const useTokenPrices = () => {
const marketAssets = getMarketAssets()
const result = useQuery<TokenPricesResult>(
queryKeys.tokenPrices(),
async () => await fetchTokenPrices(URL_GQL!, marketAssets, ADDRESS_ORACLE || ''),
async () => await fetchTokenPrices(ENV.URL_GQL!, marketAssets, ENV.ADDRESS_ORACLE || ''),
{
refetchInterval: 30000,
staleTime: Infinity,

View File

@ -1,15 +1,15 @@
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) {
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)
}
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) {
return res.status(200).json([{ denom: 'uosmo', amount: '123876' }])

View File

@ -1,15 +1,15 @@
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) {
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)
}
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) {
return res.status(200).json(account.deposits)

View File

@ -1,18 +1,18 @@
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
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) {
if (!URL_RPC || !ADDRESS_CREDIT_MANAGER) {
if (!ENV.URL_RPC || !ENV.ADDRESS_CREDIT_MANAGER) {
return res.status(404).json(ENV_MISSING_MESSAGE)
}
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: {
account_id: accountId,
},

View File

@ -2,20 +2,20 @@ import { Coin } from '@cosmjs/stargate'
import { gql, request as gqlRequest } from 'graphql-request'
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) {
if (!URL_GQL || !ADDRESS_RED_BANK) {
if (!ENV.URL_GQL || !ENV.ADDRESS_RED_BANK) {
return res.status(404).json(ENV_MISSING_MESSAGE)
}
const result = await gqlRequest<Result>(
URL_GQL,
ENV.URL_GQL,
gql`
query RedbankBalances {
bank {
balance(
address: "${ADDRESS_RED_BANK}"
address: "${ENV.ADDRESS_RED_BANK}"
) {
amount
denom

View File

@ -1,19 +1,19 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { Coin } from '@cosmjs/stargate'
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'
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!URL_API) {
if (!ENV.URL_API) {
return res.status(404).json(ENV_MISSING_MESSAGE)
}
const marketAssets = getMarketAssets()
const $liquidity = fetch(`${URL_API}/markets/liquidity`)
const $markets = fetch(`${URL_API}/markets`)
const $prices = fetch(`${URL_API}/prices`)
const $liquidity = fetch(`${ENV.URL_API}/markets/liquidity`)
const $markets = fetch(`${ENV.URL_API}/markets`)
const $prices = fetch(`${ENV.URL_API}/prices`)
const borrow: BorrowAsset[] = await Promise.all([$liquidity, $markets, $prices]).then(
async ([$liquidity, $markets, $prices]) => {

View File

@ -1,22 +1,22 @@
import { NextApiRequest, NextApiResponse } from 'next'
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'
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)
}
const markets: Market[] = await (await fetch(`${URL_API}/markets`)).json()
const markets: Market[] = await (await fetch(`${ENV.URL_API}/markets`)).json()
let query = ''
markets.forEach((asset: any) => {
query += getContractQuery(
asset.denom,
ADDRESS_RED_BANK || '',
ENV.ADDRESS_RED_BANK || '',
`
{
underlying_debt_amount: {
@ -28,7 +28,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
})
const result = await gqlRequest<DebtsQuery>(
URL_GQL,
ENV.URL_GQL,
gql`
query RedbankBalances {
debts: wasm {

View File

@ -1,22 +1,22 @@
import { NextApiRequest, NextApiResponse } from 'next'
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'
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)
}
const markets = await (await fetch(`${URL_API}/markets`)).json()
const markets = await (await fetch(`${ENV.URL_API}/markets`)).json()
let query = ''
markets.forEach((asset: any) => {
query += getContractQuery(
asset.denom,
ADDRESS_RED_BANK || '',
ENV.ADDRESS_RED_BANK || '',
`
{
underlying_liquidity_amount: {
@ -28,7 +28,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
})
const result = await gqlRequest<DepositsQuery>(
URL_GQL,
ENV.URL_GQL,
gql`
query RedbankBalances {
deposits: wasm {

View File

@ -1,11 +1,11 @@
import { gql, request as gqlRequest } from 'graphql-request'
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'
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)
}
@ -14,13 +14,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const marketQueries = marketAssets.map(
(asset: Asset) =>
`${asset.denom}: contractQuery(
contractAddress: "${ADDRESS_RED_BANK}"
contractAddress: "${ENV.ADDRESS_RED_BANK}"
query: { market: { denom: "${asset.denom}" } }
)`,
)
const result = await gqlRequest<RedBankData>(
URL_GQL,
ENV.URL_GQL,
gql`
query RedbankQuery {
rbwasmkey: wasm {

View File

@ -1,15 +1,15 @@
import { NextApiRequest, NextApiResponse } from 'next'
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) {
if (!URL_API) {
if (!ENV.URL_API) {
return res.status(404).json(ENV_MISSING_MESSAGE)
}
const $deposits = fetch(`${URL_API}/markets/deposits`)
const $debts = fetch(`${URL_API}/markets/debts`)
const $deposits = fetch(`${ENV.URL_API}/markets/deposits`)
const $debts = fetch(`${ENV.URL_API}/markets/debts`)
const liquidity: Coin[] = await Promise.all([$deposits, $debts]).then(
async ([$deposits, $debts]) => {

View File

@ -2,24 +2,24 @@ import { gql, request as gqlRequest } from 'graphql-request'
import { NextApiRequest, NextApiResponse } from 'next'
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'
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)
}
const marketAssets = getMarketAssets()
const result = await gqlRequest<TokenPricesResult>(
URL_GQL,
ENV.URL_GQL,
gql`
query PriceOracle {
prices: wasm {
${marketAssets.map((asset) => {
return `${asset.symbol}: contractQuery(
contractAddress: "${ADDRESS_ORACLE}"
contractAddress: "${ENV.ADDRESS_ORACLE}"
query: {
price: {
denom: "${asset.denom}"

View File

@ -1,15 +1,15 @@
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
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) {
if (!URL_RPC || !ADDRESS_CREDIT_MANAGER) {
if (!ENV.URL_RPC || !ENV.ADDRESS_CREDIT_MANAGER) {
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 },
})

View File

@ -1,18 +1,18 @@
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
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) {
if (!URL_RPC || !ADDRESS_ACCOUNT_NFT) {
if (!ENV.URL_RPC || !ENV.ADDRESS_ACCOUNT_NFT) {
return res.status(404).json(ENV_MISSING_MESSAGE)
}
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: {
owner: address,
},

View File

@ -1,16 +1,16 @@
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) {
if (!URL_REST) {
if (!ENV.URL_REST) {
return res.status(404).json(ENV_MISSING_MESSAGE)
}
const address = req.query.address
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) {
const data = await response.json()

Some files were not shown because too many files have changed in this diff Show More