Mp 2880 modifying farm position (#272)

* added correct resolving of account positions

* solve rendering bug for lp amount

* bugfix: add slippage to minlpamount

* fix DisplayCurrency to accept only BNCoin

* bugfix: remove prices from store

* add basic depostied vaults table

* Farm: Added deposited table

* finish deposited table, remove featured vaults:

* enable deposit more for vaults

* use controller for vault modal

* small fixes and polishing of add deposit

* fix tests, run format

* removed empty deposited table

---------

Co-authored-by: Linkie Link <linkielink.dev@gmail.com>
This commit is contained in:
Bob van der Helm 2023-06-29 13:12:11 +02:00 committed by GitHub
parent 697e83b7cb
commit 999bad4059
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 182 additions and 108 deletions

View File

@ -22,7 +22,7 @@ jest.mock('hooks/useMarketAssets', () =>
})),
)
jest.mock('hooks/broadcast/useDepositVault', () => jest.fn(() => ({})))
jest.mock('hooks/broadcast/useDepositVault', () => jest.fn(() => ({ actions: [] })))
jest.mock('components/DisplayCurrency')

View File

@ -1,3 +1,4 @@
import { render } from '@testing-library/react'
import classNames from 'classnames'
import { ChevronDown, ChevronRight } from 'components/Icons'
@ -12,14 +13,12 @@ export interface Item {
title: string
renderContent: () => React.ReactNode
isOpen?: boolean
subTitle?: string | React.ReactNode
renderSubTitle: () => React.ReactNode
toggleOpen: (index: number) => void
}
export default function AccordionContent(props: Props) {
const { title, renderContent, isOpen, subTitle, toggleOpen } = props.item
const shouldShowSubTitle = subTitle && !isOpen
const { title, renderContent, isOpen, renderSubTitle, toggleOpen } = props.item
return (
<div key={title} className='group border-b-white/10 [&:not(:last-child)]:border-b'>
@ -34,11 +33,9 @@ export default function AccordionContent(props: Props) {
>
<div>
<Text>{title}</Text>
{shouldShowSubTitle && (
<Text size='xs' className='mt-1 text-white/60'>
{subTitle}
</Text>
)}
<Text size='xs' className='mt-1 text-white/60'>
{renderSubTitle()}
</Text>
</div>
<div className='block pr-1 group-[[open]]:hidden'>
{isOpen ? <ChevronDown /> : <ChevronRight />}

View File

@ -54,6 +54,7 @@ export default function AccountSummary(props: Props) {
) : null,
isOpen: isOpen[0],
toggleOpen: (index: number) => toggleOpen(index),
renderSubTitle: () => <></>,
},
{
title: 'Balances',
@ -61,6 +62,7 @@ export default function AccountSummary(props: Props) {
props.account ? <AccountBalancesTable data={props.account} /> : null,
isOpen: isOpen[1],
toggleOpen: (index: number) => toggleOpen(index),
renderSubTitle: () => <></>,
},
]}
allowMultipleOpen

View File

@ -1,6 +1,6 @@
import { Row } from '@tanstack/react-table'
import Button from 'components/Button'
import Button from 'components/Button'
import { LockUnlocked, Plus } from 'components/Icons'
import useStore from 'store'
@ -19,6 +19,16 @@ export default function VaultExpanded(props: Props) {
})
}
function depositMoreHandler() {
useStore.setState({
vaultModal: {
vault: props.row.original,
isDeposited: true,
selectedBorrowDenoms: [props.row.original.denoms.secondary],
},
})
}
let isDeposited: boolean = false
if ((props.row.original as DepositedVault)?.amounts) {
isDeposited = true
@ -39,7 +49,11 @@ export default function VaultExpanded(props: Props) {
<div className='align-center flex justify-end gap-3 p-4'>
{isDeposited ? (
<>
<Button color='secondary' leftIcon={<Plus className='w-3' />}>
<Button
onClick={depositMoreHandler}
color='secondary'
leftIcon={<Plus className='w-3' />}
>
Deposit more
</Button>
<Button color='tertiary' leftIcon={<LockUnlocked />}>

View File

@ -76,3 +76,21 @@ function Fallback() {
return <VaultTable data={mockVaults} isLoading />
}
export function AvailableVaults() {
return (
<Card className='h-fit w-full bg-white/5' title='Available vaults'>
<Suspense fallback={<Fallback />}>
<Content type='available' />
</Suspense>
</Card>
)
}
export function DepositedVaults() {
return (
<Suspense fallback={null}>
<Content type='deposited' />
</Suspense>
)
}

View File

@ -1,4 +1,4 @@
import VaultModal from 'components/Modals/Vault/VaultModal'
import VaultModal from 'components/Modals/Vault'
import BorrowModal from 'components/Modals/Borrow/BorrowModal'
import FundAndWithdrawModal from 'components/Modals/FundWithdraw/FundAndWithdrawModal'
import AddVaultBorrowAssetsModal from 'components/Modals/AddVaultAssets/AddVaultBorrowAssetsModal'
@ -11,7 +11,6 @@ export default function ModalsContainer() {
<VaultModal />
<BorrowModal />
<FundAndWithdrawModal />
<VaultModal />
<AddVaultBorrowAssetsModal />
<UnlockModal />
<LendAndReclaimModalController />

View File

@ -40,6 +40,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
const baseCurrency = useStore((s) => s.baseCurrency)
const vaultModal = useStore((s) => s.vaultModal)
const depositIntoVault = useStore((s) => s.depositIntoVault)
const [isConfirming, setIsConfirming] = useState(false)
const { actions: depositActions, fee: depositFee } = useDepositVault({
vault: props.vault,
@ -149,8 +150,17 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
})
}
function onConfirm() {
depositIntoVault({ fee: depositFee, accountId: props.account.id, actions: depositActions })
async function onConfirm() {
setIsConfirming(true)
const isSuccess = await depositIntoVault({
fee: depositFee,
accountId: props.account.id,
actions: depositActions,
})
setIsConfirming(false)
if (isSuccess) {
useStore.setState({ vaultModal: null })
}
}
return (
@ -207,7 +217,14 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
)
})}
</div>
<Button onClick={onConfirm} color='primary' text='Deposit' rightIcon={<ArrowRight />} />
<Button
onClick={onConfirm}
color='primary'
text='Deposit'
rightIcon={<ArrowRight />}
showProgressIndicator={isConfirming}
disabled={!depositActions.length}
/>
</div>
)
}

View File

@ -50,12 +50,12 @@ export default function VaultDeposit(props: Props) {
[primaryValue, secondaryValue],
)
const primaryValuePercentage = useMemo(
() => primaryValue.div(totalValue).times(100).decimalPlaces(2).toNumber() || 50,
[primaryValue, totalValue],
)
const primaryValuePercentage = useMemo(() => {
const value = primaryValue.div(totalValue).times(100).decimalPlaces(2).toNumber()
return isNaN(value) ? 50 : value
}, [primaryValue, totalValue])
const secondaryValuePercentage = useMemo(
() => new BigNumber(100).minus(primaryValuePercentage).decimalPlaces(2).toNumber() || 50,
() => new BigNumber(100).minus(primaryValuePercentage).integerValue(2).toNumber() ?? 50,
[primaryValuePercentage],
)

View File

@ -1,52 +0,0 @@
import VaultLogo from 'components/Earn/vault/VaultLogo'
import Modal from 'components/Modal'
import Text from 'components/Text'
import { ASSETS } from 'constants/assets'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useStore from 'store'
import { CircularProgress } from 'components/CircularProgress'
import VaultModalContent from 'components/Modals/Vault/VaultModalContent'
export default function VaultModal() {
const currentAccount = useCurrentAccount()
const modal = useStore((s) => s.vaultModal)
const primaryAsset =
ASSETS.find((asset) => asset.denom === modal?.vault.denoms.primary) ?? ASSETS[0]
const secondaryAsset =
ASSETS.find((asset) => asset.denom === modal?.vault.denoms.secondary) ?? ASSETS[0]
const hasValidData = primaryAsset && currentAccount && secondaryAsset
function onClose() {
useStore.setState({ vaultModal: null })
}
return (
<Modal
open={!!(modal && hasValidData)}
onClose={onClose}
header={
modal && (
<span className='flex items-center gap-4 px-4'>
<VaultLogo vault={modal.vault} />
<Text>{`${modal.vault.symbols.primary} - ${modal.vault.symbols.secondary}`}</Text>
</span>
)
}
headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b'
contentClassName='flex flex-col'
>
{modal?.vault && currentAccount ? (
<VaultModalContent
vault={modal.vault}
primaryAsset={primaryAsset}
secondaryAsset={secondaryAsset}
account={currentAccount}
/>
) : (
<CircularProgress />
)}
</Modal>
)
}

View File

@ -13,10 +13,11 @@ import VaultDepositSubTitle from 'components/Modals/Vault/VaultDepositsSubTitle'
import { BNCoin } from 'types/classes/BNCoin'
interface Props {
vault: Vault
vault: Vault | DepositedVault
primaryAsset: Asset
secondaryAsset: Asset
account: Account
isDeposited?: boolean
}
export default function VaultModalContent(props: Props) {
@ -55,6 +56,31 @@ export default function VaultModalContent(props: Props) {
[setIsCustomRatio],
)
function getDepositSubTitle() {
if (isOpen[0] && props.isDeposited)
return 'The amounts you enter below will be added to your current position.'
if (isOpen[0]) return null
return (
<VaultDepositSubTitle
primaryAmount={primaryAmount}
secondaryAmount={secondaryAmount}
primaryAsset={props.primaryAsset}
secondaryAsset={props.secondaryAsset}
/>
)
}
function getBorrowingsSubTitle() {
if (isOpen[1] && props.isDeposited)
return 'The amounts you enter below will be added to your current position.'
if (isOpen[1]) return null
return <VaultBorrowingsSubTitle borrowings={borrowings} />
}
return (
<div className='flex flex-grow items-start gap-6 p-6'>
<Accordion
@ -76,14 +102,7 @@ export default function VaultModalContent(props: Props) {
/>
),
title: 'Deposit',
subTitle: (
<VaultDepositSubTitle
primaryAmount={primaryAmount}
secondaryAmount={secondaryAmount}
primaryAsset={props.primaryAsset}
secondaryAsset={props.secondaryAsset}
/>
),
renderSubTitle: getDepositSubTitle,
isOpen: isOpen[0],
toggleOpen: (index: number) => toggleOpen(index),
},
@ -102,7 +121,7 @@ export default function VaultModalContent(props: Props) {
/>
),
title: 'Borrow',
subTitle: <VaultBorrowingsSubTitle borrowings={borrowings} />,
renderSubTitle: getBorrowingsSubTitle,
isOpen: isOpen[1],
toggleOpen: (index: number) => toggleOpen(index),
},

View File

@ -0,0 +1,61 @@
import VaultLogo from 'components/Earn/vault/VaultLogo'
import Modal from 'components/Modal'
import Text from 'components/Text'
import { ASSETS } from 'constants/assets'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useStore from 'store'
import VaultModalContent from 'components/Modals/Vault/VaultModalContent'
export default function VaultModalController() {
const currentAccount = useCurrentAccount()
const modal = useStore((s) => s.vaultModal)
const primaryAsset = ASSETS.find((asset) => asset.denom === modal?.vault.denoms.primary)
const secondaryAsset = ASSETS.find((asset) => asset.denom === modal?.vault.denoms.secondary)
if (!modal || !currentAccount || !primaryAsset || !secondaryAsset) return null
return (
<VaultModal
currentAccount={currentAccount}
modal={modal}
primaryAsset={primaryAsset}
secondaryAsset={secondaryAsset}
/>
)
}
interface Props {
currentAccount: Account
modal: VaultModal
primaryAsset: Asset
secondaryAsset: Asset
}
function VaultModal(props: Props) {
function onClose() {
useStore.setState({ vaultModal: null })
}
return (
<Modal
open={true}
onClose={onClose}
header={
<span className='flex items-center gap-4 px-4'>
<VaultLogo vault={props.modal.vault} />
<Text>{`${props.modal.vault.symbols.primary} - ${props.modal.vault.symbols.secondary}`}</Text>
</span>
}
headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b'
contentClassName='flex flex-col'
>
<VaultModalContent
vault={props.modal.vault}
primaryAsset={props.primaryAsset}
secondaryAsset={props.secondaryAsset}
account={props.currentAccount}
isDeposited={props.modal.isDeposited}
/>
</Modal>
)
}

View File

@ -157,6 +157,7 @@ export default function NumberInput(props: Props) {
props.className,
)}
style={props.style}
placeholder='0'
/>
)
}

View File

@ -24,36 +24,31 @@ export default function useDepositVault(props: Props): { actions: Action[]; fee:
const { data: prices } = usePrices()
const slippage = useStore((s) => s.slippage)
const borrowings: BNCoin[] = useMemo(
() => props.borrowings.filter((borrowing) => borrowing.amount.gt(0)),
[props.borrowings],
)
const deposits: BNCoin[] = useMemo(
() => props.deposits.filter((deposit) => deposit.amount.gt(0)),
[props.deposits],
)
const debouncedGetMinLpToReceive = useMemo(() => debounce(getMinLpToReceive, 500), [])
const { primaryCoin, secondaryCoin, totalValue } = useMemo(
() =>
getVaultDepositCoinsAndValue(
props.vault,
props.deposits.filter((borrowing) => borrowing.amount.gt(0)),
props.borrowings.filter((borrowing) => borrowing.amount.gt(0)),
prices,
),
[props.deposits, props.borrowings, props.vault, prices],
() => getVaultDepositCoinsAndValue(props.vault, deposits, borrowings, prices),
[deposits, borrowings, props.vault, prices],
)
const borrowActions: Action[] = useMemo(() => {
return props.borrowings.map((bnCoin) => ({
return borrowings.map((bnCoin) => ({
borrow: bnCoin.toCoin(),
}))
}, [props.borrowings])
}, [borrowings])
const swapActions: Action[] = useMemo(
() =>
getVaultSwapActions(
props.vault,
props.deposits.filter((borrowing) => borrowing.amount.gt(0)),
props.borrowings.filter((borrowing) => borrowing.amount.gt(0)),
prices,
slippage,
totalValue,
),
[totalValue, prices, props.vault, props.deposits, props.borrowings, slippage],
() => getVaultSwapActions(props.vault, deposits, borrowings, prices, slippage, totalValue),
[totalValue, prices, props.vault, deposits, borrowings, slippage],
)
useMemo(async () => {
@ -77,6 +72,8 @@ export default function useDepositVault(props: Props): { actions: Action[]; fee:
])
const enterVaultActions: Action[] = useMemo(() => {
if (primaryCoin.amount.isZero() || secondaryCoin.amount.isZero()) return []
return getEnterVaultActions(props.vault, primaryCoin, secondaryCoin, minLpToReceive)
}, [props.vault, primaryCoin, secondaryCoin, minLpToReceive])

View File

@ -1,13 +1,13 @@
import Tab from 'components/Earn/Tab'
import Vaults from 'components/Earn/vault/Vaults'
import { AvailableVaults, DepositedVaults } from 'components/Earn/vault/Vaults'
export default function FarmPage() {
return (
<>
<Tab isFarm />
{/* <FeaturedVaults /> */}
<Vaults type='deposited' />
<Vaults type='available' />
<DepositedVaults />
<AvailableVaults />
</>
)
}

View File

@ -23,7 +23,8 @@ interface BorrowModal {
}
interface VaultModal {
vault: Vault
vault: Vault | DepositedVault
isDeposited?: boolean
selectedBorrowDenoms: string[]
}