[vault modal] add borrow max calcs (#337)

* [vault modal] add borrow max calcs

* fixed test
This commit is contained in:
Bob van der Helm 2023-08-03 12:03:03 +02:00 committed by GitHub
parent 1a55d8bd39
commit 103c8bed9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 90 additions and 58 deletions

View File

@ -27,6 +27,8 @@ jest.mock('hooks/broadcast/useDepositVault', () => jest.fn(() => ({ actions: []
jest.mock('components/DisplayCurrency') jest.mock('components/DisplayCurrency')
jest.mock('hooks/useHealthComputer', () => jest.fn(() => ({ computeMaxBorrowAmount: () => {} })))
const mockedDisplayCurrency = jest const mockedDisplayCurrency = jest
.mocked(DisplayCurrency) .mocked(DisplayCurrency)
.mockImplementation(() => <div>Display currency</div>) .mockImplementation(() => <div>Display currency</div>)

View File

@ -24,12 +24,15 @@ interface Props {
export default function AssetSelectTable(props: Props) { export default function AssetSelectTable(props: Props) {
const defaultSelected = useMemo(() => { const defaultSelected = useMemo(() => {
const assets = props.assets as BorrowAsset[] const assets = props.assets as BorrowAsset[]
return assets.reduce((acc, asset, index) => { return assets.reduce(
if (props.selectedDenoms?.includes(asset.denom)) { (acc, asset, index) => {
acc[index] = true if (props.selectedDenoms?.includes(asset.denom)) {
} acc[index] = true
return acc }
}, {} as { [key: number]: boolean }) return acc
},
{} as { [key: number]: boolean },
)
}, [props.selectedDenoms, props.assets]) }, [props.selectedDenoms, props.assets])
const [sorting, setSorting] = useState<SortingState>([{ id: 'symbol', desc: false }]) const [sorting, setSorting] = useState<SortingState>([{ id: 'symbol', desc: false }])
const [selected, setSelected] = useState<RowSelectionState>(defaultSelected) const [selected, setSelected] = useState<RowSelectionState>(defaultSelected)

View File

@ -16,6 +16,7 @@ import { BNCoin } from 'types/classes/BNCoin'
import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types' import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { findCoinByDenom, getAssetByDenom } from 'utils/assets' import { findCoinByDenom, getAssetByDenom } from 'utils/assets'
import { formatPercent } from 'utils/formatters' import { formatPercent } from 'utils/formatters'
import useHealthComputer from 'hooks/useHealthComputer'
export interface VaultBorrowingsProps { export interface VaultBorrowingsProps {
updatedAccount: Account updatedAccount: Account
@ -36,7 +37,19 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
const vaultModal = useStore((s) => s.vaultModal) const vaultModal = useStore((s) => s.vaultModal)
const depositIntoVault = useStore((s) => s.depositIntoVault) const depositIntoVault = useStore((s) => s.depositIntoVault)
const [isConfirming, setIsConfirming] = useState(false) const [isConfirming, setIsConfirming] = useState(false)
const maxBorrowAmounts: BNCoin[] = [] const { computeMaxBorrowAmount } = useHealthComputer(props.updatedAccount)
const maxBorrowAmounts: BNCoin[] = useMemo(() => {
return props.borrowings.map((borrowing) => {
const maxAmount = computeMaxBorrowAmount(borrowing.denom, {
vault: { address: props.vault.address },
})
return new BNCoin({
denom: borrowing.denom,
amount: maxAmount.toString(),
})
})
}, [props.borrowings, computeMaxBorrowAmount, props.vault.address])
const borrowingValue = useMemo(() => { const borrowingValue = useMemo(() => {
return props.borrowings.reduce((prev, curr) => { return props.borrowings.reduce((prev, curr) => {
@ -144,9 +157,8 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
<div className='flex flex-1 flex-col gap-4 p-4'> <div className='flex flex-1 flex-col gap-4 p-4'>
{props.borrowings.map((coin) => { {props.borrowings.map((coin) => {
const asset = getAssetByDenom(coin.denom) const asset = getAssetByDenom(coin.denom)
const maxAmount = maxBorrowAmounts.find( const maxAmount = maxBorrowAmounts.find((maxAmount) => maxAmount.denom === coin.denom)
(maxAmount) => maxAmount.denom === coin.denom, ?.amount
)?.amount
if (!asset || !maxAmount) if (!asset || !maxAmount)
return <React.Fragment key={`input-${coin.denom}`}></React.Fragment> return <React.Fragment key={`input-${coin.denom}`}></React.Fragment>
return ( return (

View File

@ -43,57 +43,66 @@ export default function useHealthComputer(account?: Account) {
const vaultPositionValues = useMemo(() => { const vaultPositionValues = useMemo(() => {
if (!account?.vaults) return null if (!account?.vaults) return null
return account.vaults.reduce((prev, curr) => { return account.vaults.reduce(
const baseCoinPrice = prices.find((price) => price.denom === curr.denoms.lp)?.amount || 0 (prev, curr) => {
prev[curr.address] = { const baseCoinPrice = prices.find((price) => price.denom === curr.denoms.lp)?.amount || 0
base_coin: { prev[curr.address] = {
amount: '0', // Not used by healthcomputer base_coin: {
denom: curr.denoms.lp, amount: '0', // Not used by healthcomputer
value: curr.amounts.unlocking.times(baseCoinPrice).integerValue().toString(), denom: curr.denoms.lp,
}, value: curr.amounts.unlocking.times(baseCoinPrice).integerValue().toString(),
vault_coin: { },
amount: '0', // Not used by healthcomputer vault_coin: {
denom: curr.denoms.vault, amount: '0', // Not used by healthcomputer
value: curr.values.primary denom: curr.denoms.vault,
.div(baseCurrencyPrice) value: curr.values.primary
.plus(curr.values.secondary.div(baseCurrencyPrice)) .div(baseCurrencyPrice)
.integerValue() .plus(curr.values.secondary.div(baseCurrencyPrice))
.toString(), .integerValue()
}, .toString(),
} },
return prev }
}, {} as { [key: string]: VaultPositionValue }) return prev
},
{} as { [key: string]: VaultPositionValue },
)
}, [account?.vaults, prices, baseCurrencyPrice]) }, [account?.vaults, prices, baseCurrencyPrice])
const priceData = useMemo(() => { const priceData = useMemo(() => {
const baseCurrencyPrice = const baseCurrencyPrice =
prices.find((price) => price.denom === baseCurrency.denom)?.amount || 0 prices.find((price) => price.denom === baseCurrency.denom)?.amount || 0
return prices.reduce((prev, curr) => { return prices.reduce(
prev[curr.denom] = curr.amount.div(baseCurrencyPrice).decimalPlaces(18).toString() (prev, curr) => {
return prev prev[curr.denom] = curr.amount.div(baseCurrencyPrice).decimalPlaces(18).toString()
}, {} as { [key: string]: string }) return prev
},
{} as { [key: string]: string },
)
}, [prices, baseCurrency.denom]) }, [prices, baseCurrency.denom])
const denomsData = useMemo( const denomsData = useMemo(
() => () =>
assetParams.reduce((prev, curr) => { assetParams.reduce(
const params: AssetParamsBaseForAddr = { (prev, curr) => {
...curr, const params: AssetParamsBaseForAddr = {
// The following overrides are required as testnet is 'broken' and new contracts are not updated yet ...curr,
// These overrides are not used by the healthcomputer internally, so they're not important anyways. // The following overrides are required as testnet is 'broken' and new contracts are not updated yet
protocol_liquidation_fee: '1', // These overrides are not used by the healthcomputer internally, so they're not important anyways.
liquidation_bonus: { protocol_liquidation_fee: '1',
max_lb: '1', liquidation_bonus: {
min_lb: '1', max_lb: '1',
slope: '1', min_lb: '1',
starting_lb: '1', slope: '1',
}, starting_lb: '1',
} },
prev[params.denom] = params }
prev[params.denom] = params
return prev return prev
}, {} as { [key: string]: AssetParamsBaseForAddr }), },
{} as { [key: string]: AssetParamsBaseForAddr },
),
[assetParams], [assetParams],
) )
@ -103,10 +112,13 @@ export default function useHealthComputer(account?: Account) {
const vaultPositionDenoms = positions.vaults.map((vault) => vault.vault.address) const vaultPositionDenoms = positions.vaults.map((vault) => vault.vault.address)
return vaultConfigs return vaultConfigs
.filter((config) => vaultPositionDenoms.includes(config.addr)) .filter((config) => vaultPositionDenoms.includes(config.addr))
.reduce((prev, curr) => { .reduce(
prev[curr.addr] = curr (prev, curr) => {
return prev prev[curr.addr] = curr
}, {} as { [key: string]: VaultConfigBaseForString }) return prev
},
{} as { [key: string]: VaultConfigBaseForString },
)
}, [vaultConfigs, positions]) }, [vaultConfigs, positions])
const healthComputer: HealthComputer | null = useMemo(() => { const healthComputer: HealthComputer | null = useMemo(() => {

View File

@ -4,7 +4,8 @@
@font-face { @font-face {
font-family: Inter; font-family: Inter;
src: url('../fonts/Inter-ExtraLight.woff2') format('woff2'), src:
url('../fonts/Inter-ExtraLight.woff2') format('woff2'),
url('../fonts/Inter-ExtraLight.woff') format('woff'); url('../fonts/Inter-ExtraLight.woff') format('woff');
font-weight: 300; font-weight: 300;
font-style: normal; font-style: normal;
@ -13,7 +14,8 @@
@font-face { @font-face {
font-family: Inter; font-family: Inter;
src: url('../fonts/Inter-Regular.woff2') format('woff2'), src:
url('../fonts/Inter-Regular.woff2') format('woff2'),
url('../fonts/Inter-Regular.woff') format('woff'); url('../fonts/Inter-Regular.woff') format('woff');
font-weight: 400; font-weight: 400;
font-style: normal; font-style: normal;
@ -22,7 +24,8 @@
@font-face { @font-face {
font-family: Inter; font-family: Inter;
src: url('../fonts/Inter-SemiBold.woff2') format('woff2'), src:
url('../fonts/Inter-SemiBold.woff2') format('woff2'),
url('../fonts/Inter-SemiBold.woff') format('woff'); url('../fonts/Inter-SemiBold.woff') format('woff');
font-weight: 600; font-weight: 600;
font-style: normal; font-style: normal;

View File

@ -105,7 +105,7 @@ export function convertAccountToPositions(account: Account): Positions {
], ],
}, },
}, },
} as VaultPosition), }) as VaultPosition,
), ),
} }
} }