Mp 2354 credit account balances (#192)

This commit is contained in:
Linkie Link 2023-05-11 15:03:00 +02:00 committed by GitHub
parent ba6df26ebd
commit 5a71ace0d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 262 additions and 39 deletions

View File

@ -38,9 +38,7 @@ export default function Accordion(props: Props) {
<Subtract />
</div>
</summary>
<div className='bg-white/5 px-4 py-0 transition-[padding] group-[[open]]:py-4'>
{item.content}
</div>
<div className='bg-white/5 transition-[padding]'>{item.content}</div>
</details>
))}
</Card>

View File

@ -0,0 +1,219 @@
'use client'
import {
ColumnDef,
flexRender,
getCoreRowModel,
getSortedRowModel,
SortingState,
useReactTable,
} from '@tanstack/react-table'
import classNames from 'classnames'
import React from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
import { SortAsc, SortDesc, SortNone } from 'components/Icons'
import Text from 'components/Text'
import { ASSETS } from 'constants/assets'
import useStore from 'store'
import { convertToDisplayAmount, demagnify } from 'utils/formatters'
interface Props {
data: Account
}
export const AcccountBalancesTable = (props: Props) => {
const displayCurrency = useStore((s) => s.displayCurrency)
const prices = useStore((s) => s.prices)
const [sorting, setSorting] = React.useState<SortingState>([])
const balanceData = React.useMemo<AccountBalanceRow[]>(() => {
const accountDeposits = props.data?.deposits ?? []
const accountLends = props.data?.lends ?? []
const deposits = accountDeposits.map((deposit) => {
const asset = ASSETS.find((asset) => asset.denom === deposit.denom) ?? ASSETS[0]
const apy = 0
return {
type: 'deposit',
symbol: asset.symbol,
denom: deposit.denom,
amount: deposit.amount,
size: demagnify(deposit.amount, asset),
value: convertToDisplayAmount(
{ amount: deposit.amount, denom: deposit.denom },
displayCurrency,
prices,
),
apy,
}
})
const lends = accountLends.map((lending) => {
const asset = ASSETS.find((asset) => asset.denom === lending.denom) ?? ASSETS[0]
const apy = 0
return {
type: 'lending',
symbol: asset.symbol,
denom: lending.denom,
amount: lending.amount,
size: demagnify(lending.amount, asset),
value: convertToDisplayAmount(
{ amount: lending.amount, denom: lending.denom },
displayCurrency,
prices,
),
apy,
}
})
return [...deposits, ...lends]
}, [displayCurrency, prices, props.data?.deposits, props.data?.lends])
const columns = React.useMemo<ColumnDef<AccountBalanceRow>[]>(
() => [
{
header: 'Asset',
accessorKey: 'symbol',
id: 'symbol',
cell: ({ row }) => {
return (
<Text size='xs'>
{row.original.symbol}
{row.original.type === 'lending' && <span className='text-profit'>(Lent)</span>}
</Text>
)
},
},
{
header: 'Value',
accessorKey: 'value',
id: 'value',
cell: ({ row }) => {
return (
<DisplayCurrency
coin={{ denom: row.original.denom, amount: row.original.amount }}
className='text-right text-xs'
/>
)
},
},
{
id: 'size',
accessorKey: 'size',
header: 'Size',
cell: ({ row }) => {
return (
<FormattedNumber
className='text-right text-xs'
amount={demagnify(
row.original.amount,
ASSETS.find((asset) => asset.denom === row.original.denom) ?? ASSETS[0],
)}
options={{ maxDecimals: 4 }}
/>
)
},
},
{
id: 'apy',
accessorKey: 'apy',
header: 'APY',
cell: ({ row }) => {
return (
<FormattedNumber
className='text-xs'
amount={row.original.apy}
options={{ maxDecimals: 2, minDecimals: 2, suffix: '%' }}
/>
)
},
},
],
[],
)
const table = useReactTable({
data: balanceData,
columns,
state: {
sorting,
},
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
debugTable: true,
})
return (
<table className='w-full'>
<thead className='border-b border-b-white/5'>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header, index) => {
return (
<th
key={header.id}
onClick={header.column.getToggleSortingHandler()}
className={classNames(
'p-2',
header.column.getCanSort() && 'cursor-pointer',
header.id === 'symbol' ? 'text-left' : 'text-right',
)}
>
<div
className={classNames(
'flex',
header.id === 'symbol' ? 'justify-start' : 'justify-end',
'align-center',
)}
>
<span className='h-6 w-6 text-white'>
{header.column.getCanSort()
? {
asc: <SortAsc />,
desc: <SortDesc />,
false: <SortNone />,
}[header.column.getIsSorted() as string] ?? null
: null}
</span>
<Text
tag='span'
size='sm'
className='flex items-center font-normal text-white/40'
>
{flexRender(header.column.columnDef.header, header.getContext())}
</Text>
</div>
</th>
)
})}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => {
return (
<tr key={row.id} className=' text-white/60'>
{row.getVisibleCells().map((cell) => {
const borderClass =
cell.row.original.type === 'deposit' ? 'border-profit' : 'border-loss'
return (
<td
key={cell.id}
className={classNames(
cell.column.id === 'symbol' ? `border-l ${borderClass}` : 'pl-4 text-right',
'p-2',
)}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
)
})}
</tr>
)
})}
</tbody>
</table>
)
}

View File

@ -44,7 +44,7 @@ export default function AccountComposition(props: Props) {
const borrowRateChange = props.change ? calculateAccountPnL(props.change, prices) : BN(0)
return (
<div className='w-full flex-wrap'>
<div className='w-full flex-wrap p-4'>
<Item
title='Total Position Value'
current={balance}

View File

@ -1,6 +1,7 @@
'use client'
import Accordion from 'components/Accordion'
import { AcccountBalancesTable } from 'components/Account/AccountBalancesTable'
import AccountComposition from 'components/Account/AccountComposition'
import AccountHealth from 'components/Account/AccountHealth'
import Card from 'components/Card'
@ -23,7 +24,7 @@ export default function AccountSummary(props: Props) {
if (!props.account) return null
return (
<div className='flex basis-[345px] flex-wrap'>
<div className='flex max-w-[345px] basis-[345px] flex-wrap'>
<Card className='mb-4 min-w-fit bg-white/10' contentClassName='flex'>
<Item>
<DisplayCurrency
@ -48,7 +49,7 @@ export default function AccountSummary(props: Props) {
content: <AccountComposition account={props.account} change={props.change} />,
open: true,
},
{ title: 'Balances', content: <p>My content</p> },
{ title: 'Balances', content: <AcccountBalancesTable data={props.account} /> },
]}
/>
</div>

View File

@ -2,8 +2,7 @@ import { Coin } from '@cosmjs/stargate'
import { FormattedNumber } from 'components/FormattedNumber'
import useStore from 'store'
import { getMarketAssets } from 'utils/assets'
import { BN } from 'utils/helpers'
import { convertToDisplayAmount } from 'utils/formatters'
interface Props {
coin: Coin
@ -15,24 +14,10 @@ export default function DisplayCurrency(props: Props) {
const displayCurrency = useStore((s) => s.displayCurrency)
const prices = useStore((s) => s.prices)
function convertToDisplayAmount(coin: Coin) {
const price = prices.find((price) => price.denom === coin.denom)
const asset = getMarketAssets().find((asset) => asset.denom === coin.denom)
const displayPrice = prices.find((price) => price.denom === displayCurrency.denom)
if (!price || !asset || !displayPrice) return '0'
return BN(coin.amount)
.shiftedBy(-1 * asset.decimals)
.times(price.amount)
.div(displayPrice.amount)
.toNumber()
}
return (
<FormattedNumber
className={props.className}
amount={convertToDisplayAmount(props.coin)}
amount={convertToDisplayAmount(props.coin, displayCurrency, prices)}
options={{
minDecimals: 0,
maxDecimals: 2,

View File

@ -98,8 +98,7 @@ export default function BorrowModal() {
const max = BN(isRepay ? getDebtAmount(modal) : modal?.marketData?.liquidity?.amount ?? '0')
useEffect(() => {
if (!selectedAccount)
setSelectedAccount(currentAccount)
if (!selectedAccount) setSelectedAccount(currentAccount)
}, [selectedAccount, currentAccount])
useEffect(() => {

View File

@ -1,6 +1,7 @@
import classNames from 'classnames'
import { Suspense } from 'react'
import { AcccountBalancesTable } from 'components/Account/AccountBalancesTable'
import AccountComposition from 'components/Account/AccountComposition'
import Card from 'components/Card'
import Loading from 'components/Loading'
@ -30,13 +31,10 @@ async function Content(props: PageProps) {
className={classNames('grid w-full grid-cols-1 gap-4', 'md:grid-cols-2', 'lg:grid-cols-3')}
>
{account.map((account: Account, index: number) => (
<Card
className='h-fit w-full bg-white/5'
title={`Account ${account.id}`}
key={index}
contentClassName='px-4 py-6'
>
<Card className='h-fit w-full bg-white/5' title={`Account ${account.id}`} key={index}>
<AccountComposition account={account} />
<Text className='mt-3 w-full bg-white/10 px-4 py-2 text-white/40'>Balances</Text>
<AcccountBalancesTable data={account} />
</Card>
))}
</div>
@ -50,13 +48,12 @@ function Fallback() {
className={classNames('grid w-full 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 bg-white/5'
title='Account'
contentClassName='px-4 py-6'
>
<Loading className='h-4 w-50' />
<Card key={i} className='h-fit w-full bg-white/5' title='Account' contentClassName='py-6'>
<div className='p-4'>
<Loading className='h-4 w-50' />
</div>
<Text className='mt-3 w-full bg-white/10 px-4 py-2 text-white/40'>Balances</Text>
<Loading className='h-4 w-full' />
</Card>
))}
</div>

View File

@ -46,7 +46,7 @@ export default function Select(props: Props) {
(option) => option?.value === props.defaultValue || option?.denom === props.defaultValue,
),
)
}, [value, props.defaultValue, props.options])
}, [value, props.defaultValue, props.options, selected])
return (
<div

View File

@ -12,3 +12,13 @@ interface AccountChange {
lends?: Coin[]
vaults?: import('types/generated/mars-mock-credit-manager/MarsMockCreditManager.types').ArrayOfVaultInfoResponse
}
interface AccountBalanceRow {
type: string
symbol: string
denom: string
amount: string
value: string | number
size: number
apy: number
}

View File

@ -154,3 +154,17 @@ export function demagnify(amount: number | string | BigNumber, asset: Asset) {
const value = BN(amount)
return value.isZero() ? 0 : value.shiftedBy(-1 * asset.decimals).toNumber()
}
export function convertToDisplayAmount(coin: Coin, displayCurrency: Asset, prices: Coin[]) {
const price = prices.find((price) => price.denom === coin.denom)
const asset = getMarketAssets().find((asset) => asset.denom === coin.denom)
const displayPrice = prices.find((price) => price.denom === displayCurrency.denom)
if (!price || !asset || !displayPrice) return '0'
return BN(coin.amount)
.shiftedBy(-1 * asset.decimals)
.times(price.amount)
.div(displayPrice.amount)
.toNumber()
}