vaults: multiple bug fixes (#493)

* vaults: multiple bug fixes

* Update src/components/Account/AccountComposition.tsx

Co-authored-by: Linkie Link <linkielink.dev@gmail.com>

* Update src/components/Account/AccountComposition.tsx

Co-authored-by: Linkie Link <linkielink.dev@gmail.com>

* Update src/components/Portfolio/Account/Summary.tsx

Co-authored-by: Linkie Link <linkielink.dev@gmail.com>

* Update src/components/Account/AccountComposition.tsx

Co-authored-by: Linkie Link <linkielink.dev@gmail.com>

* Update src/components/Portfolio/Account/Summary.tsx

Co-authored-by: Linkie Link <linkielink.dev@gmail.com>

---------

Co-authored-by: Linkie Link <linkielink.dev@gmail.com>
This commit is contained in:
Bob van der Helm 2023-09-20 19:05:17 +02:00 committed by GitHub
parent 5bf46d4da0
commit 653227921a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 59 additions and 16 deletions

View File

@ -134,7 +134,11 @@ function Item(props: ItemProps) {
{props.isPercentage ? ( {props.isPercentage ? (
<FormattedNumber <FormattedNumber
amount={current.toNumber()} amount={current.toNumber()}
options={{ suffix: '%', minDecimals: 2, maxDecimals: MAX_AMOUNT_DECIMALS }} options={{
suffix: '%',
minDecimals: 2,
maxDecimals: current.abs().isLessThan(0.1) ? MAX_AMOUNT_DECIMALS : 2,
}}
className='text-sm' className='text-sm'
animate animate
/> />
@ -152,7 +156,11 @@ function Item(props: ItemProps) {
{props.isPercentage ? ( {props.isPercentage ? (
<FormattedNumber <FormattedNumber
amount={change.toNumber()} amount={change.toNumber()}
options={{ suffix: '%', minDecimals: 2, maxDecimals: MAX_AMOUNT_DECIMALS }} options={{
suffix: '%',
minDecimals: 2,
maxDecimals: change.abs().isLessThan(0.1) ? MAX_AMOUNT_DECIMALS : 2,
}}
className={classNames('text-sm', increase ? 'text-profit' : 'text-loss')} className={classNames('text-sm', increase ? 'text-profit' : 'text-loss')}
animate animate
/> />

View File

@ -23,6 +23,7 @@ import { formatPercent } from 'utils/formatters'
import { getValueFromBNCoins, mergeBNCoinArrays } from 'utils/helpers' import { getValueFromBNCoins, mergeBNCoinArrays } from 'utils/helpers'
export interface VaultBorrowingsProps { export interface VaultBorrowingsProps {
account: Account
borrowings: BNCoin[] borrowings: BNCoin[]
deposits: BNCoin[] deposits: BNCoin[]
primaryAsset: Asset primaryAsset: Asset
@ -41,7 +42,7 @@ 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 updatedAccount = useStore((s) => s.updatedAccount) const updatedAccount = useStore((s) => s.updatedAccount)
const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount) const { computeMaxBorrowAmount } = useHealthComputer(props.account)
const [percentage, setPercentage] = useState<number>(0) const [percentage, setPercentage] = useState<number>(0)
const calculateSliderPercentage = (maxBorrowAmounts: BNCoin[], borrowings: BNCoin[]) => { const calculateSliderPercentage = (maxBorrowAmounts: BNCoin[], borrowings: BNCoin[]) => {
@ -54,7 +55,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
return 0 return 0
} }
const maxBorrowAmounts: BNCoin[] = useMemo(() => { const maxBorrowAmountsRaw: BNCoin[] = useMemo(() => {
return props.borrowings.map((borrowing) => { return props.borrowings.map((borrowing) => {
const maxAmount = computeMaxBorrowAmount(borrowing.denom, { const maxAmount = computeMaxBorrowAmount(borrowing.denom, {
vault: { address: props.vault.address }, vault: { address: props.vault.address },
@ -66,6 +67,23 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
}) })
}, [props.borrowings, computeMaxBorrowAmount, props.vault.address]) }, [props.borrowings, computeMaxBorrowAmount, props.vault.address])
const maxBorrowAmounts = useMemo(() => {
const borrowPowerLeft = props.borrowings.reduce((capLeft, borrowing) => {
const maxAmount = maxBorrowAmountsRaw.find((amount) => amount.denom === borrowing.denom)
if (!maxAmount) return capLeft
capLeft -= borrowing.amount.dividedBy(maxAmount.amount).toNumber()
return capLeft
}, 1)
return maxBorrowAmountsRaw.map((maxAmount) => {
return new BNCoin({
denom: maxAmount.denom,
amount: maxAmount.amount.times(borrowPowerLeft).integerValue().toString(),
})
})
}, [maxBorrowAmountsRaw, props.borrowings])
const totalValue = useMemo( const totalValue = useMemo(
() => getValueFromBNCoins(mergeBNCoinArrays(props.deposits, props.borrowings), prices), () => getValueFromBNCoins(mergeBNCoinArrays(props.deposits, props.borrowings), prices),
[props.borrowings, props.deposits, prices], [props.borrowings, props.deposits, prices],

View File

@ -159,6 +159,7 @@ export default function VaultModalContent(props: Props) {
{ {
renderContent: () => ( renderContent: () => (
<VaultBorrowings <VaultBorrowings
account={props.account}
borrowings={borrowCoins} borrowings={borrowCoins}
deposits={deposits} deposits={deposits}
primaryAsset={props.primaryAsset} primaryAsset={props.primaryAsset}

View File

@ -74,7 +74,11 @@ function Content(props: Props) {
<FormattedNumber <FormattedNumber
className='text-xl' className='text-xl'
amount={apr.toNumber()} amount={apr.toNumber()}
options={{ suffix: '%', maxDecimals: MAX_AMOUNT_DECIMALS, minDecimals: 2 }} options={{
suffix: '%',
maxDecimals: apr.abs().isLessThan(0.1) ? MAX_AMOUNT_DECIMALS : 2,
minDecimals: 2,
}}
/> />
), ),
sub: STATS[3].sub, sub: STATS[3].sub,

View File

@ -61,8 +61,8 @@ export default function useDepositVault(props: Props): {
}, [borrowings]) }, [borrowings])
const swapActions: Action[] = useMemo( const swapActions: Action[] = useMemo(
() => getVaultSwapActions(props.vault, deposits, borrowings, prices, slippage, totalValue), () => getVaultSwapActions(props.vault, deposits, reclaims, borrowings, prices, slippage),
[totalValue, prices, props.vault, deposits, borrowings, slippage], [props.vault, deposits, reclaims, borrowings, prices, slippage],
) )
const enterVaultActions: Action[] = useMemo(() => { const enterVaultActions: Action[] = useMemo(() => {

View File

@ -147,7 +147,7 @@ export default function useHealthComputer(account?: Account) {
if (!healthComputer) return BN_ZERO if (!healthComputer) return BN_ZERO
try { try {
return BN(max_borrow_estimate_js(healthComputer, denom, target)) return BN(max_borrow_estimate_js(healthComputer, denom, target))
.multipliedBy(1 - LTV_BUFFER) .multipliedBy(LTV_BUFFER)
.integerValue() .integerValue()
} catch (err) { } catch (err) {
console.error(err) console.error(err)

View File

@ -15,6 +15,10 @@ import { cloneAccount } from 'utils/accounts'
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
import { getValueFromBNCoins } from 'utils/helpers' import { getValueFromBNCoins } from 'utils/helpers'
import { DEFAULT_SETTINGS } from '../../constants/defaultSettings'
import { SLIPPAGE_KEY } from '../../constants/localStore'
import useLocalStorage from '../useLocalStorage'
export interface VaultValue { export interface VaultValue {
address: string address: string
value: BigNumber value: BigNumber
@ -26,6 +30,7 @@ export function useUpdatedAccount(account?: Account) {
const [updatedAccount, setUpdatedAccount] = useState<Account | undefined>( const [updatedAccount, setUpdatedAccount] = useState<Account | undefined>(
account ? cloneAccount(account) : undefined, account ? cloneAccount(account) : undefined,
) )
const [slippage] = useLocalStorage<number>(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage)
const [addedDeposits, addDeposits] = useState<BNCoin[]>([]) const [addedDeposits, addDeposits] = useState<BNCoin[]>([])
const [removedDeposits, removeDeposits] = useState<BNCoin[]>([]) const [removedDeposits, removeDeposits] = useState<BNCoin[]>([])
const [addedDebts, addDebts] = useState<BNCoin[]>([]) const [addedDebts, addDebts] = useState<BNCoin[]>([])
@ -164,10 +169,11 @@ export function useUpdatedAccount(account?: Account) {
removeLends(totalLends) removeLends(totalLends)
addDebts(borrowCoins) addDebts(borrowCoins)
const value = getValueFromBNCoins([...coins, ...borrowCoins], prices) // Value has to be adjusted for slippage
const value = getValueFromBNCoins([...coins, ...borrowCoins], prices).times(1 - slippage)
addVaultValues([{ address, value }]) addVaultValues([{ address, value }])
}, },
[account, prices], [account, prices, slippage],
) )
useEffect(() => { useEffect(() => {

View File

@ -10,7 +10,7 @@ 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.99
export const DEPOSIT_CAP_BUFFER = 0.999 export const DEPOSIT_CAP_BUFFER = 0.999
export const VAULT_DEPOSIT_BUFFER = 0.9999 export const VAULT_DEPOSIT_BUFFER = 0.999

View File

@ -5,6 +5,7 @@ import { TESTNET_VAULTS_META_DATA, VAULTS_META_DATA } from 'constants/vaults'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { Action, Uint128 } from 'types/generated/mars-credit-manager/MarsCreditManager.types' import { Action, Uint128 } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { getAssetByDenom } from 'utils/assets' import { getAssetByDenom } from 'utils/assets'
import { VAULT_DEPOSIT_BUFFER } from 'utils/constants'
import { getCoinAmount, getCoinValue } from 'utils/formatters' import { getCoinAmount, getCoinValue } from 'utils/formatters'
import { getValueFromBNCoins, mergeBNCoinArrays } from 'utils/helpers' import { getValueFromBNCoins, mergeBNCoinArrays } from 'utils/helpers'
import { getTokenPrice } from 'utils/tokens' import { getTokenPrice } from 'utils/tokens'
@ -39,14 +40,17 @@ export function getVaultDepositCoinsAndValue(
const primaryAsset = getAssetByDenom(vault.denoms.primary) ?? ASSETS[0] const primaryAsset = getAssetByDenom(vault.denoms.primary) ?? ASSETS[0]
const secondaryAsset = getAssetByDenom(vault.denoms.secondary) ?? ASSETS[0] const secondaryAsset = getAssetByDenom(vault.denoms.secondary) ?? ASSETS[0]
// The buffer is needed as sometimes the pools are a bit skew, or because of other inaccuracies in the messages
const primaryDepositAmount = halfValue const primaryDepositAmount = halfValue
.dividedBy(getTokenPrice(primaryAsset.denom, prices)) .dividedBy(getTokenPrice(primaryAsset.denom, prices))
.shiftedBy(primaryAsset.decimals) .shiftedBy(primaryAsset.decimals)
.times(VAULT_DEPOSIT_BUFFER)
.integerValue() .integerValue()
const secondaryDepositAmount = halfValue const secondaryDepositAmount = halfValue
.dividedBy(getTokenPrice(secondaryAsset.denom, prices)) .dividedBy(getTokenPrice(secondaryAsset.denom, prices))
.shiftedBy(secondaryAsset.decimals) .shiftedBy(secondaryAsset.decimals)
.times(VAULT_DEPOSIT_BUFFER)
.integerValue() .integerValue()
return { return {
@ -65,16 +69,18 @@ export function getVaultDepositCoinsAndValue(
export function getVaultSwapActions( export function getVaultSwapActions(
vault: Vault, vault: Vault,
deposits: BNCoin[], deposits: BNCoin[],
reclaims: BNCoin[],
borrowings: BNCoin[], borrowings: BNCoin[],
prices: BNCoin[], prices: BNCoin[],
slippage: number, slippage: number,
totalValue: BigNumber,
): Action[] { ): Action[] {
const swapActions: Action[] = [] const swapActions: Action[] = []
const coins = [...deposits, ...borrowings] const coins = [...deposits, ...borrowings, ...reclaims]
let primaryLeftoverValue = totalValue.dividedBy(2) const value = getValueFromBNCoins(coins, prices)
let secondaryLeftoverValue = totalValue.dividedBy(2)
let primaryLeftoverValue = value.dividedBy(2)
let secondaryLeftoverValue = value.dividedBy(2)
const [primaryCoins, secondaryCoins, otherCoins] = coins.reduce( const [primaryCoins, secondaryCoins, otherCoins] = coins.reduce(
(prev, bnCoin) => { (prev, bnCoin) => {