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

View File

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

View File

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

View File

@ -8,7 +8,9 @@ import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin'
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 {
coin: BNCoin
@ -39,13 +41,27 @@ export default function DisplayCurrency(props: Props) {
? ''
: ` ${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 (
<FormattedNumber
className={classNames(
props.className,
props.parentheses && 'before:content-["("] after:content-[")"]',
)}
amount={convertToDisplayAmount(props.coin, displayCurrency, prices).toNumber()}
amount={amount}
options={{
minDecimals: isUSD ? 2 : 0,
maxDecimals: 2,

View File

@ -6,7 +6,7 @@ import useVaults from 'hooks/useVaults'
function Content() {
const { data: vaults } = useVaults()
if (!vaults) return null
const featuredVaults = vaults.filter((vault) => vault.isFeatured)
if (!featuredVaults.length) return null
@ -14,7 +14,7 @@ function Content() {
return (
<Card
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'
>
{featuredVaults.map((vault) => (

View File

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

View File

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

View File

@ -12,6 +12,7 @@ interface Props {
options?: FormatOptions
className?: string
animate?: boolean
parentheses?: boolean
}
export const FormattedNumber = React.memo(
@ -39,13 +40,25 @@ export const FormattedNumber = React.memo(
reduceMotion
)
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)}
</p>
)
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))}
</animated.p>
)

View File

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

View File

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

View File

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

View File

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

View File

@ -1,47 +1,60 @@
import classNames from 'classnames'
import { useMemo } from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import usePrices from 'hooks/usePrices'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { formatAmountWithSymbol } from 'utils/formatters'
import Text from 'components/Text'
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 {
borrowings: BNCoin[]
displayCurrency: string
}
export default function VaultDepositSubTitle(props: Props) {
const baseCurrency = useStore((s) => s.baseCurrency)
export default function VaultBorrowingsSubTitle(props: Props) {
const { data: prices } = usePrices()
const [borrowingTexts, borrowingValue] = useMemo(() => {
const texts: string[] = []
const borrowingValue = useMemo(() => {
let borrowingValue = BN_ZERO
props.borrowings.map((coin) => {
const price = prices.find((p) => p.denom === coin.denom)?.amount
if (!price || coin.amount.isZero()) return
borrowingValue = borrowingValue.plus(coin.amount.multipliedBy(price))
texts.push(
formatAmountWithSymbol({
denom: coin.denom,
amount: coin.amount.toString(),
}),
)
borrowingValue = getCoinValue(coin, prices)
})
return [texts, borrowingValue]
return borrowingValue
}, [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 (
<>
{borrowingTexts.join(' + ')}
{borrowingTexts.length > 0 && (
<>
{` = `}
<DisplayCurrency
coin={new BNCoin({ denom: baseCurrency.denom, amount: borrowingValue.toString() })}
/>
</>
{props.borrowings.length > 0 && borrowingTexts}
{props.borrowings.length > 0 && (
<DisplayCurrency
className={classNames(
'text-xs mt-1 text-white/60 ml-1 inline',
'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 { useMemo, useState } from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import Button from 'components/Button'
import DepositCapMessage from 'components/DepositCapMessage'
import DisplayCurrency from 'components/DisplayCurrency'
import Divider from 'components/Divider'
import { Gauge } from 'components/Gauge'
import { ArrowRight, ExclamationMarkCircled } from 'components/Icons'
@ -10,14 +11,14 @@ import Slider from 'components/Slider'
import Switch from 'components/Switch'
import Text from 'components/Text'
import TokenInput from 'components/TokenInput'
import { ASSETS } from 'constants/assets'
import { BN_ZERO } from 'constants/math'
import usePrice from 'hooks/usePrice'
import { useMemo, useState } from 'react'
import useStore from 'store'
import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin'
import { accumulateAmounts } from 'utils/accounts'
import { findCoinByDenom } from 'utils/assets'
import { BN } from 'utils/helpers'
import { byDenom } from 'utils/array'
import { BN, getValueFromBNCoins } from 'utils/helpers'
import { ORACLE_DENOM } from 'constants/oracle'
interface Props {
deposits: BNCoin[]
@ -28,13 +29,14 @@ interface Props {
onChangeDeposits: (deposits: BNCoin[]) => void
onChangeIsCustomRatio: (isCustomRatio: boolean) => void
toggleOpen: (index: number) => void
displayCurrency: string
depositCapReachedCoins: BNCoin[]
}
export default function VaultDeposit(props: Props) {
const { deposits, primaryAsset, secondaryAsset, account, onChangeDeposits } = props
const baseCurrency = useStore((s) => s.baseCurrency)
const { deposits, primaryAsset, secondaryAsset, account, onChangeDeposits, displayCurrency } =
props
const displayCurrencyAsset = ASSETS.find(byDenom(displayCurrency)) ?? ASSETS[0]
const [availablePrimaryAmount, availableSecondaryAmount] = useMemo(
() => [
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],
)
const primaryPrice = usePrice(primaryAsset.denom)
const secondaryPrice = usePrice(secondaryAsset.denom)
const { data: prices } = usePrices()
const primaryPrice = prices.find(byDenom(primaryAsset.denom))?.amount ?? BN_ZERO
const secondaryPrice = prices.find(byDenom(secondaryAsset.denom))?.amount ?? BN_ZERO
const primaryCoin = useMemo(() => {
const amount = findCoinByDenom(primaryAsset.denom, deposits)?.amount.toString() || '0'
return new BNCoin({ denom: primaryAsset.denom, amount })
const amount = deposits.find(byDenom(primaryAsset.denom))?.amount ?? BN_ZERO
return new BNCoin({ denom: primaryAsset.denom, amount: amount.toString() })
}, [deposits, primaryAsset.denom])
const secondaryCoin = useMemo(() => {
const amount = findCoinByDenom(secondaryAsset.denom, deposits)?.amount.toString() || '0'
return new BNCoin({ denom: secondaryAsset.denom, amount })
const amount = deposits.find(byDenom(secondaryAsset.denom))?.amount ?? BN_ZERO
return new BNCoin({ denom: secondaryAsset.denom, amount: amount.toString() })
}, [deposits, secondaryAsset.denom])
const primaryValue = useMemo(
() => primaryCoin.amount.multipliedBy(primaryPrice),
[primaryCoin, primaryPrice],
)
const secondaryValue = useMemo(
() => secondaryCoin.amount.multipliedBy(secondaryPrice),
[secondaryCoin, secondaryPrice],
() => getValueFromBNCoins([primaryCoin], prices),
[primaryCoin, prices],
)
const totalValue = useMemo(
() => primaryValue.plus(secondaryValue),
[primaryValue, secondaryValue],
() => getValueFromBNCoins([primaryCoin, secondaryCoin], prices),
[primaryCoin, secondaryCoin, prices],
)
const primaryValuePercentage = useMemo(() => {
const value = primaryValue.dividedBy(totalValue).multipliedBy(100).decimalPlaces(2).toNumber()
return isNaN(value) ? 50 : value
}, [primaryValue, totalValue])
const secondaryValuePercentage = useMemo(
() => new BigNumber(100).minus(primaryValuePercentage).integerValue(2).toNumber() ?? 50,
[primaryValuePercentage],
@ -237,9 +237,7 @@ export default function VaultDeposit(props: Props) {
</div>
<div className='flex justify-between'>
<Text className='text-white/50'>{`${primaryAsset.symbol}-${secondaryAsset.symbol} Deposit Value`}</Text>
<DisplayCurrency
coin={new BNCoin({ denom: baseCurrency.denom, amount: totalValue.toString() })}
/>
<DisplayCurrency coin={BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, totalValue)} />
</div>
<Button
onClick={() => props.toggleOpen(1)}
@ -250,4 +248,4 @@ export default function VaultDeposit(props: Props) {
</div>
</div>
)
}
}

View File

@ -1,35 +1,56 @@
import BigNumber from 'bignumber.js'
import classNames from 'classnames'
import { useMemo } from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import usePrice from 'hooks/usePrice'
import useStore from 'store'
import Text from 'components/Text'
import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin'
import { formatAmountWithSymbol } from 'utils/formatters'
import { getValueFromBNCoins } from 'utils/helpers'
import { ORACLE_DENOM } from 'constants/oracle'
interface Props {
primaryAmount: BigNumber
secondaryAmount: BigNumber
primaryAsset: Asset
secondaryAsset: Asset
displayCurrency: string
}
export default function VaultDepositSubTitle(props: Props) {
const baseCurrency = useStore((s) => s.baseCurrency)
const primaryPrice = usePrice(props.primaryAsset.denom)
const secondaryPrice = usePrice(props.secondaryAsset.denom)
const primaryText = formatAmountWithSymbol({
denom: props.primaryAsset.denom,
amount: props.primaryAmount.toString(),
})
const secondaryText = formatAmountWithSymbol({
denom: props.secondaryAsset.denom,
amount: props.secondaryAmount.toString(),
})
const { data: prices } = usePrices()
const primaryText = useMemo(
() => (
<Text size='xs' className='inline mt-1 text-white/60'>
{formatAmountWithSymbol({
denom: props.primaryAsset.denom,
amount: props.primaryAmount.toString(),
})}
</Text>
),
[props.primaryAmount, props.primaryAsset.denom],
)
const positionValue = props.primaryAmount
.multipliedBy(primaryPrice)
.plus(props.secondaryAmount.multipliedBy(secondaryPrice))
.toNumber()
const secondaryText = useMemo(
() => (
<Text size='xs' className='inline mt-1 text-white/60 ml-1 before:pr-1 before:content-["+"]'>
{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 showSecondaryText = !props.secondaryAmount.isZero()
@ -37,15 +58,15 @@ export default function VaultDepositSubTitle(props: Props) {
return (
<>
{showPrimaryText && primaryText}
{showPrimaryText && showSecondaryText && ' + '}
{showSecondaryText && secondaryText}
{(showPrimaryText || showSecondaryText) && (
<>
{` = `}
<DisplayCurrency
coin={new BNCoin({ denom: baseCurrency.denom, amount: positionValue.toString() })}
/>
</>
<DisplayCurrency
className={classNames(
'text-xs mt-1 text-white/60 ml-1 inline',
'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 VaultDeposit from 'components/Modals/Vault/VaultDeposits'
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 useDepositVault from 'hooks/broadcast/useDepositVault'
import useDisplayAsset from 'hooks/useDisplayAsset'
import useIsOpenArray from 'hooks/useIsOpenArray'
import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array'
import { convertToDisplayAmount, magnify } from 'utils/formatters'
import { getCoinValue, magnify } from 'utils/formatters'
import { getCapLeftWithBuffer } from 'utils/generic'
import { mergeBNCoinArrays } from 'utils/helpers'
interface Props {
vault: Vault | DepositedVault
@ -26,17 +30,14 @@ interface Props {
}
export default function VaultModalContent(props: Props) {
const {
addDebts,
removeDeposits,
addedDebts,
removedDeposits,
removedLends,
removeLends,
addVaultValues,
} = useUpdatedAccount(props.account)
const { addDebts, addedDebts, removedDeposits, removedLends, simulateVaultDeposit } =
useUpdatedAccount(props.account)
const { data: prices } = usePrices()
const [displayCurrency] = useLocalStorage<string>(
DISPLAY_CURRENCY_KEY,
DEFAULT_SETTINGS.displayCurrency,
)
const [isOpen, toggleOpen] = useIsOpenArray(2, false)
const [isCustomRatio, setIsCustomRatio] = useState(false)
const [selectedCoins, setSelectedCoins] = useState<BNCoin[]>([])
@ -53,9 +54,8 @@ export default function VaultModalContent(props: Props) {
if (totalValue.isGreaterThan(capLeft)) {
const amount = magnify(
convertToDisplayAmount(
getCoinValue(
BNCoin.fromDenomAndBigNumber(props.vault.cap.denom, capLeft),
displayAsset.denom,
prices,
).toString(),
displayAsset,
@ -68,76 +68,58 @@ export default function VaultModalContent(props: Props) {
const handleDepositSelect = useCallback(
(selectedCoins: BNCoin[]) => {
const reclaims: BNCoin[] = []
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)
simulateVaultDeposit(props.vault.address, 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(
(isCustomRatio: boolean) => setIsCustomRatio(isCustomRatio),
[setIsCustomRatio],
)
const deposits = useMemo(
() => mergeBNCoinArrays(removedDeposits, removedLends),
[removedDeposits, removedLends],
)
function getDepositSubTitle() {
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
return (
<VaultDepositSubTitle
primaryAmount={
removedDeposits.find((coin) => coin.denom === props.primaryAsset.denom)?.amount || BN_ZERO
deposits.find((coin) => coin.denom === props.primaryAsset.denom)?.amount || BN_ZERO
}
secondaryAmount={
removedDeposits.find((coin) => coin.denom === props.secondaryAsset.denom)?.amount ||
BN_ZERO
deposits.find((coin) => coin.denom === props.secondaryAsset.denom)?.amount || BN_ZERO
}
primaryAsset={props.primaryAsset}
secondaryAsset={props.secondaryAsset}
displayCurrency={displayCurrency}
/>
)
}
function getBorrowingsSubTitle() {
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
return <VaultBorrowingsSubTitle borrowings={addedDebts} />
return <VaultBorrowingsSubTitle borrowings={addedDebts} displayCurrency={displayCurrency} />
}
return (
@ -156,6 +138,7 @@ export default function VaultModalContent(props: Props) {
toggleOpen={toggleOpen}
isCustomRatio={isCustomRatio}
onChangeIsCustomRatio={onChangeIsCustomRatio}
displayCurrency={displayCurrency}
depositCapReachedCoins={depositCapReachedCoins}
/>
),
@ -168,13 +151,14 @@ export default function VaultModalContent(props: Props) {
renderContent: () => (
<VaultBorrowings
borrowings={addedDebts}
deposits={removedDeposits}
deposits={deposits}
primaryAsset={props.primaryAsset}
secondaryAsset={props.secondaryAsset}
onChangeBorrowings={addDebts}
vault={props.vault}
depositActions={depositActions}
depositCapReachedCoins={depositCapReachedCoins}
displayCurrency={displayCurrency}
/>
),
title: 'Borrow',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,9 +2,9 @@ import useSWR from 'swr'
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(), {
suspense: true,
suspense,
revalidateOnFocus: false,
})
}

View File

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

View File

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

View File

@ -45,7 +45,7 @@ export const calculateAccountValue = (
acc.plus(vaultPosition.values.primary).plus(vaultPosition.values.secondary),
BN_ZERO,
) || BN_ZERO
).shiftedBy(-6)
)
}
return account[type]?.reduce((acc, position) => {
@ -85,13 +85,8 @@ export const calculateAccountApr = (
})
vaults?.forEach((vault) => {
const asset = getAssetByDenom(vault.denoms.lp)
if (!asset) return BN_ZERO
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))
const vaultValue = vault.values.primary.plus(vault.values.secondary)
const positionInterest = vaultValue.multipliedBy(convertApyToApr(vault?.apy ?? 0, 365))
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[] = []
assets.forEach((asset) => {
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()
}
export function convertToDisplayAmount(coin: BNCoin, displayCurrency: string, prices: BNCoin[]) {
const price = prices.find((price) => price.denom === coin.denom)
export function getCoinValue(coin: BNCoin, prices: BNCoin[]) {
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
return coin.amount.shiftedBy(decimals).multipliedBy(price.amount).dividedBy(displayPrice.amount)
return coin.amount.shiftedBy(decimals).multipliedBy(coinPrice.amount)
}
export function convertLiquidityRateToAPR(rate: number) {

View File

@ -1,7 +1,12 @@
import BigNumber from 'bignumber.js'
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 })
export function BN(n: BigNumber.Value) {
return new BigNumber(n)
}
@ -23,3 +28,31 @@ export function asyncThrottle<F extends (...args: any[]) => Promise<any>>(func:
throttled(resolve, reject, args)
}) 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 => {
return (
(Math.pow(1 + apy / 100, numberOfCompoundingPeriods) - 1) * 100 * numberOfCompoundingPeriods
)
const periodicRate = (1 + apy) ** (1 / numberOfCompoundingPeriods) - 1
return periodicRate * numberOfCompoundingPeriods
}
export const combineBNCoins = (coins: BNCoin[]): BNCoin[] => {

View File

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