(BN(0))
@@ -68,7 +70,13 @@ export default function VaultModalContent(props: Props) {
toggleOpen: (index: number) => toggleOpen(index),
},
{
- renderContent: () => ,
+ renderContent: () => (
+
+ ),
title: 'Borrow',
isOpen: isOpen[1],
toggleOpen: (index: number) => toggleOpen(index),
diff --git a/src/components/TokenInput.tsx b/src/components/TokenInput.tsx
index 861eb8e8..4c2e7a13 100644
--- a/src/components/TokenInput.tsx
+++ b/src/components/TokenInput.tsx
@@ -11,7 +11,7 @@ import useStore from 'store'
import { BN } from 'utils/helpers'
import { FormattedNumber } from 'components/FormattedNumber'
import Button from 'components/Button'
-import { ExclamationMarkTriangle } from 'components/Icons'
+import { ExclamationMarkTriangle, TrashBin } from 'components/Icons'
import { Tooltip } from 'components/Tooltip'
interface Props {
@@ -27,6 +27,7 @@ interface Props {
maxText?: string
warning?: string
onChangeAsset?: (asset: Asset) => void
+ onDelete?: () => void
}
export default function TokenInput(props: Props) {
@@ -79,6 +80,11 @@ export default function TokenInput(props: Props) {
max={props.max}
className='border-none p-3'
/>
+ {props.onDelete && (
+
+
+
+ )}
{props.warning && (
(account)
+
+ function getCoin(denom: string, amount: BigNumber): Coin {
+ return {
+ denom,
+ amount: amount.decimalPlaces(0).toString(),
+ }
+ }
+
+ const onChangeBorrowings = useCallback(
+ (borrowings: Map) => {
+ const debts: Coin[] = [...account.debts]
+ const deposits: Coin[] = [...account.deposits]
+ const currentDebtDenoms = debts.map((debt) => debt.denom)
+ const currentDepositDenoms = deposits.map((deposit) => deposit.denom)
+
+ borrowings.forEach((amount, denom) => {
+ if (amount.isZero()) return
+
+ if (currentDebtDenoms.includes(denom)) {
+ const index = currentDebtDenoms.indexOf(denom)
+ const newAmount = BN(debts[index].amount).plus(amount)
+ debts[index] = getCoin(denom, newAmount)
+ } else {
+ debts.push(getCoin(denom, amount))
+ }
+
+ if (currentDepositDenoms.includes(denom)) {
+ const index = currentDepositDenoms.indexOf(denom)
+ const newAmount = BN(deposits[index].amount).plus(amount)
+ deposits[index] = getCoin(denom, newAmount)
+ } else {
+ deposits.push(getCoin(denom, amount))
+ }
+ })
+
+ setUpdatedAccount({
+ ...account,
+ debts,
+ deposits,
+ })
+ },
+ [account],
+ )
+
+ return { updatedAccount, onChangeBorrowings }
+}
diff --git a/src/types/interfaces/market.d.ts b/src/types/interfaces/market.d.ts
index e28559b2..0907b921 100644
--- a/src/types/interfaces/market.d.ts
+++ b/src/types/interfaces/market.d.ts
@@ -6,4 +6,5 @@ interface Market {
depositEnabled: boolean
borrowEnabled: boolean
depositCap: string
+ maxLtv: number
}
diff --git a/src/utils/accounts.ts b/src/utils/accounts.ts
index 3200f4ad..b96a09aa 100644
--- a/src/utils/accounts.ts
+++ b/src/utils/accounts.ts
@@ -1,6 +1,7 @@
import BigNumber from 'bignumber.js'
-import { BN } from 'utils/helpers'
+import { BN, getApproximateHourlyInterest } from 'utils/helpers'
+import { getTokenValue } from 'utils/tokens'
export const calculateAccountBalance = (
account: Account | AccountChange,
@@ -59,3 +60,35 @@ export const calculateAccountBorrowRate = (
export function getAmount(denom: string, coins: Coin[]): BigNumber {
return BN(coins.find((asset) => asset.denom === denom)?.amount ?? 0)
}
+
+export function getNetCollateralValue(account: Account, marketAssets: Market[], prices: Coin[]) {
+ const depositCollateralValue = account.deposits.reduce((acc, coin) => {
+ const asset = marketAssets.find((asset) => asset.denom === coin.denom)
+
+ if (!asset) return acc
+
+ const marketValue = BN(getTokenValue(coin, prices))
+ const collateralValue = marketValue.times(asset.maxLtv)
+
+ return collateralValue.plus(acc)
+ }, BN(0))
+
+ // Implement Vault Collateral calculation (MP-2915)
+
+ const liabilitiesValue = account.debts.reduce((acc, coin) => {
+ const asset = marketAssets.find((asset) => asset.denom === coin.denom)
+
+ if (!asset) return acc
+
+ const estimatedInterestAmount = getApproximateHourlyInterest(coin.amount, asset.borrowRate)
+ const liability = BN(getTokenValue(coin, prices)).plus(estimatedInterestAmount)
+
+ return liability.plus(acc)
+ }, BN(0))
+
+ if (liabilitiesValue.isGreaterThan(depositCollateralValue)) {
+ return BN(0)
+ }
+
+ return depositCollateralValue.minus(liabilitiesValue)
+}
diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts
index dbf20a0f..7e87655f 100644
--- a/src/utils/helpers.ts
+++ b/src/utils/helpers.ts
@@ -4,3 +4,9 @@ BigNumber.config({ EXPONENTIAL_AT: 1e9 })
export function BN(n: BigNumber.Value) {
return new BigNumber(n)
}
+
+export function getApproximateHourlyInterest(amount: string, borrowRate: number) {
+ return BigNumber(borrowRate)
+ .div(24 * 365)
+ .times(amount)
+}
diff --git a/src/utils/resolvers.ts b/src/utils/resolvers.ts
index 9ff44168..0f791eda 100644
--- a/src/utils/resolvers.ts
+++ b/src/utils/resolvers.ts
@@ -21,5 +21,6 @@ export function resolveMarketResponses(responses: MarketResponse[]): Market[] {
depositEnabled: response.deposit_enabled,
borrowEnabled: response.borrow_enabled,
depositCap: response.deposit_cap,
+ maxLtv: Number(response.max_loan_to_value),
}))
}
diff --git a/src/utils/tokens.ts b/src/utils/tokens.ts
index 55d6631b..d9dd979a 100644
--- a/src/utils/tokens.ts
+++ b/src/utils/tokens.ts
@@ -1,4 +1,7 @@
+import BigNumber from 'bignumber.js'
+
import { getBaseAsset } from 'utils/assets'
+import { BN } from 'utils/helpers'
export const getTokenSymbol = (denom: string, marketAssets: Asset[]) =>
marketAssets.find((asset) => asset.denom.toLowerCase() === denom.toLowerCase())?.symbol || ''
@@ -11,3 +14,8 @@ export const getTokenIcon = (denom: string, marketAssets: Asset[]) =>
export const getTokenInfo = (denom: string, marketAssets: Asset[]) =>
marketAssets.find((asset) => asset.denom.toLowerCase() === denom.toLowerCase()) || getBaseAsset()
+
+export function getTokenValue(coin: Coin, prices: Coin[]): BigNumber {
+ const price = prices.find((price) => price.denom === coin.denom)?.amount || '0'
+ return BN(price).times(coin.amount)
+}
diff --git a/src/utils/vaults.ts b/src/utils/vaults.ts
index c3f5aaea..2837379f 100644
--- a/src/utils/vaults.ts
+++ b/src/utils/vaults.ts
@@ -1,7 +1,36 @@
+import BigNumber from 'bignumber.js'
+
import { IS_TESTNET } from 'constants/env'
import { TESTNET_VAULTS, VAULTS } from 'constants/vaults'
+import { BN } from 'utils/helpers'
+import { getNetCollateralValue } from 'utils/accounts'
export function getVaultMetaData(address: string) {
const vaults = IS_TESTNET ? TESTNET_VAULTS : VAULTS
return vaults.find((vault) => vault.address === address)
}
+
+// This should be replaced when the calculation is made part of the Health Computer (MP-2877)
+export function calculateMaxBorrowAmounts(
+ account: Account,
+ marketAssets: Market[],
+ prices: Coin[],
+ denoms: string[],
+): Map {
+ const maxAmounts = new Map()
+ const collateralValue = getNetCollateralValue(account, marketAssets, prices)
+
+ for (const denom of denoms) {
+ const borrowAsset = marketAssets.find((asset) => asset.denom === denom)
+ const borrowAssetPrice = prices.find((price) => price.denom === denom)?.amount
+
+ if (!borrowAssetPrice || !borrowAsset) continue
+
+ const borrowValue = BN(1).minus(borrowAsset.maxLtv).times(borrowAssetPrice)
+ const amount = collateralValue.dividedBy(borrowValue).decimalPlaces(0)
+
+ maxAmounts.set(denom, amount)
+ }
+
+ return maxAmounts
+}