Skip to content

Commit

Permalink
fix: TLC available to borrow
Browse files Browse the repository at this point in the history
  • Loading branch information
frontier159 committed Dec 1, 2024
1 parent f42b59b commit 91cb5a0
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 132 deletions.
75 changes: 28 additions & 47 deletions apps/dapp/src/components/Pages/Core/DappPages/Borrow/TLC/Borrow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -56,39 +56,20 @@ export const Borrow: React.FC<IProps> = ({
: '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)
: 0;

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 (
Expand All @@ -109,11 +90,11 @@ export const Borrow: React.FC<IProps> = ({
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%"
/>

Expand All @@ -138,22 +119,23 @@ export const Borrow: React.FC<IProps> = ({
</p>
</Warning>
)}
{tlcInfo && tlcInfo.strategyBalance < Number(state.borrowValue) && (
<Warning>
<InfoCircle>
<p>i</p>
</InfoCircle>
<p>
Amount exceeds available DAI.
<br />
Current max borrow:{' '}
{tlcInfo.strategyBalance
? Number(tlcInfo.strategyBalance).toFixed(4)
: 0}{' '}
DAI
</p>
</Warning>
)}
{tlcInfo &&
fromAtto(tlcInfo.availableToBorrow) < Number(state.borrowValue) && (
<Warning>
<InfoCircle>
<p>i</p>
</InfoCircle>
<p>
Amount exceeds available DAI.
<br />
Current max borrow:{' '}
{tlcInfo.availableToBorrow
? fromAtto(tlcInfo.availableToBorrow).toFixed(4)
: 0}{' '}
DAI
</p>
</Warning>
)}
<MarginTop />
<RangeLabel>Estimated DAI LTV: {getEstimatedLTV()}%</RangeLabel>
<RangeSlider
Expand Down Expand Up @@ -213,11 +195,10 @@ export const Borrow: React.FC<IProps> = ({
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,29 +59,24 @@ export const Withdraw: React.FC<IProps> = ({
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
: 0;

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 (
Expand All @@ -102,11 +97,11 @@ export const Withdraw: React.FC<IProps> = ({
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 */}
Expand Down Expand Up @@ -168,7 +163,7 @@ export const Withdraw: React.FC<IProps> = ({
// 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
Expand Down
117 changes: 49 additions & 68 deletions apps/dapp/src/components/Pages/Core/DappPages/Borrow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 } =
Expand Down Expand Up @@ -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]);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
})}`;
Expand Down

0 comments on commit 91cb5a0

Please sign in to comment.