♻️ refactor table (Farm) (#555)

* ♻️ refactor table (Farm)

* 🧽 clean up PR

* 🧽 clean up PR
This commit is contained in:
Bob van der Helm 2023-10-17 12:42:25 +02:00 committed by GitHub
parent 96ab3f64c0
commit 9e5f88ac83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 7668 additions and 9351 deletions

View File

@ -0,0 +1,19 @@
import React from 'react'
import Card from 'components/Card'
import useAvailableColumns from 'components/Earn/Farm/Table/Columns/useAvailableColumns'
import Table from 'components/Table'
type Props = {
data: Vault[]
isLoading: boolean
}
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 }]} />
</Card>
)
}

View File

@ -0,0 +1,23 @@
import React from 'react'
import { FormattedNumber } from 'components/FormattedNumber'
import Loading from 'components/Loading'
interface Props {
vault: Vault | DepositedVault
}
export default function Apy(props: Props) {
const { vault } = props
if (vault.apy === null) return <Loading />
return (
<FormattedNumber
amount={vault.apy ?? 0}
options={{ minDecimals: 2, maxDecimals: 2, suffix: '%' }}
className='text-xs'
animate
/>
)
}

View File

@ -0,0 +1,32 @@
import React from 'react'
import ActionButton from 'components/Button/ActionButton'
import Loading from 'components/Loading'
import useStore from 'store'
interface Props {
vault: Vault | DepositedVault
isLoading: boolean
}
export const Deposit = (props: Props) => {
const { vault } = props
function enterVaultHandler() {
useStore.setState({
vaultModal: {
vault,
selectedBorrowDenoms: [vault.denoms.secondary],
isCreate: true,
},
})
}
if (props.isLoading) return <Loading />
return (
<div className='flex items-center justify-end'>
<ActionButton onClick={enterVaultHandler} color='tertiary' text='Deposit' />
</div>
)
}

View File

@ -0,0 +1,46 @@
import React from 'react'
import { FormattedNumber } from 'components/FormattedNumber'
import Loading from 'components/Loading'
import TitleAndSubCell from 'components/TitleAndSubCell'
import { VAULT_DEPOSIT_BUFFER } from 'constants/vaults'
import { getAssetByDenom } from 'utils/assets'
interface Props {
vault: Vault | DepositedVault
isLoading: boolean
}
export default function DepositCap(props: Props) {
const { vault } = props
if (props.isLoading) return <Loading />
const percent = vault.cap.used
.dividedBy(vault.cap.max.multipliedBy(VAULT_DEPOSIT_BUFFER))
.multipliedBy(100)
.integerValue()
const decimals = getAssetByDenom(vault.cap.denom)?.decimals ?? 6
return (
<TitleAndSubCell
title={
<FormattedNumber
amount={vault.cap.max.toNumber()}
options={{ minDecimals: 2, abbreviated: true, decimals }}
className='text-xs'
animate
/>
}
sub={
<FormattedNumber
amount={percent.toNumber()}
options={{ minDecimals: 2, maxDecimals: 2, suffix: '% Filled' }}
className='text-xs'
animate
/>
}
/>
)
}

View File

@ -0,0 +1,22 @@
import classNames from 'classnames'
import React from 'react'
import { ChevronDown } from 'components/Icons'
import Loading from 'components/Loading'
interface Props {
isLoading: boolean
isExpanded: boolean
}
export default function Details(props: Props) {
if (props.isLoading) return <Loading />
return (
<div className='flex items-center justify-end'>
<div className={classNames('w-4', props.isExpanded && 'rotate-180')}>
<ChevronDown />
</div>
</div>
)
}

View File

@ -0,0 +1,21 @@
import React from 'react'
import { FormattedNumber } from 'components/FormattedNumber'
import Loading from 'components/Loading'
interface Props {
vault: Vault | DepositedVault
isLoading: boolean
}
export default function MaxLtv(props: Props) {
const { vault } = props
if (props.isLoading) return <Loading />
return (
<FormattedNumber
amount={vault.ltv.max * 100}
options={{ minDecimals: 0, maxDecimals: 0, suffix: '%' }}
className='text-xs'
animate
/>
)
}

View File

@ -0,0 +1,71 @@
import classNames from 'classnames'
import React from 'react'
import VaultLogo from 'components/Earn/Farm/VaultLogo'
import Text from 'components/Text'
import TitleAndSubCell from 'components/TitleAndSubCell'
import { VaultStatus } from 'types/enums/vault'
import { produceCountdown } from 'utils/formatters'
interface Props {
vault: Vault | DepositedVault
}
export default function Name(props: Props) {
const { vault } = props
const timeframe = vault.lockup.timeframe[0]
const unlockDuration = !!timeframe ? ` - (${vault.lockup.duration}${timeframe})` : ''
let remainingTime = 0
let status: VaultStatus = VaultStatus.ACTIVE
if ('status' in vault) {
status = vault.status as VaultStatus
if (vault.status === VaultStatus.UNLOCKING && vault.unlocksAt) {
remainingTime = vault.unlocksAt - Date.now()
}
}
return (
<div className='flex'>
<VaultLogo vault={vault} />
<TitleAndSubCell
className='ml-2 mr-2 text-left'
title={`${vault.name}${unlockDuration}`}
sub={vault.provider}
/>
{status === VaultStatus.UNLOCKING && (
<Text
className='group/label relative h-5 w-[84px] rounded-sm bg-green text-center leading-5 text-white'
size='xs'
>
<span
className={classNames(
'absolute inset-0 text-center',
'opacity-100 transition-opacity duration-500',
'group-hover/label:opacity-0 group-[.is-expanded]/row:opacity-0',
)}
>
Unlocking
</span>
<span
className={classNames(
'absolute inset-0 text-center',
'opacity-0 transition-opacity duration-500',
'group-hover/label:opacity-100 group-[.is-expanded]/row:opacity-100',
)}
>
{produceCountdown(remainingTime)}
</span>
</Text>
)}
{status === VaultStatus.UNLOCKED && (
<Text
className='h-5 w-[84px] rounded-sm bg-green text-center leading-5 text-white'
size='xs'
>
Unlocked
</Text>
)}
</div>
)
}

View File

@ -0,0 +1,19 @@
import React from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import { ORACLE_DENOM } from 'constants/oracle'
import { BNCoin } from 'types/classes/BNCoin'
interface Props {
vault: DepositedVault
isLoading: boolean
}
export default function PositionValue(props: Props) {
const { vault } = props
const positionValue = vault.values.primary
.plus(vault.values.secondary)
.plus(vault.values.unlocking)
.plus(vault.values.unlocked)
const coin = BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, positionValue)
return <DisplayCurrency coin={coin} className='text-xs' />
}

View File

@ -0,0 +1,18 @@
import React from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import Loading from 'components/Loading'
import { BNCoin } from 'types/classes/BNCoin'
interface Props {
vault: Vault | DepositedVault
isLoading: boolean
}
export default function TVL(props: Props) {
const { vault } = props
if (props.isLoading) return <Loading />
const coin = BNCoin.fromDenomAndBigNumber(vault.cap.denom, vault.cap.used)
return <DisplayCurrency coin={coin} className='text-xs' />
}

View File

@ -0,0 +1,51 @@
import { ColumnDef } from '@tanstack/react-table'
import React, { useMemo } from 'react'
import Apy 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'
interface Props {
isLoading: boolean
}
export default function useAvailableColumns(props: Props) {
return useMemo<ColumnDef<Vault | DepositedVault>[]>(() => {
return [
{
header: 'Vault',
accessorKey: 'name',
cell: ({ row }) => <Name vault={row.original as Vault} />,
},
{
accessorKey: 'apy',
header: 'APY',
cell: ({ row }) => <Apy vault={row.original as Vault} />,
},
{
accessorKey: 'tvl',
header: 'TVL',
cell: ({ row }) => <TVL vault={row.original as Vault} isLoading={props.isLoading} />,
},
{
accessorKey: 'cap',
header: 'Deposit Cap',
cell: ({ row }) => <DepositCap vault={row.original as Vault} isLoading={props.isLoading} />,
},
{
accessorKey: 'ltv.max',
header: 'Max LTV',
cell: ({ row }) => <MaxLTV vault={row.original as Vault} isLoading={props.isLoading} />,
},
{
accessorKey: 'details',
enableSorting: false,
header: 'Deposit',
cell: ({ row }) => <Deposit vault={row.original as Vault} isLoading={props.isLoading} />,
},
]
}, [props.isLoading])
}

View File

@ -0,0 +1,64 @@
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'
interface Props {
isLoading: boolean
}
export default function useDepositedColumns(props: Props) {
return useMemo<ColumnDef<DepositedVault>[]>(() => {
return [
{
header: 'Vault',
accessorKey: 'name',
cell: ({ row }) => <Name vault={row.original as DepositedVault} />,
},
{
header: 'Pos. Value',
cell: ({ row }: { row: Row<DepositedVault> }) => (
<PositionValue vault={row.original as DepositedVault} isLoading={props.isLoading} />
),
},
{
accessorKey: 'apy',
header: 'APY',
cell: ({ row }) => <Apy vault={row.original as DepositedVault} />,
},
{
accessorKey: 'tvl',
header: 'TVL',
cell: ({ row }) => (
<TVL vault={row.original as DepositedVault} isLoading={props.isLoading} />
),
},
{
accessorKey: 'cap',
header: 'Deposit Cap',
cell: ({ row }) => (
<DepositCap vault={row.original as DepositedVault} isLoading={props.isLoading} />
),
},
{
accessorKey: 'ltv.max',
header: 'Max LTV',
cell: ({ row }) => (
<MaxLTV vault={row.original as DepositedVault} isLoading={props.isLoading} />
),
},
{
accessorKey: 'details',
enableSorting: false,
header: 'Details',
cell: ({ row }) => <Details isLoading={props.isLoading} isExpanded={row.getIsExpanded()} />,
},
]
}, [props.isLoading])
}

View File

@ -0,0 +1,19 @@
import React from 'react'
import Card from 'components/Card'
import useDepositedColumns from 'components/Earn/Farm/Table/Columns/useDepositedColumns'
import Table from 'components/Table'
type Props = {
data: DepositedVault[]
isLoading: boolean
}
export default function DepositedVaultsTable(props: Props) {
const columns = useDepositedColumns({ isLoading: props.isLoading })
return (
<Card className='w-full h-fit bg-white/5' title={'Deposited vaults'}>
<Table columns={columns} data={props.data} initialSorting={[{ id: 'name', desc: true }]} />
</Card>
)
}

View File

@ -1,310 +0,0 @@
import {
ColumnDef,
flexRender,
getCoreRowModel,
getSortedRowModel,
Row,
SortingState,
useReactTable,
} from '@tanstack/react-table'
import classNames from 'classnames'
import React from 'react'
import ActionButton from 'components/Button/ActionButton'
import DisplayCurrency from 'components/DisplayCurrency'
import VaultExpanded from 'components/Earn/Farm/VaultExpanded'
import VaultLogo from 'components/Earn/Farm/VaultLogo'
import { VaultRow } from 'components/Earn/Farm/VaultRow'
import { FormattedNumber } from 'components/FormattedNumber'
import { ChevronDown, SortAsc, SortDesc, SortNone } from 'components/Icons'
import Loading from 'components/Loading'
import Text from 'components/Text'
import TitleAndSubCell from 'components/TitleAndSubCell'
import { BN_ONE } from 'constants/math'
import { ORACLE_DENOM } from 'constants/oracle'
import { VAULT_DEPOSIT_BUFFER } from 'constants/vaults'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { VaultStatus } from 'types/enums/vault'
import { getAssetByDenom } from 'utils/assets'
import { produceCountdown } from 'utils/formatters'
type Props = {
data: Vault[] | DepositedVault[]
isLoading?: boolean
}
export const VaultTable = (props: Props) => {
const [sorting, setSorting] = React.useState<SortingState>([{ id: 'name', desc: true }])
const columns = React.useMemo<ColumnDef<Vault | DepositedVault>[]>(() => {
return [
{
header: 'Vault',
accessorKey: 'name',
cell: ({ row }) => {
const vault = row.original as DepositedVault
const timeframe = vault.lockup.timeframe[0]
const unlockDuration = !!timeframe ? ` - (${vault.lockup.duration}${timeframe})` : ''
const status = vault.status
let remainingTime = 0
if (status === VaultStatus.UNLOCKING && vault.unlocksAt) {
remainingTime = vault.unlocksAt - Date.now()
}
return (
<div className='flex'>
<VaultLogo vault={vault} />
<TitleAndSubCell
className='ml-2 mr-2 text-left'
title={`${vault.name}${unlockDuration}`}
sub={vault.provider}
/>
{status === VaultStatus.UNLOCKING && (
<Text
className='group/label relative h-5 w-[84px] rounded-sm bg-green text-center leading-5 text-white'
size='xs'
>
<span
className={classNames(
'absolute inset-0 text-center',
'opacity-100 transition-opacity duration-500',
'group-hover/label:opacity-0 group-[.is-expanded]/row:opacity-0',
)}
>
Unlocking
</span>
<span
className={classNames(
'absolute inset-0 text-center',
'opacity-0 transition-opacity duration-500',
'group-hover/label:opacity-100 group-[.is-expanded]/row:opacity-100',
)}
>
{produceCountdown(remainingTime)}
</span>
</Text>
)}
{status === VaultStatus.UNLOCKED && (
<Text
className='h-5 w-[84px] rounded-sm bg-green text-center leading-5 text-white'
size='xs'
>
Unlocked
</Text>
)}
</div>
)
},
},
...((props.data[0] as DepositedVault)?.values
? [
{
header: 'Pos. Value',
cell: ({ row }: { row: Row<DepositedVault | Vault> }) => {
const vault = row.original as DepositedVault
const positionValue = vault.values.primary
.plus(vault.values.secondary)
.plus(vault.values.unlocking)
.plus(vault.values.unlocked)
const coin = BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, positionValue)
return <DisplayCurrency coin={coin} className='text-xs' />
},
},
]
: []),
{
accessorKey: 'apy',
header: 'APY',
cell: ({ row }) => {
const vault = row.original as DepositedVault
if (vault.apy === null) return <Loading />
return (
<FormattedNumber
amount={vault.apy ?? 0}
options={{ minDecimals: 2, maxDecimals: 2, suffix: '%' }}
className='text-xs'
animate
/>
)
},
},
{
accessorKey: 'tvl',
header: 'TVL',
cell: ({ row }) => {
const vault = row.original as DepositedVault
if (props.isLoading) return <Loading />
const coin = new BNCoin({
denom: vault.cap.denom,
amount: vault.cap.used.toString(),
})
return <DisplayCurrency coin={coin} className='text-xs' />
},
},
{
accessorKey: 'cap',
header: 'Depo. Cap',
cell: ({ row }) => {
const vault = row.original as DepositedVault
if (props.isLoading) return <Loading />
const percent = vault.cap.used
.dividedBy(vault.cap.max.multipliedBy(VAULT_DEPOSIT_BUFFER))
.multipliedBy(100)
const decimals = getAssetByDenom(vault.cap.denom)?.decimals ?? 6
return (
<TitleAndSubCell
title={
<FormattedNumber
amount={vault.cap.max.toNumber()}
options={{ minDecimals: 2, abbreviated: true, decimals }}
className='text-xs'
animate
/>
}
sub={
<FormattedNumber
amount={percent.toNumber()}
options={{ minDecimals: 2, maxDecimals: 2, suffix: '% used' }}
className='text-xs'
animate
/>
}
/>
)
},
},
{
accessorKey: 'leverage.max',
header: 'Max. Leverage',
cell: ({ row }) => {
if (props.isLoading) return <Loading />
const maxLeverage = BN_ONE.dividedBy(1 - row.original.ltv.max).toNumber()
return (
<FormattedNumber
amount={maxLeverage}
options={{ minDecimals: 2, suffix: 'x' }}
className='text-xs'
/>
)
},
},
{
accessorKey: 'details',
enableSorting: false,
header: (data) => {
const tableData = data.table.options.data as DepositedVault[]
if (tableData.length && tableData[0].status) return 'Details'
return 'Deposit'
},
cell: ({ row }) => {
const vault = row.original as DepositedVault
function enterVaultHandler() {
useStore.setState({
vaultModal: {
vault,
selectedBorrowDenoms: [vault.denoms.secondary],
isCreate: true,
},
})
}
if (props.isLoading) return <Loading />
return (
<div className='flex items-center justify-end'>
{vault.status ? (
<div className={classNames('w-4', row.getIsExpanded() && 'rotate-180')}>
<ChevronDown />
</div>
) : (
<ActionButton onClick={enterVaultHandler} color='tertiary' text='Deposit' />
)}
</div>
)
},
},
]
}, [props.data, props.isLoading])
const table = useReactTable({
data: props.data,
columns: columns,
state: {
sorting,
},
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
})
return (
<table className='w-full'>
<thead className='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',
)}
>
<div
className={classNames(
'flex',
header.id === 'name' ? 'justify-start' : 'justify-end',
'align-center',
)}
>
<span className='w-6 h-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((row) => {
if (row.getIsExpanded()) {
return (
<React.Fragment key={`${row.id}_subrow`}>
<VaultRow row={row} resetExpanded={table.resetExpanded} />
<VaultExpanded row={row} resetExpanded={table.resetExpanded} />
</React.Fragment>
)
}
return (
<VaultRow key={row.original.address} row={row} resetExpanded={table.resetExpanded} />
)
})}
</tbody>
</table>
)
}

View File

@ -1,7 +1,7 @@
import { Suspense, useMemo } from 'react'
import Card from 'components/Card'
import { VaultTable } from 'components/Earn/Farm/VaultTable'
import AvailableVaultsTable from 'components/Earn/Farm/Table/AvailableVaultsTable'
import DepositedVaultsTable from 'components/Earn/Farm/Table/DepositedVaultsTable'
import VaultUnlockBanner from 'components/Earn/Farm/VaultUnlockBanner'
import { ENV } from 'constants/env'
import { BN_ZERO } from 'constants/math'
@ -12,15 +12,10 @@ import useVaults from 'hooks/useVaults'
import { NETWORK } from 'types/enums/network'
import { VaultStatus } from 'types/enums/vault'
interface Props {
type: 'available' | 'deposited'
}
function Content(props: Props) {
function Content() {
const accountId = useAccountId()
const { data: vaults } = useVaults()
const { data: depositedVaults } = useDepositedVaults(accountId || '')
const isAvailable = props.type === 'available'
const vaultsMetaData =
ENV.NETWORK === NETWORK.TESTNET ? TESTNET_VAULTS_META_DATA : VAULTS_META_DATA
@ -44,13 +39,9 @@ function Content(props: Props) {
)
}, [vaults, depositedVaults, vaultsMetaData])
const vaultsToDisplay = isAvailable ? available : deposited
if (!vaultsToDisplay.length) return null
const unlockedVaults: DepositedVault[] = []
if (!isAvailable && depositedVaults?.length > 0) {
if (depositedVaults?.length > 0) {
depositedVaults.forEach((vault) => {
if (vault.status === VaultStatus.UNLOCKED) {
unlockedVaults.push(vault)
@ -60,13 +51,11 @@ function Content(props: Props) {
return (
<>
{!isAvailable && <VaultUnlockBanner vaults={unlockedVaults} />}
<Card
className='w-full h-fit bg-white/5'
title={isAvailable ? 'Available vaults' : 'Deposited'}
>
<VaultTable data={vaultsToDisplay} />
</Card>
<VaultUnlockBanner vaults={unlockedVaults} />
{deposited.length && (
<DepositedVaultsTable data={deposited as DepositedVault[]} isLoading={false} />
)}
{available.length && <AvailableVaultsTable data={available as Vault[]} isLoading={false} />}
</>
)
}
@ -88,25 +77,13 @@ function Fallback() {
},
}))
return (
<Card className='w-full h-fit bg-white/5' title='Available vaults'>
<VaultTable data={mockVaults} isLoading />
</Card>
)
return <AvailableVaultsTable data={mockVaults} isLoading />
}
export function AvailableVaults() {
export default function Vaults() {
return (
<Suspense fallback={<Fallback />}>
<Content type='available' />
</Suspense>
)
}
export function DepositedVaults() {
return (
<Suspense fallback={null}>
<Content type='deposited' />
<Content />
</Suspense>
)
}

View File

@ -0,0 +1,31 @@
import React from 'react'
import Button from 'components/Button'
import { PlusSquared } from 'components/Icons'
import Intro from 'components/Intro'
import { DocURL } from 'types/enums/docURL'
export default function HlsFarmIntro() {
return (
<Intro
bg='farm'
text={
<>
<span className='text-white'>Leverage farming</span> is a strategy where users borrow
funds to increase their yield farming position, aiming to earn more in rewards than the
cost of the borrowed assets.
</>
}
>
<Button
text='Learn how to Farm'
leftIcon={<PlusSquared />}
onClick={(e) => {
e.preventDefault()
window.open(DocURL.FARM_INTRO_URL, '_blank')
}}
color='secondary'
/>
</Intro>
)
}

View File

@ -0,0 +1,19 @@
import React from 'react'
import TitleAndSubCell from 'components/TitleAndSubCell'
interface Props {
strategy: HLSStrategy
}
export default function Name(props: Props) {
return (
<div className='flex'>
<TitleAndSubCell
className='ml-2 mr-2 text-left'
title={`${props.strategy.denoms.deposit}-${props.strategy.denoms.borrow}`}
sub={'Via Mars'}
/>
</div>
)
}

View File

@ -0,0 +1,17 @@
import { ColumnDef } from '@tanstack/react-table'
import { useMemo } from 'react'
import Name from 'components/HLS/Farm/Table/Columns/Name'
export default function useAvailableColumns() {
return useMemo<ColumnDef<HLSStrategy>[]>(
() => [
{
header: 'Vault',
accessorKey: 'name',
cell: ({ row }) => <Name strategy={row.original} />,
},
],
[],
)
}

View File

@ -0,0 +1,7 @@
interface Props {
data: HLSStrategy[] | DepositedHLSStrategy[]
}
export default function Index(props: Props) {
return null
}

View File

@ -0,0 +1,31 @@
import React from 'react'
import Button from 'components/Button'
import { PlusSquared } from 'components/Icons'
import Intro from 'components/Intro'
import { DocURL } from 'types/enums/docURL'
export default function HLSStakingIntro() {
return (
<Intro
bg='borrow'
text={
<>
<span className='text-white'>Leverage staking</span> is a strategy where users borrow
funds to increase their staking position, aiming to earn more in rewards than the cost of
the borrowed assets.
</>
}
>
<Button
text='Learn how to Stake'
leftIcon={<PlusSquared />}
onClick={(e) => {
e.preventDefault()
window.open(DocURL.FARM_INTRO_URL, '_blank')
}}
color='secondary'
/>
</Intro>
)
}

View File

@ -0,0 +1,130 @@
import {
ColumnDef,
flexRender,
getCoreRowModel,
getSortedRowModel,
Row,
SortingState,
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 Text from '../Text'
interface Props {
columns: ColumnDef<any>[]
data: unknown[]
initialSorting: SortingState
}
export default function Table(props: Props) {
const [sorting, setSorting] = React.useState<SortingState>(props.initialSorting)
const table = useReactTable({
data: props.data,
columns: props.columns,
state: {
sorting,
},
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
})
return (
<table className='w-full'>
<thead className='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',
)}
>
<div
className={classNames(
'flex',
header.id === 'name' ? 'justify-start' : 'justify-end',
'align-center',
)}
>
<span className='w-6 h-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((row) => {
if (row.getIsExpanded()) {
return (
<React.Fragment key={`${row.id}_subrow`}>
{getExpandedRowModel('farm', row, table.resetExpanded)}
</React.Fragment>
)
}
return getRowModel('farm', row, table.resetExpanded)
})}
</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}
/>
)
}

View File

@ -1,5 +1,5 @@
import FarmIntro from 'components/Earn/Farm/FarmIntro'
import { AvailableVaults, DepositedVaults } from 'components/Earn/Farm/Vaults'
import Vaults from 'components/Earn/Farm/Vaults'
import Tab from 'components/Earn/Tab'
import MigrationBanner from 'components/MigrationBanner'
import { EARN_TABS } from 'constants/pages'
@ -10,8 +10,7 @@ export default function FarmPage() {
<MigrationBanner />
<Tab tabs={EARN_TABS} activeTabIdx={1} />
<FarmIntro />
<DepositedVaults />
<AvailableVaults />
<Vaults />
</div>
)
}

View File

@ -1,5 +1,6 @@
import Tab from 'components/Earn/Tab'
import AvailableHLSVaults from 'components/HLS/AvailableHLSVaults'
import AvailableHLSVaults from 'components/HLS/Farm/AvailableHLSVaults'
import HlsFarmIntro from 'components/HLS/Farm/HLSFarmIntro'
import MigrationBanner from 'components/MigrationBanner'
import { HLS_TABS } from 'constants/pages'
@ -8,6 +9,7 @@ export default function HLSFarmPage() {
<div className='flex flex-wrap w-full gap-6'>
<MigrationBanner />
<Tab tabs={HLS_TABS} activeTabIdx={0} />
<HlsFarmIntro />
<AvailableHLSVaults />
</div>
)

View File

@ -1,5 +1,6 @@
import Tab from 'components/Earn/Tab'
import AvailableHlsStakingAssets from 'components/HLS/AvailableHLSStakingAssets'
import AvailableHlsStakingAssets from 'components/HLS/Staking/AvailableHLSStakingAssets'
import HLSStakingIntro from 'components/HLS/Staking/HLSStakingIntro'
import MigrationBanner from 'components/MigrationBanner'
import { HLS_TABS } from 'constants/pages'
@ -8,6 +9,7 @@ export default function HLSStakingPage() {
<div className='flex flex-wrap w-full gap-6'>
<MigrationBanner />
<Tab tabs={HLS_TABS} activeTabIdx={1} />
<HLSStakingIntro />
<AvailableHlsStakingAssets />
</div>
)

View File

@ -111,3 +111,7 @@ interface HLSStrategyNoCap {
borrow: string
}
}
interface DepositedHLSStrategy extends HLSStrategy {
depositedAmount: BigNumber
}

16005
yarn.lock

File diff suppressed because it is too large Load Diff