Full refactor tables (#556)
* 📈 Improve structure generic Table component * ♻️ Update Borrow Table and overall structure of Table comp * ♻️ Update Lend table * ✨ add loading state for lend table * 🧪 Fix unit tests
This commit is contained in:
parent
7917d24134
commit
ccc4a42354
@ -1,38 +0,0 @@
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import MarketDetails from 'components/MarketAssetTable/MarketDetails'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
const data: LendingMarketTableData = {
|
||||
asset: ASSETS[0],
|
||||
marketDepositAmount: BN('890546916'),
|
||||
accountLentValue: BN('0.5498406009348686811'),
|
||||
marketLiquidityAmount: BN('629396551'),
|
||||
marketDepositCap: BN('2500000000000'),
|
||||
marketLiquidityRate: 0.017,
|
||||
marketLiquidationThreshold: 0.61,
|
||||
marketMaxLtv: 0.59,
|
||||
borrowEnabled: true,
|
||||
}
|
||||
|
||||
jest.mock('hooks/useDisplayCurrencyPrice', () => () => {
|
||||
const { BN } = require('utils/helpers')
|
||||
|
||||
return {
|
||||
getConversionRate: () => BN(1),
|
||||
convertAmount: () => BN(1),
|
||||
symbol: 'MARS',
|
||||
}
|
||||
})
|
||||
|
||||
describe('<LendingDetails />', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('hooks/usePrices')
|
||||
})
|
||||
|
||||
it('should render', () => {
|
||||
const { container } = render(<MarketDetails type='lend' data={data} />)
|
||||
expect(container).toBeInTheDocument()
|
||||
})
|
||||
})
|
@ -14,6 +14,7 @@ const mockedDepositedVault: DepositedVault = {
|
||||
...TESTNET_VAULTS_META_DATA[0],
|
||||
status: 'active',
|
||||
apy: 1,
|
||||
apr: null,
|
||||
ltv: {
|
||||
max: 0.65,
|
||||
liq: 0.7,
|
||||
|
@ -1,88 +0,0 @@
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import VaultBorrowings, { VaultBorrowingsProps } from 'components/Modals/Vault/VaultBorrowings'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
import { TESTNET_VAULTS_META_DATA } from 'constants/vaults'
|
||||
import useStore from 'store'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
jest.mock('hooks/usePrices', () =>
|
||||
jest.fn(() => ({
|
||||
data: [],
|
||||
})),
|
||||
)
|
||||
|
||||
jest.mock('hooks/usePrice', () => jest.fn(() => '1'))
|
||||
|
||||
jest.mock('hooks/useMarketAssets', () =>
|
||||
jest.fn(() => ({
|
||||
data: [],
|
||||
})),
|
||||
)
|
||||
|
||||
jest.mock('hooks/broadcast/useDepositVault', () => jest.fn(() => ({ actions: [] })))
|
||||
|
||||
jest.mock('components/DisplayCurrency')
|
||||
|
||||
jest.mock('hooks/useHealthComputer', () =>
|
||||
jest.fn(() => ({
|
||||
computeMaxBorrowAmount: () => {},
|
||||
})),
|
||||
)
|
||||
|
||||
const mockedDisplayCurrency = jest
|
||||
.mocked(DisplayCurrency)
|
||||
.mockImplementation(() => <div>Display currency</div>)
|
||||
|
||||
const mockedVault: Vault = {
|
||||
...TESTNET_VAULTS_META_DATA[0],
|
||||
apy: 0,
|
||||
ltv: {
|
||||
liq: 0.2,
|
||||
max: 0.1,
|
||||
},
|
||||
cap: {
|
||||
denom: 'test',
|
||||
max: BN(10),
|
||||
used: BN(2),
|
||||
},
|
||||
}
|
||||
describe('<VaultBorrowings />', () => {
|
||||
const defaultProps: VaultBorrowingsProps = {
|
||||
primaryAsset: ASSETS[0],
|
||||
secondaryAsset: ASSETS[1],
|
||||
vault: mockedVault,
|
||||
borrowings: [],
|
||||
deposits: [],
|
||||
onChangeBorrowings: jest.fn(),
|
||||
depositActions: [],
|
||||
depositCapReachedCoins: [],
|
||||
displayCurrency: 'uosmo',
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
useStore.setState({
|
||||
baseCurrency: ASSETS[0],
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
useStore.clearState()
|
||||
mockedDisplayCurrency.mockClear()
|
||||
})
|
||||
|
||||
it('should render', () => {
|
||||
const { container } = render(<VaultBorrowings {...defaultProps} />)
|
||||
expect(container).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render DisplayCurrency correctly', () => {
|
||||
expect(mockedDisplayCurrency).toHaveBeenCalledTimes(1)
|
||||
expect(mockedDisplayCurrency).toHaveBeenCalledWith(
|
||||
{ coin: new BNCoin({ denom: 'usd', amount: '0' }) },
|
||||
expect.anything(),
|
||||
)
|
||||
})
|
||||
})
|
@ -1,6 +1,5 @@
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import { TooltipType } from 'components/Tooltip'
|
||||
import TooltipContent from 'components/Tooltip/TooltipContent'
|
||||
|
||||
describe('<Tooltip />', () => {
|
||||
|
@ -37,14 +37,10 @@ export default function AccountComposition(props: Props) {
|
||||
const { account } = props
|
||||
const hasChanged = !!updatedAccount
|
||||
const { data: prices } = usePrices()
|
||||
const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } =
|
||||
useBorrowMarketAssetsTableData()
|
||||
const { data } = useBorrowMarketAssetsTableData(false)
|
||||
const borrowAssetsData = useMemo(() => data?.allAssets || [], [data])
|
||||
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
|
||||
useLendingMarketAssetsTableData()
|
||||
const borrowAssetsData = useMemo(
|
||||
() => [...borrowAvailableAssets, ...accountBorrowedAssets],
|
||||
[borrowAvailableAssets, accountBorrowedAssets],
|
||||
)
|
||||
const lendingAssetsData = useMemo(
|
||||
() => [...lendingAvailableAssets, ...accountLentAssets],
|
||||
[lendingAvailableAssets, accountLentAssets],
|
||||
|
@ -76,14 +76,12 @@ function AccountDetails(props: Props) {
|
||||
return updatedLeverage
|
||||
}, [updatedAccount, prices, leverage])
|
||||
|
||||
const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } =
|
||||
useBorrowMarketAssetsTableData()
|
||||
const { data } = useBorrowMarketAssetsTableData(false)
|
||||
const borrowAssetsData = useMemo(() => data?.allAssets || [], [data])
|
||||
|
||||
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
|
||||
useLendingMarketAssetsTableData()
|
||||
const borrowAssetsData = useMemo(
|
||||
() => [...borrowAvailableAssets, ...accountBorrowedAssets],
|
||||
[borrowAvailableAssets, accountBorrowedAssets],
|
||||
)
|
||||
|
||||
const lendingAssetsData = useMemo(
|
||||
() => [...lendingAvailableAssets, ...accountLentAssets],
|
||||
[lendingAvailableAssets, accountLentAssets],
|
||||
|
@ -28,14 +28,10 @@ export default function AccountStats(props: Props) {
|
||||
[account, prices],
|
||||
)
|
||||
const { health } = useHealthComputer(account)
|
||||
const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } =
|
||||
useBorrowMarketAssetsTableData()
|
||||
const { data } = useBorrowMarketAssetsTableData(false)
|
||||
const borrowAssetsData = useMemo(() => data?.allAssets || [], [data])
|
||||
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
|
||||
useLendingMarketAssetsTableData()
|
||||
const borrowAssetsData = useMemo(
|
||||
() => [...borrowAvailableAssets, ...accountBorrowedAssets],
|
||||
[borrowAvailableAssets, accountBorrowedAssets],
|
||||
)
|
||||
const lendingAssetsData = useMemo(
|
||||
() => [...lendingAvailableAssets, ...accountLentAssets],
|
||||
[lendingAvailableAssets, accountLentAssets],
|
||||
|
@ -36,15 +36,11 @@ export default function AccountSummary(props: Props) {
|
||||
: BN_ZERO,
|
||||
[props.account, updatedAccount, prices],
|
||||
)
|
||||
const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } =
|
||||
useBorrowMarketAssetsTableData()
|
||||
const { data } = useBorrowMarketAssetsTableData(false)
|
||||
const borrowAssetsData = useMemo(() => data?.allAssets || [], [data])
|
||||
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
|
||||
useLendingMarketAssetsTableData()
|
||||
|
||||
const borrowAssetsData = useMemo(
|
||||
() => [...borrowAvailableAssets, ...accountBorrowedAssets],
|
||||
[borrowAvailableAssets, accountBorrowedAssets],
|
||||
)
|
||||
const lendingAssetsData = useMemo(
|
||||
() => [...lendingAvailableAssets, ...accountLentAssets],
|
||||
[lendingAvailableAssets, accountLentAssets],
|
||||
|
@ -1,129 +0,0 @@
|
||||
import { ColumnDef, Row, Table } from '@tanstack/react-table'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
|
||||
import AmountAndValue from 'components/AmountAndValue'
|
||||
import AssetImage from 'components/Asset/AssetImage'
|
||||
import BorrowActionButtons from 'components/Borrow/BorrowActionButtons'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import { ChevronDown, ChevronUp } from 'components/Icons'
|
||||
import Loading from 'components/Loading'
|
||||
import AssetListTable from 'components/MarketAssetTable'
|
||||
import MarketAssetTableRow from 'components/MarketAssetTable/MarketAssetTableRow'
|
||||
import MarketDetails from 'components/MarketAssetTable/MarketDetails'
|
||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { getEnabledMarketAssets } from 'utils/assets'
|
||||
|
||||
interface Props {
|
||||
title: string
|
||||
data: BorrowMarketTableData[]
|
||||
}
|
||||
|
||||
export default function BorrowTable(props: Props) {
|
||||
const { title, data } = props
|
||||
const shouldShowAccountBorrowed = !!data[0]?.debt
|
||||
const marketAssets = getEnabledMarketAssets()
|
||||
|
||||
const rowRenderer = useCallback(
|
||||
(row: Row<BorrowMarketTableData>, table: Table<BorrowMarketTableData>) => {
|
||||
return (
|
||||
<MarketAssetTableRow
|
||||
key={`borrow-asset-${row.id}`}
|
||||
isExpanded={row.getIsExpanded()}
|
||||
resetExpanded={table.resetExpanded}
|
||||
rowData={row}
|
||||
expandedActionButtons={<BorrowActionButtons data={row.original} />}
|
||||
expandedDetails={<MarketDetails data={row.original} type='borrow' />}
|
||||
/>
|
||||
)
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
||||
const columns = useMemo<ColumnDef<BorrowMarketTableData>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorKey: 'asset.name',
|
||||
header: 'Asset',
|
||||
id: 'symbol',
|
||||
cell: ({ row }) => {
|
||||
const asset = row.original.asset
|
||||
|
||||
return (
|
||||
<div className='flex items-center flex-1 gap-3'>
|
||||
<AssetImage asset={asset} size={32} />
|
||||
<TitleAndSubCell
|
||||
title={asset.symbol}
|
||||
sub={asset.name}
|
||||
className='text-left min-w-15'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
...(shouldShowAccountBorrowed
|
||||
? [
|
||||
{
|
||||
accessorKey: 'debt',
|
||||
header: 'Borrowed',
|
||||
cell: (info: any) => {
|
||||
const borrowAsset = info.row.original as BorrowMarketTableData
|
||||
const asset = marketAssets.find((asset) => asset.denom === borrowAsset.asset.denom)
|
||||
|
||||
if (!asset) return null
|
||||
|
||||
return <AmountAndValue asset={asset} amount={borrowAsset?.debt ?? BN_ZERO} />
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
accessorKey: 'borrowRate',
|
||||
header: 'Borrow Rate',
|
||||
cell: ({ row }) => {
|
||||
if (row.original.borrowRate === null) {
|
||||
return <Loading />
|
||||
}
|
||||
|
||||
return (
|
||||
<FormattedNumber
|
||||
className='justify-end text-xs'
|
||||
amount={row.original.borrowRate * 100}
|
||||
options={{ minDecimals: 2, maxDecimals: 2, suffix: '%' }}
|
||||
animate
|
||||
/>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'liquidity',
|
||||
header: 'Liquidity Available',
|
||||
cell: ({ row }) => {
|
||||
const { liquidity, asset: borrowAsset } = row.original
|
||||
const asset = marketAssets.find((asset) => asset.denom === borrowAsset.denom)
|
||||
|
||||
if (!asset) return null
|
||||
|
||||
if (liquidity === null) {
|
||||
return <Loading />
|
||||
}
|
||||
|
||||
return <AmountAndValue asset={asset} amount={liquidity.amount ?? BN_ZERO} />
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'manage',
|
||||
enableSorting: false,
|
||||
header: 'Manage',
|
||||
cell: ({ row }) => (
|
||||
<div className='flex items-center justify-end'>
|
||||
<div className='w-4'>{row.getIsExpanded() ? <ChevronUp /> : <ChevronDown />}</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
],
|
||||
[shouldShowAccountBorrowed, marketAssets],
|
||||
)
|
||||
|
||||
return <AssetListTable title={title} rowRenderer={rowRenderer} columns={columns} data={data} />
|
||||
}
|
37
src/components/Borrow/Borrowings.tsx
Normal file
37
src/components/Borrow/Borrowings.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import React from 'react'
|
||||
|
||||
import AvailableBorrowingsTable from 'components/Borrow/Table/AvailableBorrowingsTable'
|
||||
import DepositedBorrowingsTable from 'components/Borrow/Table/DepositedBorrowingsTable'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
|
||||
import { getBorrowEnabledAssets } from 'utils/assets'
|
||||
|
||||
export default function Borrowings() {
|
||||
const { data } = useBorrowMarketAssetsTableData()
|
||||
|
||||
if (!data?.allAssets?.length) {
|
||||
return <Fallback />
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<DepositedBorrowingsTable data={data.accountBorrowedAssets} isLoading={false} />
|
||||
<AvailableBorrowingsTable data={data.availableAssets} isLoading={false} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function Fallback() {
|
||||
const assets = getBorrowEnabledAssets()
|
||||
const data: BorrowMarketTableData[] = assets.map((asset) => ({
|
||||
asset,
|
||||
borrowRate: null,
|
||||
liquidity: null,
|
||||
marketMaxLtv: 0,
|
||||
marketDepositAmount: BN_ZERO,
|
||||
marketLiquidityRate: 0,
|
||||
marketLiquidityAmount: BN_ZERO,
|
||||
marketLiquidationThreshold: 0,
|
||||
}))
|
||||
|
||||
return <AvailableBorrowingsTable data={data} isLoading />
|
||||
}
|
40
src/components/Borrow/Table/AvailableBorrowingsTable.tsx
Normal file
40
src/components/Borrow/Table/AvailableBorrowingsTable.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { Row } from '@tanstack/react-table'
|
||||
import { Table as TanstackTable } from '@tanstack/table-core/build/lib/types'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import useAvailableColumns from 'components/Borrow/Table/Columns/useAvailableColumns'
|
||||
import Card from 'components/Card'
|
||||
import MarketDetails from 'components/MarketDetails'
|
||||
import Table from 'components/Table'
|
||||
|
||||
type Props = {
|
||||
data: BorrowMarketTableData[]
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export default function AvailableBorrowingsTable(props: Props) {
|
||||
const columns = useAvailableColumns({ isLoading: props.isLoading })
|
||||
|
||||
const renderExpanded = useCallback(
|
||||
(row: Row<BorrowMarketTableData>, _: TanstackTable<BorrowMarketTableData>) => (
|
||||
<MarketDetails
|
||||
row={row as Row<BorrowMarketTableData | LendingMarketTableData>}
|
||||
type='borrow'
|
||||
/>
|
||||
),
|
||||
[],
|
||||
)
|
||||
|
||||
if (!props.data.length) return null
|
||||
|
||||
return (
|
||||
<Card className='w-full h-fit bg-white/5' title={'Available to Borrow'}>
|
||||
<Table
|
||||
columns={columns}
|
||||
data={props.data}
|
||||
initialSorting={[{ id: 'asset.name', desc: true }]}
|
||||
renderExpanded={renderExpanded}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
25
src/components/Borrow/Table/Columns/BorrowRate.tsx
Normal file
25
src/components/Borrow/Table/Columns/BorrowRate.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import React from 'react'
|
||||
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import Loading from 'components/Loading'
|
||||
|
||||
export const BORROW_RATE_META = { accessorKey: 'borrowRate', header: 'Borrow Rate' }
|
||||
|
||||
interface Props {
|
||||
borrowRate: number | null
|
||||
}
|
||||
|
||||
export default function BorrowRate(props: Props) {
|
||||
if (props.borrowRate === null) {
|
||||
return <Loading />
|
||||
}
|
||||
|
||||
return (
|
||||
<FormattedNumber
|
||||
className='justify-end text-xs'
|
||||
amount={props.borrowRate * 100}
|
||||
options={{ minDecimals: 2, maxDecimals: 2, suffix: '%' }}
|
||||
animate
|
||||
/>
|
||||
)
|
||||
}
|
22
src/components/Borrow/Table/Columns/Debt.tsx
Normal file
22
src/components/Borrow/Table/Columns/Debt.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react'
|
||||
|
||||
import AmountAndValue from 'components/AmountAndValue'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { getEnabledMarketAssets } from 'utils/assets'
|
||||
|
||||
export const DEBT_META = {
|
||||
accessorKey: 'debt',
|
||||
header: 'Debt',
|
||||
}
|
||||
interface Props {
|
||||
data: BorrowMarketTableData
|
||||
}
|
||||
|
||||
export default function Debt(props: Props) {
|
||||
const marketAssets = getEnabledMarketAssets()
|
||||
const asset = marketAssets.find((asset) => asset.denom === props.data.asset.denom)
|
||||
|
||||
if (!asset) return null
|
||||
|
||||
return <AmountAndValue asset={asset} amount={props.data?.debt ?? BN_ZERO} />
|
||||
}
|
24
src/components/Borrow/Table/Columns/Liquidity.tsx
Normal file
24
src/components/Borrow/Table/Columns/Liquidity.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import React from 'react'
|
||||
|
||||
import AmountAndValue from 'components/AmountAndValue'
|
||||
import Loading from 'components/Loading'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { getEnabledMarketAssets } from 'utils/assets'
|
||||
|
||||
export const LIQUIDITY_META = { accessorKey: 'asset.name', header: 'Asset', id: 'symbol' }
|
||||
interface Props {
|
||||
data: BorrowMarketTableData
|
||||
}
|
||||
export default function Liquidity(props: Props) {
|
||||
const { liquidity, asset: borrowAsset } = props.data
|
||||
const marketAssets = getEnabledMarketAssets()
|
||||
const asset = marketAssets.find((asset) => asset.denom === borrowAsset.denom)
|
||||
|
||||
if (!asset) return null
|
||||
|
||||
if (liquidity === null) {
|
||||
return <Loading />
|
||||
}
|
||||
|
||||
return <AmountAndValue asset={asset} amount={liquidity.amount ?? BN_ZERO} />
|
||||
}
|
17
src/components/Borrow/Table/Columns/Manage.tsx
Normal file
17
src/components/Borrow/Table/Columns/Manage.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import React from 'react'
|
||||
|
||||
import { ChevronDown, ChevronUp } from 'components/Icons'
|
||||
|
||||
export const MANAGE_META = { accessorKey: 'manage', enableSorting: false, header: 'Manage' }
|
||||
|
||||
interface Props {
|
||||
isExpanded: boolean
|
||||
}
|
||||
|
||||
export default function Manage(props: Props) {
|
||||
return (
|
||||
<div className='flex items-center justify-end'>
|
||||
<div className='w-4'>{props.isExpanded ? <ChevronUp /> : <ChevronDown />}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
20
src/components/Borrow/Table/Columns/Name.tsx
Normal file
20
src/components/Borrow/Table/Columns/Name.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react'
|
||||
|
||||
import AssetImage from 'components/Asset/AssetImage'
|
||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
|
||||
export const NAME_META = { accessorKey: 'asset.name', header: 'Asset', id: 'symbol' }
|
||||
|
||||
interface Props {
|
||||
data: BorrowMarketTableData
|
||||
}
|
||||
|
||||
export default function Name(props: Props) {
|
||||
const { asset } = props.data
|
||||
return (
|
||||
<div className='flex items-center flex-1 gap-3'>
|
||||
<AssetImage asset={asset} size={32} />
|
||||
<TitleAndSubCell title={asset.symbol} sub={asset.name} className='text-left min-w-15' />
|
||||
</div>
|
||||
)
|
||||
}
|
34
src/components/Borrow/Table/Columns/useAvailableColumns.tsx
Normal file
34
src/components/Borrow/Table/Columns/useAvailableColumns.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { ColumnDef } from '@tanstack/react-table'
|
||||
import React, { useMemo } from 'react'
|
||||
|
||||
import BorrowRate, { BORROW_RATE_META } from 'components/Borrow/Table/Columns/BorrowRate'
|
||||
import Liquidity, { LIQUIDITY_META } from 'components/Borrow/Table/Columns/Liquidity'
|
||||
import Manage, { MANAGE_META } from 'components/Borrow/Table/Columns/Manage'
|
||||
import Name, { NAME_META } from 'components/Borrow/Table/Columns/Name'
|
||||
|
||||
interface Props {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export default function useAvailableColumns(props: Props) {
|
||||
return useMemo<ColumnDef<BorrowMarketTableData>[]>(() => {
|
||||
return [
|
||||
{
|
||||
...NAME_META,
|
||||
cell: ({ row }) => <Name data={row.original} />,
|
||||
},
|
||||
{
|
||||
...BORROW_RATE_META,
|
||||
cell: ({ row }) => <BorrowRate borrowRate={row.original.borrowRate} />,
|
||||
},
|
||||
{
|
||||
...LIQUIDITY_META,
|
||||
cell: ({ row }) => <Liquidity data={row.original} />,
|
||||
},
|
||||
{
|
||||
...MANAGE_META,
|
||||
cell: ({ row }) => <Manage isExpanded={row.getIsExpanded()} />,
|
||||
},
|
||||
]
|
||||
}, [])
|
||||
}
|
39
src/components/Borrow/Table/Columns/useDepositedColumns.tsx
Normal file
39
src/components/Borrow/Table/Columns/useDepositedColumns.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import { ColumnDef } from '@tanstack/react-table'
|
||||
import React, { useMemo } from 'react'
|
||||
|
||||
import BorrowRate, { BORROW_RATE_META } from 'components/Borrow/Table/Columns/BorrowRate'
|
||||
import Debt, { DEBT_META } from 'components/Borrow/Table/Columns/Debt'
|
||||
import Liquidity, { LIQUIDITY_META } from 'components/Borrow/Table/Columns/Liquidity'
|
||||
import Manage, { MANAGE_META } from 'components/Borrow/Table/Columns/Manage'
|
||||
import Name, { NAME_META } from 'components/Borrow/Table/Columns/Name'
|
||||
|
||||
interface Props {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export default function useDepositedColumns(props: Props) {
|
||||
return useMemo<ColumnDef<BorrowMarketTableData>[]>(() => {
|
||||
return [
|
||||
{
|
||||
...NAME_META,
|
||||
cell: ({ row }) => <Name data={row.original} />,
|
||||
},
|
||||
{
|
||||
...DEBT_META,
|
||||
cell: ({ row }) => <Debt data={row.original} />,
|
||||
},
|
||||
{
|
||||
...BORROW_RATE_META,
|
||||
cell: ({ row }) => <BorrowRate borrowRate={row.original.borrowRate} />,
|
||||
},
|
||||
{
|
||||
...LIQUIDITY_META,
|
||||
cell: ({ row }) => <Liquidity data={row.original} />,
|
||||
},
|
||||
{
|
||||
...MANAGE_META,
|
||||
cell: ({ row }) => <Manage isExpanded={row.getIsExpanded()} />,
|
||||
},
|
||||
]
|
||||
}, [])
|
||||
}
|
38
src/components/Borrow/Table/DepositedBorrowingsTable.tsx
Normal file
38
src/components/Borrow/Table/DepositedBorrowingsTable.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import { Row } from '@tanstack/react-table'
|
||||
import { Table as TanStackTable } from '@tanstack/table-core/build/lib/types'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import useDepositedColumns from 'components/Borrow/Table/Columns/useDepositedColumns'
|
||||
import Card from 'components/Card'
|
||||
import MarketDetails from 'components/MarketDetails'
|
||||
import Table from 'components/Table'
|
||||
|
||||
type Props = {
|
||||
data: BorrowMarketTableData[]
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export default function DepositedBorrowingsTable(props: Props) {
|
||||
const columns = useDepositedColumns({ isLoading: props.isLoading })
|
||||
|
||||
const renderExpanded = useCallback(
|
||||
(
|
||||
row: Row<BorrowMarketTableData | LendingMarketTableData>,
|
||||
table: TanStackTable<BorrowMarketTableData>,
|
||||
) => <MarketDetails row={row} type='borrow' />,
|
||||
[],
|
||||
)
|
||||
|
||||
if (!props.data.length) return null
|
||||
|
||||
return (
|
||||
<Card className='w-full h-fit bg-white/5' title='Borrowed Assets'>
|
||||
<Table
|
||||
columns={columns}
|
||||
data={props.data}
|
||||
initialSorting={[{ id: 'asset.name', desc: true }]}
|
||||
renderExpanded={renderExpanded}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
@ -11,6 +11,7 @@ type Props = {
|
||||
|
||||
export default function AvailableVaultsTable(props: Props) {
|
||||
const columns = useAvailableColumns({ isLoading: props.isLoading })
|
||||
|
||||
return (
|
||||
<Card className='w-full h-fit bg-white/5' title={'Available vaults'}>
|
||||
<Table columns={columns} data={props.data} initialSorting={[{ id: 'name', desc: true }]} />
|
||||
|
@ -3,6 +3,8 @@ import React from 'react'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import Loading from 'components/Loading'
|
||||
|
||||
export const APY_META = { accessorKey: 'apy', header: 'APY' }
|
||||
|
||||
interface Props {
|
||||
vault: Vault | DepositedVault
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
import { VAULT_DEPOSIT_BUFFER } from 'constants/vaults'
|
||||
import { getAssetByDenom } from 'utils/assets'
|
||||
|
||||
export const DEPOSIT_CAP_META = { accessorKey: 'cap', header: 'Deposit Cap' }
|
||||
|
||||
interface Props {
|
||||
vault: Vault | DepositedVault
|
||||
isLoading: boolean
|
||||
|
@ -4,6 +4,8 @@ import React from 'react'
|
||||
import { ChevronDown } from 'components/Icons'
|
||||
import Loading from 'components/Loading'
|
||||
|
||||
export const DETAILS_META = { accessorKey: 'details', enableSorting: false, header: 'Deposit' }
|
||||
|
||||
interface Props {
|
||||
isLoading: boolean
|
||||
isExpanded: boolean
|
||||
|
@ -3,6 +3,8 @@ import React from 'react'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import Loading from 'components/Loading'
|
||||
|
||||
export const LTV_MAX_META = { accessorKey: 'ltv.max', header: 'Max LTV' }
|
||||
|
||||
interface Props {
|
||||
vault: Vault | DepositedVault
|
||||
isLoading: boolean
|
||||
|
@ -7,6 +7,7 @@ import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
import { VaultStatus } from 'types/enums/vault'
|
||||
import { produceCountdown } from 'utils/formatters'
|
||||
|
||||
export const NAME_META = { header: 'Vault', accessorKey: 'name' }
|
||||
interface Props {
|
||||
vault: Vault | DepositedVault
|
||||
}
|
||||
|
@ -4,6 +4,10 @@ import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import { ORACLE_DENOM } from 'constants/oracle'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
|
||||
export const POSITION_VALUE_META = {
|
||||
header: 'Pos. Value',
|
||||
}
|
||||
|
||||
interface Props {
|
||||
vault: DepositedVault
|
||||
isLoading: boolean
|
||||
|
@ -4,6 +4,8 @@ import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import Loading from 'components/Loading'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
|
||||
export const TVL_META = { accessorKey: 'tvl', header: 'TVL' }
|
||||
|
||||
interface Props {
|
||||
vault: Vault | DepositedVault
|
||||
isLoading: boolean
|
||||
|
@ -1,12 +1,14 @@
|
||||
import { ColumnDef } from '@tanstack/react-table'
|
||||
import React, { useMemo } from 'react'
|
||||
|
||||
import Apy from 'components/Earn/Farm/Table/Columns/Apy'
|
||||
import Apy, { APY_META } from 'components/Earn/Farm/Table/Columns/Apy'
|
||||
import { Deposit } from 'components/Earn/Farm/Table/Columns/Deposit'
|
||||
import DepositCap from 'components/Earn/Farm/Table/Columns/DepositCap'
|
||||
import MaxLTV from 'components/Earn/Farm/Table/Columns/MaxLTV'
|
||||
import Name from 'components/Earn/Farm/Table/Columns/Name'
|
||||
import TVL from 'components/Earn/Farm/Table/Columns/TVL'
|
||||
import DepositCap, { DEPOSIT_CAP_META } from 'components/Earn/Farm/Table/Columns/DepositCap'
|
||||
import MaxLTV, { LTV_MAX_META } from 'components/Earn/Farm/Table/Columns/MaxLTV'
|
||||
import Name, { NAME_META } from 'components/Earn/Farm/Table/Columns/Name'
|
||||
import TVL, { TVL_META } from 'components/Earn/Farm/Table/Columns/TVL'
|
||||
|
||||
import { DETAILS_META } from './Details'
|
||||
|
||||
interface Props {
|
||||
isLoading: boolean
|
||||
@ -16,34 +18,27 @@ export default function useAvailableColumns(props: Props) {
|
||||
return useMemo<ColumnDef<Vault | DepositedVault>[]>(() => {
|
||||
return [
|
||||
{
|
||||
header: 'Vault',
|
||||
accessorKey: 'name',
|
||||
...NAME_META,
|
||||
cell: ({ row }) => <Name vault={row.original as Vault} />,
|
||||
},
|
||||
{
|
||||
accessorKey: 'apy',
|
||||
header: 'APY',
|
||||
...APY_META,
|
||||
cell: ({ row }) => <Apy vault={row.original as Vault} />,
|
||||
},
|
||||
{
|
||||
accessorKey: 'tvl',
|
||||
header: 'TVL',
|
||||
...TVL_META,
|
||||
cell: ({ row }) => <TVL vault={row.original as Vault} isLoading={props.isLoading} />,
|
||||
},
|
||||
{
|
||||
accessorKey: 'cap',
|
||||
header: 'Deposit Cap',
|
||||
...DEPOSIT_CAP_META,
|
||||
cell: ({ row }) => <DepositCap vault={row.original as Vault} isLoading={props.isLoading} />,
|
||||
},
|
||||
{
|
||||
accessorKey: 'ltv.max',
|
||||
header: 'Max LTV',
|
||||
...LTV_MAX_META,
|
||||
cell: ({ row }) => <MaxLTV vault={row.original as Vault} isLoading={props.isLoading} />,
|
||||
},
|
||||
{
|
||||
accessorKey: 'details',
|
||||
enableSorting: false,
|
||||
header: 'Deposit',
|
||||
...DETAILS_META,
|
||||
cell: ({ row }) => <Deposit vault={row.original as Vault} isLoading={props.isLoading} />,
|
||||
},
|
||||
]
|
||||
|
@ -1,13 +1,15 @@
|
||||
import { ColumnDef, Row } from '@tanstack/react-table'
|
||||
import React, { useMemo } from 'react'
|
||||
|
||||
import Apy from 'components/Earn/Farm/Table/Columns/Apy'
|
||||
import DepositCap from 'components/Earn/Farm/Table/Columns/DepositCap'
|
||||
import Details from 'components/Earn/Farm/Table/Columns/Details'
|
||||
import MaxLTV from 'components/Earn/Farm/Table/Columns/MaxLTV'
|
||||
import Name from 'components/Earn/Farm/Table/Columns/Name'
|
||||
import PositionValue from 'components/Earn/Farm/Table/Columns/PositionValue'
|
||||
import TVL from 'components/Earn/Farm/Table/Columns/TVL'
|
||||
import Apy, { APY_META } from 'components/Earn/Farm/Table/Columns/Apy'
|
||||
import DepositCap, { DEPOSIT_CAP_META } from 'components/Earn/Farm/Table/Columns/DepositCap'
|
||||
import Details, { DETAILS_META } from 'components/Earn/Farm/Table/Columns/Details'
|
||||
import MaxLTV, { LTV_MAX_META } from 'components/Earn/Farm/Table/Columns/MaxLTV'
|
||||
import Name, { NAME_META } from 'components/Earn/Farm/Table/Columns/Name'
|
||||
import PositionValue, {
|
||||
POSITION_VALUE_META,
|
||||
} from 'components/Earn/Farm/Table/Columns/PositionValue'
|
||||
import TVL, { TVL_META } from 'components/Earn/Farm/Table/Columns/TVL'
|
||||
|
||||
interface Props {
|
||||
isLoading: boolean
|
||||
@ -17,46 +19,39 @@ export default function useDepositedColumns(props: Props) {
|
||||
return useMemo<ColumnDef<DepositedVault>[]>(() => {
|
||||
return [
|
||||
{
|
||||
header: 'Vault',
|
||||
accessorKey: 'name',
|
||||
...NAME_META,
|
||||
cell: ({ row }) => <Name vault={row.original as DepositedVault} />,
|
||||
},
|
||||
{
|
||||
header: 'Pos. Value',
|
||||
...POSITION_VALUE_META,
|
||||
cell: ({ row }: { row: Row<DepositedVault> }) => (
|
||||
<PositionValue vault={row.original as DepositedVault} isLoading={props.isLoading} />
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'apy',
|
||||
header: 'APY',
|
||||
...APY_META,
|
||||
cell: ({ row }) => <Apy vault={row.original as DepositedVault} />,
|
||||
},
|
||||
{
|
||||
accessorKey: 'tvl',
|
||||
header: 'TVL',
|
||||
...TVL_META,
|
||||
cell: ({ row }) => (
|
||||
<TVL vault={row.original as DepositedVault} isLoading={props.isLoading} />
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'cap',
|
||||
header: 'Deposit Cap',
|
||||
...DEPOSIT_CAP_META,
|
||||
cell: ({ row }) => (
|
||||
<DepositCap vault={row.original as DepositedVault} isLoading={props.isLoading} />
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'ltv.max',
|
||||
header: 'Max LTV',
|
||||
...LTV_MAX_META,
|
||||
cell: ({ row }) => (
|
||||
<MaxLTV vault={row.original as DepositedVault} isLoading={props.isLoading} />
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'details',
|
||||
enableSorting: false,
|
||||
header: 'Details',
|
||||
...DETAILS_META,
|
||||
cell: ({ row }) => <Details isLoading={props.isLoading} isExpanded={row.getIsExpanded()} />,
|
||||
},
|
||||
]
|
||||
|
@ -1,7 +1,10 @@
|
||||
import React from 'react'
|
||||
import { Row } from '@tanstack/react-table'
|
||||
import { Table as TanStackTable } from '@tanstack/table-core/build/lib/types'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import Card from 'components/Card'
|
||||
import useDepositedColumns from 'components/Earn/Farm/Table/Columns/useDepositedColumns'
|
||||
import VaultExpanded from 'components/Earn/Farm/VaultExpanded'
|
||||
import Table from 'components/Table'
|
||||
|
||||
type Props = {
|
||||
@ -11,9 +14,22 @@ type Props = {
|
||||
|
||||
export default function DepositedVaultsTable(props: Props) {
|
||||
const columns = useDepositedColumns({ isLoading: props.isLoading })
|
||||
|
||||
const renderExpanded = useCallback(
|
||||
(row: Row<DepositedVault>, table: TanStackTable<DepositedVault>) => (
|
||||
<VaultExpanded row={row} resetExpanded={table.resetExpanded} />
|
||||
),
|
||||
[],
|
||||
)
|
||||
|
||||
return (
|
||||
<Card className='w-full h-fit bg-white/5' title={'Deposited vaults'}>
|
||||
<Table columns={columns} data={props.data} initialSorting={[{ id: 'name', desc: true }]} />
|
||||
<Table
|
||||
columns={columns}
|
||||
data={props.data}
|
||||
initialSorting={[{ id: 'name', desc: true }]}
|
||||
renderExpanded={renderExpanded}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
@ -13,12 +13,12 @@ import useStore from 'store'
|
||||
import { VaultStatus } from 'types/enums/vault'
|
||||
|
||||
interface Props {
|
||||
row: Row<Vault | DepositedVault>
|
||||
row: Row<DepositedVault>
|
||||
resetExpanded: (defaultState?: boolean | undefined) => void
|
||||
}
|
||||
|
||||
export default function VaultExpanded(props: Props) {
|
||||
const vault = props.row.original as DepositedVault
|
||||
const vault = props.row.original
|
||||
const accountId = useAccountId()
|
||||
const [isConfirming, setIsConfirming] = useState(false)
|
||||
const withdrawFromVaults = useStore((s) => s.withdrawFromVaults)
|
||||
@ -113,7 +113,7 @@ export default function VaultExpanded(props: Props) {
|
||||
!isExpanded && props.row.toggleExpanded()
|
||||
}}
|
||||
>
|
||||
<td colSpan={!status ? 7 : 8}>
|
||||
<td colSpan={props.row.getAllCells().length}>
|
||||
<div className='flex justify-end gap-3 p-4 align-center'>
|
||||
{status && <DepositMoreButton />}
|
||||
{status === VaultStatus.ACTIVE && <UnlockButton />}
|
||||
|
@ -1,142 +0,0 @@
|
||||
import { ColumnDef, Row, Table } from '@tanstack/react-table'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
|
||||
import AmountAndValue from 'components/AmountAndValue'
|
||||
import AssetImage from 'components/Asset/AssetImage'
|
||||
import AssetRate from 'components/Asset/AssetRate'
|
||||
import LendingActionButtons from 'components/Earn/Lend/LendingActionButtons'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import { ChevronDown, ChevronUp } from 'components/Icons'
|
||||
import AssetListTable from 'components/MarketAssetTable'
|
||||
import MarketAssetTableRow from 'components/MarketAssetTable/MarketAssetTableRow'
|
||||
import MarketDetails from 'components/MarketAssetTable/MarketDetails'
|
||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { convertLiquidityRateToAPR } from 'utils/formatters'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
interface Props {
|
||||
title: string
|
||||
data: LendingMarketTableData[]
|
||||
}
|
||||
|
||||
export default function LendingMarketsTable(props: Props) {
|
||||
const { title, data } = props
|
||||
const shouldShowAccountDeposit = !!data[0]?.accountLentValue
|
||||
|
||||
const rowRenderer = useCallback(
|
||||
(row: Row<LendingMarketTableData>, table: Table<LendingMarketTableData>) => {
|
||||
return (
|
||||
<MarketAssetTableRow
|
||||
key={`lend-asset-${row.id}`}
|
||||
isExpanded={row.getIsExpanded()}
|
||||
resetExpanded={table.resetExpanded}
|
||||
rowData={row}
|
||||
expandedActionButtons={<LendingActionButtons data={row.original} />}
|
||||
expandedDetails={<MarketDetails data={row.original} type='lend' />}
|
||||
/>
|
||||
)
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
||||
const columns = useMemo<ColumnDef<LendingMarketTableData>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorKey: 'asset.name',
|
||||
header: 'Asset',
|
||||
id: 'symbol',
|
||||
cell: ({ row }) => {
|
||||
const asset = row.original.asset
|
||||
|
||||
return (
|
||||
<div className='flex items-center flex-1 gap-3'>
|
||||
<AssetImage asset={asset} size={32} />
|
||||
<TitleAndSubCell
|
||||
title={asset.symbol}
|
||||
sub={asset.name}
|
||||
className='text-left min-w-15'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
...(shouldShowAccountDeposit
|
||||
? [
|
||||
{
|
||||
accessorKey: 'accountDepositValue',
|
||||
header: 'Deposited',
|
||||
cell: ({ row }) => {
|
||||
const amount = row.original.accountLentAmount
|
||||
|
||||
return (
|
||||
<AmountAndValue
|
||||
asset={row.original.asset}
|
||||
amount={amount ? BN(amount) : BN_ZERO}
|
||||
/>
|
||||
)
|
||||
},
|
||||
} as ColumnDef<LendingMarketTableData>,
|
||||
]
|
||||
: []),
|
||||
{
|
||||
accessorKey: 'marketLiquidityRate',
|
||||
header: 'APR',
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<AssetRate
|
||||
rate={convertLiquidityRateToAPR(row.original.marketLiquidityRate)}
|
||||
isEnabled={row.original.borrowEnabled}
|
||||
className='justify-end text-xs'
|
||||
type='apr'
|
||||
orientation='ltr'
|
||||
/>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'marketDepositCap',
|
||||
header: 'Depo. Cap',
|
||||
cell: ({ row }) => {
|
||||
const { marketDepositCap, marketDepositAmount, asset } = row.original
|
||||
const percent = marketDepositAmount
|
||||
.dividedBy(row.original.marketDepositCap)
|
||||
.multipliedBy(100)
|
||||
|
||||
return (
|
||||
<TitleAndSubCell
|
||||
className='text-xs'
|
||||
title={
|
||||
<FormattedNumber
|
||||
amount={marketDepositCap.toNumber()}
|
||||
options={{ abbreviated: true, decimals: asset.decimals }}
|
||||
animate
|
||||
/>
|
||||
}
|
||||
sub={
|
||||
<FormattedNumber
|
||||
amount={percent.toNumber()}
|
||||
options={{ minDecimals: 2, maxDecimals: 2, suffix: '% used' }}
|
||||
animate
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'manage',
|
||||
enableSorting: false,
|
||||
header: 'Manage',
|
||||
cell: ({ row }) => (
|
||||
<div className='flex items-center justify-end'>
|
||||
<div className='w-4'>{row.getIsExpanded() ? <ChevronUp /> : <ChevronDown />}</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
],
|
||||
[shouldShowAccountDeposit],
|
||||
)
|
||||
|
||||
return <AssetListTable title={title} rowRenderer={rowRenderer} columns={columns} data={data} />
|
||||
}
|
37
src/components/Earn/Lend/Lends.tsx
Normal file
37
src/components/Earn/Lend/Lends.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import React from 'react'
|
||||
|
||||
import AvailableLendsTable from 'components/Earn/Lend/Table/AvailableLendsTable'
|
||||
import DepositedLendsTable from 'components/Earn/Lend/Table/DepositedLendsTable'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
|
||||
import { getLendEnabledAssets } from 'utils/assets'
|
||||
|
||||
export default function Lends() {
|
||||
const { accountLentAssets, availableAssets, allAssets } = useLendingMarketAssetsTableData()
|
||||
|
||||
if (!allAssets?.length) {
|
||||
return <Fallback />
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<DepositedLendsTable data={accountLentAssets} isLoading={false} />
|
||||
<AvailableLendsTable data={availableAssets} isLoading={false} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function Fallback() {
|
||||
const assets = getLendEnabledAssets()
|
||||
const data: LendingMarketTableData[] = assets.map((asset) => ({
|
||||
asset,
|
||||
marketDepositCap: BN_ZERO,
|
||||
borrowEnabled: false,
|
||||
marketMaxLtv: 0,
|
||||
marketDepositAmount: BN_ZERO,
|
||||
marketLiquidityRate: 0,
|
||||
marketLiquidityAmount: BN_ZERO,
|
||||
marketLiquidationThreshold: 0,
|
||||
}))
|
||||
|
||||
return <AvailableLendsTable data={data} isLoading />
|
||||
}
|
36
src/components/Earn/Lend/Table/AvailableLendsTable.tsx
Normal file
36
src/components/Earn/Lend/Table/AvailableLendsTable.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { Row } from '@tanstack/react-table'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import Card from 'components/Card'
|
||||
import { NAME_META } from 'components/Earn/Lend/Table/Columns/Name'
|
||||
import useAvailableColumns from 'components/Earn/Lend/Table/Columns/useAvailableColumns'
|
||||
import Table from 'components/Table'
|
||||
|
||||
import MarketDetails from '../../../MarketDetails'
|
||||
|
||||
type Props = {
|
||||
data: LendingMarketTableData[]
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export default function AvailableLendsTable(props: Props) {
|
||||
const columns = useAvailableColumns({ isLoading: props.isLoading })
|
||||
|
||||
const renderExpanded = useCallback(
|
||||
(row: Row<LendingMarketTableData>) => <MarketDetails row={row} type='lend' />,
|
||||
[],
|
||||
)
|
||||
|
||||
if (!props.data.length) return null
|
||||
|
||||
return (
|
||||
<Card className='w-full h-fit bg-white/5' title={'Available Markets'}>
|
||||
<Table
|
||||
columns={columns}
|
||||
data={props.data}
|
||||
initialSorting={[{ id: NAME_META.id, desc: true }]}
|
||||
renderExpanded={renderExpanded}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
26
src/components/Earn/Lend/Table/Columns/Apr.tsx
Normal file
26
src/components/Earn/Lend/Table/Columns/Apr.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import React from 'react'
|
||||
|
||||
import AssetRate from 'components/Asset/AssetRate'
|
||||
import Loading from 'components/Loading'
|
||||
import { convertLiquidityRateToAPR } from 'utils/formatters'
|
||||
|
||||
export const APR_META = { accessorKey: 'marketLiquidityRate', header: 'APR' }
|
||||
|
||||
interface Props {
|
||||
marketLiquidityRate: number
|
||||
borrowEnabled: boolean
|
||||
isLoading: boolean
|
||||
}
|
||||
export default function Apr(props: Props) {
|
||||
if (props.isLoading) return <Loading />
|
||||
|
||||
return (
|
||||
<AssetRate
|
||||
rate={convertLiquidityRateToAPR(props.marketLiquidityRate)}
|
||||
isEnabled={props.borrowEnabled}
|
||||
className='justify-end text-xs'
|
||||
type='apr'
|
||||
orientation='ltr'
|
||||
/>
|
||||
)
|
||||
}
|
37
src/components/Earn/Lend/Table/Columns/DepositCap.tsx
Normal file
37
src/components/Earn/Lend/Table/Columns/DepositCap.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import React from 'react'
|
||||
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import Loading from 'components/Loading'
|
||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
|
||||
export const DEPOSIT_CAP_META = { accessorKey: 'marketDepositCap', header: 'Depo. Cap' }
|
||||
|
||||
interface Props {
|
||||
isLoading: boolean
|
||||
data: LendingMarketTableData
|
||||
}
|
||||
export default function DepositCap(props: Props) {
|
||||
if (props.isLoading) return <Loading />
|
||||
const { marketDepositCap, marketDepositAmount, asset } = props.data
|
||||
const percent = marketDepositAmount.dividedBy(marketDepositCap).multipliedBy(100)
|
||||
|
||||
return (
|
||||
<TitleAndSubCell
|
||||
className='text-xs'
|
||||
title={
|
||||
<FormattedNumber
|
||||
amount={marketDepositCap.toNumber()}
|
||||
options={{ abbreviated: true, decimals: asset.decimals }}
|
||||
animate
|
||||
/>
|
||||
}
|
||||
sub={
|
||||
<FormattedNumber
|
||||
amount={percent.toNumber()}
|
||||
options={{ minDecimals: 2, maxDecimals: 2, suffix: '% used' }}
|
||||
animate
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
20
src/components/Earn/Lend/Table/Columns/DepositValue.tsx
Normal file
20
src/components/Earn/Lend/Table/Columns/DepositValue.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react'
|
||||
|
||||
import AmountAndValue from 'components/AmountAndValue'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
export const DEPOSIT_VALUE_META = { accessorKey: 'accountDepositValue', header: 'Deposited' }
|
||||
|
||||
interface Props {
|
||||
asset: Asset
|
||||
lentAmount?: string
|
||||
}
|
||||
export default function DepositValue(props: Props) {
|
||||
return (
|
||||
<AmountAndValue
|
||||
asset={props.asset}
|
||||
amount={props.lentAmount ? BN(props.lentAmount) : BN_ZERO}
|
||||
/>
|
||||
)
|
||||
}
|
16
src/components/Earn/Lend/Table/Columns/Manage.tsx
Normal file
16
src/components/Earn/Lend/Table/Columns/Manage.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from 'react'
|
||||
|
||||
import { ChevronDown, ChevronUp } from 'components/Icons'
|
||||
|
||||
export const MANAGE_META = { accessorKey: 'manage', enableSorting: false, header: 'Manage' }
|
||||
|
||||
interface Props {
|
||||
isExpanded: boolean
|
||||
}
|
||||
export default function Manage(props: Props) {
|
||||
return (
|
||||
<div className='flex items-center justify-end'>
|
||||
<div className='w-4'>{props.isExpanded ? <ChevronUp /> : <ChevronDown />}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
18
src/components/Earn/Lend/Table/Columns/Name.tsx
Normal file
18
src/components/Earn/Lend/Table/Columns/Name.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import React from 'react'
|
||||
|
||||
import AssetImage from 'components/Asset/AssetImage'
|
||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
|
||||
export const NAME_META = { accessorKey: 'asset.name', header: 'Asset', id: 'symbol' }
|
||||
interface Props {
|
||||
asset: Asset
|
||||
}
|
||||
export default function Name(props: Props) {
|
||||
const { asset } = props
|
||||
return (
|
||||
<div className='flex items-center flex-1 gap-3'>
|
||||
<AssetImage asset={asset} size={32} />
|
||||
<TitleAndSubCell title={asset.symbol} sub={asset.name} className='text-left min-w-15' />
|
||||
</div>
|
||||
)
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import { ColumnDef } from '@tanstack/react-table'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import Apr, { APR_META } from 'components/Earn/Lend/Table/Columns/Apr'
|
||||
import DepositCap, { DEPOSIT_CAP_META } from 'components/Earn/Lend/Table/Columns/DepositCap'
|
||||
import Manage, { MANAGE_META } from 'components/Earn/Lend/Table/Columns/Manage'
|
||||
import Name, { NAME_META } from 'components/Earn/Lend/Table/Columns/Name'
|
||||
|
||||
interface Props {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export default function useAvailableColumns(props: Props) {
|
||||
return useMemo<ColumnDef<LendingMarketTableData>[]>(() => {
|
||||
return [
|
||||
{
|
||||
...NAME_META,
|
||||
cell: ({ row }) => <Name asset={row.original.asset} />,
|
||||
},
|
||||
{
|
||||
...APR_META,
|
||||
cell: ({ row }) => (
|
||||
<Apr
|
||||
isLoading={props.isLoading}
|
||||
borrowEnabled={row.original.borrowEnabled}
|
||||
marketLiquidityRate={row.original.marketLiquidityRate}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
...DEPOSIT_CAP_META,
|
||||
cell: ({ row }) => <DepositCap isLoading={props.isLoading} data={row.original} />,
|
||||
},
|
||||
{
|
||||
...MANAGE_META,
|
||||
cell: ({ row }) => <Manage isExpanded={row.getIsExpanded()} />,
|
||||
},
|
||||
]
|
||||
}, [])
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
import { ColumnDef } from '@tanstack/react-table'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import Apr, { APR_META } from 'components/Earn/Lend/Table/Columns/Apr'
|
||||
import DepositCap, { DEPOSIT_CAP_META } from 'components/Earn/Lend/Table/Columns/DepositCap'
|
||||
import DepositValue, { DEPOSIT_VALUE_META } from 'components/Earn/Lend/Table/Columns/DepositValue'
|
||||
import Manage, { MANAGE_META } from 'components/Earn/Lend/Table/Columns/Manage'
|
||||
import Name, { NAME_META } from 'components/Earn/Lend/Table/Columns/Name'
|
||||
|
||||
interface Props {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export default function useDepositedColumns(props: Props) {
|
||||
return useMemo<ColumnDef<LendingMarketTableData>[]>(() => {
|
||||
return [
|
||||
{
|
||||
...NAME_META,
|
||||
cell: ({ row }) => <Name asset={row.original.asset} />,
|
||||
},
|
||||
{
|
||||
...DEPOSIT_VALUE_META,
|
||||
cell: ({ row }) => (
|
||||
<DepositValue asset={row.original.asset} lentAmount={row.original.accountLentAmount} />
|
||||
),
|
||||
},
|
||||
{
|
||||
...APR_META,
|
||||
cell: ({ row }) => (
|
||||
<Apr
|
||||
isLoading={props.isLoading}
|
||||
borrowEnabled={row.original.borrowEnabled}
|
||||
marketLiquidityRate={row.original.marketLiquidityRate}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
...DEPOSIT_CAP_META,
|
||||
cell: ({ row }) => <DepositCap isLoading={props.isLoading} data={row.original} />,
|
||||
},
|
||||
{
|
||||
...MANAGE_META,
|
||||
cell: ({ row }) => <Manage isExpanded={row.getIsExpanded()} />,
|
||||
},
|
||||
]
|
||||
}, [])
|
||||
}
|
35
src/components/Earn/Lend/Table/DepositedLendsTable.tsx
Normal file
35
src/components/Earn/Lend/Table/DepositedLendsTable.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import { Row } from '@tanstack/react-table'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import Card from 'components/Card'
|
||||
import { NAME_META } from 'components/Earn/Lend/Table/Columns/Name'
|
||||
import useDepositedColumns from 'components/Earn/Lend/Table/Columns/useDepositedColumns'
|
||||
import MarketDetails from 'components/MarketDetails'
|
||||
import Table from 'components/Table'
|
||||
|
||||
type Props = {
|
||||
data: LendingMarketTableData[]
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export default function DepositedLendsTable(props: Props) {
|
||||
const columns = useDepositedColumns({ isLoading: props.isLoading })
|
||||
|
||||
const renderExpanded = useCallback(
|
||||
(row: Row<LendingMarketTableData>) => <MarketDetails row={row} type='borrow' />,
|
||||
[],
|
||||
)
|
||||
|
||||
if (!props.data.length) return null
|
||||
|
||||
return (
|
||||
<Card className='w-full h-fit bg-white/5' title={'Lent Assets'}>
|
||||
<Table
|
||||
columns={columns}
|
||||
data={props.data}
|
||||
initialSorting={[{ id: NAME_META.id, desc: true }]}
|
||||
renderExpanded={renderExpanded}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
import { flexRender, Row } from '@tanstack/react-table'
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Text from 'components/Text'
|
||||
|
||||
type Props<TData> = {
|
||||
rowData: Row<TData>
|
||||
resetExpanded: (defaultState?: boolean | undefined) => void
|
||||
isExpanded: boolean
|
||||
expandedActionButtons?: JSX.Element
|
||||
expandedDetails?: JSX.Element
|
||||
}
|
||||
|
||||
function AssetListTableRow<TData>(props: Props<TData>) {
|
||||
const renderFullRow = (key: string, content: JSX.Element) => (
|
||||
<tr key={key} className='bg-black/20'>
|
||||
<td
|
||||
className='border-b border-white border-opacity-10 p-4'
|
||||
colSpan={props.rowData.getAllCells().length}
|
||||
>
|
||||
{content}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
|
||||
const renderExpanded = () => {
|
||||
return (
|
||||
<>
|
||||
{props.expandedActionButtons &&
|
||||
renderFullRow(
|
||||
`${props.rowData.id}-expanded-actions`,
|
||||
<div className='flex flex-1 flex-row justify-between'>
|
||||
<Text className='mt-1 flex p-0 font-bold' size='base'>
|
||||
Details
|
||||
</Text>
|
||||
<div>{props.expandedActionButtons}</div>
|
||||
</div>,
|
||||
)}
|
||||
{props.expandedDetails &&
|
||||
renderFullRow(`${props.rowData.id}-expanded-details`, props.expandedDetails)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<tr
|
||||
key={props.rowData.id}
|
||||
className={classNames(
|
||||
'hover:cursor-pointer transition-colors',
|
||||
|
||||
props.rowData.getIsExpanded() ? 'bg-black/20' : 'bg-white/0 hover:bg-white/5',
|
||||
)}
|
||||
onClick={() => {
|
||||
const isExpanded = props.rowData.getIsExpanded()
|
||||
props.resetExpanded()
|
||||
!isExpanded && props.rowData.toggleExpanded()
|
||||
}}
|
||||
>
|
||||
{props.rowData.getVisibleCells().map((cell) => {
|
||||
return (
|
||||
<td key={cell.id} className={'p-4 text-right'}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
{props.isExpanded && renderExpanded()}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AssetListTableRow
|
@ -1,102 +0,0 @@
|
||||
import {
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getSortedRowModel,
|
||||
Row,
|
||||
SortingState,
|
||||
Table,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table'
|
||||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
|
||||
import Card from 'components/Card'
|
||||
import { SortAsc, SortDesc, SortNone } from 'components/Icons'
|
||||
import Text from 'components/Text'
|
||||
|
||||
interface Props<TData> {
|
||||
title: string
|
||||
data: TData[]
|
||||
columns: ColumnDef<TData>[]
|
||||
sorting?: SortingState
|
||||
rowRenderer: (row: Row<TData>, table: Table<TData>) => JSX.Element
|
||||
}
|
||||
|
||||
function AssetListTable<TData>(props: Props<TData>) {
|
||||
const { title, data, columns } = props
|
||||
const [sorting, setSorting] = React.useState<SortingState>(props.sorting ?? [])
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
state: {
|
||||
sorting,
|
||||
},
|
||||
onSortingChange: setSorting,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
})
|
||||
|
||||
const _rowRenderer = (row: Row<TData>) => props.rowRenderer(row, table)
|
||||
|
||||
if (!data.length) return null
|
||||
|
||||
return (
|
||||
<Card className='mb-4 h-fit w-full bg-white/5' title={title}>
|
||||
<table className='w-full'>
|
||||
<thead className='border-b border-white border-opacity-10 bg-black/20'>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<th
|
||||
key={header.id}
|
||||
onClick={header.column.getToggleSortingHandler()}
|
||||
className={classNames(
|
||||
'px-4 py-3',
|
||||
header.column.getCanSort() && 'hover:cursor-pointer',
|
||||
header.id === 'symbol' ? 'text-left' : 'text-right',
|
||||
{
|
||||
'w-32': header.id === 'manage',
|
||||
'w-48': header.id === 'depositCap',
|
||||
},
|
||||
)}
|
||||
>
|
||||
<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='xs'
|
||||
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(_rowRenderer)}</tbody>
|
||||
</table>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default AssetListTable
|
@ -1,3 +1,4 @@
|
||||
import { Row } from '@tanstack/react-table'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
@ -5,7 +6,7 @@ import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice'
|
||||
|
||||
interface Props {
|
||||
data: BorrowMarketTableData | LendingMarketTableData
|
||||
row: Row<BorrowMarketTableData | LendingMarketTableData>
|
||||
type: 'borrow' | 'lend'
|
||||
}
|
||||
|
||||
@ -15,7 +16,7 @@ interface Detail {
|
||||
title: string
|
||||
}
|
||||
|
||||
export default function MarketDetails({ data, type }: Props) {
|
||||
export default function MarketDetails({ row, type }: Props) {
|
||||
const {
|
||||
convertAmount,
|
||||
getConversionRate,
|
||||
@ -28,7 +29,7 @@ export default function MarketDetails({ data, type }: Props) {
|
||||
marketDepositAmount,
|
||||
marketLiquidityAmount,
|
||||
marketLiquidationThreshold,
|
||||
} = data
|
||||
} = row.original
|
||||
|
||||
const totalBorrowed = marketDepositAmount.minus(marketLiquidityAmount)
|
||||
|
||||
@ -36,7 +37,6 @@ export default function MarketDetails({ data, type }: Props) {
|
||||
const isDollar = displayCurrencySymbol === '$'
|
||||
|
||||
function getLendingMarketDetails() {
|
||||
const depositCap = (data as LendingMarketTableData).marketDepositCap
|
||||
return [
|
||||
{
|
||||
amount: convertAmount(asset, marketDepositAmount).toNumber(),
|
||||
@ -107,7 +107,6 @@ export default function MarketDetails({ data, type }: Props) {
|
||||
if (type === 'lend') return getLendingMarketDetails()
|
||||
return getBorrowMarketDetails()
|
||||
}, [
|
||||
data,
|
||||
type,
|
||||
asset,
|
||||
marketDepositAmount,
|
||||
@ -120,23 +119,27 @@ export default function MarketDetails({ data, type }: Props) {
|
||||
])
|
||||
|
||||
return (
|
||||
<div className='flex justify-between flex-1 bg-white rounded-md bg-opacity-5'>
|
||||
{details.map((detail, index) => (
|
||||
<TitleAndSubCell
|
||||
key={index}
|
||||
className='text-center'
|
||||
containerClassName='m-5 ml-10 mr-10 space-y-1'
|
||||
title={
|
||||
<FormattedNumber
|
||||
className='text-xs text-center'
|
||||
amount={detail.amount}
|
||||
options={detail.options}
|
||||
animate
|
||||
<tr>
|
||||
<td colSpan={row.getAllCells().length}>
|
||||
<div className='flex justify-between flex-1 bg-white rounded-md bg-opacity-5'>
|
||||
{details.map((detail, index) => (
|
||||
<TitleAndSubCell
|
||||
key={index}
|
||||
className='text-center'
|
||||
containerClassName='m-5 ml-10 mr-10 space-y-1'
|
||||
title={
|
||||
<FormattedNumber
|
||||
className='text-xs text-center'
|
||||
amount={detail.amount}
|
||||
options={detail.options}
|
||||
animate
|
||||
/>
|
||||
}
|
||||
sub={detail.title}
|
||||
/>
|
||||
}
|
||||
sub={detail.title}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import React, { Suspense } from 'react'
|
||||
import React, { Suspense, useMemo } from 'react'
|
||||
|
||||
import AccountBalancesTable from 'components/Account/AccountBalancesTable'
|
||||
import Card from 'components/Card'
|
||||
@ -15,7 +15,8 @@ interface Props {
|
||||
function Content(props: Props) {
|
||||
const { data: account } = useAccount(props.accountId, true)
|
||||
|
||||
const { allAssets: borrowAssets } = useBorrowMarketAssetsTableData()
|
||||
const { data } = useBorrowMarketAssetsTableData(false)
|
||||
const borrowAssets = useMemo(() => data?.allAssets || [], [data])
|
||||
const { allAssets: lendingAssets } = useLendingMarketAssetsTableData()
|
||||
|
||||
if (!account || !borrowAssets.length || !lendingAssets.length) {
|
||||
|
@ -20,7 +20,8 @@ function Content(props: Props) {
|
||||
const { data: account } = useAccount(props.accountId, true)
|
||||
const { data: prices } = usePrices()
|
||||
const { health } = useHealthComputer(account)
|
||||
const { allAssets: borrowAssets } = useBorrowMarketAssetsTableData()
|
||||
const { data } = useBorrowMarketAssetsTableData(false)
|
||||
const borrowAssets = useMemo(() => data?.allAssets || [], [data])
|
||||
const { allAssets: lendingAssets } = useLendingMarketAssetsTableData()
|
||||
|
||||
const stats = useMemo(() => {
|
||||
|
@ -34,7 +34,8 @@ export default function PortfolioCard(props: Props) {
|
||||
const { data: prices } = usePrices()
|
||||
const currentAccountId = useAccountId()
|
||||
const { allAssets: lendingAssets } = useLendingMarketAssetsTableData()
|
||||
const { allAssets: borrowAssets } = useBorrowMarketAssetsTableData()
|
||||
const { data } = useBorrowMarketAssetsTableData(false)
|
||||
const borrowAssets = useMemo(() => data?.allAssets || [], [data])
|
||||
const [reduceMotion] = useLocalStorage<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
|
||||
const address = useStore((s) => s.address)
|
||||
|
||||
|
@ -17,7 +17,8 @@ export default function PortfolioSummary() {
|
||||
const { address: urlAddress } = useParams()
|
||||
const walletAddress = useStore((s) => s.address)
|
||||
const { data: prices } = usePrices()
|
||||
const { allAssets: borrowAssets } = useBorrowMarketAssetsTableData()
|
||||
const { data } = useBorrowMarketAssetsTableData(false)
|
||||
const borrowAssets = useMemo(() => data?.allAssets || [], [data])
|
||||
const { allAssets: lendingAssets } = useLendingMarketAssetsTableData()
|
||||
const { data: accounts } = useAccounts(urlAddress || walletAddress)
|
||||
|
||||
|
42
src/components/Table/Row.tsx
Normal file
42
src/components/Table/Row.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { flexRender, Row as TanstackRow, Table as TanstackTable } from '@tanstack/react-table'
|
||||
import classNames from 'classnames'
|
||||
|
||||
interface Props<T> {
|
||||
row: TanstackRow<T>
|
||||
table: TanstackTable<T>
|
||||
renderExpanded?: (row: TanstackRow<T>, table: TanstackTable<T>) => JSX.Element
|
||||
rowClassName?: string
|
||||
rowClickHandler?: () => void
|
||||
}
|
||||
|
||||
export default function Row<T>(props: Props<T>) {
|
||||
return (
|
||||
<>
|
||||
<tr
|
||||
key={props.row.id}
|
||||
className={classNames(
|
||||
'bg-white/3 group/row border-b border-t border-white/5 transition-colors hover:bg-white/5',
|
||||
props.renderExpanded && 'hover:cursor-pointer',
|
||||
props.row.getIsExpanded() && 'is-expanded',
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
const isExpanded = props.row.getIsExpanded()
|
||||
props.table.resetExpanded()
|
||||
!isExpanded && props.row.toggleExpanded()
|
||||
}}
|
||||
>
|
||||
{props.row.getVisibleCells().map((cell) => {
|
||||
return (
|
||||
<td key={cell.id} className={'p-4 text-right'}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
{props.row.getIsExpanded() &&
|
||||
props.renderExpanded &&
|
||||
props.renderExpanded(props.row, props.table)}
|
||||
</>
|
||||
)
|
||||
}
|
@ -3,26 +3,26 @@ import {
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getSortedRowModel,
|
||||
Row,
|
||||
SortingState,
|
||||
Row as TanstackRow,
|
||||
Table as TanstackTable,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table'
|
||||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
|
||||
import VaultExpanded from 'components/Earn/Farm/VaultExpanded'
|
||||
import { VaultRow } from 'components/Earn/Farm/VaultRow'
|
||||
import { SortAsc, SortDesc, SortNone } from 'components/Icons'
|
||||
import Row from 'components/Table/Row'
|
||||
import Text from 'components/Text'
|
||||
|
||||
import Text from '../Text'
|
||||
|
||||
interface Props {
|
||||
columns: ColumnDef<any>[]
|
||||
data: unknown[]
|
||||
interface Props<T> {
|
||||
columns: ColumnDef<T>[]
|
||||
data: T[]
|
||||
initialSorting: SortingState
|
||||
renderExpanded?: (row: TanstackRow<T>, table: TanstackTable<T>) => JSX.Element
|
||||
}
|
||||
|
||||
export default function Table(props: Props) {
|
||||
export default function Table<T>(props: Props<T>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>(props.initialSorting)
|
||||
|
||||
const table = useReactTable({
|
||||
@ -83,48 +83,10 @@ export default function Table(props: Props) {
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{table.getRowModel().rows.map((row) => {
|
||||
if (row.getIsExpanded()) {
|
||||
return (
|
||||
<React.Fragment key={`${row.id}_subrow`}>
|
||||
{getExpandedRowModel('farm', row, table.resetExpanded)}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
return getRowModel('farm', row, table.resetExpanded)
|
||||
})}
|
||||
{table.getRowModel().rows.map((row) => (
|
||||
<Row key={row.id} row={row} table={table} renderExpanded={props.renderExpanded} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
|
||||
function getExpandedRowModel(
|
||||
type: 'farm',
|
||||
row: unknown,
|
||||
resetExpanded: (defaultState?: boolean | undefined) => void,
|
||||
) {
|
||||
switch (type) {
|
||||
case 'farm':
|
||||
return (
|
||||
<>
|
||||
<VaultRow row={row as Row<Vault>} resetExpanded={resetExpanded} />
|
||||
<VaultExpanded row={row as Row<Vault>} resetExpanded={resetExpanded} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function getRowModel(
|
||||
type: 'farm',
|
||||
row: Row<unknown>,
|
||||
resetExpanded: (defaultState?: boolean | undefined) => void,
|
||||
) {
|
||||
return (
|
||||
<VaultRow
|
||||
key={(row as Row<Vault>).original.address}
|
||||
row={row as Row<Vault>}
|
||||
resetExpanded={resetExpanded}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -8,14 +8,10 @@ import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableDa
|
||||
|
||||
export default function AccountDetailsCard() {
|
||||
const account = useCurrentAccount()
|
||||
const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } =
|
||||
useBorrowMarketAssetsTableData()
|
||||
const { data } = useBorrowMarketAssetsTableData(false)
|
||||
const borrowAssetsData = useMemo(() => data?.allAssets || [], [data])
|
||||
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
|
||||
useLendingMarketAssetsTableData()
|
||||
const borrowAssetsData = useMemo(
|
||||
() => [...borrowAvailableAssets, ...accountBorrowedAssets],
|
||||
[borrowAvailableAssets, accountBorrowedAssets],
|
||||
)
|
||||
const lendingAssetsData = useMemo(
|
||||
() => [...lendingAvailableAssets, ...accountLentAssets],
|
||||
[lendingAvailableAssets, accountLentAssets],
|
||||
|
@ -17,6 +17,7 @@ export const ASSETS: Asset[] = [
|
||||
logo: '/images/tokens/osmo.svg',
|
||||
isEnabled: true,
|
||||
isMarket: true,
|
||||
isBorrowEnabled: true,
|
||||
isDisplayCurrency: true,
|
||||
isAutoLendEnabled: true,
|
||||
pythPriceFeedId: '5867f5683c757393a0670ef0f701490950fe93fdb006d181c8265a831ac0c5c6',
|
||||
@ -38,6 +39,7 @@ export const ASSETS: Asset[] = [
|
||||
isMarket: true,
|
||||
isDisplayCurrency: true,
|
||||
isAutoLendEnabled: true,
|
||||
isBorrowEnabled: true,
|
||||
pythPriceFeedId: 'b00b60f88b03a6a625a8d1c048c3f66653edf217439983d037e7222c4e612819',
|
||||
poolId: 1,
|
||||
},
|
||||
@ -71,6 +73,7 @@ export const ASSETS: Asset[] = [
|
||||
isMarket: ENV.NETWORK !== NETWORK.TESTNET,
|
||||
isDisplayCurrency: ENV.NETWORK !== NETWORK.TESTNET,
|
||||
isAutoLendEnabled: true,
|
||||
isBorrowEnabled: true,
|
||||
pythPriceFeedId: 'e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43',
|
||||
poolId: 712,
|
||||
},
|
||||
@ -88,6 +91,7 @@ export const ASSETS: Asset[] = [
|
||||
isMarket: ENV.NETWORK !== NETWORK.TESTNET,
|
||||
isDisplayCurrency: ENV.NETWORK !== NETWORK.TESTNET,
|
||||
isAutoLendEnabled: true,
|
||||
isBorrowEnabled: true,
|
||||
pythPriceFeedId: 'ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace',
|
||||
poolId: 704,
|
||||
},
|
||||
@ -126,6 +130,7 @@ export const ASSETS: Asset[] = [
|
||||
isMarket: true,
|
||||
isDisplayCurrency: true,
|
||||
isStable: true,
|
||||
isBorrowEnabled: true,
|
||||
isAutoLendEnabled: true,
|
||||
pythPriceFeedId: 'eaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a',
|
||||
poolId: 678,
|
||||
|
6
src/hooks/useBorrowEnabledMarkets.ts
Normal file
6
src/hooks/useBorrowEnabledMarkets.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import useMarketAssets from 'hooks/useMarketAssets'
|
||||
|
||||
export default function useBorrowEnabledMarkets() {
|
||||
const { data: markets } = useMarketAssets()
|
||||
return markets.filter((market) => market.borrowEnabled)
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
import { useMemo } from 'react'
|
||||
import useSWR from 'swr'
|
||||
|
||||
import useCurrentAccountDebts from 'hooks/useCurrentAccountDebts'
|
||||
import useDepositEnabledMarkets from 'hooks/useDepositEnabledMarkets'
|
||||
import useMarketBorrowings from 'hooks/useMarketBorrowings'
|
||||
import useMarketDeposits from 'hooks/useMarketDeposits'
|
||||
import useMarketLiquidities from 'hooks/useMarketLiquidities'
|
||||
@ -9,47 +8,55 @@ import { byDenom } from 'utils/array'
|
||||
import { getAssetByDenom } from 'utils/assets'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
export default function useBorrowMarketAssetsTableData(): {
|
||||
accountBorrowedAssets: BorrowMarketTableData[]
|
||||
availableAssets: BorrowMarketTableData[]
|
||||
allAssets: BorrowMarketTableData[]
|
||||
} {
|
||||
const markets = useDepositEnabledMarkets()
|
||||
import useBorrowEnabledMarkets from './useBorrowEnabledMarkets'
|
||||
|
||||
export default function useBorrowMarketAssetsTableData(suspense = true) {
|
||||
const markets = useBorrowEnabledMarkets()
|
||||
const accountDebts = useCurrentAccountDebts()
|
||||
const { data: borrowData } = useMarketBorrowings()
|
||||
const { data: marketDeposits } = useMarketDeposits()
|
||||
const { data: marketLiquidities } = useMarketLiquidities()
|
||||
|
||||
return useMemo(() => {
|
||||
const accountBorrowedAssets: BorrowMarketTableData[] = [],
|
||||
availableAssets: BorrowMarketTableData[] = []
|
||||
return useSWR(
|
||||
'borrowMarketAssetsTableData',
|
||||
async (): Promise<{
|
||||
accountBorrowedAssets: BorrowMarketTableData[]
|
||||
availableAssets: BorrowMarketTableData[]
|
||||
allAssets: BorrowMarketTableData[]
|
||||
}> => {
|
||||
const accountBorrowedAssets: BorrowMarketTableData[] = [],
|
||||
availableAssets: BorrowMarketTableData[] = []
|
||||
|
||||
markets.forEach(({ denom, liquidityRate, liquidationThreshold, maxLtv }) => {
|
||||
const asset = getAssetByDenom(denom) as Asset
|
||||
const borrow = borrowData.find((borrow) => borrow.denom === denom)
|
||||
const marketDepositAmount = BN(marketDeposits.find(byDenom(denom))?.amount ?? 0)
|
||||
const marketLiquidityAmount = BN(marketLiquidities.find(byDenom(denom))?.amount ?? 0)
|
||||
markets.forEach(({ denom, liquidityRate, liquidationThreshold, maxLtv }) => {
|
||||
const asset = getAssetByDenom(denom) as Asset
|
||||
const borrow = borrowData.find((borrow) => borrow.denom === denom)
|
||||
const marketDepositAmount = BN(marketDeposits.find(byDenom(denom))?.amount ?? 0)
|
||||
const marketLiquidityAmount = BN(marketLiquidities.find(byDenom(denom))?.amount ?? 0)
|
||||
|
||||
const debt = accountDebts?.find((debt) => debt.denom === denom)
|
||||
if (!borrow) return
|
||||
const debt = accountDebts?.find((debt) => debt.denom === denom)
|
||||
if (!borrow) return
|
||||
|
||||
const borrowMarketAsset: BorrowMarketTableData = {
|
||||
...borrow,
|
||||
asset,
|
||||
debt: debt?.amount,
|
||||
marketDepositAmount,
|
||||
marketLiquidityAmount,
|
||||
marketLiquidityRate: liquidityRate,
|
||||
marketLiquidationThreshold: liquidationThreshold,
|
||||
marketMaxLtv: maxLtv,
|
||||
const borrowMarketAsset: BorrowMarketTableData = {
|
||||
...borrow,
|
||||
asset,
|
||||
debt: debt?.amount,
|
||||
marketDepositAmount,
|
||||
marketLiquidityAmount,
|
||||
marketLiquidityRate: liquidityRate,
|
||||
marketLiquidationThreshold: liquidationThreshold,
|
||||
marketMaxLtv: maxLtv,
|
||||
}
|
||||
;(borrowMarketAsset.debt ? accountBorrowedAssets : availableAssets).push(borrowMarketAsset)
|
||||
})
|
||||
|
||||
return {
|
||||
accountBorrowedAssets,
|
||||
availableAssets,
|
||||
allAssets: [...accountBorrowedAssets, ...availableAssets],
|
||||
}
|
||||
;(borrowMarketAsset.debt ? accountBorrowedAssets : availableAssets).push(borrowMarketAsset)
|
||||
})
|
||||
|
||||
return {
|
||||
accountBorrowedAssets,
|
||||
availableAssets,
|
||||
allAssets: [...accountBorrowedAssets, ...availableAssets],
|
||||
}
|
||||
}, [accountDebts, borrowData, markets, marketDeposits, marketLiquidities])
|
||||
},
|
||||
{
|
||||
suspense,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -1,17 +1,13 @@
|
||||
import Borrowings from 'components/Borrow/Borrowings'
|
||||
import BorrowIntro from 'components/Borrow/BorrowIntro'
|
||||
import BorrowTable from 'components/Borrow/BorrowTable'
|
||||
import MigrationBanner from 'components/MigrationBanner'
|
||||
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
|
||||
|
||||
export default function BorrowPage() {
|
||||
const { accountBorrowedAssets, availableAssets } = useBorrowMarketAssetsTableData()
|
||||
|
||||
return (
|
||||
<div className='flex flex-wrap w-full gap-6'>
|
||||
<MigrationBanner />
|
||||
<BorrowIntro />
|
||||
<BorrowTable data={accountBorrowedAssets} title='Borrowed Assets' />
|
||||
<BorrowTable data={availableAssets} title='Available to borrow' />
|
||||
<Borrowings />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -1,19 +1,16 @@
|
||||
import LendingMarketsTable from 'components/Earn/Lend/LendingMarketsTable'
|
||||
import LendIntro from 'components/Earn/Lend/LendIntro'
|
||||
import Lends from 'components/Earn/Lend/Lends'
|
||||
import Tab from 'components/Earn/Tab'
|
||||
import MigrationBanner from 'components/MigrationBanner'
|
||||
import { EARN_TABS } from 'constants/pages'
|
||||
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
|
||||
|
||||
export default function LendPage() {
|
||||
const { accountLentAssets, availableAssets } = useLendingMarketAssetsTableData()
|
||||
return (
|
||||
<div className='flex flex-wrap w-full gap-6'>
|
||||
<MigrationBanner />
|
||||
<Tab tabs={EARN_TABS} activeTabIdx={0} />
|
||||
<LendIntro />
|
||||
<LendingMarketsTable data={accountLentAssets} title='Lent Assets' />
|
||||
<LendingMarketsTable data={availableAssets} title='Available Markets' />
|
||||
<Lends />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
1
src/types/interfaces/asset.d.ts
vendored
1
src/types/interfaces/asset.d.ts
vendored
@ -50,6 +50,7 @@ interface Asset {
|
||||
isStable?: boolean
|
||||
isFavorite?: boolean
|
||||
isAutoLendEnabled?: boolean
|
||||
isBorrowEnabled?: boolean
|
||||
pythPriceFeedId?: string
|
||||
forceFetchPrice?: boolean
|
||||
testnetDenom?: string
|
||||
|
@ -35,3 +35,7 @@ export function findCoinByDenom(denom: string, coins: BigNumberCoin[]) {
|
||||
export function getLendEnabledAssets() {
|
||||
return ASSETS.filter((asset) => asset.isAutoLendEnabled)
|
||||
}
|
||||
|
||||
export function getBorrowEnabledAssets() {
|
||||
return ASSETS.filter((asset) => asset.isBorrowEnabled)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user