Mp 3270 deposit cap messages (#416)

* Add Deposit cap for AssetSelector

* Add DepositCap component for Trade

* DepositCap to Vault and Fund

* DepositCap to Vault and Fund

* Small bugixes

* formatting fixes

* formatting fixes

* formatting fixes

* formatting fixes

* run format
This commit is contained in:
Bob van der Helm 2023-09-04 09:46:13 +02:00 committed by GitHub
parent 28ccf8ba84
commit d87fbd2a15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 357 additions and 143 deletions

View File

@ -1,12 +1,12 @@
import { render } from '@testing-library/react' import { render } from '@testing-library/react'
import { ASSETS } from 'constants/assets'
import { BN } from 'utils/helpers'
import useStore from 'store'
import DisplayCurrency from 'components/DisplayCurrency' import DisplayCurrency from 'components/DisplayCurrency'
import VaultBorrowings, { VaultBorrowingsProps } from 'components/Modals/Vault/VaultBorrowings' import VaultBorrowings, { VaultBorrowingsProps } from 'components/Modals/Vault/VaultBorrowings'
import { ASSETS } from 'constants/assets'
import { TESTNET_VAULTS_META_DATA } from 'constants/vaults' import { TESTNET_VAULTS_META_DATA } from 'constants/vaults'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { BN } from 'utils/helpers'
jest.mock('hooks/usePrices', () => jest.mock('hooks/usePrices', () =>
jest.fn(() => ({ jest.fn(() => ({
@ -58,6 +58,7 @@ describe('<VaultBorrowings />', () => {
deposits: [], deposits: [],
onChangeBorrowings: jest.fn(), onChangeBorrowings: jest.fn(),
depositActions: [], depositActions: [],
depositCapReachedCoins: [],
} }
beforeAll(() => { beforeAll(() => {

View File

@ -33,9 +33,7 @@ export default async function calculateAssetIncentivesApy(
const annualEmission = totalActiveEmissionValue.multipliedBy(SECONDS_IN_A_YEAR) const annualEmission = totalActiveEmissionValue.multipliedBy(SECONDS_IN_A_YEAR)
const totalAnnualReturnsValue = annualEmission.plus(marketReturns) const totalAnnualReturnsValue = annualEmission.plus(marketReturns)
const incentivesApy = totalAnnualReturnsValue.dividedBy(marketLiquidityValue).multipliedBy(100) return totalAnnualReturnsValue.dividedBy(marketLiquidityValue).multipliedBy(100)
return incentivesApy
} catch (ex) { } catch (ex) {
console.error(ex) console.error(ex)
return null return null

View File

@ -3,15 +3,16 @@ import { getParamsQueryClient, getRedBankQueryClient } from 'api/cosmwasm-client
export default async function getMarket(denom: string): Promise<Market> { export default async function getMarket(denom: string): Promise<Market> {
try { try {
const redbankClient = await getRedBankQueryClient() const redBankClient = await getRedBankQueryClient()
const paramsClient = await getParamsQueryClient() const paramsClient = await getParamsQueryClient()
const [market, assetParams] = await Promise.all([ const [market, assetParams, assetCap] = await Promise.all([
redbankClient.market({ denom }), redBankClient.market({ denom }),
paramsClient.assetParams({ denom }), paramsClient.assetParams({ denom }),
paramsClient.totalDeposit({ denom }),
]) ])
return resolveMarketResponse(market, assetParams) return resolveMarketResponse(market, assetParams, assetCap)
} catch (ex) { } catch (ex) {
throw ex throw ex
} }

View File

@ -3,12 +3,10 @@ import { getRedBankQueryClient } from 'api/cosmwasm-client'
export default async function getUnderlyingLiquidityAmount(market: Market): Promise<string> { export default async function getUnderlyingLiquidityAmount(market: Market): Promise<string> {
try { try {
const client = await getRedBankQueryClient() const client = await getRedBankQueryClient()
const marketLiquidityAmount: string = await client.underlyingLiquidityAmount({ return await client.underlyingLiquidityAmount({
denom: market.denom, denom: market.denom,
amountScaled: market.collateralTotalScaled, amountScaled: market.collateralTotalScaled,
}) })
return marketLiquidityAmount
} catch (ex) { } catch (ex) {
throw ex throw ex
} }

View File

@ -4,23 +4,31 @@ import iterateContractQuery from 'utils/iterateContractQuery'
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
import { resolveMarketResponse } from 'utils/resolvers' import { resolveMarketResponse } from 'utils/resolvers'
import { Market as RedBankMarket } from 'types/generated/mars-red-bank/MarsRedBank.types' import { Market as RedBankMarket } from 'types/generated/mars-red-bank/MarsRedBank.types'
import { AssetParamsBaseForAddr as AssetParams } from 'types/generated/mars-params/MarsParams.types' import {
AssetParamsBaseForAddr as AssetParams,
TotalDepositResponse,
} from 'types/generated/mars-params/MarsParams.types'
export default async function getMarkets(): Promise<Market[]> { export default async function getMarkets(): Promise<Market[]> {
try { try {
const redbankClient = await getRedBankQueryClient() const redBankClient = await getRedBankQueryClient()
const paramsClient = await getParamsQueryClient() const paramsClient = await getParamsQueryClient()
const enabledAssets = getEnabledMarketAssets() const enabledAssets = getEnabledMarketAssets()
const [markets, assetParams] = await Promise.all([ const capQueries = enabledAssets.map((asset) =>
iterateContractQuery(redbankClient.markets), paramsClient.totalDeposit({ denom: asset.denom }),
)
const [markets, assetParams, assetCaps] = await Promise.all([
iterateContractQuery(redBankClient.markets),
iterateContractQuery(paramsClient.allAssetParams), iterateContractQuery(paramsClient.allAssetParams),
Promise.all(capQueries),
]) ])
return enabledAssets.map((asset) => return enabledAssets.map((asset) =>
resolveMarketResponse( resolveMarketResponse(
markets.find(byDenom(asset.denom)) as RedBankMarket, markets.find(byDenom(asset.denom)) as RedBankMarket,
assetParams.find(byDenom(asset.denom)) as AssetParams, assetParams.find(byDenom(asset.denom)) as AssetParams,
assetCaps.find(byDenom(asset.denom)) as TotalDepositResponse,
), ),
) )
} catch (ex) { } catch (ex) {

View File

@ -1,8 +1,9 @@
import { useCallback, useEffect, useMemo, useState } from 'react' import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import Button from 'components/Button' import Button from 'components/Button'
import Card from 'components/Card' import Card from 'components/Card'
import DepositCapMessage from 'components/DepositCapMessage'
import FullOverlayContent from 'components/FullOverlayContent' import FullOverlayContent from 'components/FullOverlayContent'
import { Plus } from 'components/Icons' import { Plus } from 'components/Icons'
import SwitchAutoLend from 'components/Switch/SwitchAutoLend' import SwitchAutoLend from 'components/Switch/SwitchAutoLend'
@ -12,6 +13,7 @@ import WalletBridges from 'components/Wallet/WalletBridges'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import useAccounts from 'hooks/useAccounts' import useAccounts from 'hooks/useAccounts'
import useCurrentAccount from 'hooks/useCurrentAccount' import useCurrentAccount from 'hooks/useCurrentAccount'
import useMarketAssets from 'hooks/useMarketAssets'
import useToggle from 'hooks/useToggle' import useToggle from 'hooks/useToggle'
import useWalletBalances from 'hooks/useWalletBalances' import useWalletBalances from 'hooks/useWalletBalances'
import useStore from 'store' import useStore from 'store'
@ -19,6 +21,7 @@ import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
import { getAssetByDenom, getBaseAsset } from 'utils/assets' import { getAssetByDenom, getBaseAsset } from 'utils/assets'
import { defaultFee } from 'utils/constants' import { defaultFee } from 'utils/constants'
import { getCapLeftWithBuffer } from 'utils/generic'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
export default function AccountFund() { export default function AccountFund() {
@ -36,6 +39,7 @@ export default function AccountFund() {
const hasAssetSelected = fundingAssets.length > 0 const hasAssetSelected = fundingAssets.length > 0
const hasFundingAssets = const hasFundingAssets =
fundingAssets.length > 0 && fundingAssets.every((a) => a.toCoin().amount !== '0') fundingAssets.length > 0 && fundingAssets.every((a) => a.toCoin().amount !== '0')
const { data: marketAssets } = useMarketAssets()
const baseBalance = useMemo( const baseBalance = useMemo(
() => walletBalances.find(byDenom(baseAsset.denom))?.amount ?? '0', () => walletBalances.find(byDenom(baseAsset.denom))?.amount ?? '0',
@ -84,16 +88,15 @@ export default function AccountFund() {
setFundingAssets(newFundingAssets) setFundingAssets(newFundingAssets)
}, [selectedDenoms, fundingAssets]) }, [selectedDenoms, fundingAssets])
const updateFundingAssets = useCallback( const updateFundingAssets = useCallback((amount: BigNumber, denom: string) => {
(amount: BigNumber, denom: string) => { setFundingAssets((fundingAssets) => {
const assetToUpdate = fundingAssets.find(byDenom(denom)) const updateIdx = fundingAssets.findIndex(byDenom(denom))
if (assetToUpdate) { if (updateIdx === -1) return fundingAssets
assetToUpdate.amount = amount
setFundingAssets([...fundingAssets.filter((a) => a.denom !== denom), assetToUpdate]) fundingAssets[updateIdx].amount = amount
} return [...fundingAssets]
}, })
[fundingAssets], }, [])
)
useEffect(() => { useEffect(() => {
if (BN(baseBalance).isLessThan(defaultFee.amount[0].amount)) { if (BN(baseBalance).isLessThan(defaultFee.amount[0].amount)) {
@ -106,6 +109,21 @@ export default function AccountFund() {
setSelectedAccountId(currentAccount?.id ?? accountId) setSelectedAccountId(currentAccount?.id ?? accountId)
}, [accounts, selectedAccountId, accountId, currentAccount]) }, [accounts, selectedAccountId, accountId, currentAccount])
const depositCapReachedCoins = useMemo(() => {
const depositCapReachedCoins: BNCoin[] = []
fundingAssets.forEach((asset) => {
const marketAsset = marketAssets.find(byDenom(asset.denom))
if (!marketAsset) return
const capLeft = getCapLeftWithBuffer(marketAsset.cap)
if (asset.amount.isLessThanOrEqualTo(capLeft)) return
depositCapReachedCoins.push(BNCoin.fromDenomAndBigNumber(asset.denom, capLeft))
})
return depositCapReachedCoins
}, [fundingAssets, marketAssets])
if (!selectedAccountId) return null if (!selectedAccountId) return null
return ( return (
@ -147,6 +165,12 @@ export default function AccountFund() {
onClick={handleSelectAssetsClick} onClick={handleSelectAssetsClick}
disabled={isFunding} disabled={isFunding}
/> />
<DepositCapMessage
action='fund'
coins={depositCapReachedCoins}
className='pr-4 py-2 mt-4'
showIcon
/>
<SwitchAutoLend <SwitchAutoLend
className='pt-4 mt-4 border border-transparent border-t-white/10' className='pt-4 mt-4 border border-transparent border-t-white/10'
accountId={selectedAccountId} accountId={selectedAccountId}
@ -155,7 +179,7 @@ export default function AccountFund() {
className='w-full mt-4' className='w-full mt-4'
text='Fund account' text='Fund account'
color='tertiary' color='tertiary'
disabled={!hasFundingAssets} disabled={!hasFundingAssets || depositCapReachedCoins.length > 0}
showProgressIndicator={isFunding} showProgressIndicator={isFunding}
onClick={handleClick} onClick={handleClick}
size='lg' size='lg'

View File

@ -0,0 +1,50 @@
import classNames from 'classnames'
import { HTMLAttributes } from 'react'
import { FormattedNumber } from 'components/FormattedNumber'
import { InfoCircle } from 'components/Icons'
import Text from 'components/Text'
import { BNCoin } from 'types/classes/BNCoin'
import { getAssetByDenom } from 'utils/assets'
interface Props extends HTMLAttributes<HTMLDivElement> {
action: 'buy' | 'deposit' | 'fund'
coins: BNCoin[]
showIcon?: boolean
}
export default function DepositCapMessage(props: Props) {
if (!props.coins.length) return null
return (
<div className={classNames('flex items-start', props.className)}>
{props.showIcon && <InfoCircle width={26} className='mr-5' />}
<div className='flex-col gap-2 flex'>
<Text size='sm'>Deposit Cap Reached</Text>
<Text size='xs' className='text-white/40'>{`Unfortunately you're not able to ${
props.action
} more than the following amount${props.coins.length > 1 ? 's' : ''}:`}</Text>
{props.coins.map((coin) => {
const asset = getAssetByDenom(coin.denom)
if (!asset) return null
return (
<div key={coin.denom} className='flex gap-1'>
<Text size='xs'>Cap Left:</Text>
<FormattedNumber
amount={coin.amount.toNumber()}
options={{
abbreviated: true,
decimals: asset.decimals,
suffix: ` ${asset.symbol}`,
}}
className='text-white/60 text-xs'
/>
</div>
)
})}
</div>
</div>
)
}

View File

@ -1,5 +1,5 @@
import { useMemo } from 'react'
import classNames from 'classnames' import classNames from 'classnames'
import { useMemo } from 'react'
import { FormattedNumber } from 'components/FormattedNumber' import { FormattedNumber } from 'components/FormattedNumber'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings' import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
@ -14,7 +14,7 @@ interface Props {
coin: BNCoin coin: BNCoin
className?: string className?: string
isApproximation?: boolean isApproximation?: boolean
parantheses?: boolean parentheses?: boolean
} }
export default function DisplayCurrency(props: Props) { export default function DisplayCurrency(props: Props) {
@ -43,7 +43,7 @@ export default function DisplayCurrency(props: Props) {
<FormattedNumber <FormattedNumber
className={classNames( className={classNames(
props.className, props.className,
props.parantheses && 'before:content-["("] after:content-[")"]', props.parentheses && 'before:content-["("] after:content-[")"]',
)} )}
amount={convertToDisplayAmount(props.coin, displayCurrency, prices).toNumber()} amount={convertToDisplayAmount(props.coin, displayCurrency, prices).toNumber()}
options={{ options={{

View File

@ -107,7 +107,7 @@ export default function VaultExpanded(props: Props) {
!isExpanded && props.row.toggleExpanded() !isExpanded && props.row.toggleExpanded()
}} }}
> >
<td colSpan={!status ? 5 : 6}> <td colSpan={!status ? 7 : 8}>
<div className='align-center flex justify-end gap-3 p-4'> <div className='align-center flex justify-end gap-3 p-4'>
{status && <DepositMoreButton />} {status && <DepositMoreButton />}
{status === VaultStatus.ACTIVE && <UnlockButton />} {status === VaultStatus.ACTIVE && <UnlockButton />}

View File

@ -182,6 +182,7 @@ export const VaultTable = (props: Props) => {
accessorKey: 'ltv.max', accessorKey: 'ltv.max',
header: 'Max LTV', header: 'Max LTV',
cell: ({ row }) => { cell: ({ row }) => {
if (props.isLoading) return <Loading />
return <Text className='text-xs'>{formatPercent(row.original.ltv.max)}</Text> return <Text className='text-xs'>{formatPercent(row.original.ltv.max)}</Text>
}, },
}, },
@ -189,6 +190,7 @@ export const VaultTable = (props: Props) => {
accessorKey: 'ltv.liq', accessorKey: 'ltv.liq',
header: 'Liq. LTV', header: 'Liq. LTV',
cell: ({ row }) => { cell: ({ row }) => {
if (props.isLoading) return <Loading />
return <Text className='text-xs'>{formatPercent(row.original.ltv.liq)}</Text> return <Text className='text-xs'>{formatPercent(row.original.ltv.liq)}</Text>
}, },
}, },

View File

@ -9,11 +9,11 @@ import Settings from 'components/Settings'
import Wallet from 'components/Wallet' import Wallet from 'components/Wallet'
import useStore from 'store' import useStore from 'store'
export const menuTree: { page: Page; label: string }[] = [ export const menuTree: { pages: Page[]; label: string }[] = [
{ page: 'trade', label: 'Trade' }, { pages: ['trade'], label: 'Trade' },
{ page: 'lend', label: 'Earn' }, { pages: ['lend', 'farm'], label: 'Earn' },
{ page: 'borrow', label: 'Borrow' }, { pages: ['borrow'], label: 'Borrow' },
{ page: 'portfolio', label: 'Portfolio' }, { pages: ['portfolio'], label: 'Portfolio' },
] ]
export default function DesktopHeader() { export default function DesktopHeader() {

View File

@ -207,7 +207,7 @@ function BorrowModal(props: Props) {
asset.denom, asset.denom,
modal.marketData?.liquidity?.amount ?? BN_ZERO, modal.marketData?.liquidity?.amount ?? BN_ZERO,
)} )}
parantheses parentheses
/> />
</div> </div>
<Text size='xs' className='text-white/50' tag='span'> <Text size='xs' className='text-white/50' tag='span'>

View File

@ -1,6 +1,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react' import React, { useCallback, useEffect, useMemo, useState } from 'react'
import Button from 'components/Button' import Button from 'components/Button'
import DepositCapMessage from 'components/DepositCapMessage'
import { ArrowRight, Plus } from 'components/Icons' import { ArrowRight, Plus } from 'components/Icons'
import SwitchAutoLend from 'components/Switch/SwitchAutoLend' import SwitchAutoLend from 'components/Switch/SwitchAutoLend'
import Text from 'components/Text' import Text from 'components/Text'
@ -8,6 +9,7 @@ import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import WalletBridges from 'components/Wallet/WalletBridges' import WalletBridges from 'components/Wallet/WalletBridges'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import useAutoLendEnabledAccountIds from 'hooks/useAutoLendEnabledAccountIds' import useAutoLendEnabledAccountIds from 'hooks/useAutoLendEnabledAccountIds'
import useMarketAssets from 'hooks/useMarketAssets'
import useToggle from 'hooks/useToggle' import useToggle from 'hooks/useToggle'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount' import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import useWalletBalances from 'hooks/useWalletBalances' import useWalletBalances from 'hooks/useWalletBalances'
@ -16,6 +18,7 @@ import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
import { getAssetByDenom, getBaseAsset } from 'utils/assets' import { getAssetByDenom, getBaseAsset } from 'utils/assets'
import { defaultFee } from 'utils/constants' import { defaultFee } from 'utils/constants'
import { getCapLeftWithBuffer } from 'utils/generic'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
interface Props { interface Props {
@ -38,7 +41,7 @@ export default function FundAccount(props: Props) {
const { autoLendEnabledAccountIds } = useAutoLendEnabledAccountIds() const { autoLendEnabledAccountIds } = useAutoLendEnabledAccountIds()
const isAutoLendEnabled = autoLendEnabledAccountIds.includes(accountId) const isAutoLendEnabled = autoLendEnabledAccountIds.includes(accountId)
const { simulateDeposits } = useUpdatedAccount(account) const { simulateDeposits } = useUpdatedAccount(account)
const { data: marketAssets } = useMarketAssets()
const baseBalance = useMemo( const baseBalance = useMemo(
() => walletBalances.find(byDenom(baseAsset.denom))?.amount ?? '0', () => walletBalances.find(byDenom(baseAsset.denom))?.amount ?? '0',
[walletBalances, baseAsset], [walletBalances, baseAsset],
@ -87,16 +90,15 @@ export default function FundAccount(props: Props) {
setFundingAssets(newFundingAssets) setFundingAssets(newFundingAssets)
}, [selectedDenoms, fundingAssets]) }, [selectedDenoms, fundingAssets])
const updateFundingAssets = useCallback( const updateFundingAssets = useCallback((amount: BigNumber, denom: string) => {
(amount: BigNumber, denom: string) => { setFundingAssets((fundingAssets) => {
const assetToUpdate = fundingAssets.find(byDenom(denom)) const updateIdx = fundingAssets.findIndex(byDenom(denom))
if (assetToUpdate) { if (updateIdx === -1) return fundingAssets
assetToUpdate.amount = amount
setFundingAssets([...fundingAssets.filter((a) => a.denom !== denom), assetToUpdate]) fundingAssets[updateIdx].amount = amount
} return [...fundingAssets]
}, })
[fundingAssets], }, [])
)
useEffect(() => { useEffect(() => {
simulateDeposits(isAutoLendEnabled ? 'lend' : 'deposit', fundingAssets) simulateDeposits(isAutoLendEnabled ? 'lend' : 'deposit', fundingAssets)
@ -108,6 +110,21 @@ export default function FundAccount(props: Props) {
} }
}, [baseBalance]) }, [baseBalance])
const depositCapReachedCoins = useMemo(() => {
const depositCapReachedCoins: BNCoin[] = []
fundingAssets.forEach((asset) => {
const marketAsset = marketAssets.find(byDenom(asset.denom))
if (!marketAsset) return
const capLeft = getCapLeftWithBuffer(marketAsset.cap)
if (asset.amount.isLessThanOrEqualTo(capLeft)) return
depositCapReachedCoins.push(BNCoin.fromDenomAndBigNumber(asset.denom, capLeft))
})
return depositCapReachedCoins
}, [fundingAssets, marketAssets])
return ( return (
<> <>
<div className='flex flex-wrap items-start'> <div className='flex flex-wrap items-start'>
@ -139,6 +156,12 @@ export default function FundAccount(props: Props) {
onClick={handleSelectAssetsClick} onClick={handleSelectAssetsClick}
disabled={isFunding} disabled={isFunding}
/> />
<DepositCapMessage
action='fund'
coins={depositCapReachedCoins}
className='pr-4 py-2 mt-4'
showIcon
/>
<SwitchAutoLend <SwitchAutoLend
className='pt-4 mt-4 border border-transparent border-t-white/10' className='pt-4 mt-4 border border-transparent border-t-white/10'
accountId={accountId} accountId={accountId}
@ -148,7 +171,7 @@ export default function FundAccount(props: Props) {
className='w-full mt-4' className='w-full mt-4'
text='Fund account' text='Fund account'
rightIcon={<ArrowRight />} rightIcon={<ArrowRight />}
disabled={!hasFundingAssets} disabled={!hasFundingAssets || depositCapReachedCoins.length > 0}
showProgressIndicator={isFunding} showProgressIndicator={isFunding}
onClick={handleClick} onClick={handleClick}
/> />

View File

@ -2,6 +2,7 @@ import BigNumber from 'bignumber.js'
import React, { useEffect, useMemo, useState } from 'react' import React, { useEffect, useMemo, useState } from 'react'
import Button from 'components/Button' import Button from 'components/Button'
import DepositCapMessage from 'components/DepositCapMessage'
import DisplayCurrency from 'components/DisplayCurrency' import DisplayCurrency from 'components/DisplayCurrency'
import Divider from 'components/Divider' import Divider from 'components/Divider'
import { ArrowRight, ExclamationMarkCircled } from 'components/Icons' import { ArrowRight, ExclamationMarkCircled } from 'components/Icons'
@ -27,6 +28,7 @@ export interface VaultBorrowingsProps {
vault: Vault vault: Vault
depositActions: Action[] depositActions: Action[]
onChangeBorrowings: (borrowings: BNCoin[]) => void onChangeBorrowings: (borrowings: BNCoin[]) => void
depositCapReachedCoins: BNCoin[]
} }
export default function VaultBorrowings(props: VaultBorrowingsProps) { export default function VaultBorrowings(props: VaultBorrowingsProps) {
@ -187,12 +189,21 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
</Text> </Text>
</div> </div>
)} )}
<Button <Button
text='Select borrow assets +' text='Select borrow assets +'
color='tertiary' color='tertiary'
onClick={addAsset} onClick={addAsset}
disabled={isConfirming} disabled={isConfirming}
/> />
<DepositCapMessage
action='deposit'
coins={props.depositCapReachedCoins}
className='px-4 y-2'
showIcon
/>
<Divider /> <Divider />
<div className='flex flex-col gap-2'> <div className='flex flex-col gap-2'>
<div className='flex justify-between'> <div className='flex justify-between'>
@ -221,7 +232,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
text='Deposit' text='Deposit'
rightIcon={<ArrowRight />} rightIcon={<ArrowRight />}
showProgressIndicator={isConfirming} showProgressIndicator={isConfirming}
disabled={!props.depositActions.length} disabled={!props.depositActions.length || props.depositCapReachedCoins.length > 0}
/> />
</div> </div>
) )

View File

@ -1,7 +1,7 @@
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import { useMemo, useState } from 'react'
import Button from 'components/Button' import Button from 'components/Button'
import DepositCapMessage from 'components/DepositCapMessage'
import DisplayCurrency from 'components/DisplayCurrency' 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'
@ -12,6 +12,7 @@ import Text from 'components/Text'
import TokenInput from 'components/TokenInput' import TokenInput from 'components/TokenInput'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import usePrice from 'hooks/usePrice' import usePrice from 'hooks/usePrice'
import { useMemo, useState } from 'react'
import useStore from 'store' 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'
@ -27,6 +28,7 @@ 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
depositCapReachedCoins: BNCoin[]
} }
export default function VaultDeposit(props: Props) { export default function VaultDeposit(props: Props) {
@ -158,7 +160,7 @@ export default function VaultDeposit(props: Props) {
return ( return (
<div className='flex flex-col'> <div className='flex flex-col'>
<div className='flex gap-4 p-4'> <div className='flex gap-4 pl-3 p-4'>
<div className='flex flex-col items-center justify-between gap-1 pb-[30px] pt-2'> <div className='flex flex-col items-center justify-between gap-1 pb-[30px] pt-2'>
<Gauge <Gauge
percentage={primaryValuePercentage} percentage={primaryValuePercentage}
@ -202,6 +204,14 @@ export default function VaultDeposit(props: Props) {
/> />
</div> </div>
</div> </div>
<DepositCapMessage
action='deposit'
coins={props.depositCapReachedCoins}
className='px-4 "y-2'
showIcon
/>
<div className='flex flex-col gap-6 p-4 pt-2'> <div className='flex flex-col gap-6 p-4 pt-2'>
{disableInput ? ( {disableInput ? (
<div> <div>

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
import Accordion from 'components/Accordion' import Accordion from 'components/Accordion'
import AccountSummary from 'components/Account/AccountSummary' import AccountSummary from 'components/Account/AccountSummary'
@ -8,10 +8,14 @@ import VaultDeposit from 'components/Modals/Vault/VaultDeposits'
import VaultDepositSubTitle from 'components/Modals/Vault/VaultDepositsSubTitle' import VaultDepositSubTitle from 'components/Modals/Vault/VaultDepositsSubTitle'
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 useIsOpenArray from 'hooks/useIsOpenArray' import useIsOpenArray from 'hooks/useIsOpenArray'
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 { byDenom } from 'utils/array'
import { convertToDisplayAmount, magnify } from 'utils/formatters'
import { getCapLeftWithBuffer } from 'utils/generic'
interface Props { interface Props {
vault: Vault | DepositedVault vault: Vault | DepositedVault
@ -29,14 +33,14 @@ export default function VaultModalContent(props: Props) {
removedDeposits, removedDeposits,
removedLends, removedLends,
removeLends, removeLends,
updatedAccount,
addVaultValues, addVaultValues,
} = useUpdatedAccount(props.account) } = useUpdatedAccount(props.account)
const { data: prices } = usePrices()
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[]>([])
const displayAsset = useDisplayAsset()
const { actions: depositActions, totalValue } = useDepositVault({ const { actions: depositActions, totalValue } = useDepositVault({
vault: props.vault, vault: props.vault,
reclaims: removedLends, reclaims: removedLends,
@ -44,6 +48,24 @@ export default function VaultModalContent(props: Props) {
borrowings: addedDebts, borrowings: addedDebts,
}) })
const depositCapReachedCoins = useMemo(() => {
const capLeft = getCapLeftWithBuffer(props.vault.cap)
if (totalValue.isGreaterThan(capLeft)) {
const amount = magnify(
convertToDisplayAmount(
BNCoin.fromDenomAndBigNumber(props.vault.cap.denom, capLeft),
displayAsset.denom,
prices,
).toString(),
displayAsset,
)
return [BNCoin.fromDenomAndBigNumber(displayAsset.denom, amount)]
}
return []
}, [displayAsset, prices, props.vault.cap, totalValue])
const handleDepositSelect = useCallback( const handleDepositSelect = useCallback(
(selectedCoins: BNCoin[]) => { (selectedCoins: BNCoin[]) => {
const reclaims: BNCoin[] = [] const reclaims: BNCoin[] = []
@ -134,6 +156,7 @@ export default function VaultModalContent(props: Props) {
toggleOpen={toggleOpen} toggleOpen={toggleOpen}
isCustomRatio={isCustomRatio} isCustomRatio={isCustomRatio}
onChangeIsCustomRatio={onChangeIsCustomRatio} onChangeIsCustomRatio={onChangeIsCustomRatio}
depositCapReachedCoins={depositCapReachedCoins}
/> />
), ),
title: 'Deposit', title: 'Deposit',
@ -151,6 +174,7 @@ export default function VaultModalContent(props: Props) {
onChangeBorrowings={addDebts} onChangeBorrowings={addDebts}
vault={props.vault} vault={props.vault}
depositActions={depositActions} depositActions={depositActions}
depositCapReachedCoins={depositCapReachedCoins}
/> />
), ),
title: 'Borrow', title: 'Borrow',

View File

@ -1,5 +1,5 @@
import { useParams } from 'react-router-dom'
import classNames from 'classnames' import classNames from 'classnames'
import { useParams } from 'react-router-dom'
import { menuTree } from 'components/Header/DesktopHeader' import { menuTree } from 'components/Header/DesktopHeader'
import { Logo } from 'components/Icons' import { Logo } from 'components/Icons'
@ -11,8 +11,8 @@ export default function DesktopNavigation() {
const { address, accountId } = useParams() const { address, accountId } = useParams()
const focusComponent = useStore((s) => s.focusComponent) const focusComponent = useStore((s) => s.focusComponent)
function getIsActive(href: string) { function getIsActive(pages: string[]) {
return location.pathname.includes(href) return pages.some((page) => location.pathname.includes(page))
} }
return ( return (
@ -31,8 +31,8 @@ export default function DesktopNavigation() {
{menuTree.map((item, index) => ( {menuTree.map((item, index) => (
<NavLink <NavLink
key={index} key={index}
href={getRoute(item.page, address, accountId)} href={getRoute(item.pages[0], address, accountId)}
isActive={getIsActive(item.page)} isActive={getIsActive(item.pages)}
> >
{item.label} {item.label}
</NavLink> </NavLink>

View File

@ -12,12 +12,10 @@ export const NavLink = (props: Props) => {
return ( return (
<Link <Link
to={props.href} to={props.href}
className={({ isActive }) => className={classNames(
classNames(
'text-sm font-semibold hover:text-white active:text-white', 'text-sm font-semibold hover:text-white active:text-white',
isActive ? 'pointer-events-none text-white' : 'text-white/60', props.isActive ? 'pointer-events-none text-white' : 'text-white/60',
) )}
}
> >
{props.children} {props.children}
</Link> </Link>

View File

@ -10,6 +10,7 @@ import { BNCoin } from 'types/classes/BNCoin'
interface Props { interface Props {
asset: Asset asset: Asset
onSelectAsset: (asset: Asset) => void onSelectAsset: (asset: Asset) => void
depositCap?: DepositCap
} }
export default function AssetItem(props: Props) { export default function AssetItem(props: Props) {
@ -40,6 +41,8 @@ export default function AssetItem(props: Props) {
{asset.isFavorite ? <StarFilled width={16} /> : <StarOutlined width={16} />} {asset.isFavorite ? <StarFilled width={16} /> : <StarOutlined width={16} />}
</div> </div>
<AssetImage asset={asset} size={24} /> <AssetImage asset={asset} size={24} />
<div className='flex-col'>
<div className='flex gap-1'>
<Text size='sm' className='text-left'> <Text size='sm' className='text-left'>
{asset.name} {asset.name}
</Text> </Text>
@ -47,6 +50,17 @@ export default function AssetItem(props: Props) {
<Text size='xs'>{asset.symbol}</Text> <Text size='xs'>{asset.symbol}</Text>
</div> </div>
</div> </div>
{props.depositCap && (
<div className='flex gap-1'>
<span className='text-left text-white/60 text-xs'>Cap Left: </span>
<DisplayCurrency
className='text-left text-white/60 text-xs'
coin={BNCoin.fromDenomAndBigNumber(props.depositCap.denom, props.depositCap.max)}
/>
</div>
)}
</div>
</div>
<DisplayCurrency <DisplayCurrency
className='text-sm' className='text-sm'
coin={ coin={

View File

@ -3,6 +3,8 @@ import classNames from 'classnames'
import { ChevronDown } from 'components/Icons' import { ChevronDown } from 'components/Icons'
import Text from 'components/Text' import Text from 'components/Text'
import AssetItem from 'components/Trade/TradeModule/AssetSelector/AssetItem' import AssetItem from 'components/Trade/TradeModule/AssetSelector/AssetItem'
import useMarketAssets from 'hooks/useMarketAssets'
import { byDenom } from 'utils/array'
interface Props { interface Props {
type: 'buy' | 'sell' type: 'buy' | 'sell'
@ -13,6 +15,8 @@ interface Props {
} }
export default function AssetList(props: Props) { export default function AssetList(props: Props) {
const { data: marketAssets } = useMarketAssets()
return ( return (
<section> <section>
<button <button
@ -34,6 +38,9 @@ export default function AssetList(props: Props) {
key={`${props.type}-${asset.symbol}`} key={`${props.type}-${asset.symbol}`}
asset={asset} asset={asset}
onSelectAsset={props.onChangeAsset} onSelectAsset={props.onChangeAsset}
depositCap={
props.type === 'buy' ? marketAssets?.find(byDenom(asset.denom))?.cap : undefined
}
/> />
))} ))}
</ul> </ul>

View File

@ -4,8 +4,8 @@ import { InfoCircle } from 'components/Icons'
import Text from 'components/Text' import Text from 'components/Text'
import { Tooltip } from 'components/Tooltip' import { Tooltip } from 'components/Tooltip'
import { ORDER_TYPE_TABS } from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector/constants' import { ORDER_TYPE_TABS } from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector/constants'
import ConditionalWrapper from 'hocs/ConditionalWrapper'
import { AvailableOrderType } from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector/types' import { AvailableOrderType } from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector/types'
import ConditionalWrapper from 'hocs/ConditionalWrapper'
interface Props { interface Props {
selected: AvailableOrderType selected: AvailableOrderType
@ -46,9 +46,9 @@ export default function OrderTypeSelector(props: Props) {
} }
const className = { const className = {
wrapper: 'flex flex-1 flex-row px-3 pt-3', wrapper: 'flex flex-1 flex-row px-3 pt-4',
tab: 'mr-4 pb-2 cursor-pointer select-none flex flex-row', tab: 'mr-4 pb-2 cursor-pointer select-none flex flex-row',
selectedTab: 'border-b-2 border-pink border-solid', selectedTab: 'border-b-2 border-pink border-solid',
disabledTab: 'opacity-50 pointer-events-none', disabledTab: 'opacity-20 pointer-events-none',
infoCircle: 'w-4 h-4 ml-2 mt-1', infoCircle: 'w-4 h-4 ml-2 mt-1',
} }

View File

@ -2,6 +2,7 @@ import debounce from 'lodash.debounce'
import { useCallback, useEffect, useMemo, useState } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
import estimateExactIn from 'api/swap/estimateExactIn' import estimateExactIn from 'api/swap/estimateExactIn'
import DepositCapMessage from 'components/DepositCapMessage'
import Divider from 'components/Divider' import Divider from 'components/Divider'
import RangeInput from 'components/RangeInput' import RangeInput from 'components/RangeInput'
import AssetAmountInput from 'components/Trade/TradeModule/SwapForm/AssetAmountInput' import AssetAmountInput from 'components/Trade/TradeModule/SwapForm/AssetAmountInput'
@ -16,6 +17,7 @@ import useAutoLendEnabledAccountIds from 'hooks/useAutoLendEnabledAccountIds'
import useCurrentAccount from 'hooks/useCurrentAccount' import useCurrentAccount from 'hooks/useCurrentAccount'
import useHealthComputer from 'hooks/useHealthComputer' import useHealthComputer from 'hooks/useHealthComputer'
import useLocalStorage from 'hooks/useLocalStorage' import useLocalStorage from 'hooks/useLocalStorage'
import useMarketAssets from 'hooks/useMarketAssets'
import useMarketBorrowings from 'hooks/useMarketBorrowings' import useMarketBorrowings from 'hooks/useMarketBorrowings'
import usePrices from 'hooks/usePrices' import usePrices from 'hooks/usePrices'
import useToggle from 'hooks/useToggle' import useToggle from 'hooks/useToggle'
@ -24,6 +26,7 @@ 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 { getCapLeftWithBuffer } from 'utils/generic'
import { asyncThrottle, BN } from 'utils/helpers' import { asyncThrottle, BN } from 'utils/helpers'
interface Props { interface Props {
@ -39,6 +42,7 @@ export default function SwapForm(props: Props) {
const [slippage] = useLocalStorage(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage) const [slippage] = useLocalStorage(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage)
const { computeMaxSwapAmount } = useHealthComputer(account) const { computeMaxSwapAmount } = useHealthComputer(account)
const { data: borrowAssets } = useMarketBorrowings() const { data: borrowAssets } = useMarketBorrowings()
const { data: marketAssets } = useMarketAssets()
const [isMarginChecked, setMarginChecked] = useToggle() const [isMarginChecked, setMarginChecked] = useToggle()
const [buyAssetAmount, setBuyAssetAmount] = useState(BN_ZERO) const [buyAssetAmount, setBuyAssetAmount] = useState(BN_ZERO)
@ -58,6 +62,19 @@ export default function SwapForm(props: Props) {
[borrowAssets, sellAsset.denom], [borrowAssets, sellAsset.denom],
) )
const depositCapReachedCoins: BNCoin[] = useMemo(() => {
const buyMarketAsset = marketAssets.find(byDenom(buyAsset.denom))
if (!buyMarketAsset) return []
const depositCapLeft = getCapLeftWithBuffer(buyMarketAsset.cap)
if (buyAssetAmount.isGreaterThan(depositCapLeft)) {
return [BNCoin.fromDenomAndBigNumber(buyAsset.denom, depositCapLeft)]
}
return []
}, [marketAssets, buyAsset.denom, buyAssetAmount])
const onChangeSellAmount = useCallback( const onChangeSellAmount = useCallback(
(amount: BigNumber) => { (amount: BigNumber) => {
setSellAssetAmount(amount) setSellAssetAmount(amount)
@ -233,6 +250,7 @@ export default function SwapForm(props: Props) {
/> />
<Divider /> <Divider />
<OrderTypeSelector selected={selectedOrderType} onChange={setSelectedOrderType} /> <OrderTypeSelector selected={selectedOrderType} onChange={setSelectedOrderType} />
<div className='flex flex-col gap-6 px-3 mt-6'>
<AssetAmountInput <AssetAmountInput
label='Buy' label='Buy'
max={maxBuyableAmountEstimation} max={maxBuyableAmountEstimation}
@ -241,12 +259,10 @@ export default function SwapForm(props: Props) {
asset={buyAsset} asset={buyAsset}
assetUSDValue={buyAssetValue} assetUSDValue={buyAssetValue}
maxButtonLabel='Max Amount:' maxButtonLabel='Max Amount:'
containerClassName='mx-3 my-6'
disabled={isConfirming} disabled={isConfirming}
/> />
<RangeInput <RangeInput
wrapperClassName='p-4'
disabled={isConfirming || maxBuyableAmountEstimation.isZero()} disabled={isConfirming || maxBuyableAmountEstimation.isZero()}
onChange={handleRangeInputChange} onChange={handleRangeInputChange}
value={buyAssetAmount.shiftedBy(-buyAsset.decimals).toNumber()} value={buyAssetAmount.shiftedBy(-buyAsset.decimals).toNumber()}
@ -258,6 +274,8 @@ export default function SwapForm(props: Props) {
} }
/> />
<DepositCapMessage action='buy' coins={depositCapReachedCoins} className='p-4 bg-white/5' />
<AssetAmountInput <AssetAmountInput
label='Sell' label='Sell'
max={maxSellAmount} max={maxSellAmount}
@ -266,17 +284,15 @@ export default function SwapForm(props: Props) {
assetUSDValue={sellAssetValue} assetUSDValue={sellAssetValue}
asset={sellAsset} asset={sellAsset}
maxButtonLabel='Balance:' maxButtonLabel='Balance:'
containerClassName='mx-3'
disabled={isConfirming} disabled={isConfirming}
/> />
<TradeSummary <TradeSummary
containerClassName='m-3 mt-10'
buyAsset={buyAsset} buyAsset={buyAsset}
sellAsset={sellAsset} sellAsset={sellAsset}
borrowRate={borrowAsset?.borrowRate} borrowRate={borrowAsset?.borrowRate}
buyAction={handleBuyClick} buyAction={handleBuyClick}
buyButtonDisabled={sellAssetAmount.isZero()} buyButtonDisabled={sellAssetAmount.isZero() || depositCapReachedCoins.length > 0}
showProgressIndicator={isConfirming} showProgressIndicator={isConfirming}
isMargin={isMarginChecked} isMargin={isMarginChecked}
borrowAmount={ borrowAmount={
@ -286,6 +302,7 @@ export default function SwapForm(props: Props) {
} }
estimatedFee={estimatedFee} estimatedFee={estimatedFee}
/> />
</div>
</> </>
) )
} }

View File

@ -0,0 +1,14 @@
import { ASSETS } from 'constants/assets'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { DISPLAY_CURRENCY_KEY } from 'constants/localStore'
import useLocalStorage from 'hooks/useLocalStorage'
import { byDenom } from 'utils/array'
export default function useDisplayAsset() {
const [displayCurrency] = useLocalStorage<string>(
DISPLAY_CURRENCY_KEY,
DEFAULT_SETTINGS.displayCurrency,
)
return ASSETS.find(byDenom(displayCurrency)) ?? ASSETS[0]
}

View File

@ -15,6 +15,7 @@ import {
HealthComputer, HealthComputer,
} from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types' } from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types'
import { convertAccountToPositions } from 'utils/accounts' import { convertAccountToPositions } from 'utils/accounts'
import { LTV_BUFFER } from 'utils/constants'
import { import {
BorrowTarget, BorrowTarget,
compute_health_js, compute_health_js,
@ -24,7 +25,6 @@ import {
SwapKind, SwapKind,
} from 'utils/health_computer' } from 'utils/health_computer'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
import { LTV_BUFFER } from 'utils/constants'
export default function useHealthComputer(account?: Account) { export default function useHealthComputer(account?: Account) {
const { data: prices } = usePrices() const { data: prices } = usePrices()

View File

@ -23,7 +23,7 @@ function useLendingMarketAssetsTableData(): {
const accountLentAssets: LendingMarketTableData[] = [], const accountLentAssets: LendingMarketTableData[] = [],
availableAssets: LendingMarketTableData[] = [] availableAssets: LendingMarketTableData[] = []
markets.forEach(({ denom, depositCap, liquidityRate, liquidationThreshold, maxLtv }) => { markets.forEach(({ denom, cap, liquidityRate, liquidationThreshold, maxLtv }) => {
const asset = getAssetByDenom(denom) as Asset const asset = getAssetByDenom(denom) as Asset
const marketDepositAmount = BN(marketDeposits.find(byDenom(denom))?.amount ?? 0) const marketDepositAmount = BN(marketDeposits.find(byDenom(denom))?.amount ?? 0)
const marketLiquidityAmount = BN(marketLiquidities.find(byDenom(denom))?.amount ?? 0) const marketLiquidityAmount = BN(marketLiquidities.find(byDenom(denom))?.amount ?? 0)
@ -38,7 +38,7 @@ function useLendingMarketAssetsTableData(): {
accountLentValue, accountLentValue,
accountLentAmount, accountLentAmount,
marketLiquidityAmount, marketLiquidityAmount,
marketDepositCap: BN(depositCap), marketDepositCap: cap.max,
marketLiquidityRate: liquidityRate, marketLiquidityRate: liquidityRate,
marketLiquidationThreshold: liquidationThreshold, marketLiquidationThreshold: liquidationThreshold,
marketMaxLtv: maxLtv, marketMaxLtv: maxLtv,

View File

@ -49,10 +49,6 @@ interface PseudoAsset {
symbol: string symbol: string
} }
interface OtherAsset extends Omit<Asset, 'symbol'> {
symbol: 'MARS'
}
interface BorrowAsset extends Asset { interface BorrowAsset extends Asset {
borrowRate: number | null borrowRate: number | null
liquidity: { liquidity: {

View File

@ -5,7 +5,7 @@ interface Market {
collateralTotalScaled: string collateralTotalScaled: string
depositEnabled: boolean depositEnabled: boolean
borrowEnabled: boolean borrowEnabled: boolean
depositCap: string cap: DepositCap
maxLtv: number maxLtv: number
liquidityRate: number liquidityRate: number
liquidationThreshold: number liquidationThreshold: number

View File

@ -26,11 +26,7 @@ interface VaultInfo {
max: number max: number
liq: number liq: number
} }
cap: { cap: DepositCap
denom: string
used: BigNumber
max: BigNumber
}
} }
interface VaultConfig extends VaultMetaData, VaultInfo {} interface VaultConfig extends VaultMetaData, VaultInfo {}
@ -74,3 +70,9 @@ interface VaultPositionFlatAmounts {
unlocking: BigNumber unlocking: BigNumber
unlocked: BigNumber unlocked: BigNumber
} }
interface DepositCap {
denom: string
used: BigNumber
max: BigNumber
}

View File

@ -11,3 +11,5 @@ export const defaultFee: StdFee = {
export const SECONDS_IN_A_YEAR = 31540000 export const SECONDS_IN_A_YEAR = 31540000
export const LTV_BUFFER = 0.01 export const LTV_BUFFER = 0.01
export const DEPOSIT_CAP_BUFFER = 0.999

5
src/utils/generic.ts Normal file
View File

@ -0,0 +1,5 @@
import { DEPOSIT_CAP_BUFFER } from 'utils/constants'
export function getCapLeftWithBuffer(cap: DepositCap) {
return cap.max.minus(cap.used).times(DEPOSIT_CAP_BUFFER).integerValue()
}

View File

@ -1,9 +1,14 @@
import {
AssetParamsBaseForAddr as AssetParams,
TotalDepositResponse,
} from 'types/generated/mars-params/MarsParams.types'
import { Market as RedBankMarket } from 'types/generated/mars-red-bank/MarsRedBank.types' import { Market as RedBankMarket } from 'types/generated/mars-red-bank/MarsRedBank.types'
import { AssetParamsBaseForAddr as AssetParams } from 'types/generated/mars-params/MarsParams.types' import { BN } from 'utils/helpers'
export function resolveMarketResponse( export function resolveMarketResponse(
marketResponse: RedBankMarket, marketResponse: RedBankMarket,
assetParamsResponse: AssetParams, assetParamsResponse: AssetParams,
assetCapResponse: TotalDepositResponse,
): Market { ): Market {
return { return {
denom: marketResponse.denom, denom: marketResponse.denom,
@ -12,7 +17,11 @@ export function resolveMarketResponse(
collateralTotalScaled: marketResponse.collateral_total_scaled, collateralTotalScaled: marketResponse.collateral_total_scaled,
depositEnabled: assetParamsResponse.red_bank.deposit_enabled, depositEnabled: assetParamsResponse.red_bank.deposit_enabled,
borrowEnabled: assetParamsResponse.red_bank.borrow_enabled, borrowEnabled: assetParamsResponse.red_bank.borrow_enabled,
depositCap: assetParamsResponse.deposit_cap, cap: {
denom: assetCapResponse.denom,
used: BN(assetCapResponse.amount),
max: BN(assetParamsResponse.deposit_cap),
},
maxLtv: Number(assetParamsResponse.max_loan_to_value), maxLtv: Number(assetParamsResponse.max_loan_to_value),
liquidityRate: Number(marketResponse.liquidity_rate), liquidityRate: Number(marketResponse.liquidity_rate),
liquidationThreshold: Number(assetParamsResponse.liquidation_threshold), liquidationThreshold: Number(assetParamsResponse.liquidation_threshold),