diff --git a/earn/src/components/lend/BorrowingWidget.tsx b/earn/src/components/lend/BorrowingWidget.tsx index a4569892..80fb849f 100644 --- a/earn/src/components/lend/BorrowingWidget.tsx +++ b/earn/src/components/lend/BorrowingWidget.tsx @@ -1,21 +1,17 @@ -import { useContext, useEffect, useMemo, useState } from 'react'; +import { useMemo, useState } from 'react'; import { SendTransactionResult } from '@wagmi/core'; import TokenIcon from 'shared/lib/components/common/TokenIcon'; import { Display, Text } from 'shared/lib/components/common/Typography'; import { GREY_600, GREY_700 } from 'shared/lib/data/constants/Colors'; import { GN } from 'shared/lib/data/GoodNumber'; -import useSafeState from 'shared/lib/data/hooks/UseSafeState'; import { Token } from 'shared/lib/data/Token'; import { formatTokenAmount, roundPercentage } from 'shared/lib/util/Numbers'; import styled from 'styled-components'; -import { useProvider } from 'wagmi'; -import { ChainContext } from '../../App'; import { computeLTV } from '../../data/BalanceSheet'; import { BorrowerNftBorrower } from '../../data/BorrowerNft'; import { LendingPair, LendingPairBalancesMap } from '../../data/LendingPair'; -import { fetchMarketInfos, MarketInfo } from '../../data/MarketInfo'; import { rgba } from '../../util/Colors'; import HealthGauge from '../common/HealthGauge'; import BorrowModal from './modal/BorrowModal'; @@ -148,7 +144,6 @@ function filterBySelection(lendingPairs: LendingPair[], selection: Token | null) export default function BorrowingWidget(props: BorrowingWidgetProps) { const { borrowers, lendingPairs, tokenBalances, tokenColors, setPendingTxn } = props; - const [marketInfos, setMarketInfos] = useSafeState>(new Map()); // selection/hover state for Available Table const [selectedCollateral, setSelectedCollateral] = useState(null); const [selectedBorrows, setSelectedBorrows] = useState(null); @@ -157,33 +152,6 @@ export default function BorrowingWidget(props: BorrowingWidgetProps) { const [selectedBorrower, setSelectedBorrower] = useState(null); const [hoveredBorrower, setHoveredBorrower] = useState(null); - const { activeChain } = useContext(ChainContext); - const provider = useProvider(); - - // Fetch market infos for all borrowers - useEffect(() => { - (async () => { - const markets = - borrowers?.map((borrower) => { - return { - lender0: borrower.lender0, - lender1: borrower.lender1, - token0Decimals: borrower.token0.decimals, - token1Decimals: borrower.token1.decimals, - }; - }) ?? []; - const uniqueMarkets = markets?.filter((market, index) => { - return markets.findIndex((m) => m.lender0 === market.lender0 && m.lender1 === market.lender1) === index; - }); - const marketInfosData = await fetchMarketInfos(uniqueMarkets, activeChain.id, provider); - const marketInfosMapped = marketInfosData.reduce((acc, marketInfo) => { - acc.set(`${marketInfo.lender0.toLowerCase()}-${marketInfo.lender1.toLowerCase()}`, marketInfo); - return acc; - }, new Map()); - setMarketInfos(marketInfosMapped); - })(); - }, [borrowers, activeChain.id, provider, setMarketInfos]); - const filteredCollateralEntries = useMemo( () => filterBySelection(lendingPairs, selectedBorrows), [lendingPairs, selectedBorrows] @@ -288,11 +256,9 @@ export default function BorrowingWidget(props: BorrowingWidgetProps) { ? account.liabilities.amount0 : account.liabilities.amount1; const liabilityColor = tokenColors.get(liability.address); - const marketInfo = marketInfos.get( - `${account.lender0.toLowerCase()}-${account.lender1.toLowerCase()}` - ); + const lendingPair = lendingPairs.find((pair) => pair.uniswapPool === account.uniswapPool); const apr = - ((isBorrowingToken0 ? marketInfo?.borrowerAPR0 : marketInfo?.borrowerAPR1) ?? 0) * 100; + (lendingPair?.[isBorrowingToken0 ? 'kitty0Info' : 'kitty1Info'].borrowAPR || 0) * 100; const roundedApr = Math.round(apr * 100) / 100; return ( pair.uniswapPool === selectedBorrower.borrower.uniswapPool)} setIsOpen={() => { setSelectedBorrower(null); }} diff --git a/earn/src/components/lend/modal/UpdateBorrowerModal.tsx b/earn/src/components/lend/modal/UpdateBorrowerModal.tsx index 3765738a..d02c7631 100644 --- a/earn/src/components/lend/modal/UpdateBorrowerModal.tsx +++ b/earn/src/components/lend/modal/UpdateBorrowerModal.tsx @@ -8,7 +8,7 @@ import { GREY_700 } from 'shared/lib/data/constants/Colors'; import styled from 'styled-components'; import { BorrowerNftBorrower } from '../../../data/BorrowerNft'; -import { MarketInfo } from '../../../data/MarketInfo'; +import { LendingPair } from '../../../data/LendingPair'; import BorrowModalContent from './content/BorrowModalContent'; import RepayModalContent from './content/RepayModalContent'; @@ -50,13 +50,13 @@ const TabButton = styled.button` export type UpdateBorrowerModalProps = { isOpen: boolean; borrower: BorrowerNftBorrower; - marketInfo?: MarketInfo; + lendingPair?: LendingPair; setIsOpen: (isOpen: boolean) => void; setPendingTxn: (pendingTxn: SendTransactionResult | null) => void; }; export default function UpdateBorrowerModal(props: UpdateBorrowerModalProps) { - const { isOpen, borrower, marketInfo, setIsOpen, setPendingTxn } = props; + const { isOpen, borrower, lendingPair, setIsOpen, setPendingTxn } = props; const [confirmationType, setConfirmationType] = useState(ConfirmationType.BORROW); return ( @@ -86,7 +86,7 @@ export default function UpdateBorrowerModal(props: UpdateBorrowerModalProps) { diff --git a/earn/src/components/lend/modal/content/BorrowModalContent.tsx b/earn/src/components/lend/modal/content/BorrowModalContent.tsx index ed3f82cc..72c13107 100644 --- a/earn/src/components/lend/modal/content/BorrowModalContent.tsx +++ b/earn/src/components/lend/modal/content/BorrowModalContent.tsx @@ -23,8 +23,8 @@ import { useAccount, useBalance, useContractRead, useContractWrite, usePrepareCo import { ChainContext } from '../../../../App'; import { isHealthy, maxBorrowAndWithdraw } from '../../../../data/BalanceSheet'; import { BorrowerNftBorrower } from '../../../../data/BorrowerNft'; +import { LendingPair } from '../../../../data/LendingPair'; import { Liabilities } from '../../../../data/MarginAccount'; -import { MarketInfo } from '../../../../data/MarketInfo'; import { RateModel, yieldPerSecondToAPR } from '../../../../data/RateModel'; import HealthBar from '../../../borrow/HealthBar'; @@ -169,13 +169,13 @@ function ConfirmButton(props: ConfirmButtonProps) { export type BorrowModalContentProps = { borrower: BorrowerNftBorrower; - marketInfo?: MarketInfo; + lendingPair?: LendingPair; setIsOpen: (isOpen: boolean) => void; setPendingTxnResult: (result: SendTransactionResult | null) => void; }; export default function BorrowModalContent(props: BorrowModalContentProps) { - const { borrower, marketInfo, setIsOpen, setPendingTxnResult } = props; + const { borrower, lendingPair, setIsOpen, setPendingTxnResult } = props; const [additionalBorrowAmountStr, setAdditionalBorrowAmountStr] = useState(''); @@ -219,9 +219,15 @@ export default function BorrowModalContent(props: BorrowModalContentProps) { const requiredAnte = accountEtherBalance !== undefined && accountEtherBalance.lt(ante) ? ante.sub(accountEtherBalance) : GN.zero(18); - const maxBorrowsBasedOnMarket = isBorrowingToken0 - ? marketInfo?.lender0AvailableAssets - : marketInfo?.lender1AvailableAssets; + const lenderInfo = lendingPair?.[isBorrowingToken0 ? 'kitty0Info' : 'kitty1Info']; + const inventoryTotal = lenderInfo?.inventory || 0; + const inventoryAvailable = inventoryTotal * (lenderInfo?.utilization || 0); + + // Compute updated utilization and apr + const inventoryAvailableNew = inventoryAvailable - borrowAmount.toNumber(); + let utilizationNew = inventoryTotal > 0 ? 1 - inventoryAvailableNew / inventoryTotal : 0; + if (inventoryAvailableNew < 0) utilizationNew = 1; + const apr = yieldPerSecondToAPR(RateModel.computeYieldPerSecond(utilizationNew)) * 100; // TODO: use GN const maxBorrowsBasedOnHealth = maxBorrowAndWithdraw( @@ -236,7 +242,7 @@ export default function BorrowModalContent(props: BorrowModalContentProps) { )[isBorrowingToken0 ? 0 : 1]; // TODO: use GN - const max = Math.min(maxBorrowsBasedOnHealth, maxBorrowsBasedOnMarket?.toNumber() ?? 0); + const max = Math.min(maxBorrowsBasedOnHealth, inventoryAvailable); // Mitigate the case when the number is represented in scientific notation const eightyPercentMaxBorrowAmount = GN.fromNumber(max, borrowToken.decimals).recklessMul(80).recklessDiv(100); @@ -258,21 +264,10 @@ export default function BorrowModalContent(props: BorrowModalContentProps) { borrower.token1.decimals ); - const availableAssets = isBorrowingToken0 ? marketInfo?.lender0AvailableAssets : marketInfo?.lender1AvailableAssets; - const remainingAvailableAssets = availableAssets?.sub(borrowAmount); - - const lenderTotalAssets = isBorrowingToken0 ? marketInfo?.lender0TotalAssets : marketInfo?.lender1TotalAssets; - // TODO: use GN - const newUtilization = - lenderTotalAssets && remainingAvailableAssets && lenderTotalAssets.isGtZero() - ? 1 - remainingAvailableAssets.div(lenderTotalAssets).toNumber() - : 0; - const apr = yieldPerSecondToAPR(RateModel.computeYieldPerSecond(newUtilization)) * 100; - // A user is considered unhealthy if their health is 1 or less const isUnhealthy = newHealth <= 1; // A user cannot borrow more than the total supply of the market - const notEnoughSupply = maxBorrowsBasedOnMarket?.lt(borrowAmount) ?? false; + const notEnoughSupply = borrowAmount.toNumber() > inventoryAvailable; return ( <> @@ -338,7 +333,7 @@ export default function BorrowModalContent(props: BorrowModalContentProps) {