Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use lendingPairs instead of fetching duplicate data in marketInfos #801

Merged
merged 1 commit into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 4 additions & 40 deletions earn/src/components/lend/BorrowingWidget.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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<Map<string, MarketInfo>>(new Map());
// selection/hover state for Available Table
const [selectedCollateral, setSelectedCollateral] = useState<Token | null>(null);
const [selectedBorrows, setSelectedBorrows] = useState<Token | null>(null);
Expand All @@ -157,33 +152,6 @@ export default function BorrowingWidget(props: BorrowingWidgetProps) {
const [selectedBorrower, setSelectedBorrower] = useState<SelectedBorrower | null>(null);
const [hoveredBorrower, setHoveredBorrower] = useState<BorrowerNftBorrower | null>(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<string, MarketInfo>());
setMarketInfos(marketInfosMapped);
})();
}, [borrowers, activeChain.id, provider, setMarketInfos]);

const filteredCollateralEntries = useMemo(
() => filterBySelection(lendingPairs, selectedBorrows),
[lendingPairs, selectedBorrows]
Expand Down Expand Up @@ -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 (
<AvailableContainer
Expand Down Expand Up @@ -487,9 +453,7 @@ export default function BorrowingWidget(props: BorrowingWidgetProps) {
<UpdateBorrowerModal
isOpen={selectedBorrower != null}
borrower={selectedBorrower.borrower}
marketInfo={marketInfos.get(
`${selectedBorrower.borrower.lender0.toLowerCase()}-${selectedBorrower.borrower.lender1.toLowerCase()}`
)}
lendingPair={lendingPairs.find((pair) => pair.uniswapPool === selectedBorrower.borrower.uniswapPool)}
setIsOpen={() => {
setSelectedBorrower(null);
}}
Expand Down
8 changes: 4 additions & 4 deletions earn/src/components/lend/modal/UpdateBorrowerModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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>(ConfirmationType.BORROW);

return (
Expand Down Expand Up @@ -86,7 +86,7 @@ export default function UpdateBorrowerModal(props: UpdateBorrowerModalProps) {
<BorrowModalContent
borrower={borrower}
setIsOpen={setIsOpen}
marketInfo={marketInfo}
lendingPair={lendingPair}
setPendingTxnResult={setPendingTxn}
/>
</Tab.Panel>
Expand Down
35 changes: 15 additions & 20 deletions earn/src/components/lend/modal/content/BorrowModalContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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('');

Expand Down Expand Up @@ -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(
Expand All @@ -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);
Expand All @@ -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 (
<>
Expand Down Expand Up @@ -338,7 +333,7 @@ export default function BorrowModalContent(props: BorrowModalContentProps) {
<ConfirmButton
borrowAmount={borrowAmount}
borrower={borrower}
isLoading={marketInfo === undefined}
isLoading={lendingPair === undefined}
isUnhealthy={isUnhealthy}
notEnoughSupply={notEnoughSupply}
requiredAnte={requiredAnte}
Expand Down
Loading