diff --git a/apps/dapp/src/components/Pages/Core/DappPages/Borrow/TLC/Borrow.tsx b/apps/dapp/src/components/Pages/Core/DappPages/Borrow/TLC/Borrow.tsx index d7c9b109f..334b0dfbf 100644 --- a/apps/dapp/src/components/Pages/Core/DappPages/Borrow/TLC/Borrow.tsx +++ b/apps/dapp/src/components/Pages/Core/DappPages/Borrow/TLC/Borrow.tsx @@ -22,7 +22,7 @@ import { } from '../index'; import { fromAtto, toAtto, ZERO } from 'utils/bigNumber'; import styled from 'styled-components'; -import { ReactNode, useEffect, useMemo, useState } from 'react'; +import { ReactNode, useMemo, useState } from 'react'; interface IProps { accountPosition: ITlcDataTypes.AccountPositionStructOutput | undefined; @@ -56,10 +56,7 @@ export const Borrow: React.FC = ({ : '0.00'; }; - const maxBorrowValueWithCircuitBreaker = useMemo((): { - value: number; - isCircuitBreakerActive: boolean; - } => { + const maxBorrowValue = useMemo((): number => { const userMaxBorrow = accountPosition ? fromAtto(accountPosition.collateral) * prices.tpi * (MAX_LTV / 100) - fromAtto(accountPosition.currentDebt) @@ -67,28 +64,12 @@ export const Borrow: React.FC = ({ const userMaxBorrowBigNumber = toAtto(userMaxBorrow); - let returnValue = { value: userMaxBorrow, isCircuitBreakerActive: false }; - - // Check if the dai circuit breaker is active - if ( - tlcInfo && - tlcInfo.daiCircuitBreakerRemaining.lt(userMaxBorrowBigNumber) - ) { - returnValue = { - value: fromAtto(tlcInfo.daiCircuitBreakerRemaining), - isCircuitBreakerActive: true, - }; - } - // Check if trvAvailable from the contract is less than the user max borrow - if (tlcInfo && tlcInfo.trvAvailable.lt(userMaxBorrowBigNumber)) { - returnValue = { - value: fromAtto(tlcInfo.trvAvailable || ZERO), - isCircuitBreakerActive: true, - }; + if (tlcInfo && tlcInfo.availableToBorrow.lt(userMaxBorrowBigNumber)) { + return fromAtto(tlcInfo.availableToBorrow || ZERO); } - return returnValue; + return userMaxBorrow; }, [tlcInfo, accountPosition, prices.tpi]); return ( @@ -109,11 +90,11 @@ export const Borrow: React.FC = ({ onHintClick={() => { setState({ ...state, - borrowValue: maxBorrowValueWithCircuitBreaker.value.toFixed(2), + borrowValue: maxBorrowValue.toFixed(2), }); }} min={1000} - hint={`Max: ${maxBorrowValueWithCircuitBreaker.value.toFixed(2)}`} + hint={`Max: ${maxBorrowValue.toFixed(2)}`} width="100%" /> @@ -138,22 +119,23 @@ export const Borrow: React.FC = ({

)} - {tlcInfo && tlcInfo.strategyBalance < Number(state.borrowValue) && ( - - -

i

-
-

- Amount exceeds available DAI. -
- Current max borrow:{' '} - {tlcInfo.strategyBalance - ? Number(tlcInfo.strategyBalance).toFixed(4) - : 0}{' '} - DAI -

-
- )} + {tlcInfo && + fromAtto(tlcInfo.availableToBorrow) < Number(state.borrowValue) && ( + + +

i

+
+

+ Amount exceeds available DAI. +
+ Current max borrow:{' '} + {tlcInfo.availableToBorrow + ? fromAtto(tlcInfo.availableToBorrow).toFixed(4) + : 0}{' '} + DAI +

+
+ )} Estimated DAI LTV: {getEstimatedLTV()}% = ({ fromAtto(accountPosition.maxBorrow) < Number(state.borrowValue)) || (tlcInfo && tlcInfo.minBorrow > Number(state.borrowValue)) || - (tlcInfo && tlcInfo.strategyBalance < Number(state.borrowValue)) || - Number(getEstimatedLTV()) > MAX_LTV || - (maxBorrowValueWithCircuitBreaker.isCircuitBreakerActive && - Number(state.borrowValue) > - maxBorrowValueWithCircuitBreaker.value) + (tlcInfo && + fromAtto(tlcInfo.availableToBorrow) < + Number(state.borrowValue)) || + Number(getEstimatedLTV()) > MAX_LTV } > Borrow diff --git a/apps/dapp/src/components/Pages/Core/DappPages/Borrow/TLC/Withdraw.tsx b/apps/dapp/src/components/Pages/Core/DappPages/Borrow/TLC/Withdraw.tsx index cdf3a6690..b22992cc6 100644 --- a/apps/dapp/src/components/Pages/Core/DappPages/Borrow/TLC/Withdraw.tsx +++ b/apps/dapp/src/components/Pages/Core/DappPages/Borrow/TLC/Withdraw.tsx @@ -59,10 +59,7 @@ export const Withdraw: React.FC = ({ return getEstimatedCollateral() * prices.tpi * (MAX_LTV / 100); }; - const maxWithdrawWithCircuitBreaker = useMemo((): { - value: number; - isCircuitBreakerActive: boolean; - } => { + const maxWithdrawValue = useMemo((): number => { const userMaxWithdraw = accountPosition ? fromAtto(accountPosition.collateral) - fromAtto(accountPosition.currentDebt) / (MAX_LTV / 100) / prices.tpi @@ -70,18 +67,16 @@ export const Withdraw: React.FC = ({ const userMaxWithdrawBigNumber = toAtto(userMaxWithdraw); - if (!tlcInfo) { - return { value: userMaxWithdraw, isCircuitBreakerActive: false }; + if ( + tlcInfo && + tlcInfo.circuitBreakers.templeCircuitBreakerRemaining.lt( + userMaxWithdrawBigNumber + ) + ) { + return fromAtto(tlcInfo.circuitBreakers.templeCircuitBreakerRemaining); } - if (tlcInfo.templeCircuitBreakerRemaining.lt(userMaxWithdrawBigNumber)) { - return { - value: fromAtto(tlcInfo.templeCircuitBreakerRemaining), - isCircuitBreakerActive: true, - }; - } - - return { value: userMaxWithdraw, isCircuitBreakerActive: false }; + return userMaxWithdraw; }, [accountPosition, prices.tpi, tlcInfo]); return ( @@ -102,11 +97,11 @@ export const Withdraw: React.FC = ({ onHintClick={() => { setState({ ...state, - withdrawValue: maxWithdrawWithCircuitBreaker.value.toFixed(2), + withdrawValue: maxWithdrawValue.toFixed(2), }); }} min={0} - hint={`Max: ${maxWithdrawWithCircuitBreaker.value.toFixed(2)}`} + hint={`Max: ${maxWithdrawValue.toFixed(2)}`} width="100%" /> {/* Only display if user has borrows */} @@ -168,7 +163,7 @@ export const Withdraw: React.FC = ({ // Disable if amount is 0 or greater than max withdraw disabled={ Number(state.withdrawValue) <= 0 || - Number(state.withdrawValue) > maxWithdrawWithCircuitBreaker.value + Number(state.withdrawValue) > maxWithdrawValue } > Withdraw diff --git a/apps/dapp/src/components/Pages/Core/DappPages/Borrow/index.tsx b/apps/dapp/src/components/Pages/Core/DappPages/Borrow/index.tsx index 01bce1b18..a4f744f49 100644 --- a/apps/dapp/src/components/Pages/Core/DappPages/Borrow/index.tsx +++ b/apps/dapp/src/components/Pages/Core/DappPages/Borrow/index.tsx @@ -15,7 +15,7 @@ import { queryTlcPrices, subgraphQuery, } from 'utils/subgraph'; -import { BigNumber, ethers } from 'ethers'; +import { BigNumber } from 'ethers'; import daiImg from 'assets/images/newui-images/tokens/dai.png'; import templeImg from 'assets/images/newui-images/tokens/temple.png'; import { formatToken } from 'utils/formatter'; @@ -49,18 +49,23 @@ export type TlcInfo = { minBorrow: number; borrowRate: number; liquidationLtv: number; - strategyBalance: number; debtCeiling: number; - daiCircuitBreakerRemaining: BigNumber; - templeCircuitBreakerRemaining: BigNumber; + circuitBreakers: { + daiCircuitBreakerRemaining: BigNumber; + templeCircuitBreakerRemaining: BigNumber; + }; outstandingUserDebt: number; - trvAvailable: BigNumber; + availableToBorrow: BigNumber; }; export const MAX_LTV = 85; export type Prices = { templePrice: number; daiPrice: number; tpi: number }; +const minBN = (v1: BigNumber, v2: BigNumber): BigNumber => { + return v1.lt(v2) ? v1 : v2; +}; + export const BorrowPage = () => { const [{}, connect] = useConnectWallet(); const { balance, wallet, updateBalance, signer, ensureAllowance } = @@ -147,60 +152,48 @@ export const BorrowPage = () => { const tlcContract = new TempleLineOfCredit__factory(signer).attach( env.contracts.tlc ); - const debtPosition = await tlcContract.totalDebtPosition(); - const totalUserDebt = debtPosition.totalDebt; - const utilizationRatio = debtPosition.utilizationRatio; - const outstandingUserDebt = debtPosition[2]; - - // NOTE: We are intentionally rounding here to nearest 1e18 - const debtCeiling = totalUserDebt - .div(utilizationRatio) - .mul(ethers.utils.parseEther('1')); - - const userAvailableToBorrowFromTlc = debtCeiling.sub(totalUserDebt); - const trvContract = new TreasuryReservesVault__factory(signer).attach( env.contracts.treasuryReservesVault ); - const trvAvailable = await trvContract.totalAvailable(env.contracts.dai); - - const strategyAvailalableToBorrowFromTrv = - await trvContract.availableForStrategyToBorrow( + const [ + debtPosition, + debtCeiling, + trvAvailableCash, + tlcAvailalableToBorrow, + [debtTokenConfig, debtTokenData], + circuitBreakers, + ] = await Promise.all([ + tlcContract.totalDebtPosition(), + trvContract.strategyDebtCeiling( env.contracts.strategies.tlcStrategy, env.contracts.dai - ); - - // available to borrow - // return the lesser of userAvailableToBorrowFromTlc and strategyAvailalableToBorrowFromTrv - const maxAvailableToBorrow = userAvailableToBorrowFromTlc.gte( - strategyAvailalableToBorrowFromTrv - ) - ? strategyAvailalableToBorrowFromTrv - : userAvailableToBorrowFromTlc; - - // Getting the max borrow LTV and interest rate - const [debtTokenConfig, debtTokenData] = - await tlcContract.debtTokenDetails(); - const maxLtv = debtTokenConfig.maxLtvRatio; - - // current borrow apy - const currentBorrowInterestRate = aprToApy( - fromAtto(debtTokenData.interestRate) + ), + trvContract.totalAvailable(env.contracts.dai), + trvContract.availableForStrategyToBorrow( + env.contracts.strategies.tlcStrategy, + env.contracts.dai + ), + tlcContract.debtTokenDetails(), + getCircuitBreakers(), + ]); + + // The minimum of: + // - What cash is available + // - The TLC free to borrow under it's debt ceiling cap + // - The circuit breaker availability + const trvAvailable = minBN( + minBN(trvAvailableCash, tlcAvailalableToBorrow), + circuitBreakers?.daiCircuitBreakerRemaining || ZERO ); - const circuitBreakers = await getCircuitBreakers(); - return { debtCeiling: fromAtto(debtCeiling), - strategyBalance: fromAtto(maxAvailableToBorrow), - borrowRate: currentBorrowInterestRate, - liquidationLtv: fromAtto(maxLtv), - outstandingUserDebt: fromAtto(outstandingUserDebt), - trvAvailable: trvAvailable, - daiCircuitBreakerRemaining: circuitBreakers?.daiCircuitBreakerRemaining, - templeCircuitBreakerRemaining: - circuitBreakers?.templeCircuitBreakerRemaining, + availableToBorrow: trvAvailable, + borrowRate: aprToApy(fromAtto(debtTokenData.interestRate)), + liquidationLtv: fromAtto(debtTokenConfig.maxLtvRatio), + outstandingUserDebt: fromAtto(debtPosition.totalDebt), + circuitBreakers, }; }, [signer, getCircuitBreakers]); @@ -238,14 +231,13 @@ export const BorrowPage = () => { minBorrow: parseFloat(response.tlcDailySnapshots[0].minBorrowAmount), borrowRate: tlcInfoFromContracts?.borrowRate || 0, liquidationLtv: tlcInfoFromContracts?.liquidationLtv || 0, - strategyBalance: tlcInfoFromContracts?.strategyBalance || 0, debtCeiling: tlcInfoFromContracts?.debtCeiling || 0, - daiCircuitBreakerRemaining: - tlcInfoFromContracts?.daiCircuitBreakerRemaining || ZERO, - templeCircuitBreakerRemaining: - tlcInfoFromContracts?.templeCircuitBreakerRemaining || ZERO, outstandingUserDebt: tlcInfoFromContracts?.outstandingUserDebt || 0, - trvAvailable: tlcInfoFromContracts?.trvAvailable || ZERO, + availableToBorrow: tlcInfoFromContracts?.availableToBorrow || ZERO, + circuitBreakers: tlcInfoFromContracts?.circuitBreakers || { + daiCircuitBreakerRemaining: ZERO, + templeCircuitBreakerRemaining: ZERO, + }, }); } catch (e) { setMetricsLoading(false); @@ -488,19 +480,8 @@ export const BorrowPage = () => { const availableToBorrow = useMemo(() => { if (!tlcInfo) return '...'; - const availableAsBigNumber = toAtto(tlcInfo.strategyBalance); - let borrowableAmount = tlcInfo.strategyBalance; - - if (tlcInfo.daiCircuitBreakerRemaining.lt(availableAsBigNumber)) { - borrowableAmount = fromAtto(tlcInfo.daiCircuitBreakerRemaining); - } - - const trvAvailable = fromAtto(tlcInfo.trvAvailable); - if (trvAvailable < borrowableAmount) { - borrowableAmount = trvAvailable; - } - - return `$${Number(borrowableAmount).toLocaleString('en', { + const borrowableAmount = Number(fromAtto(tlcInfo.availableToBorrow)); + return `$${borrowableAmount.toLocaleString('en', { minimumFractionDigits: 2, maximumFractionDigits: 2, })}`;