Mp 3330 compare accounts via use updated account for vaults (#415)

* feat: added simulateTrade

* MP-3330: added vault positions to the useUpdated Account

* tidy: format

* tidy: refactor

* Health indicator change preview (#410)

* fix: adjusted the AccountDetail width

* feat: added health indicator updates

* Update src/components/Account/AccountDetails.tsx

Co-authored-by: Yusuf Seyrek <yusufseyrek@users.noreply.github.com>

* fix: created a function for the back- and foregroundColors

* fix: updated tailwind conf

* fix: fixed the useHealthColorAndLabel function

---------

Co-authored-by: Yusuf Seyrek <yusufseyrek@users.noreply.github.com>
Co-authored-by: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com>

* fix: added updatedAccount to AccountSummary (#414)

Co-authored-by: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com>

* fix: added a new way of calulating values to vaults

* fix: refactored the displayCurrency

* fix: fixed the subtitles, to not nest <p> tags

* MP-3330: added comparison to the vault mechanics

* fix: fixed tests

* fix: updated the borrow slider percentage on vaults

* fix: addressed change request

* update displayValue stuff

* fixed wrong display conversions

* fix: fixed the display price and renamed getDepositsAndLendsAfterCoinSpent

* fix test and update DisplayCurrency

* tidy: refactor

* tidy: rename method

---------

Co-authored-by: Yusuf Seyrek <yusufseyrek@users.noreply.github.com>
Co-authored-by: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com>
This commit is contained in:
Linkie Link 2023-09-05 09:25:57 +02:00 committed by GitHub
parent b7019023f0
commit f1ee4bd7f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 423 additions and 283 deletions

View File

@ -59,6 +59,7 @@ describe('<VaultBorrowings />', () => {
onChangeBorrowings: jest.fn(), onChangeBorrowings: jest.fn(),
depositActions: [], depositActions: [],
depositCapReachedCoins: [], depositCapReachedCoins: [],
displayCurrency: 'uosmo',
} }
beforeAll(() => { beforeAll(() => {
@ -80,7 +81,7 @@ describe('<VaultBorrowings />', () => {
it('should render DisplayCurrency correctly', () => { it('should render DisplayCurrency correctly', () => {
expect(mockedDisplayCurrency).toHaveBeenCalledTimes(1) expect(mockedDisplayCurrency).toHaveBeenCalledTimes(1)
expect(mockedDisplayCurrency).toHaveBeenCalledWith( expect(mockedDisplayCurrency).toHaveBeenCalledWith(
{ coin: new BNCoin({ denom: 'uosmo', amount: '0' }) }, { coin: new BNCoin({ denom: 'usd', amount: '0' }) },
expect.anything(), expect.anything(),
) )
}) })

View File

@ -32,9 +32,7 @@ export default function AccordionContent(props: Props) {
> >
<div> <div>
<Text>{title}</Text> <Text>{title}</Text>
<Text size='xs' className='mt-1 text-white/60'> {renderSubTitle()}
{renderSubTitle()}
</Text>
</div> </div>
<div className='block pr-1 group-[[open]]:hidden'> <div className='block pr-1 group-[[open]]:hidden'>
{isOpen ? <ChevronDown className='h-1.5' /> : <ChevronRight className='w-1.5' />} {isOpen ? <ChevronDown className='h-1.5' /> : <ChevronRight className='w-1.5' />}

View File

@ -17,20 +17,20 @@ import { FormattedNumber } from 'components/FormattedNumber'
import { SortAsc, SortDesc, SortNone } from 'components/Icons' import { SortAsc, SortDesc, SortNone } from 'components/Icons'
import Text from 'components/Text' import Text from 'components/Text'
import { ASSETS } from 'constants/assets' import { ASSETS } from 'constants/assets'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings' import { BN_ZERO } from 'constants/math'
import { DISPLAY_CURRENCY_KEY } from 'constants/localStore'
import useCurrentAccount from 'hooks/useCurrentAccount' import useCurrentAccount from 'hooks/useCurrentAccount'
import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices' import usePrices from 'hooks/usePrices'
import useStore from 'store' import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
import { getAssetByDenom } from 'utils/assets' import { getAssetByDenom } from 'utils/assets'
import { convertLiquidityRateToAPR, convertToDisplayAmount, demagnify } from 'utils/formatters' import { convertLiquidityRateToAPR, demagnify, getCoinValue } from 'utils/formatters'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
import { convertAprToApy } from 'utils/parsers' import { convertAprToApy } from 'utils/parsers'
import { getPage, getRoute } from 'utils/route' import { getPage, getRoute } from 'utils/route'
import { ORACLE_DENOM } from '../../constants/oracle'
interface Props { interface Props {
account: Account account: Account
lendingData: LendingMarketTableData[] lendingData: LendingMarketTableData[]
@ -38,11 +38,20 @@ interface Props {
tableBodyClassName?: string tableBodyClassName?: string
} }
interface PositionValue {
type: 'deposits' | 'borrowing' | 'lending'
symbol: string
size: number
value: string
denom: string
amount: BigNumber
apy: number
}
function calculatePositionValues( function calculatePositionValues(
type: 'deposits' | 'borrowing' | 'lending', type: 'deposits' | 'borrowing' | 'lending',
asset: Asset, asset: Asset,
prices: BNCoin[], prices: BNCoin[],
displayCurrencyDenom: string,
position: BNCoin, position: BNCoin,
apy: number, apy: number,
) { ) {
@ -51,22 +60,29 @@ function calculatePositionValues(
type, type,
symbol: asset.symbol, symbol: asset.symbol,
size: demagnify(amount, asset), size: demagnify(amount, asset),
value: convertToDisplayAmount( value: getCoinValue(BNCoin.fromDenomAndBigNumber(denom, amount), prices).toString(),
BNCoin.fromDenomAndBigNumber(denom, amount),
displayCurrencyDenom,
prices,
).toString(),
denom, denom,
amount: type === 'borrowing' ? amount.negated() : amount, amount: type === 'borrowing' ? amount.negated() : amount,
apy, apy,
} }
} }
function calculateVaultValues(vault: DepositedVault, apy: number) {
const { name } = vault
const totalValue = vault.values.primary.plus(vault.values.secondary)
return {
type: 'vault',
symbol: name,
size: 0,
value: totalValue.toString(),
denom: vault.denoms.lp,
amount: BN_ZERO,
apy,
}
}
export default function AccountBalancesTable(props: Props) { export default function AccountBalancesTable(props: Props) {
const [displayCurrency] = useLocalStorage<string>(
DISPLAY_CURRENCY_KEY,
DEFAULT_SETTINGS.displayCurrency,
)
const { data: prices } = usePrices() const { data: prices } = usePrices()
const currentAccount = useCurrentAccount() const currentAccount = useCurrentAccount()
const navigate = useNavigate() const navigate = useNavigate()
@ -77,31 +93,38 @@ export default function AccountBalancesTable(props: Props) {
const accountDeposits = props.account?.deposits ?? [] const accountDeposits = props.account?.deposits ?? []
const accountLends = props.account?.lends ?? [] const accountLends = props.account?.lends ?? []
const accountDebts = props.account?.debts ?? [] const accountDebts = props.account?.debts ?? []
const accountVaults = props.account?.vaults ?? []
const deposits = accountDeposits.map((deposit) => { const deposits: PositionValue[] = []
const asset = ASSETS.find((asset) => asset.denom === deposit.denom) ?? ASSETS[0] accountDeposits.forEach((deposit) => {
const asset = ASSETS.find(byDenom(deposit.denom))
if (!asset) return
const apy = 0 const apy = 0
return calculatePositionValues('deposits', asset, prices, displayCurrency, deposit, apy) deposits.push(calculatePositionValues('deposits', asset, prices, deposit, apy))
}) })
const lends = accountLends.map((lending) => { const lends = accountLends.map((lending) => {
const asset = ASSETS.find((asset) => asset.denom === lending.denom) ?? ASSETS[0] const asset = ASSETS.find(byDenom(lending.denom)) ?? ASSETS[0]
const apr = convertLiquidityRateToAPR( const apr = convertLiquidityRateToAPR(
props.lendingData.find((market) => market.asset.denom === lending.denom) props.lendingData.find((market) => market.asset.denom === lending.denom)
?.marketLiquidityRate ?? 0, ?.marketLiquidityRate ?? 0,
) )
const apy = convertAprToApy(apr, 365) const apy = convertAprToApy(apr, 365)
return calculatePositionValues('lending', asset, prices, displayCurrency, lending, apy) return calculatePositionValues('lending', asset, prices, lending, apy)
})
const vaults = accountVaults.map((vault) => {
const apy = (vault.apy ?? 0) * 100
return calculateVaultValues(vault, apy)
}) })
const debts = accountDebts.map((debt) => { const debts = accountDebts.map((debt) => {
const asset = ASSETS.find(byDenom(debt.denom)) ?? ASSETS[0] const asset = ASSETS.find(byDenom(debt.denom)) ?? ASSETS[0]
const apy = const apy =
props.borrowingData.find((market) => market.asset.denom === debt.denom)?.borrowRate ?? 0 props.borrowingData.find((market) => market.asset.denom === debt.denom)?.borrowRate ?? 0
return calculatePositionValues('borrowing', asset, prices, displayCurrency, debt, apy * -100) return calculatePositionValues('borrowing', asset, prices, debt, apy * -100)
}) })
return [...deposits, ...lends, ...vaults, ...debts]
return [...deposits, ...lends, ...debts] }, [prices, props.account, props.borrowingData, props.lendingData])
}, [displayCurrency, prices, props.account, props.borrowingData, props.lendingData])
const columns = React.useMemo<ColumnDef<AccountBalanceRow>[]>( const columns = React.useMemo<ColumnDef<AccountBalanceRow>[]>(
() => [ () => [
@ -114,6 +137,7 @@ export default function AccountBalancesTable(props: Props) {
<Text size='xs'> <Text size='xs'>
{row.original.symbol} {row.original.symbol}
{row.original.type === 'lending' && <span className='ml-1 text-profit'>(lent)</span>} {row.original.type === 'lending' && <span className='ml-1 text-profit'>(lent)</span>}
{row.original.type === 'vault' && <span className='ml-1 text-profit'>(farm)</span>}
</Text> </Text>
) )
}, },
@ -124,8 +148,8 @@ export default function AccountBalancesTable(props: Props) {
id: 'value', id: 'value',
cell: ({ row }) => { cell: ({ row }) => {
const coin = new BNCoin({ const coin = new BNCoin({
denom: row.original.denom, denom: ORACLE_DENOM,
amount: row.original.amount.toString(), amount: row.original.value.toString(),
}) })
return <DisplayCurrency coin={coin} className='text-xs text-right' /> return <DisplayCurrency coin={coin} className='text-xs text-right' />
}, },
@ -135,6 +159,8 @@ export default function AccountBalancesTable(props: Props) {
accessorKey: 'size', accessorKey: 'size',
header: 'Size', header: 'Size',
cell: ({ row }) => { cell: ({ row }) => {
if (row.original.amount.isEqualTo(BN_ZERO))
return <span className='w-full text-xs text-center'>&ndash;</span>
const amount = demagnify( const amount = demagnify(
row.original.amount, row.original.amount,
getAssetByDenom(row.original.denom) ?? ASSETS[0], getAssetByDenom(row.original.denom) ?? ASSETS[0],
@ -154,7 +180,7 @@ export default function AccountBalancesTable(props: Props) {
accessorKey: 'apy', accessorKey: 'apy',
header: 'APY', header: 'APY',
cell: ({ row }) => { cell: ({ row }) => {
if (row.original.apy === 0) if (row.original.type === 'deposit')
return <span className='w-full text-xs text-center'>&ndash;</span> return <span className='w-full text-xs text-center'>&ndash;</span>
return ( return (
<FormattedNumber <FormattedNumber
@ -210,7 +236,7 @@ export default function AccountBalancesTable(props: Props) {
<thead className='border-b border-white/5'> <thead className='border-b border-white/5'>
{table.getHeaderGroups().map((headerGroup) => ( {table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}> <tr key={headerGroup.id}>
{headerGroup.headers.map((header, index) => { {headerGroup.headers.map((header) => {
return ( return (
<th <th
key={header.id} key={header.id}
@ -262,7 +288,7 @@ export default function AccountBalancesTable(props: Props) {
<td <td
key={cell.id} key={cell.id}
className={classNames( className={classNames(
cell.column.id === 'symbol' ? `border-l ${borderClass}` : 'pl-4 text-right', cell.column.id === 'symbol' ? `border-l ${borderClass}` : 'text-right',
'p-2', 'p-2',
)} )}
> >

View File

@ -51,12 +51,12 @@ function AccountDetails(props: Props) {
const { health: updatedHealth } = useHealthComputer(updatedAccount || account) const { health: updatedHealth } = useHealthComputer(updatedAccount || account)
const { data: prices } = usePrices() const { data: prices } = usePrices()
const accountBalanceValue = useMemo( const accountBalanceValue = useMemo(
() => calculateAccountBalanceValue(updatedAccount ? updatedAccount : account, prices), () => calculateAccountBalanceValue(updatedAccount ?? account, prices),
[updatedAccount, account, prices], [updatedAccount, account, prices],
) )
const coin = BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, accountBalanceValue) const coin = BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, accountBalanceValue)
const leverage = useMemo( const leverage = useMemo(
() => calculateAccountLeverage(updatedAccount ? updatedAccount : account, prices), () => calculateAccountLeverage(updatedAccount ?? account, prices),
[account, updatedAccount, prices], [account, updatedAccount, prices],
) )
const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } = const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } =

View File

@ -8,7 +8,9 @@ import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices' import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { getDisplayCurrencies } from 'utils/assets' import { getDisplayCurrencies } from 'utils/assets'
import { convertToDisplayAmount } from 'utils/formatters' import { getCoinValue } from 'utils/formatters'
import { BN } from 'utils/helpers'
import { ORACLE_DENOM } from 'constants/oracle'
interface Props { interface Props {
coin: BNCoin coin: BNCoin
@ -39,13 +41,27 @@ export default function DisplayCurrency(props: Props) {
? '' ? ''
: ` ${displayCurrencyAsset.symbol ? ` ${displayCurrencyAsset.symbol}` : ''}` : ` ${displayCurrencyAsset.symbol ? ` ${displayCurrencyAsset.symbol}` : ''}`
const amount = useMemo(() => {
const coinValue = getCoinValue(props.coin, prices)
if (displayCurrency === ORACLE_DENOM) return coinValue.toNumber()
const displayDecimals = displayCurrencyAsset.decimals
const displayPrice = getCoinValue(
BNCoin.fromDenomAndBigNumber(displayCurrency, BN(1).shiftedBy(displayDecimals)),
prices,
)
return coinValue.div(displayPrice).toNumber()
}, [displayCurrency, displayCurrencyAsset.decimals, prices, props.coin])
return ( return (
<FormattedNumber <FormattedNumber
className={classNames( className={classNames(
props.className, props.className,
props.parentheses && 'before:content-["("] after:content-[")"]', props.parentheses && 'before:content-["("] after:content-[")"]',
)} )}
amount={convertToDisplayAmount(props.coin, displayCurrency, prices).toNumber()} amount={amount}
options={{ options={{
minDecimals: isUSD ? 2 : 0, minDecimals: isUSD ? 2 : 0,
maxDecimals: 2, maxDecimals: 2,

View File

@ -6,7 +6,7 @@ import useVaults from 'hooks/useVaults'
function Content() { function Content() {
const { data: vaults } = useVaults() const { data: vaults } = useVaults()
if (!vaults) return null
const featuredVaults = vaults.filter((vault) => vault.isFeatured) const featuredVaults = vaults.filter((vault) => vault.isFeatured)
if (!featuredVaults.length) return null if (!featuredVaults.length) return null
@ -14,7 +14,7 @@ function Content() {
return ( return (
<Card <Card
title='Featured vaults' title='Featured vaults'
className='mb-4 h-fit w-full bg-white/5' className='w-full mb-4 h-fit bg-white/5'
contentClassName='grid grid-cols-3' contentClassName='grid grid-cols-3'
> >
{featuredVaults.map((vault) => ( {featuredVaults.map((vault) => (

View File

@ -5,11 +5,11 @@ import Card from 'components/Card'
import { VaultTable } from 'components/Earn/Farm/VaultTable' import { VaultTable } from 'components/Earn/Farm/VaultTable'
import VaultUnlockBanner from 'components/Earn/Farm/VaultUnlockBanner' import VaultUnlockBanner from 'components/Earn/Farm/VaultUnlockBanner'
import { IS_TESTNET } from 'constants/env' import { IS_TESTNET } from 'constants/env'
import { BN_ZERO } from 'constants/math'
import { TESTNET_VAULTS_META_DATA, VAULTS_META_DATA } from 'constants/vaults' import { TESTNET_VAULTS_META_DATA, VAULTS_META_DATA } from 'constants/vaults'
import useDepositedVaults from 'hooks/useDepositedVaults' import useDepositedVaults from 'hooks/useDepositedVaults'
import useVaults from 'hooks/useVaults' import useVaults from 'hooks/useVaults'
import { VaultStatus } from 'types/enums/vault' import { VaultStatus } from 'types/enums/vault'
import { BN_ZERO } from 'constants/math'
interface Props { interface Props {
type: 'available' | 'deposited' type: 'available' | 'deposited'
@ -26,6 +26,7 @@ function Content(props: Props) {
const { deposited, available } = useMemo(() => { const { deposited, available } = useMemo(() => {
return vaultsMetaData.reduce( return vaultsMetaData.reduce(
(prev: { deposited: DepositedVault[]; available: Vault[] }, curr) => { (prev: { deposited: DepositedVault[]; available: Vault[] }, curr) => {
if (!vaults) return prev
const vault = vaults.find((vault) => vault.address === curr.address) const vault = vaults.find((vault) => vault.address === curr.address)
const depositedVault = depositedVaults?.find((vault) => vault.address === curr.address) const depositedVault = depositedVaults?.find((vault) => vault.address === curr.address)
@ -59,7 +60,7 @@ function Content(props: Props) {
<> <>
{!isAvailable && <VaultUnlockBanner vaults={unlockedVaults} />} {!isAvailable && <VaultUnlockBanner vaults={unlockedVaults} />}
<Card <Card
className='h-fit w-full bg-white/5' className='w-full h-fit bg-white/5'
title={isAvailable ? 'Available vaults' : 'Deposited'} title={isAvailable ? 'Available vaults' : 'Deposited'}
> >
<VaultTable data={vaultsToDisplay} /> <VaultTable data={vaultsToDisplay} />
@ -85,7 +86,7 @@ function Fallback() {
})) }))
return ( return (
<Card className='h-fit w-full bg-white/5' title='Available vaults'> <Card className='w-full h-fit bg-white/5' title='Available vaults'>
<VaultTable data={mockVaults} isLoading /> <VaultTable data={mockVaults} isLoading />
</Card> </Card>
) )

View File

@ -9,10 +9,12 @@ import AssetListTable from 'components/MarketAssetTable'
import MarketAssetTableRow from 'components/MarketAssetTable/MarketAssetTableRow' import MarketAssetTableRow from 'components/MarketAssetTable/MarketAssetTableRow'
import MarketDetails from 'components/MarketAssetTable/MarketDetails' import MarketDetails from 'components/MarketAssetTable/MarketDetails'
import TitleAndSubCell from 'components/TitleAndSubCell' import TitleAndSubCell from 'components/TitleAndSubCell'
import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice'
import { convertLiquidityRateToAPR, demagnify } from 'utils/formatters' import { convertLiquidityRateToAPR, demagnify } from 'utils/formatters'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
import { BN_ZERO } from '../../../constants/math'
import AmountAndValue from '../../AmountAndValue'
interface Props { interface Props {
title: string title: string
data: LendingMarketTableData[] data: LendingMarketTableData[]
@ -20,7 +22,6 @@ interface Props {
export default function LendingMarketsTable(props: Props) { export default function LendingMarketsTable(props: Props) {
const { title, data } = props const { title, data } = props
const { symbol: displayCurrencySymbol } = useDisplayCurrencyPrice()
const shouldShowAccountDeposit = !!data[0]?.accountLentValue const shouldShowAccountDeposit = !!data[0]?.accountLentValue
const rowRenderer = useCallback( const rowRenderer = useCallback(
@ -49,12 +50,12 @@ export default function LendingMarketsTable(props: Props) {
const asset = row.original.asset const asset = row.original.asset
return ( return (
<div className='flex flex-1 items-center gap-3'> <div className='flex items-center flex-1 gap-3'>
<AssetImage asset={asset} size={32} /> <AssetImage asset={asset} size={32} />
<TitleAndSubCell <TitleAndSubCell
title={asset.symbol} title={asset.symbol}
sub={asset.name} sub={asset.name}
className='min-w-15 text-left' className='text-left min-w-15'
/> />
</div> </div>
) )
@ -66,14 +67,12 @@ export default function LendingMarketsTable(props: Props) {
accessorKey: 'accountDepositValue', accessorKey: 'accountDepositValue',
header: 'Deposited', header: 'Deposited',
cell: ({ row }) => { cell: ({ row }) => {
const accountDepositValue = row.original.accountLentValue as BigNumber const amount = row.original.accountLentAmount
return ( return (
<FormattedNumber <AmountAndValue
className='text-xs' asset={row.original.asset}
animate amount={amount ? BN(amount) : BN_ZERO}
amount={accountDepositValue.toNumber()}
options={{ suffix: ` ${displayCurrencySymbol}` }}
/> />
) )
}, },
@ -135,7 +134,7 @@ export default function LendingMarketsTable(props: Props) {
), ),
}, },
], ],
[displayCurrencySymbol, shouldShowAccountDeposit], [shouldShowAccountDeposit],
) )
return <AssetListTable title={title} rowRenderer={rowRenderer} columns={columns} data={data} /> return <AssetListTable title={title} rowRenderer={rowRenderer} columns={columns} data={data} />

View File

@ -12,6 +12,7 @@ interface Props {
options?: FormatOptions options?: FormatOptions
className?: string className?: string
animate?: boolean animate?: boolean
parentheses?: boolean
} }
export const FormattedNumber = React.memo( export const FormattedNumber = React.memo(
@ -39,13 +40,25 @@ export const FormattedNumber = React.memo(
reduceMotion reduceMotion
) )
return ( return (
<p className={classNames('number', props.className)}> <p
className={classNames(
'number',
props.parentheses && 'before:content-["("] after:content-[")"]',
props.className,
)}
>
{formatValue(props.amount.toString(), props.options)} {formatValue(props.amount.toString(), props.options)}
</p> </p>
) )
return ( return (
<animated.p className={classNames('number', props.className)}> <animated.p
className={classNames(
'number',
props.parentheses && 'before:content-["("] after:content-[")"]',
props.className,
)}
>
{springAmount.number.to((num) => formatValue(num, props.options))} {springAmount.number.to((num) => formatValue(num, props.options))}
</animated.p> </animated.p>
) )

View File

@ -133,7 +133,7 @@ export default function AssetSelectTable(props: Props) {
<td <td
key={cell.id} key={cell.id}
className={classNames( className={classNames(
cell.column.id === 'select' ? `` : 'pl-4 text-right', cell.column.id === 'select' ? `` : 'text-right',
'px-4 py-3', 'px-4 py-3',
)} )}
> >

View File

@ -20,7 +20,7 @@ import useCurrentAccount from 'hooks/useCurrentAccount'
import useHealthComputer from 'hooks/useHealthComputer' import useHealthComputer from 'hooks/useHealthComputer'
import useToggle from 'hooks/useToggle' import useToggle from 'hooks/useToggle'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount' import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import { getDepositsAndLendsAfterCoinSpent } from 'hooks/useUpdatedAccount/functions' import { getDepositAndLendCoinsToSpend } from 'hooks/useUpdatedAccount/functions'
import useStore from 'store' import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
@ -77,7 +77,7 @@ function BorrowModal(props: Props) {
if (!asset) return if (!asset) return
setIsConfirming(true) setIsConfirming(true)
let result let result
const { lends } = getDepositsAndLendsAfterCoinSpent( const { lend } = getDepositAndLendCoinsToSpend(
BNCoin.fromDenomAndBigNumber(asset.denom, amount), BNCoin.fromDenomAndBigNumber(asset.denom, amount),
account, account,
) )
@ -85,8 +85,8 @@ function BorrowModal(props: Props) {
result = await repay({ result = await repay({
accountId: account.id, accountId: account.id,
coin: BNCoin.fromDenomAndBigNumber(asset.denom, amount), coin: BNCoin.fromDenomAndBigNumber(asset.denom, amount),
accountBalance: max.isEqualTo(amount), accountBalance: percentage === 100,
lends, lend,
}) })
} else { } else {
result = await borrow({ result = await borrow({

View File

@ -14,7 +14,7 @@ import useToggle from 'hooks/useToggle'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount' import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import useStore from 'store' import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { cloneAccount, getMergedBalances, removeDepositsAndLends } from 'utils/accounts' import { cloneAccount, getMergedBalancesForAsset, removeDepositsAndLends } from 'utils/accounts'
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
import { getEnabledMarketAssets } from 'utils/assets' import { getEnabledMarketAssets } from 'utils/assets'
@ -36,7 +36,7 @@ export default function WithdrawFromAccount(props: Props) {
const accountClone = cloneAccount(account) const accountClone = cloneAccount(account)
const borrowAccount = removeDepositsAndLends(accountClone, currentAsset.denom) const borrowAccount = removeDepositsAndLends(accountClone, currentAsset.denom)
const { computeMaxBorrowAmount } = useHealthComputer(borrowAccount) const { computeMaxBorrowAmount } = useHealthComputer(borrowAccount)
const balances = getMergedBalances(account, getEnabledMarketAssets()) const balances = getMergedBalancesForAsset(account, getEnabledMarketAssets())
const maxWithdrawAmount = computeMaxWithdrawAmount(currentAsset.denom) const maxWithdrawAmount = computeMaxWithdrawAmount(currentAsset.denom)
const maxWithdrawWithBorrowAmount = computeMaxBorrowAmount(currentAsset.denom, 'wallet').plus( const maxWithdrawWithBorrowAmount = computeMaxBorrowAmount(currentAsset.denom, 'wallet').plus(
maxWithdrawAmount, maxWithdrawAmount,

View File

@ -19,6 +19,8 @@ import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.ty
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
import { findCoinByDenom, getAssetByDenom } from 'utils/assets' import { findCoinByDenom, getAssetByDenom } from 'utils/assets'
import { formatPercent } from 'utils/formatters' import { formatPercent } from 'utils/formatters'
import { getValueFromBNCoins, mergeBNCoinArrays } from 'utils/helpers'
import { ORACLE_DENOM } from 'constants/oracle'
export interface VaultBorrowingsProps { export interface VaultBorrowingsProps {
borrowings: BNCoin[] borrowings: BNCoin[]
@ -28,18 +30,29 @@ export interface VaultBorrowingsProps {
vault: Vault vault: Vault
depositActions: Action[] depositActions: Action[]
onChangeBorrowings: (borrowings: BNCoin[]) => void onChangeBorrowings: (borrowings: BNCoin[]) => void
displayCurrency: string
depositCapReachedCoins: BNCoin[] depositCapReachedCoins: BNCoin[]
} }
export default function VaultBorrowings(props: VaultBorrowingsProps) { export default function VaultBorrowings(props: VaultBorrowingsProps) {
const { data: marketAssets } = useMarketAssets() const { data: marketAssets } = useMarketAssets()
const { data: prices } = usePrices() const { data: prices } = usePrices()
const baseCurrency = useStore((s) => s.baseCurrency)
const vaultModal = useStore((s) => s.vaultModal) const vaultModal = useStore((s) => s.vaultModal)
const depositIntoVault = useStore((s) => s.depositIntoVault) const depositIntoVault = useStore((s) => s.depositIntoVault)
const [isConfirming, setIsConfirming] = useState(false) const [isConfirming, setIsConfirming] = useState(false)
const updatedAccount = useStore((s) => s.updatedAccount) const updatedAccount = useStore((s) => s.updatedAccount)
const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount) const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount)
const [percentage, setPercentage] = useState<number>(0)
const calculateSliderPercentage = (maxBorrowAmounts: BNCoin[], borrowings: BNCoin[]) => {
if (borrowings.length === 1) {
const amount = borrowings[0].amount
if (amount.isZero()) return 0
const max = maxBorrowAmounts.find(byDenom(borrowings[0].denom))?.amount || BN_ZERO
return amount.times(100).dividedBy(max).toNumber()
}
return 0
}
const maxBorrowAmounts: BNCoin[] = useMemo(() => { const maxBorrowAmounts: BNCoin[] = useMemo(() => {
return props.borrowings.map((borrowing) => { return props.borrowings.map((borrowing) => {
@ -53,25 +66,10 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
}) })
}, [props.borrowings, computeMaxBorrowAmount, props.vault.address]) }, [props.borrowings, computeMaxBorrowAmount, props.vault.address])
const borrowingValue = useMemo(() => { const totalValue = useMemo(
return props.borrowings.reduce((prev, curr) => { () => getValueFromBNCoins(mergeBNCoinArrays(props.deposits, props.borrowings), prices),
const price = prices.find((price) => price.denom === curr.denom)?.amount [props.borrowings, props.deposits, prices],
if (!price) return prev )
return prev.plus(curr.amount.multipliedBy(price))
}, BN_ZERO as BigNumber)
}, [props.borrowings, prices])
const totalValue = useMemo(() => {
const depositValue = props.deposits.reduce((prev, curr) => {
const price = prices.find((price) => price.denom === curr.denom)?.amount
if (!price) return prev
const value = curr.amount.multipliedBy(price)
return prev.plus(value)
}, BN_ZERO as BigNumber)
return depositValue.plus(borrowingValue)
}, [props.deposits, borrowingValue, prices])
useEffect(() => { useEffect(() => {
const selectedBorrowDenoms = vaultModal?.selectedBorrowDenoms || [] const selectedBorrowDenoms = vaultModal?.selectedBorrowDenoms || []
@ -81,25 +79,24 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
) { ) {
return return
} }
const updatedBorrowings = selectedBorrowDenoms.map((denom) => { const updatedBorrowings = selectedBorrowDenoms.map((denom) => {
const amount = findCoinByDenom(denom, props.borrowings)?.amount || BN_ZERO const amount = findCoinByDenom(denom, props.borrowings)?.amount || BN_ZERO
return new BNCoin({ return new BNCoin({
denom, denom,
amount: amount.toString(), amount: amount.toString(),
}) })
}) })
props.onChangeBorrowings(updatedBorrowings) props.onChangeBorrowings(updatedBorrowings)
}, [vaultModal, props]) setPercentage(calculateSliderPercentage(maxBorrowAmounts, updatedBorrowings))
}, [vaultModal, props, maxBorrowAmounts])
const [percentage, setPercentage] = useState<number>(0)
function onChangeSlider(value: number) { function onChangeSlider(value: number) {
if (props.borrowings.length !== 1) return if (props.borrowings.length !== 1) return
const denom = props.borrowings[0].denom const denom = props.borrowings[0].denom
const currentAmount = props.borrowings[0].amount const currentAmount = props.borrowings[0].amount
const maxAmount = maxBorrowAmounts.find((coin) => coin.denom === denom)?.amount ?? BN_ZERO const maxAmount = maxBorrowAmounts.find(byDenom(denom))?.amount ?? BN_ZERO
const newBorrowings: BNCoin[] = [ const newBorrowings: BNCoin[] = [
new BNCoin({ new BNCoin({
denom, denom,
@ -123,7 +120,8 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
function onDelete(denom: string) { function onDelete(denom: string) {
const index = props.borrowings.findIndex((coin) => coin.denom === denom) const index = props.borrowings.findIndex((coin) => coin.denom === denom)
props.borrowings.splice(index, 1) props.borrowings.splice(index, 1)
props.onChangeBorrowings([...props.borrowings]) const newBorrowings = [...props.borrowings]
props.onChangeBorrowings(newBorrowings)
if (!vaultModal) return if (!vaultModal) return
useStore.setState({ useStore.setState({
@ -132,6 +130,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
selectedBorrowDenoms: props.borrowings.map((coin) => coin.denom), selectedBorrowDenoms: props.borrowings.map((coin) => coin.denom),
}, },
}) })
setPercentage(calculateSliderPercentage(maxBorrowAmounts, newBorrowings))
} }
function addAsset() { function addAsset() {
@ -140,6 +139,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
selectedDenoms: props.borrowings.map((coin) => coin.denom), selectedDenoms: props.borrowings.map((coin) => coin.denom),
}, },
}) })
setPercentage(calculateSliderPercentage(maxBorrowAmounts, props.borrowings))
} }
async function onConfirm() { async function onConfirm() {
@ -209,7 +209,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
<div className='flex justify-between'> <div className='flex justify-between'>
<Text className='text-white/50'>{`${props.primaryAsset.symbol}-${props.secondaryAsset.symbol} Position Value`}</Text> <Text className='text-white/50'>{`${props.primaryAsset.symbol}-${props.secondaryAsset.symbol} Position Value`}</Text>
<DisplayCurrency <DisplayCurrency
coin={new BNCoin({ denom: baseCurrency.denom, amount: totalValue.toString() })} coin={new BNCoin({ denom: ORACLE_DENOM, amount: totalValue.toString() })}
/> />
</div> </div>
{props.borrowings.map((coin) => { {props.borrowings.map((coin) => {

View File

@ -1,47 +1,60 @@
import classNames from 'classnames'
import { useMemo } from 'react' import { useMemo } from 'react'
import DisplayCurrency from 'components/DisplayCurrency' import DisplayCurrency from 'components/DisplayCurrency'
import usePrices from 'hooks/usePrices' import Text from 'components/Text'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { formatAmountWithSymbol } from 'utils/formatters'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin'
import { formatAmountWithSymbol, getCoinValue } from 'utils/formatters'
import { ORACLE_DENOM } from 'constants/oracle'
interface Props { interface Props {
borrowings: BNCoin[] borrowings: BNCoin[]
displayCurrency: string
} }
export default function VaultDepositSubTitle(props: Props) { export default function VaultBorrowingsSubTitle(props: Props) {
const baseCurrency = useStore((s) => s.baseCurrency)
const { data: prices } = usePrices() const { data: prices } = usePrices()
const [borrowingTexts, borrowingValue] = useMemo(() => { const borrowingValue = useMemo(() => {
const texts: string[] = []
let borrowingValue = BN_ZERO let borrowingValue = BN_ZERO
props.borrowings.map((coin) => { props.borrowings.map((coin) => {
const price = prices.find((p) => p.denom === coin.denom)?.amount const price = prices.find((p) => p.denom === coin.denom)?.amount
if (!price || coin.amount.isZero()) return if (!price || coin.amount.isZero()) return
borrowingValue = borrowingValue.plus(coin.amount.multipliedBy(price)) borrowingValue = getCoinValue(coin, prices)
texts.push(
formatAmountWithSymbol({
denom: coin.denom,
amount: coin.amount.toString(),
}),
)
}) })
return [texts, borrowingValue] return borrowingValue
}, [props.borrowings, prices]) }, [props.borrowings, prices])
const borrowingTexts = useMemo(
() =>
props.borrowings.map((borrowing, index) => (
<Text
key={index}
size='xs'
className={classNames(
'inline mt-1 text-white/60',
index !== 0 && 'ml-1 before:pr-1 before:content-["+"]',
)}
>
{formatAmountWithSymbol(borrowing.toCoin())}
</Text>
)),
[props.borrowings],
)
return ( return (
<> <>
{borrowingTexts.join(' + ')} {props.borrowings.length > 0 && borrowingTexts}
{borrowingTexts.length > 0 && ( {props.borrowings.length > 0 && (
<> <DisplayCurrency
{` = `} className={classNames(
<DisplayCurrency 'text-xs mt-1 text-white/60 ml-1 inline',
coin={new BNCoin({ denom: baseCurrency.denom, amount: borrowingValue.toString() })} 'before:content-["="] before:pr-1',
/> )}
</> coin={new BNCoin({ denom: ORACLE_DENOM, amount: borrowingValue.toString() })}
/>
)} )}
</> </>
) )

View File

@ -1,8 +1,9 @@
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import { useMemo, useState } from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import Button from 'components/Button' import Button from 'components/Button'
import DepositCapMessage from 'components/DepositCapMessage' import DepositCapMessage from 'components/DepositCapMessage'
import DisplayCurrency from 'components/DisplayCurrency'
import Divider from 'components/Divider' import Divider from 'components/Divider'
import { Gauge } from 'components/Gauge' import { Gauge } from 'components/Gauge'
import { ArrowRight, ExclamationMarkCircled } from 'components/Icons' import { ArrowRight, ExclamationMarkCircled } from 'components/Icons'
@ -10,14 +11,14 @@ import Slider from 'components/Slider'
import Switch from 'components/Switch' import Switch from 'components/Switch'
import Text from 'components/Text' import Text from 'components/Text'
import TokenInput from 'components/TokenInput' import TokenInput from 'components/TokenInput'
import { ASSETS } from 'constants/assets'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import usePrice from 'hooks/usePrice' import usePrices from 'hooks/usePrices'
import { useMemo, useState } from 'react'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { accumulateAmounts } from 'utils/accounts' import { accumulateAmounts } from 'utils/accounts'
import { findCoinByDenom } from 'utils/assets' import { byDenom } from 'utils/array'
import { BN } from 'utils/helpers' import { BN, getValueFromBNCoins } from 'utils/helpers'
import { ORACLE_DENOM } from 'constants/oracle'
interface Props { interface Props {
deposits: BNCoin[] deposits: BNCoin[]
@ -28,13 +29,14 @@ interface Props {
onChangeDeposits: (deposits: BNCoin[]) => void onChangeDeposits: (deposits: BNCoin[]) => void
onChangeIsCustomRatio: (isCustomRatio: boolean) => void onChangeIsCustomRatio: (isCustomRatio: boolean) => void
toggleOpen: (index: number) => void toggleOpen: (index: number) => void
displayCurrency: string
depositCapReachedCoins: BNCoin[] depositCapReachedCoins: BNCoin[]
} }
export default function VaultDeposit(props: Props) { export default function VaultDeposit(props: Props) {
const { deposits, primaryAsset, secondaryAsset, account, onChangeDeposits } = props const { deposits, primaryAsset, secondaryAsset, account, onChangeDeposits, displayCurrency } =
const baseCurrency = useStore((s) => s.baseCurrency) props
const displayCurrencyAsset = ASSETS.find(byDenom(displayCurrency)) ?? ASSETS[0]
const [availablePrimaryAmount, availableSecondaryAmount] = useMemo( const [availablePrimaryAmount, availableSecondaryAmount] = useMemo(
() => [ () => [
accumulateAmounts(primaryAsset.denom, [...account.deposits, ...account.lends]), accumulateAmounts(primaryAsset.denom, [...account.deposits, ...account.lends]),
@ -42,37 +44,35 @@ export default function VaultDeposit(props: Props) {
], ],
[account.deposits, account.lends, primaryAsset.denom, secondaryAsset.denom], [account.deposits, account.lends, primaryAsset.denom, secondaryAsset.denom],
) )
const { data: prices } = usePrices()
const primaryPrice = usePrice(primaryAsset.denom) const primaryPrice = prices.find(byDenom(primaryAsset.denom))?.amount ?? BN_ZERO
const secondaryPrice = usePrice(secondaryAsset.denom) const secondaryPrice = prices.find(byDenom(secondaryAsset.denom))?.amount ?? BN_ZERO
const primaryCoin = useMemo(() => { const primaryCoin = useMemo(() => {
const amount = findCoinByDenom(primaryAsset.denom, deposits)?.amount.toString() || '0' const amount = deposits.find(byDenom(primaryAsset.denom))?.amount ?? BN_ZERO
return new BNCoin({ denom: primaryAsset.denom, amount }) return new BNCoin({ denom: primaryAsset.denom, amount: amount.toString() })
}, [deposits, primaryAsset.denom]) }, [deposits, primaryAsset.denom])
const secondaryCoin = useMemo(() => { const secondaryCoin = useMemo(() => {
const amount = findCoinByDenom(secondaryAsset.denom, deposits)?.amount.toString() || '0' const amount = deposits.find(byDenom(secondaryAsset.denom))?.amount ?? BN_ZERO
return new BNCoin({ denom: secondaryAsset.denom, amount }) return new BNCoin({ denom: secondaryAsset.denom, amount: amount.toString() })
}, [deposits, secondaryAsset.denom]) }, [deposits, secondaryAsset.denom])
const primaryValue = useMemo( const primaryValue = useMemo(
() => primaryCoin.amount.multipliedBy(primaryPrice), () => getValueFromBNCoins([primaryCoin], prices),
[primaryCoin, primaryPrice], [primaryCoin, prices],
)
const secondaryValue = useMemo(
() => secondaryCoin.amount.multipliedBy(secondaryPrice),
[secondaryCoin, secondaryPrice],
) )
const totalValue = useMemo( const totalValue = useMemo(
() => primaryValue.plus(secondaryValue), () => getValueFromBNCoins([primaryCoin, secondaryCoin], prices),
[primaryValue, secondaryValue], [primaryCoin, secondaryCoin, prices],
) )
const primaryValuePercentage = useMemo(() => { const primaryValuePercentage = useMemo(() => {
const value = primaryValue.dividedBy(totalValue).multipliedBy(100).decimalPlaces(2).toNumber() const value = primaryValue.dividedBy(totalValue).multipliedBy(100).decimalPlaces(2).toNumber()
return isNaN(value) ? 50 : value return isNaN(value) ? 50 : value
}, [primaryValue, totalValue]) }, [primaryValue, totalValue])
const secondaryValuePercentage = useMemo( const secondaryValuePercentage = useMemo(
() => new BigNumber(100).minus(primaryValuePercentage).integerValue(2).toNumber() ?? 50, () => new BigNumber(100).minus(primaryValuePercentage).integerValue(2).toNumber() ?? 50,
[primaryValuePercentage], [primaryValuePercentage],
@ -237,9 +237,7 @@ export default function VaultDeposit(props: Props) {
</div> </div>
<div className='flex justify-between'> <div className='flex justify-between'>
<Text className='text-white/50'>{`${primaryAsset.symbol}-${secondaryAsset.symbol} Deposit Value`}</Text> <Text className='text-white/50'>{`${primaryAsset.symbol}-${secondaryAsset.symbol} Deposit Value`}</Text>
<DisplayCurrency <DisplayCurrency coin={BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, totalValue)} />
coin={new BNCoin({ denom: baseCurrency.denom, amount: totalValue.toString() })}
/>
</div> </div>
<Button <Button
onClick={() => props.toggleOpen(1)} onClick={() => props.toggleOpen(1)}
@ -250,4 +248,4 @@ export default function VaultDeposit(props: Props) {
</div> </div>
</div> </div>
) )
} }

View File

@ -1,35 +1,56 @@
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import classNames from 'classnames'
import { useMemo } from 'react'
import DisplayCurrency from 'components/DisplayCurrency' import DisplayCurrency from 'components/DisplayCurrency'
import usePrice from 'hooks/usePrice' import Text from 'components/Text'
import useStore from 'store' import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { formatAmountWithSymbol } from 'utils/formatters' import { formatAmountWithSymbol } from 'utils/formatters'
import { getValueFromBNCoins } from 'utils/helpers'
import { ORACLE_DENOM } from 'constants/oracle'
interface Props { interface Props {
primaryAmount: BigNumber primaryAmount: BigNumber
secondaryAmount: BigNumber secondaryAmount: BigNumber
primaryAsset: Asset primaryAsset: Asset
secondaryAsset: Asset secondaryAsset: Asset
displayCurrency: string
} }
export default function VaultDepositSubTitle(props: Props) { export default function VaultDepositSubTitle(props: Props) {
const baseCurrency = useStore((s) => s.baseCurrency) const { data: prices } = usePrices()
const primaryPrice = usePrice(props.primaryAsset.denom) const primaryText = useMemo(
const secondaryPrice = usePrice(props.secondaryAsset.denom) () => (
const primaryText = formatAmountWithSymbol({ <Text size='xs' className='inline mt-1 text-white/60'>
denom: props.primaryAsset.denom, {formatAmountWithSymbol({
amount: props.primaryAmount.toString(), denom: props.primaryAsset.denom,
}) amount: props.primaryAmount.toString(),
const secondaryText = formatAmountWithSymbol({ })}
denom: props.secondaryAsset.denom, </Text>
amount: props.secondaryAmount.toString(), ),
}) [props.primaryAmount, props.primaryAsset.denom],
)
const positionValue = props.primaryAmount const secondaryText = useMemo(
.multipliedBy(primaryPrice) () => (
.plus(props.secondaryAmount.multipliedBy(secondaryPrice)) <Text size='xs' className='inline mt-1 text-white/60 ml-1 before:pr-1 before:content-["+"]'>
.toNumber() {formatAmountWithSymbol({
denom: props.secondaryAsset.denom,
amount: props.secondaryAmount.toString(),
})}
</Text>
),
[props.secondaryAmount, props.secondaryAsset.denom],
)
const positionValue = getValueFromBNCoins(
[
BNCoin.fromDenomAndBigNumber(props.primaryAsset.denom, props.primaryAmount),
BNCoin.fromDenomAndBigNumber(props.secondaryAsset.denom, props.secondaryAmount),
],
prices,
)
const showPrimaryText = !props.primaryAmount.isZero() const showPrimaryText = !props.primaryAmount.isZero()
const showSecondaryText = !props.secondaryAmount.isZero() const showSecondaryText = !props.secondaryAmount.isZero()
@ -37,15 +58,15 @@ export default function VaultDepositSubTitle(props: Props) {
return ( return (
<> <>
{showPrimaryText && primaryText} {showPrimaryText && primaryText}
{showPrimaryText && showSecondaryText && ' + '}
{showSecondaryText && secondaryText} {showSecondaryText && secondaryText}
{(showPrimaryText || showSecondaryText) && ( {(showPrimaryText || showSecondaryText) && (
<> <DisplayCurrency
{` = `} className={classNames(
<DisplayCurrency 'text-xs mt-1 text-white/60 ml-1 inline',
coin={new BNCoin({ denom: baseCurrency.denom, amount: positionValue.toString() })} 'before:content-["="] before:pr-1',
/> )}
</> coin={new BNCoin({ denom: ORACLE_DENOM, amount: positionValue.toString() })}
/>
)} )}
</> </>
) )

View File

@ -6,16 +6,20 @@ import VaultBorrowings from 'components/Modals/Vault/VaultBorrowings'
import VaultBorrowingsSubTitle from 'components/Modals/Vault/VaultBorrowingsSubTitle' import VaultBorrowingsSubTitle from 'components/Modals/Vault/VaultBorrowingsSubTitle'
import VaultDeposit from 'components/Modals/Vault/VaultDeposits' import VaultDeposit from 'components/Modals/Vault/VaultDeposits'
import VaultDepositSubTitle from 'components/Modals/Vault/VaultDepositsSubTitle' import VaultDepositSubTitle from 'components/Modals/Vault/VaultDepositsSubTitle'
import Text from 'components/Text'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { DISPLAY_CURRENCY_KEY } from 'constants/localStore'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import useDepositVault from 'hooks/broadcast/useDepositVault' import useDepositVault from 'hooks/broadcast/useDepositVault'
import useDisplayAsset from 'hooks/useDisplayAsset' import useDisplayAsset from 'hooks/useDisplayAsset'
import useIsOpenArray from 'hooks/useIsOpenArray' import useIsOpenArray from 'hooks/useIsOpenArray'
import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices' import usePrices from 'hooks/usePrices'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount' import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array' import { getCoinValue, magnify } from 'utils/formatters'
import { convertToDisplayAmount, magnify } from 'utils/formatters'
import { getCapLeftWithBuffer } from 'utils/generic' import { getCapLeftWithBuffer } from 'utils/generic'
import { mergeBNCoinArrays } from 'utils/helpers'
interface Props { interface Props {
vault: Vault | DepositedVault vault: Vault | DepositedVault
@ -26,17 +30,14 @@ interface Props {
} }
export default function VaultModalContent(props: Props) { export default function VaultModalContent(props: Props) {
const { const { addDebts, addedDebts, removedDeposits, removedLends, simulateVaultDeposit } =
addDebts, useUpdatedAccount(props.account)
removeDeposits,
addedDebts,
removedDeposits,
removedLends,
removeLends,
addVaultValues,
} = useUpdatedAccount(props.account)
const { data: prices } = usePrices() const { data: prices } = usePrices()
const [displayCurrency] = useLocalStorage<string>(
DISPLAY_CURRENCY_KEY,
DEFAULT_SETTINGS.displayCurrency,
)
const [isOpen, toggleOpen] = useIsOpenArray(2, false) const [isOpen, toggleOpen] = useIsOpenArray(2, false)
const [isCustomRatio, setIsCustomRatio] = useState(false) const [isCustomRatio, setIsCustomRatio] = useState(false)
const [selectedCoins, setSelectedCoins] = useState<BNCoin[]>([]) const [selectedCoins, setSelectedCoins] = useState<BNCoin[]>([])
@ -53,9 +54,8 @@ export default function VaultModalContent(props: Props) {
if (totalValue.isGreaterThan(capLeft)) { if (totalValue.isGreaterThan(capLeft)) {
const amount = magnify( const amount = magnify(
convertToDisplayAmount( getCoinValue(
BNCoin.fromDenomAndBigNumber(props.vault.cap.denom, capLeft), BNCoin.fromDenomAndBigNumber(props.vault.cap.denom, capLeft),
displayAsset.denom,
prices, prices,
).toString(), ).toString(),
displayAsset, displayAsset,
@ -68,76 +68,58 @@ export default function VaultModalContent(props: Props) {
const handleDepositSelect = useCallback( const handleDepositSelect = useCallback(
(selectedCoins: BNCoin[]) => { (selectedCoins: BNCoin[]) => {
const reclaims: BNCoin[] = [] simulateVaultDeposit(props.vault.address, selectedCoins)
const deposits: BNCoin[] = []
selectedCoins.forEach((selectedCoin) => {
const { denom, amount: selectedAmount } = selectedCoin
const accountDepositForSelectedCoin: BigNumber =
props.account.deposits.find(byDenom(denom))?.amount ?? BN_ZERO
if (selectedAmount.gt(accountDepositForSelectedCoin)) {
reclaims.push(
BNCoin.fromDenomAndBigNumber(
denom,
selectedAmount.minus(accountDepositForSelectedCoin),
),
)
deposits.push(BNCoin.fromDenomAndBigNumber(denom, accountDepositForSelectedCoin))
} else {
deposits.push(selectedCoin)
}
})
removeLends(reclaims)
removeDeposits(deposits)
setSelectedCoins(selectedCoins) setSelectedCoins(selectedCoins)
}, },
[props.account.deposits, removeDeposits, removeLends], [props.vault.address, simulateVaultDeposit],
) )
useEffect(() => {
addVaultValues([
{
address: props.vault.address,
value: totalValue,
},
])
}, [totalValue, addVaultValues, props.vault.address])
const onChangeIsCustomRatio = useCallback( const onChangeIsCustomRatio = useCallback(
(isCustomRatio: boolean) => setIsCustomRatio(isCustomRatio), (isCustomRatio: boolean) => setIsCustomRatio(isCustomRatio),
[setIsCustomRatio], [setIsCustomRatio],
) )
const deposits = useMemo(
() => mergeBNCoinArrays(removedDeposits, removedLends),
[removedDeposits, removedLends],
)
function getDepositSubTitle() { function getDepositSubTitle() {
if (isOpen[0] && props.isDeposited) if (isOpen[0] && props.isDeposited)
return 'The amounts you enter below will be added to your current position.' return (
<Text size='xs' className='mt-1 text-white/60'>
The amounts you enter below will be added to your current position.
</Text>
)
if (isOpen[0]) return null if (isOpen[0]) return null
return ( return (
<VaultDepositSubTitle <VaultDepositSubTitle
primaryAmount={ primaryAmount={
removedDeposits.find((coin) => coin.denom === props.primaryAsset.denom)?.amount || BN_ZERO deposits.find((coin) => coin.denom === props.primaryAsset.denom)?.amount || BN_ZERO
} }
secondaryAmount={ secondaryAmount={
removedDeposits.find((coin) => coin.denom === props.secondaryAsset.denom)?.amount || deposits.find((coin) => coin.denom === props.secondaryAsset.denom)?.amount || BN_ZERO
BN_ZERO
} }
primaryAsset={props.primaryAsset} primaryAsset={props.primaryAsset}
secondaryAsset={props.secondaryAsset} secondaryAsset={props.secondaryAsset}
displayCurrency={displayCurrency}
/> />
) )
} }
function getBorrowingsSubTitle() { function getBorrowingsSubTitle() {
if (isOpen[1] && props.isDeposited) if (isOpen[1] && props.isDeposited)
return 'The amounts you enter below will be added to your current position.' return (
<Text size='xs' className='mt-1 text-white/60'>
The amounts you enter below will be added to your current position.
</Text>
)
if (isOpen[1]) return null if (isOpen[1]) return null
return <VaultBorrowingsSubTitle borrowings={addedDebts} /> return <VaultBorrowingsSubTitle borrowings={addedDebts} displayCurrency={displayCurrency} />
} }
return ( return (
@ -156,6 +138,7 @@ export default function VaultModalContent(props: Props) {
toggleOpen={toggleOpen} toggleOpen={toggleOpen}
isCustomRatio={isCustomRatio} isCustomRatio={isCustomRatio}
onChangeIsCustomRatio={onChangeIsCustomRatio} onChangeIsCustomRatio={onChangeIsCustomRatio}
displayCurrency={displayCurrency}
depositCapReachedCoins={depositCapReachedCoins} depositCapReachedCoins={depositCapReachedCoins}
/> />
), ),
@ -168,13 +151,14 @@ export default function VaultModalContent(props: Props) {
renderContent: () => ( renderContent: () => (
<VaultBorrowings <VaultBorrowings
borrowings={addedDebts} borrowings={addedDebts}
deposits={removedDeposits} deposits={deposits}
primaryAsset={props.primaryAsset} primaryAsset={props.primaryAsset}
secondaryAsset={props.secondaryAsset} secondaryAsset={props.secondaryAsset}
onChangeBorrowings={addDebts} onChangeBorrowings={addDebts}
vault={props.vault} vault={props.vault}
depositActions={depositActions} depositActions={depositActions}
depositCapReachedCoins={depositCapReachedCoins} depositCapReachedCoins={depositCapReachedCoins}
displayCurrency={displayCurrency}
/> />
), ),
title: 'Borrow', title: 'Borrow',

View File

@ -4,8 +4,8 @@ import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber' import { FormattedNumber } from 'components/FormattedNumber'
import TitleAndSubCell from 'components/TitleAndSubCell' import TitleAndSubCell from 'components/TitleAndSubCell'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import { BNCoin } from 'types/classes/BNCoin'
import { PRICE_ORACLE_DECIMALS } from 'constants/query' import { PRICE_ORACLE_DECIMALS } from 'constants/query'
import { BNCoin } from 'types/classes/BNCoin'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
interface Props { interface Props {
@ -28,7 +28,7 @@ export default function VaultModalContentHeader({ vault }: Props) {
}, [vault]) }, [vault])
return ( return (
<div className='flex gap-6 border-b border-white/5 px-6 py-4 gradient-header'> <div className='flex gap-6 px-6 py-4 border-b border-white/5 gradient-header'>
{vault.apy && ( {vault.apy && (
<> <>
<TitleAndSubCell <TitleAndSubCell
@ -39,14 +39,13 @@ export default function VaultModalContentHeader({ vault }: Props) {
options={{ suffix: '%', decimals: -2 }} options={{ suffix: '%', decimals: -2 }}
animate animate
/> />
(
<FormattedNumber <FormattedNumber
className='ml-2 text-xs' className='ml-2 text-xs'
amount={vault.apy / 365} amount={vault.apy / 365}
options={{ suffix: '%/day', decimals: -2 }} options={{ suffix: '%/day', decimals: -2 }}
parentheses
animate animate
/> />
)
</div> </div>
} }
sub={'Deposit APY'} sub={'Deposit APY'}

View File

@ -19,7 +19,9 @@ import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
import { defaultFee } from 'utils/constants' import { defaultFee } from 'utils/constants'
import { convertToDisplayAmount, formatAmountWithSymbol } from 'utils/formatters' import { formatAmountWithSymbol, getCoinValue } from 'utils/formatters'
import { ORACLE_DENOM } from '../constants/oracle'
const renderIncentives = (unclaimedRewards: BNCoin[]) => { const renderIncentives = (unclaimedRewards: BNCoin[]) => {
if (unclaimedRewards.length === 0) if (unclaimedRewards.length === 0)
@ -58,14 +60,14 @@ export default function RewardsCenter() {
const totalRewardsCoin = useMemo(() => { const totalRewardsCoin = useMemo(() => {
let total = 0 let total = 0
unclaimedRewards.forEach((reward) => { unclaimedRewards.forEach((reward) => {
total = total + convertToDisplayAmount(reward, displayCurrency, prices).toNumber() total = total + getCoinValue(reward, prices).toNumber()
}) })
return new BNCoin({ return new BNCoin({
denom: displayCurrency, denom: ORACLE_DENOM,
amount: total.toString(), amount: total.toString(),
}) })
}, [displayCurrency, prices, unclaimedRewards]) }, [prices, unclaimedRewards])
const hasIncentives = unclaimedRewards.length > 0 const hasIncentives = unclaimedRewards.length > 0

View File

@ -73,8 +73,6 @@ export default function Slider(props: Props) {
setShowTooltip(false) setShowTooltip(false)
} }
// draggable workaround - to solve node compatibility issues
// TODO: find a replacement for react-draggable
const DraggableElement: any = Draggable const DraggableElement: any = Draggable
return ( return (

View File

@ -63,7 +63,6 @@ export const MOCK_DEPOSITED_VAULT_POSITION = {
unlocking: BN_ZERO, unlocking: BN_ZERO,
}, },
status: VaultStatus.ACTIVE, status: VaultStatus.ACTIVE,
apy: null,
ltv: { ltv: {
liq: 0, liq: 0,
max: 0, max: 0,

View File

@ -4,6 +4,7 @@ import { useMemo, useState } from 'react'
import getMinLpToReceive from 'api/vaults/getMinLpToReceive' import getMinLpToReceive from 'api/vaults/getMinLpToReceive'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings' import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { SLIPPAGE_KEY } from 'constants/localStore' import { SLIPPAGE_KEY } from 'constants/localStore'
import { BN_ZERO } from 'constants/math'
import useLocalStorage from 'hooks/useLocalStorage' import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices' import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
@ -13,7 +14,6 @@ import {
getVaultDepositCoinsAndValue, getVaultDepositCoinsAndValue,
getVaultSwapActions, getVaultSwapActions,
} from 'utils/vaults' } from 'utils/vaults'
import { BN_ZERO } from 'constants/math'
interface Props { interface Props {
vault: Vault vault: Vault
@ -21,6 +21,7 @@ interface Props {
deposits: BNCoin[] deposits: BNCoin[]
borrowings: BNCoin[] borrowings: BNCoin[]
} }
export default function useDepositVault(props: Props): { export default function useDepositVault(props: Props): {
actions: Action[] actions: Action[]
minLpToReceive: string minLpToReceive: string
@ -38,12 +39,16 @@ export default function useDepositVault(props: Props): {
() => props.deposits.filter((deposit) => deposit.amount.gt(0)), () => props.deposits.filter((deposit) => deposit.amount.gt(0)),
[props.deposits], [props.deposits],
) )
const reclaims: BNCoin[] = useMemo(
() => props.reclaims.filter((reclaim) => reclaim.amount.gt(0)),
[props.reclaims],
)
const debouncedGetMinLpToReceive = useMemo(() => debounce(getMinLpToReceive, 500), []) const debouncedGetMinLpToReceive = useMemo(() => debounce(getMinLpToReceive, 500), [])
const { primaryCoin, secondaryCoin, totalValue } = useMemo( const { primaryCoin, secondaryCoin, totalValue } = useMemo(
() => getVaultDepositCoinsAndValue(props.vault, deposits, borrowings, prices), () => getVaultDepositCoinsAndValue(props.vault, deposits, borrowings, reclaims, prices),
[deposits, borrowings, props.vault, prices], [reclaims, deposits, borrowings, props.vault, prices],
) )
const reclaimActions: Action[] = useMemo(() => { const reclaimActions: Action[] = useMemo(() => {

View File

@ -177,7 +177,6 @@ export default function useHealthComputer(account?: Account) {
}, },
[healthComputer], [healthComputer],
) )
const health = useMemo(() => { const health = useMemo(() => {
if (healthFactor > 10) return 100 if (healthFactor > 10) return 100
if (healthFactor < 0) return 0 if (healthFactor < 0) return 0

View File

@ -40,6 +40,7 @@ export function removeCoins(coinsToRemove: BNCoin[], currentCoins: BNCoin[]) {
export function addValueToVaults( export function addValueToVaults(
vaultValues: VaultValue[], vaultValues: VaultValue[],
vaults: DepositedVault[], vaults: DepositedVault[],
availableVaults: Vault[],
): DepositedVault[] { ): DepositedVault[] {
const currentVaultAddresses = vaults.map((vault) => vault.address) const currentVaultAddresses = vaults.map((vault) => vault.address)
@ -55,10 +56,12 @@ export function addValueToVaults(
const vaultMetaData = getVaultMetaData(vaultValue.address) const vaultMetaData = getVaultMetaData(vaultValue.address)
if (!vaultMetaData) return if (!vaultMetaData) return
const apy = availableVaults.find((vault) => vault.address === vaultValue.address)?.apy ?? null
vaults.push({ vaults.push({
...vaultMetaData, ...vaultMetaData,
...MOCK_DEPOSITED_VAULT_POSITION, ...MOCK_DEPOSITED_VAULT_POSITION,
apy,
values: { values: {
primary: halfValue, primary: halfValue,
secondary: halfValue, secondary: halfValue,
@ -70,10 +73,10 @@ export function addValueToVaults(
return vaults return vaults
} }
export function getDepositsAndLendsAfterCoinSpent(coin: BNCoin, account?: Account) { export function getDepositAndLendCoinsToSpend(coin: BNCoin, account?: Account) {
const makeOutput = (depositsAmount: BigNumber, lendsAmount: BigNumber) => ({ const makeOutput = (depositsAmount: BigNumber, lendsAmount: BigNumber) => ({
deposits: BNCoin.fromDenomAndBigNumber(coin.denom, depositsAmount), deposit: BNCoin.fromDenomAndBigNumber(coin.denom, depositsAmount),
lends: BNCoin.fromDenomAndBigNumber(coin.denom, lendsAmount), lend: BNCoin.fromDenomAndBigNumber(coin.denom, lendsAmount),
}) })
if (!account) return makeOutput(BN_ZERO, BN_ZERO) if (!account) return makeOutput(BN_ZERO, BN_ZERO)

View File

@ -1,16 +1,19 @@
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import usePrices from 'hooks/usePrices'
import { import {
addCoins, addCoins,
addValueToVaults, addValueToVaults,
getDepositsAndLendsAfterCoinSpent, getDepositAndLendCoinsToSpend,
removeCoins, removeCoins,
} from 'hooks/useUpdatedAccount/functions' } from 'hooks/useUpdatedAccount/functions'
import useVaults from 'hooks/useVaults'
import useStore from 'store' import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { cloneAccount } from 'utils/accounts' import { cloneAccount } from 'utils/accounts'
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
import { getValueFromBNCoins } from 'utils/helpers'
export interface VaultValue { export interface VaultValue {
address: string address: string
@ -18,6 +21,8 @@ export interface VaultValue {
} }
export function useUpdatedAccount(account?: Account) { export function useUpdatedAccount(account?: Account) {
const { data: availableVaults } = useVaults(false)
const { data: prices } = usePrices()
const [updatedAccount, setUpdatedAccount] = useState<Account | undefined>( const [updatedAccount, setUpdatedAccount] = useState<Account | undefined>(
account ? cloneAccount(account) : undefined, account ? cloneAccount(account) : undefined,
) )
@ -84,10 +89,10 @@ export function useUpdatedAccount(account?: Account) {
const simulateRepay = useCallback( const simulateRepay = useCallback(
(coin: BNCoin) => { (coin: BNCoin) => {
if (!account) return if (!account) return
const { deposits, lends } = getDepositsAndLendsAfterCoinSpent(coin, account) const { deposit, lend } = getDepositAndLendCoinsToSpend(coin, account)
removeDebts([coin]) removeDebts([coin])
removeDeposits([deposits]) removeDeposits([deposit])
removeLends([lends]) removeLends([lend])
}, },
[account, removeDebts, removeDeposits, removeLends], [account, removeDebts, removeDeposits, removeLends],
) )
@ -110,10 +115,10 @@ export function useUpdatedAccount(account?: Account) {
removeLends([]) removeLends([])
addDebts([]) addDebts([])
const { deposits, lends } = getDepositsAndLendsAfterCoinSpent(coin, account) const { deposit, lend } = getDepositAndLendCoinsToSpend(coin, account)
const totalBalance = deposits.amount.plus(lends.amount) const totalBalance = deposit.amount.plus(lend.amount)
removeDeposits([deposits]) removeDeposits([deposit])
removeLends([lends]) removeLends([lend])
if (withdrawWithBorrowing) { if (withdrawWithBorrowing) {
addDebts([BNCoin.fromDenomAndBigNumber(coin.denom, coin.amount.minus(totalBalance))]) addDebts([BNCoin.fromDenomAndBigNumber(coin.denom, coin.amount.minus(totalBalance))])
} }
@ -129,10 +134,10 @@ export function useUpdatedAccount(account?: Account) {
addDeposits([]) addDeposits([])
addLends([]) addLends([])
const { deposits, lends } = getDepositsAndLendsAfterCoinSpent(removeCoin, account) const { deposit, lend } = getDepositAndLendCoinsToSpend(removeCoin, account)
removeDeposits([deposits]) removeDeposits([deposit])
removeLends([lends]) removeLends([lend])
if (target === 'deposit') addDeposits([addCoin]) if (target === 'deposit') addDeposits([addCoin])
if (target === 'lend') addLends([addCoin]) if (target === 'lend') addLends([addCoin])
@ -141,13 +146,38 @@ export function useUpdatedAccount(account?: Account) {
[account, addDebts, addDeposits, addLends, removeDeposits, removeLends], [account, addDebts, addDeposits, addLends, removeDeposits, removeLends],
) )
const simulateVaultDeposit = useCallback(
(address: string, coins: BNCoin[]) => {
if (!account) return
const value = getValueFromBNCoins(coins, prices)
const totalDeposits: BNCoin[] = []
const totalLends: BNCoin[] = []
coins.forEach((coin) => {
const { deposit, lend } = getDepositAndLendCoinsToSpend(coin, account)
totalDeposits.push(deposit)
totalLends.push(lend)
})
addVaultValues([{ address, value }])
removeDeposits(totalDeposits)
removeLends(totalLends)
},
[account, prices],
)
useEffect(() => { useEffect(() => {
if (!account) return if (!account) return
const accountCopy = cloneAccount(account) const accountCopy = cloneAccount(account)
accountCopy.deposits = addCoins(addedDeposits, [...accountCopy.deposits]) accountCopy.deposits = addCoins(addedDeposits, [...accountCopy.deposits])
accountCopy.debts = addCoins(addedDebts, [...accountCopy.debts]) accountCopy.debts = addCoins(addedDebts, [...accountCopy.debts])
accountCopy.vaults = addValueToVaults(addedVaultValues, [...accountCopy.vaults]) accountCopy.vaults = addValueToVaults(
addedVaultValues,
[...accountCopy.vaults],
availableVaults ?? [],
)
accountCopy.deposits = removeCoins(removedDeposits, [...accountCopy.deposits]) accountCopy.deposits = removeCoins(removedDeposits, [...accountCopy.deposits])
accountCopy.debts = removeCoins(removedDebts, [...accountCopy.debts]) accountCopy.debts = removeCoins(removedDebts, [...accountCopy.debts])
accountCopy.lends = addCoins(addedLends, [...accountCopy.lends]) accountCopy.lends = addCoins(addedLends, [...accountCopy.lends])
@ -165,6 +195,7 @@ export function useUpdatedAccount(account?: Account) {
addedVaultValues, addedVaultValues,
addedLends, addedLends,
removedLends, removedLends,
availableVaults,
]) ])
return { return {
@ -188,6 +219,7 @@ export function useUpdatedAccount(account?: Account) {
simulateLending, simulateLending,
simulateRepay, simulateRepay,
simulateTrade, simulateTrade,
simulateVaultDeposit,
simulateWithdraw, simulateWithdraw,
} }
} }

View File

@ -2,9 +2,9 @@ import useSWR from 'swr'
import getVaults from 'api/vaults/getVaults' import getVaults from 'api/vaults/getVaults'
export default function useVaults(address?: string) { export default function useVaults(suspense: boolean = true, address?: string) {
return useSWR(`vaults${address}`, () => getVaults(), { return useSWR(`vaults${address}`, () => getVaults(), {
suspense: true, suspense,
revalidateOnFocus: false, revalidateOnFocus: false,
}) })
} }

View File

@ -362,7 +362,7 @@ export default function createBroadcastSlice(
accountId: string accountId: string
coin: BNCoin coin: BNCoin
accountBalance?: boolean accountBalance?: boolean
lends?: BNCoin lend?: BNCoin
}) => { }) => {
const actions: Action[] = [ const actions: Action[] = [
{ {
@ -372,8 +372,8 @@ export default function createBroadcastSlice(
}, },
] ]
if (options.lends && options.lends.amount.isGreaterThan(0)) if (options.lend && options.lend.amount.isGreaterThan(0))
actions.unshift({ reclaim: options.lends.toActionCoin() }) actions.unshift({ reclaim: options.lend.toActionCoin() })
const msg: CreditManagerExecuteMsg = { const msg: CreditManagerExecuteMsg = {
update_credit_account: { update_credit_account: {

View File

@ -29,7 +29,7 @@ interface BroadcastSlice {
accountId: string accountId: string
coin: BNCoin coin: BNCoin
accountBalance?: boolean accountBalance?: boolean
lends?: BNCoin lend?: BNCoin
}) => Promise<boolean> }) => Promise<boolean>
swap: (options: { swap: (options: {
accountId: string accountId: string

View File

@ -45,7 +45,7 @@ export const calculateAccountValue = (
acc.plus(vaultPosition.values.primary).plus(vaultPosition.values.secondary), acc.plus(vaultPosition.values.primary).plus(vaultPosition.values.secondary),
BN_ZERO, BN_ZERO,
) || BN_ZERO ) || BN_ZERO
).shiftedBy(-6) )
} }
return account[type]?.reduce((acc, position) => { return account[type]?.reduce((acc, position) => {
@ -85,13 +85,8 @@ export const calculateAccountApr = (
}) })
vaults?.forEach((vault) => { vaults?.forEach((vault) => {
const asset = getAssetByDenom(vault.denoms.lp) const vaultValue = vault.values.primary.plus(vault.values.secondary)
if (!asset) return BN_ZERO const positionInterest = vaultValue.multipliedBy(convertApyToApr(vault?.apy ?? 0, 365))
const price = prices.find(byDenom(vault.denoms.lp))?.amount ?? 0
const amount = BN(vault.amounts.locked).shiftedBy(-asset.decimals)
const positionInterest = amount
.multipliedBy(price)
.multipliedBy(convertApyToApr(vault?.apy ?? 0, 365))
totalVaultsInterestValue = totalVaultsInterestValue.plus(positionInterest) totalVaultsInterestValue = totalVaultsInterestValue.plus(positionInterest)
}) })
@ -219,7 +214,7 @@ export function removeDepositsAndLends(account: Account, denom: string) {
} }
} }
export function getMergedBalances(account: Account, assets: Asset[]) { export function getMergedBalancesForAsset(account: Account, assets: Asset[]) {
const balances: BNCoin[] = [] const balances: BNCoin[] = []
assets.forEach((asset) => { assets.forEach((asset) => {
const balance = accumulateAmounts(asset.denom, [...account.deposits, ...account.lends]) const balance = accumulateAmounts(asset.denom, [...account.deposits, ...account.lends])

View File

@ -177,16 +177,14 @@ export function demagnify(amount: number | string | BigNumber, asset: Asset | Ps
return value.isZero() ? 0 : value.shiftedBy(-1 * asset.decimals).toNumber() return value.isZero() ? 0 : value.shiftedBy(-1 * asset.decimals).toNumber()
} }
export function convertToDisplayAmount(coin: BNCoin, displayCurrency: string, prices: BNCoin[]) { export function getCoinValue(coin: BNCoin, prices: BNCoin[]) {
const price = prices.find((price) => price.denom === coin.denom)
const asset = getAllAssets().find((asset) => asset.denom === coin.denom) const asset = getAllAssets().find((asset) => asset.denom === coin.denom)
const displayPrice = prices.find((price) => price.denom === displayCurrency) const coinPrice = prices.find((price) => price.denom === coin.denom)
if (!price || !displayPrice || !asset) return BN_ZERO if (!coinPrice || !asset) return BN_ZERO
const decimals = asset.denom === ORACLE_DENOM ? 0 : asset.decimals * -1 const decimals = asset.denom === ORACLE_DENOM ? 0 : asset.decimals * -1
return coin.amount.shiftedBy(decimals).multipliedBy(coinPrice.amount)
return coin.amount.shiftedBy(decimals).multipliedBy(price.amount).dividedBy(displayPrice.amount)
} }
export function convertLiquidityRateToAPR(rate: number) { export function convertLiquidityRateToAPR(rate: number) {

View File

@ -1,7 +1,12 @@
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import throttle from 'lodash.throttle' import throttle from 'lodash.throttle'
import { BN_ZERO } from 'constants/math'
import { BNCoin } from 'types/classes/BNCoin'
import { getCoinValue } from 'utils/formatters'
BigNumber.config({ EXPONENTIAL_AT: 1e9 }) BigNumber.config({ EXPONENTIAL_AT: 1e9 })
export function BN(n: BigNumber.Value) { export function BN(n: BigNumber.Value) {
return new BigNumber(n) return new BigNumber(n)
} }
@ -23,3 +28,31 @@ export function asyncThrottle<F extends (...args: any[]) => Promise<any>>(func:
throttled(resolve, reject, args) throttled(resolve, reject, args)
}) as ReturnType<F> }) as ReturnType<F>
} }
export function mergeBNCoinArrays(array1: BNCoin[], array2: BNCoin[]) {
const merged: BNCoin[] = []
array1.forEach((coin) => {
merged.push(new BNCoin(coin.toCoin()))
})
array2.forEach((coin) => {
const index = merged.findIndex((i) => i.denom === coin.denom)
if (index === -1) {
merged.push(new BNCoin(coin.toCoin()))
} else {
merged[index].amount = merged[index].amount.plus(coin.amount)
}
})
return merged
}
export function getValueFromBNCoins(coins: BNCoin[], prices: BNCoin[]): BigNumber {
let totalValue = BN_ZERO
coins.forEach((coin) => {
totalValue = totalValue.plus(getCoinValue(coin, prices))
})
return totalValue
}

View File

@ -17,9 +17,8 @@ export const convertAprToApy = (apr: number, numberOfCompoundingPeriods: number)
} }
export const convertApyToApr = (apy: number, numberOfCompoundingPeriods: number): number => { export const convertApyToApr = (apy: number, numberOfCompoundingPeriods: number): number => {
return ( const periodicRate = (1 + apy) ** (1 / numberOfCompoundingPeriods) - 1
(Math.pow(1 + apy / 100, numberOfCompoundingPeriods) - 1) * 100 * numberOfCompoundingPeriods return periodicRate * numberOfCompoundingPeriods
)
} }
export const combineBNCoins = (coins: BNCoin[]): BNCoin[] => { export const combineBNCoins = (coins: BNCoin[]): BNCoin[] => {

View File

@ -1,10 +1,14 @@
import { ASSETS } from 'constants/assets'
import { IS_TESTNET } from 'constants/env' import { IS_TESTNET } from 'constants/env'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import { TESTNET_VAULTS_META_DATA, VAULTS_META_DATA } from 'constants/vaults' import { TESTNET_VAULTS_META_DATA, VAULTS_META_DATA } from 'constants/vaults'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types' import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { getAssetByDenom } from 'utils/assets'
import { getTokenPrice, getTokenValue } from 'utils/tokens' import { getTokenPrice, getTokenValue } from 'utils/tokens'
import { getValueFromBNCoins, mergeBNCoinArrays } from './helpers'
export function getVaultsMetaData() { export function getVaultsMetaData() {
return IS_TESTNET ? TESTNET_VAULTS_META_DATA : VAULTS_META_DATA return IS_TESTNET ? TESTNET_VAULTS_META_DATA : VAULTS_META_DATA
} }
@ -18,23 +22,25 @@ export function getVaultDepositCoinsAndValue(
vault: Vault, vault: Vault,
deposits: BNCoin[], deposits: BNCoin[],
borrowings: BNCoin[], borrowings: BNCoin[],
reclaims: BNCoin[],
prices: BNCoin[], prices: BNCoin[],
) { ) {
const totalValue = [...deposits, ...borrowings].reduce((prev, bnCoin) => { const depositsAndReclaims = mergeBNCoinArrays(deposits, reclaims)
const price = prices.find((coin) => coin.denom === bnCoin.denom)?.amount const borrowingsAndDepositsAndReclaims = mergeBNCoinArrays(borrowings, depositsAndReclaims)
if (!price) return prev const totalValue = getValueFromBNCoins(borrowingsAndDepositsAndReclaims, prices)
return prev.plus(bnCoin.amount.multipliedBy(price))
}, BN_ZERO)
const halfValue = totalValue.dividedBy(2) const halfValue = totalValue.dividedBy(2)
const primaryAsset = getAssetByDenom(vault.denoms.primary) ?? ASSETS[0]
const secondaryAsset = getAssetByDenom(vault.denoms.secondary) ?? ASSETS[0]
const primaryDepositAmount = halfValue const primaryDepositAmount = halfValue
.dividedBy(getTokenPrice(vault.denoms.primary, prices)) .dividedBy(getTokenPrice(primaryAsset.denom, prices))
.shiftedBy(primaryAsset.decimals)
.integerValue() .integerValue()
const secondaryDepositAmount = halfValue const secondaryDepositAmount = halfValue
.dividedBy(getTokenPrice(vault.denoms.secondary, prices)) .dividedBy(getTokenPrice(secondaryAsset.denom, prices))
.shiftedBy(secondaryAsset.decimals)
.integerValue() .integerValue()
return { return {
@ -46,7 +52,7 @@ export function getVaultDepositCoinsAndValue(
denom: vault.denoms.secondary, denom: vault.denoms.secondary,
amount: secondaryDepositAmount.toString(), amount: secondaryDepositAmount.toString(),
}), }),
totalValue: totalValue.integerValue(), totalValue: totalValue,
} }
} }
@ -115,12 +121,14 @@ export function getVaultSwapActions(
value = value.minus(swapValue) value = value.minus(swapValue)
amount = amount.minus(swapAmount) amount = amount.minus(swapAmount)
primaryLeftoverValue = primaryLeftoverValue.minus(swapValue) primaryLeftoverValue = primaryLeftoverValue.minus(swapValue)
swapActions.push(getSwapAction(bnCoin.denom, vault.denoms.primary, swapAmount, slippage)) if (swapAmount.isGreaterThan(BN_ZERO))
swapActions.push(getSwapAction(bnCoin.denom, vault.denoms.primary, swapAmount, slippage))
} }
if (secondaryLeftoverValue.isGreaterThan(0)) { if (secondaryLeftoverValue.isGreaterThan(0)) {
secondaryLeftoverValue = secondaryLeftoverValue.minus(value) secondaryLeftoverValue = secondaryLeftoverValue.minus(value)
swapActions.push(getSwapAction(bnCoin.denom, vault.denoms.secondary, amount, slippage)) if (amount.isGreaterThan(BN_ZERO))
swapActions.push(getSwapAction(bnCoin.denom, vault.denoms.secondary, amount, slippage))
} }
}) })