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

Remove MarketInfo data type #820

Merged
merged 5 commits into from
Feb 23, 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
33 changes: 11 additions & 22 deletions earn/src/components/advanced/BorrowMetrics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import { GREY_700 } from 'shared/lib/data/constants/Colors';
import { formatTokenAmount } from 'shared/lib/util/Numbers';
import styled from 'styled-components';

import { computeLiquidationThresholds, getAssets, sqrtRatioToPrice } from '../../data/BalanceSheet';
import { computeLiquidationThresholds, getAssets, sqrtRatioToPrice, sqrtRatioToTick } from '../../data/BalanceSheet';
import { RESPONSIVE_BREAKPOINT_MD, RESPONSIVE_BREAKPOINT_SM } from '../../data/constants/Breakpoints';
import { MarginAccount } from '../../data/MarginAccount';
import { UniswapPosition } from '../../data/Uniswap';

const BORROW_TITLE_TEXT_COLOR = 'rgba(130, 160, 182, 1)';
const MAX_HEALTH = 10;
Expand Down Expand Up @@ -161,20 +160,24 @@ export type BorrowMetricsProps = {
marginAccount?: MarginAccount;
dailyInterest0: number;
dailyInterest1: number;
uniswapPositions: readonly UniswapPosition[];
userHasNoMarginAccounts: boolean;
};

export function BorrowMetrics(props: BorrowMetricsProps) {
const { marginAccount, dailyInterest0, dailyInterest1, uniswapPositions, userHasNoMarginAccounts } = props;
const { marginAccount, dailyInterest0, dailyInterest1, userHasNoMarginAccounts } = props;

const [token0Collateral, token1Collateral] = useMemo(
() => marginAccount?.assets.amountsAt(sqrtRatioToTick(marginAccount.sqrtPriceX96)) ?? [0, 0],
[marginAccount]
);

const maxSafeCollateralFall = useMemo(() => {
if (!marginAccount) return null;

const { lowerSqrtRatio, upperSqrtRatio, minSqrtRatio, maxSqrtRatio } = computeLiquidationThresholds(
marginAccount.assets,
marginAccount.liabilities,
uniswapPositions,
marginAccount.assets.uniswapPositions,
marginAccount.sqrtPriceX96,
marginAccount.iv,
marginAccount.nSigma,
Expand All @@ -188,22 +191,11 @@ export function BorrowMetrics(props: BorrowMetricsProps) {
sqrtRatioToPrice(sp, marginAccount.token0.decimals, marginAccount.token1.decimals)
);

const assets = getAssets(
marginAccount.assets.token0Raw,
marginAccount.assets.token1Raw,
uniswapPositions,
lowerSqrtRatio,
upperSqrtRatio,
marginAccount.token0.decimals,
marginAccount.token1.decimals
);
const assets = getAssets(marginAccount.assets, lowerSqrtRatio, upperSqrtRatio);

// Compute the value of all assets (collateral) at 3 different prices (current, lower, and upper)
// Denominated in units of token1
let assetValueCurrent =
(marginAccount.assets.token0Raw + marginAccount.assets.uni0) * current +
marginAccount.assets.token1Raw +
marginAccount.assets.uni1;
let assetValueCurrent = token0Collateral * current + token1Collateral;
let assetValueAtLower = assets.amount0AtA * lower + assets.amount1AtA;
let assetValueAtUpper = assets.amount0AtB * upper + assets.amount1AtB;

Expand All @@ -229,7 +221,7 @@ export function BorrowMetrics(props: BorrowMetricsProps) {
// Since we don't know whether the user is thinking in terms of "X per Y" or "Y per X",
// we return the minimum. Error on the side of being too conservative.
return Math.min(percentChange0, percentChange1);
}, [marginAccount, uniswapPositions]);
}, [marginAccount, token0Collateral, token1Collateral]);

if (!marginAccount)
return (
Expand All @@ -254,9 +246,6 @@ export function BorrowMetrics(props: BorrowMetricsProps) {
else liquidationDistanceText = `${(maxSafeCollateralFall * 100).toPrecision(2)}% drop in collateral value`;
}

const token0Collateral = marginAccount.assets.token0Raw + marginAccount.assets.uni0;
const token1Collateral = marginAccount.assets.token1Raw + marginAccount.assets.uni1;

return (
<MetricsGrid>
<MetricsGridUpper>
Expand Down
40 changes: 19 additions & 21 deletions earn/src/components/advanced/GlobalStatsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import { roundPercentage } from 'shared/lib/util/Numbers';
import styled from 'styled-components';

import { RESPONSIVE_BREAKPOINT_XS } from '../../data/constants/Breakpoints';
import { MarginAccount } from '../../data/MarginAccount';
import { MarketInfo } from '../../data/MarketInfo';
import { LendingPair } from '../../data/LendingPair';

const STAT_LABEL_TEXT_COLOR = 'rgba(130, 160, 182, 1)';
const STAT_VALUE_TEXT_COLOR = 'rgba(255, 255, 255, 1)';
Expand Down Expand Up @@ -49,79 +48,78 @@ const StatContainer = styled.div`
`;

export type GlobalStatsTableProps = {
marginAccount?: MarginAccount;
marketInfo?: MarketInfo;
market?: LendingPair;
};

export default function GlobalStatsTable(props: GlobalStatsTableProps) {
const { marginAccount, marketInfo } = props;
const { market } = props;

return (
<Wrapper>
<Text size='M'>Pair Stats</Text>
<StatsWidgetGrid>
<StatContainer>
<Text size='M' color={STAT_LABEL_TEXT_COLOR}>
{marginAccount?.token0.symbol} Total Supply
{market?.token0.symbol} Total Supply
</Text>
<Display size='S' color={STAT_VALUE_TEXT_COLOR}>
{marketInfo?.lender0TotalAssets.toString(GNFormat.LOSSY_HUMAN) ?? '-'}
{market?.kitty0Info.totalAssets.toString(GNFormat.LOSSY_HUMAN) ?? '-'}
</Display>
</StatContainer>
<StatContainer>
<Text size='M' color={STAT_LABEL_TEXT_COLOR}>
{marginAccount?.token1.symbol} Total Supply
{market?.token1.symbol} Total Supply
</Text>
<Display size='S' color={STAT_VALUE_TEXT_COLOR}>
{marketInfo?.lender1TotalAssets.toString(GNFormat.LOSSY_HUMAN) ?? '-'}
{market?.kitty1Info.totalAssets.toString(GNFormat.LOSSY_HUMAN) ?? '-'}
</Display>
</StatContainer>
<StatContainer>
<Text size='M' color={STAT_LABEL_TEXT_COLOR}>
{marginAccount?.token0.symbol} Borrows
{market?.token0.symbol} Borrows
</Text>
<Display size='S' color={STAT_VALUE_TEXT_COLOR}>
{marketInfo?.lender0TotalBorrows.toString(GNFormat.LOSSY_HUMAN) ?? '-'}
{market?.kitty0Info.totalBorrows.toString(GNFormat.LOSSY_HUMAN) ?? '-'}
</Display>
</StatContainer>
<StatContainer>
<Text size='M' color={STAT_LABEL_TEXT_COLOR}>
{marginAccount?.token1.symbol} Borrows
{market?.token1.symbol} Borrows
</Text>
<Display size='S' color={STAT_VALUE_TEXT_COLOR}>
{marketInfo?.lender1TotalBorrows.toString(GNFormat.LOSSY_HUMAN) ?? '-'}
{market?.kitty1Info.totalBorrows.toString(GNFormat.LOSSY_HUMAN) ?? '-'}
</Display>
</StatContainer>
<StatContainer>
<Text size='M' color={STAT_LABEL_TEXT_COLOR}>
{marginAccount?.token0.symbol} Utilization
{market?.token0.symbol} Utilization
</Text>
<Display size='S' color={STAT_VALUE_TEXT_COLOR}>
{marketInfo ? roundPercentage(marketInfo.lender0Utilization * 100, 2) : '-'}%
{market ? roundPercentage(market.kitty0Info.utilization * 100, 2) : '-'}%
</Display>
</StatContainer>
<StatContainer>
<Text size='M' color={STAT_LABEL_TEXT_COLOR}>
{marginAccount?.token1.symbol} Utilization
{market?.token1.symbol} Utilization
</Text>
<Display size='S' color={STAT_VALUE_TEXT_COLOR}>
{marketInfo ? roundPercentage(marketInfo.lender1Utilization * 100, 2) : '-'}%
{market ? roundPercentage(market.kitty1Info.utilization * 100, 2) : '-'}%
</Display>
</StatContainer>
<StatContainer>
<Text size='M' color={STAT_LABEL_TEXT_COLOR}>
{marginAccount?.token0.symbol} Borrow APR
{market?.token0.symbol} Borrow APR
</Text>
<Display size='S' color={STAT_VALUE_TEXT_COLOR}>
{marketInfo ? roundPercentage(marketInfo.borrowerAPR0 * 100, 2) : '-'}%
{market ? roundPercentage(market.kitty0Info.borrowAPR * 100, 2) : '-'}%
</Display>
</StatContainer>
<StatContainer>
<Text size='M' color={STAT_LABEL_TEXT_COLOR}>
{marginAccount?.token1.symbol} Borrow APR
{market?.token1.symbol} Borrow APR
</Text>
<Display size='S' color={STAT_VALUE_TEXT_COLOR}>
{marketInfo ? roundPercentage(marketInfo.borrowerAPR1 * 100, 2) : '-'}%
{market ? roundPercentage(market.kitty1Info.borrowAPR * 100, 2) : '-'}%
</Display>
</StatContainer>
</StatsWidgetGrid>
Expand Down
7 changes: 3 additions & 4 deletions earn/src/components/advanced/UniswapPositionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,12 @@ function UniswapPositionCard(props: UniswapPositionCardProps) {

export type UniswapPositionListProps = {
borrower?: BorrowerNftBorrower;
uniswapPositions: readonly UniswapPosition[];
withdrawableUniswapNFTs: Map<number, UniswapNFTPosition>;
setPendingTxn: (pendingTxn: SendTransactionResult | null) => void;
};

export function UniswapPositionList(props: UniswapPositionListProps) {
const { borrower, uniswapPositions, withdrawableUniswapNFTs, setPendingTxn } = props;
const { borrower, withdrawableUniswapNFTs, setPendingTxn } = props;
const [selectedUniswapPosition, setSelectedUniswapPosition] = useState<SelectedUniswapPosition | null>(null);

return (
Expand All @@ -187,7 +186,7 @@ export function UniswapPositionList(props: UniswapPositionListProps) {
<Text size='S'>{slot}</Text>
<UniswapPositionCard
borrower={borrower}
uniswapPosition={uniswapPositions.at(index)}
uniswapPosition={borrower?.assets.uniswapPositions.at(index)}
withdrawableUniswapNFTs={withdrawableUniswapNFTs}
setSelectedUniswapPosition={setSelectedUniswapPosition}
setPendingTxn={props.setPendingTxn}
Expand All @@ -201,7 +200,7 @@ export function UniswapPositionList(props: UniswapPositionListProps) {
isOpen={selectedUniswapPosition !== null}
borrower={borrower}
uniswapPosition={selectedUniswapPosition.uniswapPosition}
existingUniswapPositions={uniswapPositions}
existingUniswapPositions={borrower.assets.uniswapPositions}
uniswapNFTPosition={selectedUniswapPosition.withdrawableNFT}
setIsOpen={() => {
setSelectedUniswapPosition(null);
Expand Down
34 changes: 7 additions & 27 deletions earn/src/components/advanced/modal/AddCollateralModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { Text } from 'shared/lib/components/common/Typography';

import { BorrowerNftBorrower } from '../../../data/BorrowerNft';
import { MAX_UNISWAP_POSITIONS } from '../../../data/constants/Values';
import { MarketInfo } from '../../../data/MarketInfo';
import { UniswapNFTPosition, UniswapPosition } from '../../../data/Uniswap';
import { UniswapNFTPosition } from '../../../data/Uniswap';
import { AddCollateralTab } from './tab/AddCollateralTab';
import { AddUniswapNFTAsCollateralTab } from './tab/AddUniswapNFTAsCollateralTab';

Expand All @@ -22,33 +21,19 @@ enum AddCollateralModalState {

export type AddCollateralModalProps = {
borrower: BorrowerNftBorrower;
marketInfo: MarketInfo;
isLoadingUniswapPositions: boolean;
existingUniswapPositions: readonly UniswapPosition[];
uniswapNFTPositions: Map<number, UniswapNFTPosition>;
isOpen: boolean;
setIsOpen: (open: boolean) => void;
setPendingTxn: (pendingTxn: SendTransactionResult | null) => void;
};

export default function AddCollateralModal(props: AddCollateralModalProps) {
const {
borrower,
isLoadingUniswapPositions,
existingUniswapPositions,
uniswapNFTPositions,
isOpen,
setIsOpen,
setPendingTxn,
} = props;
const { borrower, uniswapNFTPositions, isOpen, setIsOpen, setPendingTxn } = props;

const [modalState, setModalState] = useState(() => {
// Only show the select collateral type modal if there are uniswap NFT positions and the user has not already
// added the maximum number of uniswap positions.
if (
uniswapNFTPositions.size > 0 &&
existingUniswapPositions.length < MAX_UNISWAP_POSITIONS &&
!isLoadingUniswapPositions
) {
if (uniswapNFTPositions.size > 0 && borrower.assets.uniswapPositions.length < MAX_UNISWAP_POSITIONS) {
return AddCollateralModalState.SELECT_COLLATERAL_TYPE;
}
return AddCollateralModalState.TOKENS;
Expand All @@ -59,17 +44,13 @@ export default function AddCollateralModal(props: AddCollateralModalProps) {
setModalState(() => {
// Only show the select collateral type modal if there are uniswap NFT positions and the user has not already
// added the maximum number of uniswap positions.
if (
uniswapNFTPositions.size > 0 &&
existingUniswapPositions.length < MAX_UNISWAP_POSITIONS &&
!isLoadingUniswapPositions
) {
if (uniswapNFTPositions.size > 0 && borrower.assets.uniswapPositions.length < MAX_UNISWAP_POSITIONS) {
return AddCollateralModalState.SELECT_COLLATERAL_TYPE;
}
return AddCollateralModalState.TOKENS;
});
}
}, [existingUniswapPositions.length, isLoadingUniswapPositions, isOpen, uniswapNFTPositions.size]);
}, [borrower.assets.uniswapPositions, isOpen, uniswapNFTPositions.size]);

const defaultUniswapNFTPosition = uniswapNFTPositions.size > 0 ? Array.from(uniswapNFTPositions.entries())[0] : null;

Expand Down Expand Up @@ -101,7 +82,6 @@ export default function AddCollateralModal(props: AddCollateralModalProps) {
{modalState === AddCollateralModalState.TOKENS && (
<AddCollateralTab
marginAccount={borrower}
uniswapPositions={existingUniswapPositions}
isOpen={isOpen}
setPendingTxn={setPendingTxn}
setIsOpen={setIsOpen}
Expand All @@ -110,7 +90,7 @@ export default function AddCollateralModal(props: AddCollateralModalProps) {
{modalState === AddCollateralModalState.UNISWAP_NFTS && defaultUniswapNFTPosition != null && (
<AddUniswapNFTAsCollateralTab
borrower={borrower}
existingUniswapPositions={existingUniswapPositions}
existingUniswapPositions={borrower.assets.uniswapPositions}
uniswapNFTPositions={uniswapNFTPositions}
defaultUniswapNFTPosition={defaultUniswapNFTPosition}
setPendingTxn={setPendingTxn}
Expand Down
31 changes: 10 additions & 21 deletions earn/src/components/advanced/modal/BorrowModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@ import { useAccount, useContractRead, useContractWrite, usePrepareContractWrite
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 { UniswapPosition } from '../../../data/Uniswap';
import HealthBar from '../../common/HealthBar';
import TokenAmountSelectInput from '../../portfolio/TokenAmountSelectInput';

Expand Down Expand Up @@ -179,16 +177,15 @@ function BorrowButton(props: BorrowButtonProps) {

export type BorrowModalProps = {
borrower: BorrowerNftBorrower;
uniswapPositions: readonly UniswapPosition[];
marketInfo: MarketInfo;
market: LendingPair;
accountEtherBalance?: GN;
isOpen: boolean;
setIsOpen: (open: boolean) => void;
setPendingTxn: (pendingTxn: SendTransactionResult | null) => void;
};

export default function BorrowModal(props: BorrowModalProps) {
const { borrower, uniswapPositions, marketInfo, accountEtherBalance, isOpen, setIsOpen, setPendingTxn } = props;
const { borrower, market, accountEtherBalance, isOpen, setIsOpen, setPendingTxn } = props;
const { activeChain } = useContext(ChainContext);

const [borrowAmountStr, setBorrowAmountStr] = useState('');
Expand Down Expand Up @@ -234,20 +231,21 @@ export default function BorrowModal(props: BorrowModalProps) {
return null;
}

const gnMaxBorrowsBasedOnMarket = isToken0 ? marketInfo.lender0AvailableAssets : marketInfo.lender1AvailableAssets;
// TODO: use GN
const maxBorrowsBasedOnHealth = maxBorrowAndWithdraw(
borrower.assets,
borrower.liabilities,
uniswapPositions,
borrower.sqrtPriceX96,
borrower.iv,
borrower.nSigma,
borrower.token0.decimals,
borrower.token1.decimals
)[isToken0 ? 0 : 1];
// TODO: use GN
const max = Math.min(maxBorrowsBasedOnHealth, gnMaxBorrowsBasedOnMarket.toNumber());

const max = Math.min(
maxBorrowsBasedOnHealth,
market[isToken0 ? 'kitty0Info' : 'kitty1Info'].availableAssets.toNumber()
);
// Mitigate the case when the number is represented in scientific notation
const gnEightyPercentMax = GN.fromNumber(max, borrowToken.decimals).recklessMul(80).recklessDiv(100);
const maxString = gnEightyPercentMax.toString(GNFormat.DECIMAL);
Expand All @@ -261,28 +259,19 @@ export default function BorrowModal(props: BorrowModalProps) {
const { health: newHealth } = isHealthy(
borrower.assets,
newLiabilities,
uniswapPositions,
borrower.sqrtPriceX96,
borrower.iv,
borrower.nSigma,
borrower.token0.decimals,
borrower.token1.decimals
);

const availableAssets = isToken0 ? marketInfo.lender0AvailableAssets : marketInfo.lender1AvailableAssets;
const remainingAvailableAssets = availableAssets.sub(borrowAmount);

const lenderTotalAssets = isToken0 ? marketInfo.lender0TotalAssets : marketInfo.lender1TotalAssets;
// TODO: use GN
const newUtilization = lenderTotalAssets.isGtZero()
? 1 - remainingAvailableAssets.div(lenderTotalAssets).toNumber()
: 0;
const apr = yieldPerSecondToAPR(RateModel.computeYieldPerSecond(newUtilization)) * 100;
const apr = market[isToken0 ? 'kitty0Info' : 'kitty1Info'].hypotheticalBorrowAPR(borrowAmount) * 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 = gnMaxBorrowsBasedOnMarket.lt(borrowAmount);
const notEnoughSupply = borrowAmount.gt(market[isToken0 ? 'kitty0Info' : 'kitty1Info'].availableAssets);

return (
<Modal isOpen={isOpen} title='Borrow' setIsOpen={setIsOpen} maxHeight='650px'>
Expand Down
Loading
Loading