update layout for modal, implement borrow tables (#105)
This commit is contained in:
parent
493ec7c44c
commit
cbb0700455
@ -1,4 +1,5 @@
|
|||||||
import Background from 'components/Background'
|
import Background from 'components/Background'
|
||||||
|
import FetchPrices from 'components/FetchPrices'
|
||||||
import { Modals } from 'components/Modals'
|
import { Modals } from 'components/Modals'
|
||||||
import DesktopNavigation from 'components/Navigation/DesktopNavigation'
|
import DesktopNavigation from 'components/Navigation/DesktopNavigation'
|
||||||
import Toaster from 'components/Toaster'
|
import Toaster from 'components/Toaster'
|
||||||
@ -19,6 +20,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|||||||
</WalletConnectProvider>
|
</WalletConnectProvider>
|
||||||
<Modals />
|
<Modals />
|
||||||
<Toaster />
|
<Toaster />
|
||||||
|
<FetchPrices />
|
||||||
<main className='relative flex lg:min-h-[calc(100vh-120px)]'>
|
<main className='relative flex lg:min-h-[calc(100vh-120px)]'>
|
||||||
<div className='flex flex-grow flex-col flex-wrap'>{children}</div>
|
<div className='flex flex-grow flex-col flex-wrap'>{children}</div>
|
||||||
</main>
|
</main>
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import Loading from 'components/Loading'
|
|
||||||
|
|
||||||
export default function page() {
|
|
||||||
return <Loading />
|
|
||||||
}
|
|
@ -17,10 +17,7 @@ export default async function page({ params }: { params: PageParams }) {
|
|||||||
if (debt) {
|
if (debt) {
|
||||||
prev.active.push({
|
prev.active.push({
|
||||||
...borrow,
|
...borrow,
|
||||||
debt: {
|
debt: debt.amount,
|
||||||
amount: '100000',
|
|
||||||
value: '12389478321',
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
prev.available.push(borrow)
|
prev.available.push(borrow)
|
||||||
|
@ -10,7 +10,6 @@ import { ArrowRightLine, ChevronDown, ChevronLeft } from 'components/Icons'
|
|||||||
import { LabelValuePair } from 'components/LabelValuePair'
|
import { LabelValuePair } from 'components/LabelValuePair'
|
||||||
import { PositionsList } from 'components/PositionsList'
|
import { PositionsList } from 'components/PositionsList'
|
||||||
import { useAccountStats } from 'hooks/data/useAccountStats'
|
import { useAccountStats } from 'hooks/data/useAccountStats'
|
||||||
import { useBalances } from 'hooks/data/useBalances'
|
|
||||||
import { convertFromGwei } from 'utils/formatters'
|
import { convertFromGwei } from 'utils/formatters'
|
||||||
import { createRiskData } from 'utils/risk'
|
import { createRiskData } from 'utils/risk'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
@ -23,7 +22,6 @@ export const AccountDetails = () => {
|
|||||||
const marketAssets = getMarketAssets()
|
const marketAssets = getMarketAssets()
|
||||||
const baseAsset = getBaseAsset()
|
const baseAsset = getBaseAsset()
|
||||||
|
|
||||||
const balances = useBalances()
|
|
||||||
const accountStats = useAccountStats()
|
const accountStats = useAccountStats()
|
||||||
|
|
||||||
const [showManageMenu, setShowManageMenu] = useState(false)
|
const [showManageMenu, setShowManageMenu] = useState(false)
|
||||||
@ -118,7 +116,6 @@ export const AccountDetails = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{riskData && <RiskChart data={riskData} />}
|
{riskData && <RiskChart data={riskData} />}
|
||||||
<PositionsList title='Balances' data={balances} />
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import { Text } from 'components/Text'
|
|||||||
import { useAccountStats } from 'hooks/data/useAccountStats'
|
import { useAccountStats } from 'hooks/data/useAccountStats'
|
||||||
import { useCreditAccounts } from 'hooks/queries/useCreditAccounts'
|
import { useCreditAccounts } from 'hooks/queries/useCreditAccounts'
|
||||||
import { getBaseAsset } from 'utils/assets'
|
import { getBaseAsset } from 'utils/assets'
|
||||||
import { formatValue } from 'utils/formatters'
|
import { formatLeverage, formatValue } from 'utils/formatters'
|
||||||
|
|
||||||
export const AccountStatus = () => {
|
export const AccountStatus = () => {
|
||||||
const baseAsset = getBaseAsset()
|
const baseAsset = getBaseAsset()
|
||||||
@ -41,7 +41,7 @@ export const AccountStatus = () => {
|
|||||||
.dividedBy(10 ** baseAsset.decimals)
|
.dividedBy(10 ** baseAsset.decimals)
|
||||||
.toNumber()}
|
.toNumber()}
|
||||||
animate
|
animate
|
||||||
prefix='$: '
|
options={{ prefix: '$: ' }}
|
||||||
/>
|
/>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
@ -50,10 +50,9 @@ export const AccountStatus = () => {
|
|||||||
label='Lvg'
|
label='Lvg'
|
||||||
tooltip={
|
tooltip={
|
||||||
<Text size='sm'>
|
<Text size='sm'>
|
||||||
Current Leverage:{' '}
|
Current Leverage: {formatLeverage(accountStats.currentLeverage)}
|
||||||
{formatValue(accountStats.currentLeverage, 0, 2, true, false, 'x')}
|
|
||||||
<br />
|
<br />
|
||||||
Max Leverage: {formatValue(accountStats.maxLeverage, 0, 2, true, false, 'x')}
|
Max Leverage: {formatLeverage(accountStats.maxLeverage)}
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -63,7 +62,8 @@ export const AccountStatus = () => {
|
|||||||
label='Risk'
|
label='Risk'
|
||||||
tooltip={
|
tooltip={
|
||||||
<Text size='sm'>
|
<Text size='sm'>
|
||||||
Current Risk: {formatValue(accountStats.risk * 100, 0, 2, true, false, '%')}
|
Current Risk:{' '}
|
||||||
|
{formatValue(accountStats.risk * 100, { minDecimals: 0, suffix: '%' })}
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -13,7 +13,7 @@ export const ConfirmModal = () => {
|
|||||||
const deleteOpen = useStore((s) => s.deleteAccountModal)
|
const deleteOpen = useStore((s) => s.deleteAccountModal)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal open={createOpen || deleteOpen}>
|
<Modal title='Confirm' open={createOpen || deleteOpen}>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'relative flex h-[630px] w-full flex-wrap items-center justify-center p-6',
|
'relative flex h-[630px] w-full flex-wrap items-center justify-center p-6',
|
||||||
|
@ -94,7 +94,7 @@ export const FundAccountModal = () => {
|
|||||||
const percentageValue = isNaN(amount) ? 0 : (amount * 100) / walletAmount
|
const percentageValue = isNaN(amount) ? 0 : (amount * 100) / walletAmount
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal open={open} setOpen={setOpen}>
|
<Modal title='Fund account' open={open} setOpen={setOpen}>
|
||||||
<div className='flex min-h-[520px] w-full'>
|
<div className='flex min-h-[520px] w-full'>
|
||||||
{balanceIsLoading && (
|
{balanceIsLoading && (
|
||||||
<div className='absolute inset-0 z-40 grid place-items-center bg-black/50'>
|
<div className='absolute inset-0 z-40 grid place-items-center bg-black/50'>
|
||||||
|
@ -25,11 +25,13 @@ export const RiskChart = ({ data }: RiskChartProps) => {
|
|||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
className='px-3 pb-2 text-lg'
|
className='px-3 pb-2 text-lg'
|
||||||
amount={currentRisk * 100}
|
amount={currentRisk * 100}
|
||||||
maxDecimals={0}
|
options={{
|
||||||
minDecimals={0}
|
maxDecimals: 0,
|
||||||
|
minDecimals: 0,
|
||||||
|
prefix: 'Risk score: ',
|
||||||
|
suffix: '/100',
|
||||||
|
}}
|
||||||
animate
|
animate
|
||||||
prefix='Risk Score: '
|
|
||||||
suffix='/100'
|
|
||||||
/>
|
/>
|
||||||
<div className='-ml-6 h-[100px] w-[412px]'>
|
<div className='-ml-6 h-[100px] w-[412px]'>
|
||||||
<ResponsiveContainer width='100%' height='100%'>
|
<ResponsiveContainer width='100%' height='100%'>
|
||||||
@ -77,7 +79,9 @@ export const RiskChart = ({ data }: RiskChartProps) => {
|
|||||||
return (
|
return (
|
||||||
<div className='max-w-[320px] rounded-lg px-4 py-2 shadow-tooltip gradient-tooltip '>
|
<div className='max-w-[320px] rounded-lg px-4 py-2 shadow-tooltip gradient-tooltip '>
|
||||||
<Text size='sm'>{moment(label).format('MM-DD-YYYY')}</Text>
|
<Text size='sm'>{moment(label).format('MM-DD-YYYY')}</Text>
|
||||||
<Text size='sm'>Risk: {formatValue(risk, 0, 0, true, false, '%')}</Text>
|
<Text size='sm'>
|
||||||
|
Risk: {formatValue(risk, { minDecimals: 0, maxDecimals: 0, suffix: '%' })}
|
||||||
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import React, { useEffect, useMemo, useState } from 'react'
|
|||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
import { BorrowCapacity } from 'components/BorrowCapacity'
|
import { BorrowCapacity } from 'components/BorrowCapacity'
|
||||||
import { convertFromGwei, formatValue } from 'utils/formatters'
|
import { convertFromGwei, formatLeverage, formatValue } from 'utils/formatters'
|
||||||
import { getTokenDecimals, getTokenSymbol } from 'utils/tokens'
|
import { getTokenDecimals, getTokenSymbol } from 'utils/tokens'
|
||||||
import { CircularProgress } from 'components/CircularProgress'
|
import { CircularProgress } from 'components/CircularProgress'
|
||||||
import { Button } from 'components/Button'
|
import { Button } from 'components/Button'
|
||||||
@ -17,7 +17,6 @@ import { LabelValuePair } from 'components/LabelValuePair'
|
|||||||
import { Modal } from 'components/Modal'
|
import { Modal } from 'components/Modal'
|
||||||
import { PositionsList } from 'components/PositionsList'
|
import { PositionsList } from 'components/PositionsList'
|
||||||
import { useAccountStats } from 'hooks/data/useAccountStats'
|
import { useAccountStats } from 'hooks/data/useAccountStats'
|
||||||
import { useBalances } from 'hooks/data/useBalances'
|
|
||||||
import { useCalculateMaxWithdrawAmount } from 'hooks/data/useCalculateMaxWithdrawAmount'
|
import { useCalculateMaxWithdrawAmount } from 'hooks/data/useCalculateMaxWithdrawAmount'
|
||||||
import { useWithdrawFunds } from 'hooks/mutations/useWithdrawFunds'
|
import { useWithdrawFunds } from 'hooks/mutations/useWithdrawFunds'
|
||||||
import { useCreditAccountPositions } from 'hooks/queries/useCreditAccountPositions'
|
import { useCreditAccountPositions } from 'hooks/queries/useCreditAccountPositions'
|
||||||
@ -46,7 +45,6 @@ export const WithdrawModal = () => {
|
|||||||
// EXTERNAL HOOKS
|
// EXTERNAL HOOKS
|
||||||
// ---------------
|
// ---------------
|
||||||
const { data: tokenPrices } = useTokenPrices()
|
const { data: tokenPrices } = useTokenPrices()
|
||||||
const balances = useBalances()
|
|
||||||
|
|
||||||
const selectedTokenSymbol = getTokenSymbol(selectedToken, marketAssets)
|
const selectedTokenSymbol = getTokenSymbol(selectedToken, marketAssets)
|
||||||
const selectedTokenDecimals = getTokenDecimals(selectedToken, marketAssets)
|
const selectedTokenDecimals = getTokenDecimals(selectedToken, marketAssets)
|
||||||
@ -153,7 +151,7 @@ export const WithdrawModal = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal open={open} setOpen={setOpen}>
|
<Modal title='Withdraw' open={open} setOpen={setOpen}>
|
||||||
<div className='flex min-h-[470px] w-full flex-wrap'>
|
<div className='flex min-h-[470px] w-full flex-wrap'>
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<div className='absolute inset-0 z-40 grid place-items-center bg-black/50'>
|
<div className='absolute inset-0 z-40 grid place-items-center bg-black/50'>
|
||||||
@ -205,7 +203,7 @@ export const WithdrawModal = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Text size='xs' uppercase className='mb-2 text-white/60'>
|
<Text size='xs' uppercase className='mb-2 text-white/60'>
|
||||||
Available: {formatValue(maxWithdrawAmount, 0, 4, true, false, false, false, false)}
|
Available: {formatValue(maxWithdrawAmount, { minDecimals: 0, maxDecimals: 4 })}
|
||||||
</Text>
|
</Text>
|
||||||
<Slider
|
<Slider
|
||||||
className='mb-6'
|
className='mb-6'
|
||||||
@ -265,7 +263,7 @@ export const WithdrawModal = () => {
|
|||||||
amount={BigNumber(accountStats.netWorth)
|
amount={BigNumber(accountStats.netWorth)
|
||||||
.dividedBy(10 ** baseAsset.decimals)
|
.dividedBy(10 ** baseAsset.decimals)
|
||||||
.toNumber()}
|
.toNumber()}
|
||||||
prefix='$: '
|
options={{ prefix: '$: ' }}
|
||||||
animate
|
animate
|
||||||
/>
|
/>
|
||||||
</Text>
|
</Text>
|
||||||
@ -275,11 +273,9 @@ export const WithdrawModal = () => {
|
|||||||
label='Lvg'
|
label='Lvg'
|
||||||
tooltip={
|
tooltip={
|
||||||
<Text size='sm'>
|
<Text size='sm'>
|
||||||
Current Leverage:{' '}
|
Current Leverage: {formatLeverage(accountStats.currentLeverage)}
|
||||||
{formatValue(accountStats.currentLeverage, 0, 2, true, false, 'x')}
|
|
||||||
<br />
|
<br />
|
||||||
Max Leverage:{' '}
|
Max Leverage: {formatLeverage(accountStats.maxLeverage)}
|
||||||
{formatValue(accountStats.maxLeverage, 0, 2, true, false, 'x')}
|
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -288,7 +284,8 @@ export const WithdrawModal = () => {
|
|||||||
label='Risk'
|
label='Risk'
|
||||||
tooltip={
|
tooltip={
|
||||||
<Text size='sm'>
|
<Text size='sm'>
|
||||||
Current Risk: {formatValue(accountStats.risk * 100, 0, 2, true, false, '%')}
|
Current Risk:{' '}
|
||||||
|
{formatValue(accountStats.risk * 100, { minDecimals: 0, suffix: '%' })}
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -331,7 +328,6 @@ export const WithdrawModal = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<PositionsList title='Balances' data={balances} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
27
src/components/AmountAndValue.tsx
Normal file
27
src/components/AmountAndValue.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { FormattedNumber } from './FormattedNumber'
|
||||||
|
import TitleAndSubCell from './TitleAndSubCell'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
asset: Asset
|
||||||
|
amount: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AmountAndValue(props: Props) {
|
||||||
|
return (
|
||||||
|
<TitleAndSubCell
|
||||||
|
title={
|
||||||
|
<FormattedNumber
|
||||||
|
amount={props.amount}
|
||||||
|
options={{ decimals: props.asset.decimals, abbreviated: true }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
sub={
|
||||||
|
<FormattedNumber
|
||||||
|
amount={props.amount}
|
||||||
|
options={{ prefix: '$', abbreviated: true, decimals: props.asset.decimals }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
className='justify-end'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
@ -1,11 +1,14 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Row } from '@tanstack/react-table'
|
import { Row } from '@tanstack/react-table'
|
||||||
|
|
||||||
import { getMarketAssets } from 'utils/assets'
|
import { getMarketAssets } from 'utils/assets'
|
||||||
import { Button } from 'components/Button'
|
import { Button } from 'components/Button'
|
||||||
|
import useStore from 'store'
|
||||||
|
|
||||||
type AssetRowProps = {
|
type AssetRowProps = {
|
||||||
row: Row<BorrowAsset>
|
row: Row<BorrowAsset | BorrowAssetActive>
|
||||||
onBorrowClick: () => void
|
onBorrowClick: () => void
|
||||||
onRepayClick: () => void
|
onRepayClick: () => void
|
||||||
resetExpanded: (defaultState?: boolean | undefined) => void
|
resetExpanded: (defaultState?: boolean | undefined) => void
|
||||||
@ -14,9 +17,22 @@ type AssetRowProps = {
|
|||||||
export default function AssetExpanded(props: AssetRowProps) {
|
export default function AssetExpanded(props: AssetRowProps) {
|
||||||
const marketAssets = getMarketAssets()
|
const marketAssets = getMarketAssets()
|
||||||
const asset = marketAssets.find((asset) => asset.denom === props.row.original.denom)
|
const asset = marketAssets.find((asset) => asset.denom === props.row.original.denom)
|
||||||
|
let isActive: boolean = false
|
||||||
|
|
||||||
|
if ((props.row.original as BorrowAssetActive)?.debt) {
|
||||||
|
isActive = true
|
||||||
|
}
|
||||||
|
|
||||||
if (!asset) return null
|
if (!asset) return null
|
||||||
|
|
||||||
|
function borrowHandler() {
|
||||||
|
useStore.setState({ borrowModal: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
function repayHandler() {
|
||||||
|
useStore.setState({ repayModal: true })
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr
|
<tr
|
||||||
key={props.row.id}
|
key={props.row.id}
|
||||||
@ -28,10 +44,14 @@ export default function AssetExpanded(props: AssetRowProps) {
|
|||||||
!isExpanded && props.row.toggleExpanded()
|
!isExpanded && props.row.toggleExpanded()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<td colSpan={4}>
|
<td colSpan={isActive ? 5 : 4}>
|
||||||
<div className='flex justify-end p-4'>
|
<div className='flex justify-end p-4'>
|
||||||
<Button color='secondary' text='CLick me' onClick={() => {}} />
|
<Button
|
||||||
<Button color='primary' text='CLick me' />
|
onClick={borrowHandler}
|
||||||
|
color='primary'
|
||||||
|
text={isActive ? 'Borrow more' : 'Borrow'}
|
||||||
|
/>
|
||||||
|
{isActive && <Button color='primary' text='Repay' />}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -4,7 +4,7 @@ import { flexRender, Row } from '@tanstack/react-table'
|
|||||||
import { getMarketAssets } from 'utils/assets'
|
import { getMarketAssets } from 'utils/assets'
|
||||||
|
|
||||||
type AssetRowProps = {
|
type AssetRowProps = {
|
||||||
row: Row<BorrowAsset>
|
row: Row<BorrowAsset | BorrowAssetActive>
|
||||||
resetExpanded: (defaultState?: boolean | undefined) => void
|
resetExpanded: (defaultState?: boolean | undefined) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,11 @@ import classNames from 'classnames'
|
|||||||
import { AssetRow } from 'components/Borrow/AssetRow'
|
import { AssetRow } from 'components/Borrow/AssetRow'
|
||||||
import { ChevronDown, ChevronUp } from 'components/Icons'
|
import { ChevronDown, ChevronUp } from 'components/Icons'
|
||||||
import { getMarketAssets } from 'utils/assets'
|
import { 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 { formatPercent } from 'utils/formatters'
|
||||||
|
|
||||||
import AssetExpanded from './AssetExpanded'
|
import AssetExpanded from './AssetExpanded'
|
||||||
|
|
||||||
@ -26,7 +31,7 @@ export const BorrowTable = (props: Props) => {
|
|||||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||||
const marketAssets = getMarketAssets()
|
const marketAssets = getMarketAssets()
|
||||||
|
|
||||||
const columns = React.useMemo<ColumnDef<BorrowAsset>[]>(
|
const columns = React.useMemo<ColumnDef<BorrowAsset | BorrowAssetActive>[]>(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
header: 'Asset',
|
header: 'Asset',
|
||||||
@ -37,12 +42,9 @@ export const BorrowTable = (props: Props) => {
|
|||||||
if (!asset) return null
|
if (!asset) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-1 items-center'>
|
<div className='flex flex-1 items-center gap-3'>
|
||||||
<Image src={asset.logo} alt='token' width={32} height={32} />
|
<Image src={asset.logo} alt='token' width={32} height={32} />
|
||||||
<div className='pl-2'>
|
<TitleAndSubCell title={asset.symbol} sub={asset.name} />
|
||||||
<div>{asset.symbol}</div>
|
|
||||||
<div className='text-xs'>{asset.name}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -50,17 +52,38 @@ export const BorrowTable = (props: Props) => {
|
|||||||
{
|
{
|
||||||
accessorKey: 'borrowRate',
|
accessorKey: 'borrowRate',
|
||||||
header: 'Borrow Rate',
|
header: 'Borrow Rate',
|
||||||
cell: ({ row }) => <div>{(Number(row.original.borrowRate) * 100).toFixed(2)}%</div>,
|
cell: ({ row }) => (
|
||||||
|
<Text className='justify-end' size='sm'>
|
||||||
|
{formatPercent(row.original.borrowRate)}
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
|
...((props.data[0] as BorrowAssetActive)?.debt
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
accessorKey: 'debt',
|
||||||
|
header: 'Borrowed',
|
||||||
|
cell: (info: any) => {
|
||||||
|
const borrowAsset = info.row.original as BorrowAssetActive
|
||||||
|
const asset = marketAssets.find((asset) => asset.denom === borrowAsset.denom)
|
||||||
|
|
||||||
|
if (!asset) return null
|
||||||
|
|
||||||
|
return <AmountAndValue asset={asset} amount={borrowAsset.debt} />
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
accessorKey: 'liquidity',
|
accessorKey: 'liquidity',
|
||||||
header: 'Liquidity Available',
|
header: 'Liquidity Available',
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => {
|
||||||
<div className='items-right flex flex-col'>
|
const asset = marketAssets.find((asset) => asset.denom === row.original.denom)
|
||||||
<div className=''>{row.original.liquidity.amount}</div>
|
|
||||||
<div className='text-xs opacity-60'>${row.original.liquidity.value}</div>
|
if (!asset) return null
|
||||||
</div>
|
|
||||||
),
|
return <AmountAndValue asset={asset} amount={row.original.liquidity.amount} />
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'status',
|
accessorKey: 'status',
|
||||||
@ -91,7 +114,7 @@ export const BorrowTable = (props: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<table className='w-full'>
|
<table className='w-full'>
|
||||||
<thead className='bg-white/5'>
|
<thead className='bg-black/20'>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
<tr key={headerGroup.id}>
|
<tr key={headerGroup.id}>
|
||||||
{headerGroup.headers.map((header, index) => {
|
{headerGroup.headers.map((header, index) => {
|
||||||
@ -100,7 +123,7 @@ export const BorrowTable = (props: Props) => {
|
|||||||
key={header.id}
|
key={header.id}
|
||||||
onClick={header.column.getToggleSortingHandler()}
|
onClick={header.column.getToggleSortingHandler()}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'px-4 py-2',
|
'px-4 py-3',
|
||||||
header.column.getCanSort() && 'cursor-pointer',
|
header.column.getCanSort() && 'cursor-pointer',
|
||||||
header.id === 'symbol' ? 'text-left' : 'text-right',
|
header.id === 'symbol' ? 'text-left' : 'text-right',
|
||||||
)}
|
)}
|
||||||
@ -109,6 +132,7 @@ export const BorrowTable = (props: Props) => {
|
|||||||
className={classNames(
|
className={classNames(
|
||||||
'flex',
|
'flex',
|
||||||
header.id === 'symbol' ? 'justify-start' : 'justify-end',
|
header.id === 'symbol' ? 'justify-start' : 'justify-end',
|
||||||
|
'align-center',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{header.column.getCanSort()
|
{header.column.getCanSort()
|
||||||
@ -124,7 +148,9 @@ export const BorrowTable = (props: Props) => {
|
|||||||
),
|
),
|
||||||
}[header.column.getIsSorted() as string] ?? null
|
}[header.column.getIsSorted() as string] ?? null
|
||||||
: null}
|
: null}
|
||||||
<span>{flexRender(header.column.columnDef.header, header.getContext())}</span>
|
<Text tag='span' size='sm' className='font-normal text-white/40'>
|
||||||
|
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
)
|
)
|
||||||
|
@ -121,11 +121,12 @@ export const BorrowCapacity = ({
|
|||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
className='text-white'
|
className='text-white'
|
||||||
animate
|
animate
|
||||||
|
options={{
|
||||||
|
minDecimals: decimals,
|
||||||
|
maxDecimals: decimals,
|
||||||
|
suffix: '%',
|
||||||
|
}}
|
||||||
amount={percentOfMaxRound}
|
amount={percentOfMaxRound}
|
||||||
minDecimals={decimals}
|
|
||||||
maxDecimals={decimals}
|
|
||||||
suffix='%'
|
|
||||||
abbreviated={false}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
@ -1,314 +1,25 @@
|
|||||||
import { Dialog, Switch, Transition } from '@headlessui/react'
|
|
||||||
import BigNumber from 'bignumber.js'
|
|
||||||
import React, { useMemo, useState } from 'react'
|
|
||||||
import { NumericFormat } from 'react-number-format'
|
|
||||||
|
|
||||||
import { Button } from 'components/Button'
|
|
||||||
import { CircularProgress } from 'components/CircularProgress'
|
|
||||||
import { ContainerSecondary } from 'components/ContainerSecondary'
|
|
||||||
import { Gauge } from 'components/Gauge'
|
|
||||||
import { PositionsList } from 'components/PositionsList'
|
|
||||||
import { ProgressBar } from 'components/ProgressBar'
|
|
||||||
import { Slider } from 'components/Slider'
|
|
||||||
import { Text } from 'components/Text'
|
|
||||||
import { Tooltip } from 'components/Tooltip'
|
|
||||||
import { useAccountStats } from 'hooks/data/useAccountStats'
|
|
||||||
import { useBalances } from 'hooks/data/useBalances'
|
|
||||||
import { useCalculateMaxBorrowAmount } from 'hooks/data/useCalculateMaxBorrowAmount'
|
|
||||||
import { useBorrowFunds } from 'hooks/mutations/useBorrowFunds'
|
|
||||||
import { useAllBalances } from 'hooks/queries/useAllBalances'
|
|
||||||
import { useMarkets } from 'hooks/queries/useMarkets'
|
|
||||||
import { useTokenPrices } from 'hooks/queries/useTokenPrices'
|
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import { getBaseAsset, getMarketAssets } from 'utils/assets'
|
|
||||||
import { formatCurrency, formatValue } from 'utils/formatters'
|
|
||||||
import { getTokenDecimals, getTokenSymbol } from 'utils/tokens'
|
|
||||||
|
|
||||||
type Props = {
|
import { Modal } from './Modal'
|
||||||
show: boolean
|
import TitleAndSubCell from './TitleAndSubCell'
|
||||||
onClose: () => void
|
|
||||||
tokenDenom: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const BorrowModal = ({ show, onClose, tokenDenom }: Props) => {
|
export default function BorrowModal() {
|
||||||
const [amount, setAmount] = useState(0)
|
const open = useStore((s) => s.borrowModal)
|
||||||
const [isBorrowToCreditAccount, setIsBorrowToCreditAccount] = useState(false)
|
|
||||||
|
|
||||||
const selectedAccount = useStore((s) => s.selectedAccount)
|
function setOpen(isOpen: boolean) {
|
||||||
const marketAssets = getMarketAssets()
|
useStore.setState({ borrowModal: isOpen })
|
||||||
const baseAsset = getBaseAsset()
|
|
||||||
|
|
||||||
const balances = useBalances()
|
|
||||||
|
|
||||||
const { actions, borrowAmount } = useMemo(() => {
|
|
||||||
const borrowAmount = BigNumber(amount)
|
|
||||||
.times(10 ** getTokenDecimals(tokenDenom, marketAssets))
|
|
||||||
.toNumber()
|
|
||||||
|
|
||||||
const withdrawAmount = isBorrowToCreditAccount ? 0 : borrowAmount
|
|
||||||
|
|
||||||
return {
|
|
||||||
borrowAmount,
|
|
||||||
withdrawAmount,
|
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
type: 'borrow',
|
|
||||||
amount: borrowAmount,
|
|
||||||
denom: tokenDenom,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'withdraw',
|
|
||||||
amount: withdrawAmount,
|
|
||||||
denom: tokenDenom,
|
|
||||||
},
|
|
||||||
] as AccountStatsAction[],
|
|
||||||
}
|
|
||||||
}, [amount, isBorrowToCreditAccount, tokenDenom, marketAssets])
|
|
||||||
|
|
||||||
const accountStats = useAccountStats(actions)
|
|
||||||
|
|
||||||
const tokenSymbol = getTokenSymbol(tokenDenom, marketAssets)
|
|
||||||
|
|
||||||
const { mutate, isLoading } = useBorrowFunds(borrowAmount, tokenDenom, !isBorrowToCreditAccount, {
|
|
||||||
onSuccess: () => {
|
|
||||||
onClose()
|
|
||||||
useStore.setState({
|
|
||||||
toast: { message: `${amount} ${tokenSymbol} successfully Borrowed` },
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const { data: tokenPrices } = useTokenPrices()
|
|
||||||
const { data: balancesData } = useAllBalances()
|
|
||||||
const { data: marketsData } = useMarkets()
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
mutate()
|
|
||||||
}
|
|
||||||
|
|
||||||
const walletAmount = useMemo(() => {
|
|
||||||
return BigNumber(balancesData?.find((balance) => balance.denom === tokenDenom)?.amount ?? 0)
|
|
||||||
.div(10 ** getTokenDecimals(tokenDenom, marketAssets))
|
|
||||||
.toNumber()
|
|
||||||
}, [balancesData, tokenDenom, marketAssets])
|
|
||||||
|
|
||||||
const tokenPrice = tokenPrices?.[tokenDenom] ?? 0
|
|
||||||
const borrowRate = Number(marketsData?.[tokenDenom]?.borrow_rate)
|
|
||||||
|
|
||||||
const maxValue = useCalculateMaxBorrowAmount(tokenDenom, isBorrowToCreditAccount)
|
|
||||||
|
|
||||||
const percentageValue = useMemo(() => {
|
|
||||||
if (isNaN(amount) || maxValue === 0) return 0
|
|
||||||
|
|
||||||
return (amount * 100) / maxValue
|
|
||||||
}, [amount, maxValue])
|
|
||||||
|
|
||||||
const handleValueChange = (value: number) => {
|
|
||||||
if (value > maxValue) {
|
|
||||||
setAmount(maxValue)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setAmount(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSliderValueChange = (value: number[]) => {
|
|
||||||
const decimal = value[0] / 100
|
|
||||||
const tokenDecimals = getTokenDecimals(tokenDenom, marketAssets)
|
|
||||||
// limit decimal precision based on token contract decimals
|
|
||||||
const newAmount = Number((decimal * maxValue).toFixed(tokenDecimals))
|
|
||||||
|
|
||||||
setAmount(newAmount)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleBorrowTargetChange = () => {
|
|
||||||
setIsBorrowToCreditAccount((c) => !c)
|
|
||||||
// reset amount due to max value calculations changing depending on borrow target
|
|
||||||
setAmount(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Transition appear show={show} as={React.Fragment}>
|
<Modal open={open} setOpen={setOpen} title='Borrow OSMO'>
|
||||||
<Dialog as='div' className='relative z-10' onClose={onClose}>
|
<div className='flex gap-3'>
|
||||||
<Transition.Child
|
<TitleAndSubCell title='10.00%' sub={'Borrow rate'} />
|
||||||
as={React.Fragment}
|
<div className='h-100 w-[1px] bg-white/10'></div>
|
||||||
enter='ease-out duration-300'
|
<TitleAndSubCell title='$200' sub={'Borrowed'} />
|
||||||
enterFrom='opacity-0'
|
<div className='h-100 w-[1px] bg-white/10'></div>
|
||||||
enterTo='opacity-100'
|
<TitleAndSubCell title='10.5M ($105M)' sub={'Liquidity available'} />
|
||||||
leave='ease-in duration-200'
|
|
||||||
leaveFrom='opacity-100'
|
|
||||||
leaveTo='opacity-0'
|
|
||||||
>
|
|
||||||
<div className='fixed inset-0 bg-black bg-opacity-80' />
|
|
||||||
</Transition.Child>
|
|
||||||
|
|
||||||
<div className='fixed inset-0 overflow-y-auto'>
|
|
||||||
<div className='flex min-h-full items-center justify-center p-4'>
|
|
||||||
<Transition.Child
|
|
||||||
as={React.Fragment}
|
|
||||||
enter='ease-out duration-300'
|
|
||||||
enterFrom='opacity-0 scale-95'
|
|
||||||
enterTo='opacity-100 scale-100'
|
|
||||||
leave='ease-in duration-200'
|
|
||||||
leaveFrom='opacity-100 scale-100'
|
|
||||||
leaveTo='opacity-0 scale-95'
|
|
||||||
>
|
|
||||||
<Dialog.Panel className='flex w-full max-w-3xl transform overflow-hidden rounded-2xl bg-[#585A74] align-middle shadow-xl transition-all'>
|
|
||||||
{isLoading && (
|
|
||||||
<div className='absolute inset-0 z-40 grid place-items-center bg-black/50'>
|
|
||||||
<CircularProgress />
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
<div className='flex'></div>
|
||||||
<div className='flex flex-1 flex-col p-4'>
|
</Modal>
|
||||||
<Dialog.Title as='h3' className='mb-4 text-center font-medium'>
|
|
||||||
Borrow {tokenSymbol}
|
|
||||||
</Dialog.Title>
|
|
||||||
<div className='mb-4 flex flex-col gap-2 text-sm'>
|
|
||||||
<ContainerSecondary>
|
|
||||||
<p className='mb-1'>
|
|
||||||
In wallet: {walletAmount.toLocaleString()} {tokenSymbol}
|
|
||||||
</p>
|
|
||||||
<p className='mb-5'>Borrow Rate: {(borrowRate * 100).toFixed(2)}%</p>
|
|
||||||
|
|
||||||
<div className='mb-7'>
|
|
||||||
<p className='mb-2 font-semibold uppercase tracking-widest'>Amount</p>
|
|
||||||
<NumericFormat
|
|
||||||
className='mb-2 h-[32px] w-full rounded-lg border border-black/50 bg-transparent px-2'
|
|
||||||
value={amount}
|
|
||||||
placeholder='0'
|
|
||||||
allowNegative={false}
|
|
||||||
onValueChange={(v) => handleValueChange(v.floatValue || 0)}
|
|
||||||
suffix={` ${tokenSymbol}`}
|
|
||||||
decimalScale={getTokenDecimals(tokenDenom, marketAssets)}
|
|
||||||
/>
|
|
||||||
<div className='flex justify-between text-xs tracking-widest'>
|
|
||||||
<div>
|
|
||||||
1 {tokenSymbol} = {formatCurrency(tokenPrice)}
|
|
||||||
</div>
|
|
||||||
<div>{formatCurrency(tokenPrice * amount)}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Slider
|
|
||||||
className='mb-6'
|
|
||||||
value={percentageValue}
|
|
||||||
onChange={handleSliderValueChange}
|
|
||||||
onMaxClick={() => setAmount(maxValue)}
|
|
||||||
/>
|
|
||||||
</ContainerSecondary>
|
|
||||||
<ContainerSecondary className='flex items-center justify-between'>
|
|
||||||
<div className='flex'>
|
|
||||||
Borrow to Credit Account{' '}
|
|
||||||
<Tooltip
|
|
||||||
className='ml-2'
|
|
||||||
content={
|
|
||||||
<>
|
|
||||||
<Text size='sm' className='mb-2'>
|
|
||||||
OFF = Borrow directly into your wallet by using your account Assets
|
|
||||||
as collateral. The borrowed asset will become a liability in your
|
|
||||||
account.
|
|
||||||
</Text>
|
|
||||||
<Text size='sm'>
|
|
||||||
ON = Borrow into your Account. The borrowed asset will be available
|
|
||||||
in the account as an Asset and appear also as a liability in your
|
|
||||||
account.
|
|
||||||
</Text>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Switch
|
|
||||||
checked={isBorrowToCreditAccount}
|
|
||||||
onChange={handleBorrowTargetChange}
|
|
||||||
className={`${
|
|
||||||
isBorrowToCreditAccount ? 'bg-blue-600' : 'bg-gray-400'
|
|
||||||
} relative inline-flex h-6 w-11 items-center rounded-full`}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={`${
|
|
||||||
isBorrowToCreditAccount ? 'translate-x-6' : 'translate-x-1'
|
|
||||||
} inline-block h-4 w-4 transform rounded-full bg-white transition`}
|
|
||||||
/>
|
|
||||||
</Switch>
|
|
||||||
</ContainerSecondary>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
className='mt-auto'
|
|
||||||
onClick={handleSubmit}
|
|
||||||
disabled={amount === 0 || !amount}
|
|
||||||
>
|
|
||||||
Borrow
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='flex w-1/2 flex-col justify-center bg-[#4A4C60] p-4'>
|
|
||||||
<p className='text-bold mb-3 text-xs uppercase text-white/50'>About</p>
|
|
||||||
<h4 className='mb-4 text-xl'>Account {selectedAccount}</h4>
|
|
||||||
<div className='mb-2 rounded-md border border-white/20 p-3'>
|
|
||||||
{accountStats && (
|
|
||||||
<div className='flex items-center gap-x-3'>
|
|
||||||
<p className='flex-1 text-xs'>
|
|
||||||
{formatCurrency(
|
|
||||||
BigNumber(accountStats.netWorth)
|
|
||||||
.dividedBy(10 ** baseAsset.decimals)
|
|
||||||
.toNumber(),
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<Gauge
|
|
||||||
value={accountStats.currentLeverage / accountStats.maxLeverage}
|
|
||||||
label='Lvg'
|
|
||||||
tooltip={
|
|
||||||
<Text size='sm'>
|
|
||||||
Current Leverage:{' '}
|
|
||||||
{formatValue(accountStats.currentLeverage, 0, 2, true, false, 'x')}
|
|
||||||
<br />
|
|
||||||
Max Leverage:{' '}
|
|
||||||
{formatValue(accountStats.maxLeverage, 0, 2, true, false, 'x')}
|
|
||||||
</Text>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Gauge
|
|
||||||
value={accountStats.risk}
|
|
||||||
label='Risk'
|
|
||||||
tooltip={
|
|
||||||
<Text size='sm'>
|
|
||||||
Current Risk:{' '}
|
|
||||||
{formatValue(accountStats.risk * 100, 0, 2, true, false, '%')}
|
|
||||||
</Text>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ProgressBar value={accountStats.health} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className='mb-2 rounded-md border border-white/20 p-3 text-sm'>
|
|
||||||
<div className='mb-1 flex justify-between'>
|
|
||||||
<div>Total Position:</div>
|
|
||||||
<div className='font-semibold'>
|
|
||||||
{formatCurrency(
|
|
||||||
BigNumber(accountStats?.totalPosition ?? 0)
|
|
||||||
.dividedBy(10 ** baseAsset.decimals)
|
|
||||||
.toNumber(),
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex justify-between'>
|
|
||||||
<div>Total Liabilities:</div>
|
|
||||||
<div className='font-semibold'>
|
|
||||||
{formatCurrency(
|
|
||||||
BigNumber(accountStats?.totalDebt ?? 0)
|
|
||||||
.dividedBy(10 ** baseAsset.decimals)
|
|
||||||
.toNumber(),
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<PositionsList title='Balances' data={balances} />
|
|
||||||
</div>
|
|
||||||
</Dialog.Panel>
|
|
||||||
</Transition.Child>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
</Transition>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { ReactNode } from 'react'
|
import { ReactNode } from 'react'
|
||||||
|
|
||||||
|
import { Text } from 'components/Text'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
@ -15,8 +17,10 @@ export const Card = (props: Props) => {
|
|||||||
'h-fit w-full max-w-full overflow-hidden rounded-md border-[1px] border-white/20',
|
'h-fit w-full max-w-full overflow-hidden rounded-md border-[1px] border-white/20',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className='bg-white/10 p-4 font-semibold'>{props.title}</div>
|
<Text size='lg' className='bg-white/10 p-4 font-semibold'>
|
||||||
<div className=''>{props.children}</div>
|
{props.title}
|
||||||
|
</Text>
|
||||||
|
<div>{props.children}</div>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
15
src/components/FetchPrices.tsx
Normal file
15
src/components/FetchPrices.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import useSWR from 'swr'
|
||||||
|
|
||||||
|
import useStore from 'store'
|
||||||
|
import { getPrices } from 'utils/api'
|
||||||
|
|
||||||
|
export default function FetchPrices() {
|
||||||
|
useSWR('prices', getPrices, {
|
||||||
|
refreshInterval: 30000,
|
||||||
|
onSuccess: (prices) => useStore.setState({ prices }),
|
||||||
|
})
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
@ -5,64 +5,60 @@ import React, { useEffect, useRef } from 'react'
|
|||||||
import { animated, useSpring } from 'react-spring'
|
import { animated, useSpring } from 'react-spring'
|
||||||
|
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import { formatValue } from 'utils/formatters'
|
import { FormatOptions, formatValue } from 'utils/formatters'
|
||||||
|
|
||||||
export const FormattedNumber = React.memo(
|
interface Props {
|
||||||
({
|
amount: number | string
|
||||||
amount,
|
options?: FormatOptions
|
||||||
animate = false,
|
className?: string
|
||||||
className,
|
animate?: boolean
|
||||||
minDecimals = 2,
|
}
|
||||||
maxDecimals = 2,
|
|
||||||
thousandSeparator = true,
|
export const FormattedNumber = React.memo((props: Props) => {
|
||||||
prefix = false,
|
|
||||||
suffix = false,
|
|
||||||
rounded = false,
|
|
||||||
abbreviated = false,
|
|
||||||
}: FormattedNumberProps) => {
|
|
||||||
const enableAnimations = useStore((s) => s.enableAnimations)
|
const enableAnimations = useStore((s) => s.enableAnimations)
|
||||||
const prevAmountRef = useRef<number>(0)
|
const prevAmountRef = useRef<number>(0)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (prevAmountRef.current !== Number(amount)) prevAmountRef.current = Number(amount)
|
if (prevAmountRef.current !== Number(props.amount)) prevAmountRef.current = Number(props.amount)
|
||||||
}, [amount])
|
}, [props.amount])
|
||||||
|
|
||||||
const springAmount = useSpring({
|
const springAmount = useSpring({
|
||||||
number: Number(amount),
|
number: Number(props.amount),
|
||||||
from: { number: prevAmountRef.current },
|
from: { number: prevAmountRef.current },
|
||||||
config: { duration: 1000 },
|
config: { duration: 1000 },
|
||||||
})
|
})
|
||||||
|
|
||||||
return (prevAmountRef.current === amount && amount === 0) || !animate || !enableAnimations ? (
|
return (prevAmountRef.current === props.amount && props.amount === 0) ||
|
||||||
<span className={classNames('number', className)}>
|
!props.animate ||
|
||||||
{formatValue(
|
!enableAnimations ? (
|
||||||
amount,
|
<span className={classNames('number', props.className)}>
|
||||||
minDecimals,
|
{formatValue(props.amount, {
|
||||||
maxDecimals,
|
minDecimals: props.options?.minDecimals,
|
||||||
thousandSeparator,
|
maxDecimals: props.options?.maxDecimals,
|
||||||
prefix,
|
thousandSeparator: props.options?.thousandSeparator,
|
||||||
suffix,
|
prefix: props.options?.prefix,
|
||||||
rounded,
|
suffix: props.options?.suffix,
|
||||||
abbreviated,
|
rounded: props.options?.rounded,
|
||||||
)}
|
abbreviated: props.options?.abbreviated,
|
||||||
|
decimals: props.options?.decimals,
|
||||||
|
})}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<animated.span className={classNames('number', className)}>
|
<animated.span className={classNames('number', props.className)}>
|
||||||
{springAmount.number.to((num) =>
|
{springAmount.number.to((num) =>
|
||||||
formatValue(
|
formatValue(num, {
|
||||||
num,
|
minDecimals: props.options?.minDecimals,
|
||||||
minDecimals,
|
maxDecimals: props.options?.maxDecimals,
|
||||||
maxDecimals,
|
thousandSeparator: props.options?.thousandSeparator,
|
||||||
thousandSeparator,
|
prefix: props.options?.prefix,
|
||||||
prefix,
|
suffix: props.options?.suffix,
|
||||||
suffix,
|
rounded: props.options?.rounded,
|
||||||
rounded,
|
abbreviated: props.options?.abbreviated,
|
||||||
abbreviated,
|
decimals: props.options?.decimals,
|
||||||
),
|
}),
|
||||||
)}
|
)}
|
||||||
</animated.span>
|
</animated.span>
|
||||||
)
|
)
|
||||||
},
|
})
|
||||||
)
|
|
||||||
|
|
||||||
FormattedNumber.displayName = 'FormattedNumber'
|
FormattedNumber.displayName = 'FormattedNumber'
|
||||||
|
@ -2,9 +2,12 @@ import classNames from 'classnames'
|
|||||||
import { ReactNode } from 'react'
|
import { ReactNode } from 'react'
|
||||||
|
|
||||||
import { Close } from 'components/Icons'
|
import { Close } from 'components/Icons'
|
||||||
import { Card } from 'components/Card'
|
import { Text } from 'components/Text'
|
||||||
|
|
||||||
|
import { Button } from './Button'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
title: string
|
||||||
children?: ReactNode | string
|
children?: ReactNode | string
|
||||||
content?: ReactNode | string
|
content?: ReactNode | string
|
||||||
className?: string
|
className?: string
|
||||||
@ -12,31 +15,28 @@ interface Props {
|
|||||||
setOpen?: (open: boolean) => void
|
setOpen?: (open: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Modal = ({ children, content, className, open, setOpen }: Props) => {
|
export const Modal = (props: Props) => {
|
||||||
const onClickAway = () => {
|
const onClickAway = () => {
|
||||||
if (setOpen) setOpen(false)
|
if (props.setOpen) props.setOpen(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return open ? (
|
return props.open ? (
|
||||||
<div className='fixed top-0 left-0 z-20 h-screen w-screen'>
|
<div className='fixed top-0 left-0 z-20 h-screen w-screen'>
|
||||||
<div className='relative flex h-full w-full items-center justify-center'>
|
<div className='relative flex h-full w-full items-center justify-center'>
|
||||||
<Card
|
<section
|
||||||
title='Modal'
|
className={classNames(
|
||||||
className={classNames('relative z-40 w-[790px] max-w-full p-0', className)}
|
'relative z-40 w-[790px] max-w-full rounded-md border-[1px] border-white/20 bg-white/5 p-6 backdrop-blur-3xl ',
|
||||||
>
|
props.className,
|
||||||
{setOpen && (
|
|
||||||
<span
|
|
||||||
className='absolute top-4 right-4 z-50 w-[32px] text-white opacity-60 hover:cursor-pointer hover:opacity-100'
|
|
||||||
onClick={onClickAway}
|
|
||||||
role='button'
|
|
||||||
>
|
|
||||||
<Close />
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
{children ? children : content}
|
>
|
||||||
</Card>
|
<div className='flex justify-between pb-6'>
|
||||||
|
<Text>{props.title}</Text>
|
||||||
|
<Button onClick={onClickAway} text='X' color='tertiary' />
|
||||||
|
</div>
|
||||||
|
<div>{props.children ? props.children : props.content}</div>
|
||||||
|
</section>
|
||||||
<div
|
<div
|
||||||
className='fixed top-0 left-0 z-30 block h-full w-full bg-black/70 backdrop-blur hover:cursor-pointer'
|
className='fixed top-0 left-0 z-30 block h-full w-full bg-black/50 hover:cursor-pointer'
|
||||||
onClick={onClickAway}
|
onClick={onClickAway}
|
||||||
role='button'
|
role='button'
|
||||||
/>
|
/>
|
||||||
|
@ -3,10 +3,13 @@
|
|||||||
import { ConfirmModal } from 'components/Account/ConfirmModal'
|
import { ConfirmModal } from 'components/Account/ConfirmModal'
|
||||||
import { FundAccountModal } from 'components/Account/FundAccountModal'
|
import { FundAccountModal } from 'components/Account/FundAccountModal'
|
||||||
|
|
||||||
|
import BorrowModal from './BorrowModal'
|
||||||
|
|
||||||
export const Modals = () => (
|
export const Modals = () => (
|
||||||
<>
|
<>
|
||||||
<FundAccountModal />
|
<FundAccountModal />
|
||||||
{/* <WithdrawModal /> */}
|
{/* <WithdrawModal /> */}
|
||||||
<ConfirmModal />
|
<ConfirmModal />
|
||||||
|
<BorrowModal />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -13,7 +13,6 @@ import { useRepayFunds } from 'hooks/mutations/useRepayFunds'
|
|||||||
import { useAllBalances } from 'hooks/queries/useAllBalances'
|
import { useAllBalances } from 'hooks/queries/useAllBalances'
|
||||||
import { useCreditAccountPositions } from 'hooks/queries/useCreditAccountPositions'
|
import { useCreditAccountPositions } from 'hooks/queries/useCreditAccountPositions'
|
||||||
import { useTokenPrices } from 'hooks/queries/useTokenPrices'
|
import { useTokenPrices } from 'hooks/queries/useTokenPrices'
|
||||||
import { formatCurrency } from 'utils/formatters'
|
|
||||||
import { getTokenDecimals, getTokenSymbol } from 'utils/tokens'
|
import { getTokenDecimals, getTokenSymbol } from 'utils/tokens'
|
||||||
import { getMarketAssets } from 'utils/assets'
|
import { getMarketAssets } from 'utils/assets'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
@ -33,6 +32,7 @@ export const RepayModal = ({ show, onClose, tokenDenom }: Props) => {
|
|||||||
const selectedAccount = useStore((s) => s.selectedAccount)
|
const selectedAccount = useStore((s) => s.selectedAccount)
|
||||||
const { data: positionsData } = useCreditAccountPositions(selectedAccount ?? '')
|
const { data: positionsData } = useCreditAccountPositions(selectedAccount ?? '')
|
||||||
const marketAssets = getMarketAssets()
|
const marketAssets = getMarketAssets()
|
||||||
|
const formatCurrency = useStore((s) => s.formatCurrency)
|
||||||
|
|
||||||
const tokenSymbol = getTokenSymbol(tokenDenom, marketAssets)
|
const tokenSymbol = getTokenSymbol(tokenDenom, marketAssets)
|
||||||
|
|
||||||
@ -155,9 +155,11 @@ export const RepayModal = ({ show, onClose, tokenDenom }: Props) => {
|
|||||||
/>
|
/>
|
||||||
<div className='flex justify-between text-xs tracking-widest'>
|
<div className='flex justify-between text-xs tracking-widest'>
|
||||||
<div>
|
<div>
|
||||||
1 {tokenSymbol} = {formatCurrency(tokenPrice)}
|
1 {tokenSymbol} = {formatCurrency({ denom: tokenDenom, amount: '1' })}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{formatCurrency({ denom: tokenDenom, amount: amount.toString() })}
|
||||||
</div>
|
</div>
|
||||||
<div>{formatCurrency(tokenPrice * amount)}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ export const Text = ({
|
|||||||
<HtmlElement
|
<HtmlElement
|
||||||
className={classNames(
|
className={classNames(
|
||||||
className,
|
className,
|
||||||
|
'flex items-center',
|
||||||
uppercase ? `text-${sizeClass}-caps` : `text-${sizeClass}`,
|
uppercase ? `text-${sizeClass}-caps` : `text-${sizeClass}`,
|
||||||
monospace && 'number',
|
monospace && 'number',
|
||||||
)}
|
)}
|
||||||
|
20
src/components/TitleAndSubCell.tsx
Normal file
20
src/components/TitleAndSubCell.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { Text } from 'components/Text'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title: string | React.ReactNode
|
||||||
|
sub: string | React.ReactNode
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TitleAndSubCell(props: Props) {
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col gap-[0.5]'>
|
||||||
|
<Text className={props.className} size='sm'>
|
||||||
|
{props.title}
|
||||||
|
</Text>
|
||||||
|
<Text size='sm' className={'text-white/50 ' + props.className}>
|
||||||
|
{props.sub}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -104,7 +104,7 @@ export default function ConnectedButton() {
|
|||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<CircularProgress size={12} />
|
<CircularProgress size={12} />
|
||||||
) : (
|
) : (
|
||||||
`${formatValue(walletAmount, 2, 2, true, false, ` ${baseAsset.symbol}`)}`
|
`${formatValue(walletAmount, { suffix: baseAsset.symbol })}`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react'
|
|
||||||
|
|
||||||
import { useCreditAccountPositions } from 'hooks/queries/useCreditAccountPositions'
|
|
||||||
import { useMarkets } from 'hooks/queries/useMarkets'
|
|
||||||
import { useTokenPrices } from 'hooks/queries/useTokenPrices'
|
|
||||||
import { formatBalances } from 'utils/balances'
|
|
||||||
import useStore from 'store'
|
|
||||||
import { getMarketAssets } from 'utils/assets'
|
|
||||||
|
|
||||||
export const useBalances = () => {
|
|
||||||
const [balanceData, setBalanceData] = useState<PositionsData[]>()
|
|
||||||
|
|
||||||
const { data: marketsData } = useMarkets()
|
|
||||||
const { data: tokenPrices } = useTokenPrices()
|
|
||||||
const selectedAccount = useStore((s) => s.selectedAccount)
|
|
||||||
const marketAssets = getMarketAssets()
|
|
||||||
|
|
||||||
const { data: positionsData, isLoading: isLoadingPositions } = useCreditAccountPositions(
|
|
||||||
selectedAccount ?? '',
|
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const balances =
|
|
||||||
positionsData?.coins && tokenPrices
|
|
||||||
? formatBalances(positionsData.coins, tokenPrices, false, marketAssets)
|
|
||||||
: []
|
|
||||||
const debtBalances =
|
|
||||||
positionsData?.debts && tokenPrices
|
|
||||||
? formatBalances(positionsData.debts, tokenPrices, true, marketAssets, marketsData)
|
|
||||||
: []
|
|
||||||
|
|
||||||
setBalanceData([...balances, ...debtBalances])
|
|
||||||
}, [positionsData, marketsData, tokenPrices, marketAssets])
|
|
||||||
|
|
||||||
return balanceData
|
|
||||||
}
|
|
@ -12,7 +12,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
const account = await (await fetch(`${URL_API}/accounts/${accountId}`)).json()
|
const account = await (await fetch(`${URL_API}/accounts/${accountId}`)).json()
|
||||||
|
|
||||||
if (account) {
|
if (account) {
|
||||||
return res.status(200).json(account.debts)
|
return res.status(200).json([{ denom: 'uosmo', amount: '123876' }])
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(404)
|
return res.status(404)
|
||||||
|
@ -1,23 +1,52 @@
|
|||||||
|
import { Coin } from '@cosmjs/stargate'
|
||||||
|
import BigNumber from 'bignumber.js'
|
||||||
import { GetState, SetState } from 'zustand'
|
import { GetState, SetState } from 'zustand'
|
||||||
|
|
||||||
|
import { getMarketAssets } from 'utils/assets'
|
||||||
|
import { formatValue } from 'utils/formatters'
|
||||||
|
|
||||||
export interface CommonSlice {
|
export interface CommonSlice {
|
||||||
|
borrowModal: boolean
|
||||||
createAccountModal: boolean
|
createAccountModal: boolean
|
||||||
deleteAccountModal: boolean
|
deleteAccountModal: boolean
|
||||||
enableAnimations: boolean
|
enableAnimations: boolean
|
||||||
|
repayModal: boolean
|
||||||
fundAccountModal: boolean
|
fundAccountModal: boolean
|
||||||
|
prices: Coin[]
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
selectedAccount: string | null
|
selectedAccount: string | null
|
||||||
withdrawModal: boolean
|
withdrawModal: boolean
|
||||||
|
formatCurrency: (coin: Coin) => string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createCommonSlice(set: SetState<CommonSlice>, get: GetState<CommonSlice>) {
|
export function createCommonSlice(set: SetState<CommonSlice>, get: GetState<CommonSlice>) {
|
||||||
return {
|
return {
|
||||||
|
borrowModal: false,
|
||||||
createAccountModal: false,
|
createAccountModal: false,
|
||||||
deleteAccountModal: false,
|
deleteAccountModal: false,
|
||||||
|
repayModal: false,
|
||||||
enableAnimations: true,
|
enableAnimations: true,
|
||||||
fundAccountModal: false,
|
fundAccountModal: false,
|
||||||
|
prices: [],
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
selectedAccount: null,
|
selectedAccount: null,
|
||||||
withdrawModal: false,
|
withdrawModal: false,
|
||||||
|
formatCurrency: (coin: Coin) => {
|
||||||
|
const price = get().prices.find((price) => price.denom === coin.denom)
|
||||||
|
const marketAsset = getMarketAssets().find((asset) => asset.denom === coin.denom)
|
||||||
|
|
||||||
|
if (!price || !marketAsset) return ''
|
||||||
|
|
||||||
|
return formatValue(
|
||||||
|
new BigNumber(coin.amount)
|
||||||
|
.times(price.amount)
|
||||||
|
.dividedBy(10 ** marketAsset.decimals)
|
||||||
|
.toNumber(),
|
||||||
|
{
|
||||||
|
minDecimals: 0,
|
||||||
|
prefix: '$',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,20 +66,21 @@ a {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ORBS */
|
/* ORBS */
|
||||||
@mixin orbs($count) {
|
@mixin orbs($count, $hue) {
|
||||||
$text-shadow: ();
|
$text-shadow: ();
|
||||||
@for $i from 0 through $count {
|
@for $i from 0 through $count {
|
||||||
$text-shadow: $text-shadow,
|
$text-shadow: $text-shadow,
|
||||||
(-0.5+ (random()) * 3) +
|
(-0.5+ (random()) * 3) + em (-0.5+ (random()) * 3) + em 10px rgb(92, 5, 92);
|
||||||
em
|
// hsla((random() * 50)+$hue, 100%, 45%);
|
||||||
(-0.5+ (random()) * 3) +
|
|
||||||
em
|
|
||||||
7px
|
|
||||||
hsla((random() * 50)+210, 100%, 45%, 0.3);
|
|
||||||
}
|
}
|
||||||
text-shadow: $text-shadow;
|
text-shadow: $text-shadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin newOrbs($count, $color) {
|
||||||
|
filter: blur(4px);
|
||||||
|
background: radial-gradient(circle at center, rgba($color, 0.25) 0%, rgba($color, 0) 20%);
|
||||||
|
}
|
||||||
|
|
||||||
.background {
|
.background {
|
||||||
font-family: serif;
|
font-family: serif;
|
||||||
font-size: 90px;
|
font-size: 90px;
|
||||||
@ -98,18 +99,27 @@ a {
|
|||||||
width: 3em;
|
width: 3em;
|
||||||
height: 3em;
|
height: 3em;
|
||||||
content: '.';
|
content: '.';
|
||||||
|
color: transparent;
|
||||||
mix-blend-mode: screen;
|
mix-blend-mode: screen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
top: 10%;
|
||||||
|
left: 10%;
|
||||||
|
@include newOrbs(1, rgb(177, 47, 37));
|
||||||
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
@include orbs(15);
|
top: 80%;
|
||||||
|
left: 80%;
|
||||||
|
@include newOrbs(1, rgb(83, 7, 129));
|
||||||
animation-duration: 300s;
|
animation-duration: 300s;
|
||||||
animation-delay: -50s;
|
// animation-delay: -50s;
|
||||||
animation: 180s -15s move infinite ease-in-out alternate;
|
animation: 180s -15s move infinite ease-in-out alternate;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
@include orbs(25);
|
// @include orbs(15, 260);
|
||||||
animation-duration: 600s;
|
animation-duration: 600s;
|
||||||
animation: 180s 0s move infinite ease-in-out alternate;
|
animation: 180s 0s move infinite ease-in-out alternate;
|
||||||
}
|
}
|
||||||
|
5
src/types/interfaces/asset.d.ts
vendored
5
src/types/interfaces/asset.d.ts
vendored
@ -26,8 +26,5 @@ interface BorrowAsset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface BorrowAssetActive extends BorrowAsset {
|
interface BorrowAssetActive extends BorrowAsset {
|
||||||
debt: {
|
debt: string
|
||||||
amount: string
|
|
||||||
value: string
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
import { Coin } from '@cosmjs/stargate'
|
|
||||||
|
|
||||||
import { convertFromGwei, getTokenTotalUSDValue } from 'utils/formatters'
|
|
||||||
import { getTokenSymbol } from 'utils/tokens'
|
|
||||||
|
|
||||||
export const formatBalances = (
|
|
||||||
positionData: Coin[],
|
|
||||||
tokenPrices: KeyValuePair,
|
|
||||||
debt: boolean,
|
|
||||||
marketAssets: Asset[],
|
|
||||||
marketsData?: MarketData,
|
|
||||||
): PositionsData[] => {
|
|
||||||
const balances: PositionsData[] = []
|
|
||||||
|
|
||||||
positionData.forEach((coin) => {
|
|
||||||
const dataEntry: PositionsData = {
|
|
||||||
asset: {
|
|
||||||
amount: getTokenSymbol(coin.denom, marketAssets),
|
|
||||||
type: debt ? 'debt' : undefined,
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
amount: getTokenTotalUSDValue(coin.amount, coin.denom, marketAssets, tokenPrices),
|
|
||||||
format: 'number',
|
|
||||||
prefix: '$',
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
amount: convertFromGwei(coin.amount, coin.denom, marketAssets),
|
|
||||||
format: 'number',
|
|
||||||
maxDecimals: 4,
|
|
||||||
minDecimals: 0,
|
|
||||||
},
|
|
||||||
apy: {
|
|
||||||
amount: debt ? Number(marketsData?.[coin.denom].borrow_rate) * 100 : '-',
|
|
||||||
format: debt ? 'number' : 'string',
|
|
||||||
suffix: '%',
|
|
||||||
minDecimals: 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
balances.push(dataEntry)
|
|
||||||
})
|
|
||||||
|
|
||||||
return balances
|
|
||||||
}
|
|
@ -10,28 +10,6 @@ export function truncate(text = '', [h, t]: [number, number] = [6, 6]): string {
|
|||||||
return text.length > h + t ? [head, tail].join('...') : text
|
return text.length > h + t ? [head, tail].join('...') : text
|
||||||
}
|
}
|
||||||
|
|
||||||
export const formatCurrency = (value: string | number) => {
|
|
||||||
return Number(value).toLocaleString('en-US', {
|
|
||||||
style: 'currency',
|
|
||||||
currency: 'USD',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getTokenTotalUSDValue = (
|
|
||||||
amount: string,
|
|
||||||
denom: string,
|
|
||||||
marketAssets: Asset[],
|
|
||||||
tokenPrices?: KeyValuePair,
|
|
||||||
) => {
|
|
||||||
if (!tokenPrices) return 0
|
|
||||||
|
|
||||||
return (
|
|
||||||
BigNumber(amount)
|
|
||||||
.div(10 ** getTokenDecimals(denom, marketAssets))
|
|
||||||
.toNumber() * tokenPrices[denom]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const convertFromGwei = (amount: string | number, denom: string, marketAssets: Asset[]) => {
|
export const convertFromGwei = (amount: string | number, denom: string, marketAssets: Asset[]) => {
|
||||||
return BigNumber(amount)
|
return BigNumber(amount)
|
||||||
.div(10 ** getTokenDecimals(denom, marketAssets))
|
.div(10 ** getTokenDecimals(denom, marketAssets))
|
||||||
@ -44,26 +22,34 @@ export const convertToGwei = (amount: string | number, denom: string, marketAsse
|
|||||||
.toNumber()
|
.toNumber()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const formatValue = (
|
export interface FormatOptions {
|
||||||
amount: number | string,
|
decimals?: number
|
||||||
minDecimals = 2,
|
minDecimals?: number
|
||||||
maxDecimals = 2,
|
maxDecimals?: number
|
||||||
thousandSeparator = true,
|
thousandSeparator?: boolean
|
||||||
prefix: boolean | string = false,
|
prefix?: string
|
||||||
suffix: boolean | string = false,
|
suffix?: string
|
||||||
rounded = false,
|
rounded?: boolean
|
||||||
abbreviated = false,
|
abbreviated?: boolean
|
||||||
): string => {
|
}
|
||||||
|
|
||||||
|
export const formatValue = (amount: number | string, options?: FormatOptions): string => {
|
||||||
let numberOfZeroDecimals: number | null = null
|
let numberOfZeroDecimals: number | null = null
|
||||||
|
const minDecimals = options?.minDecimals ?? 2
|
||||||
|
const maxDecimals = options?.maxDecimals ?? 2
|
||||||
|
const thousandSeparator = options?.thousandSeparator ?? true
|
||||||
|
|
||||||
if (typeof amount === 'string') {
|
if (typeof amount === 'string') {
|
||||||
const decimals = amount.split('.')[1] ?? null
|
const decimals = amount.split('.')[1] ?? null
|
||||||
if (decimals && Number(decimals) === 0) {
|
if (decimals && Number(decimals) === 0) {
|
||||||
numberOfZeroDecimals = decimals.length
|
numberOfZeroDecimals = decimals.length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let convertedAmount: number | string = +amount || 0
|
let convertedAmount: number | string = new BigNumber(amount)
|
||||||
|
.dividedBy(10 ** (options?.decimals ?? 0))
|
||||||
|
.toNumber()
|
||||||
|
|
||||||
const amountSuffix = abbreviated
|
const amountSuffix = options?.abbreviated
|
||||||
? convertedAmount >= 1_000_000_000
|
? convertedAmount >= 1_000_000_000
|
||||||
? 'B'
|
? 'B'
|
||||||
: convertedAmount >= 1_000_000
|
: convertedAmount >= 1_000_000
|
||||||
@ -73,19 +59,17 @@ export const formatValue = (
|
|||||||
: false
|
: false
|
||||||
: ''
|
: ''
|
||||||
|
|
||||||
const amountPrefix = prefix
|
|
||||||
|
|
||||||
if (amountSuffix === 'B') {
|
if (amountSuffix === 'B') {
|
||||||
convertedAmount = Number(amount) / 1_000_000_000
|
convertedAmount = Number(convertedAmount) / 1_000_000_000
|
||||||
}
|
}
|
||||||
if (amountSuffix === 'M') {
|
if (amountSuffix === 'M') {
|
||||||
convertedAmount = Number(amount) / 1_000_000
|
convertedAmount = Number(convertedAmount) / 1_000_000
|
||||||
}
|
}
|
||||||
if (amountSuffix === 'K') {
|
if (amountSuffix === 'K') {
|
||||||
convertedAmount = Number(amount) / 1_000
|
convertedAmount = Number(convertedAmount) / 1_000
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rounded) {
|
if (options?.rounded) {
|
||||||
convertedAmount = convertedAmount.toFixed(maxDecimals)
|
convertedAmount = convertedAmount.toFixed(maxDecimals)
|
||||||
} else {
|
} else {
|
||||||
const amountFractions = String(convertedAmount).split('.')
|
const amountFractions = String(convertedAmount).split('.')
|
||||||
@ -112,8 +96,8 @@ export const formatValue = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
let returnValue = ''
|
let returnValue = ''
|
||||||
if (amountPrefix) {
|
if (options?.prefix) {
|
||||||
returnValue += amountPrefix
|
returnValue += options.prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
returnValue += convertedAmount
|
returnValue += convertedAmount
|
||||||
@ -131,9 +115,23 @@ export const formatValue = (
|
|||||||
returnValue += amountSuffix
|
returnValue += amountSuffix
|
||||||
}
|
}
|
||||||
|
|
||||||
if (suffix) {
|
if (options?.suffix) {
|
||||||
returnValue += suffix
|
returnValue += options.suffix
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValue
|
return returnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatLeverage(leverage: number) {
|
||||||
|
return formatValue(leverage, {
|
||||||
|
minDecimals: 0,
|
||||||
|
suffix: 'x',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatPercent(percent: number | string) {
|
||||||
|
return formatValue(+percent * 100, {
|
||||||
|
minDecimals: 0,
|
||||||
|
suffix: '%',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user