Skip to content

Commit f2b7532

Browse files
committed
feat: new guard for borrowing power on debt swap
1 parent aca6602 commit f2b7532

File tree

9 files changed

+133
-4
lines changed

9 files changed

+133
-4
lines changed

src/components/transactions/Swap/errors/SwapErrors.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
hasInsufficientBalance,
1717
InsufficientBalanceGuard,
1818
} from './shared/InsufficientBalanceGuard';
19+
import { InsufficientBorrowPowerBlockingGuard } from './shared/InsufficientBorrowPowerBlockingGuard';
1920
import {
2021
hasInsufficientLiquidity,
2122
InsufficientLiquidityBlockingGuard,
@@ -124,6 +125,16 @@ export const SwapErrors = ({
124125
);
125126
}
126127

128+
if (isProtocolSwapState(state) && state.swapType === SwapType.DebtSwap) {
129+
return (
130+
<InsufficientBorrowPowerBlockingGuard
131+
state={state}
132+
setState={setState}
133+
isSwapFlowSelected={state.isSwapFlowSelected}
134+
/>
135+
);
136+
}
137+
127138
if (!state.error) {
128139
return null;
129140
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Trans } from '@lingui/macro';
2+
import { SxProps, Typography } from '@mui/material';
3+
import { Warning } from 'src/components/primitives/Warning';
4+
5+
export const InsufficientBorrowPowerBlockingError = ({ sx }: { sx?: SxProps }) => {
6+
return (
7+
<Warning severity="error" sx={{ mt: 4, ...sx }} icon={false}>
8+
<Typography variant="caption">
9+
<Trans>
10+
Insufficient collateral to cover new borrow position. Wallet must have borrowing power
11+
remaining to perform debt switch.
12+
</Trans>
13+
</Typography>
14+
</Warning>
15+
);
16+
};
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { SxProps } from '@mui/material';
2+
import { Dispatch, useEffect } from 'react';
3+
import { useAppDataContext } from 'src/hooks/app-data-provider/useAppDataProvider';
4+
5+
import {
6+
ActionsBlockedReason,
7+
ProtocolSwapState,
8+
SwapError,
9+
SwapState,
10+
SwapType,
11+
} from '../../types';
12+
import { InsufficientBorrowPowerBlockingError } from './InsufficientBorrowPowerBlockingError';
13+
14+
export const hasInsufficientBorrowPower = (state: SwapState, availableBorrowsUSD?: string) => {
15+
if (state.swapType !== SwapType.DebtSwap) return false;
16+
17+
const buyAmount = state.buyAmountFormatted;
18+
const sellAmount = state.sellAmountFormatted;
19+
const sourceReserve = state.sourceReserve?.reserve;
20+
const destinationReserve = state.destinationReserve?.reserve;
21+
if (!buyAmount || !sellAmount || !sourceReserve || !destinationReserve) return false;
22+
23+
const repayUsd = Number(buyAmount) * Number(sourceReserve.priceInUSD);
24+
const newDebtUsd = Number(sellAmount) * Number(destinationReserve.priceInUSD);
25+
const priceImpactDifference = newDebtUsd - repayUsd;
26+
27+
const available = Number(availableBorrowsUSD || '0');
28+
29+
return available === 0 || priceImpactDifference > available;
30+
};
31+
32+
export const InsufficientBorrowPowerBlockingGuard = ({
33+
state,
34+
setState,
35+
sx,
36+
isSwapFlowSelected,
37+
}: {
38+
state: ProtocolSwapState;
39+
setState: Dispatch<Partial<SwapState>>;
40+
sx?: SxProps;
41+
isSwapFlowSelected: boolean;
42+
}) => {
43+
const { user: extendedUser } = useAppDataContext();
44+
45+
useEffect(() => {
46+
const isBlocking = hasInsufficientBorrowPower(state, extendedUser?.availableBorrowsUSD);
47+
48+
if (isBlocking) {
49+
const isAlreadyBlockingError =
50+
state.error?.rawError instanceof Error &&
51+
state.error.rawError.message === 'InsufficientBorrowPowerError';
52+
53+
if (!isAlreadyBlockingError) {
54+
const blockingError: SwapError = {
55+
rawError: new Error('InsufficientBorrowPowerError'),
56+
message:
57+
'Insufficient collateral to cover new borrow position. Wallet must have borrowing power remaining to perform debt switch.',
58+
actionBlocked: true,
59+
};
60+
setState({
61+
error: blockingError,
62+
actionsBlocked: {
63+
[ActionsBlockedReason.INSUFFICIENT_BORROW_POWER]: true,
64+
},
65+
});
66+
}
67+
} else {
68+
const isBlockingError =
69+
state.error?.rawError instanceof Error &&
70+
state.error.rawError.message === 'InsufficientBorrowPowerError';
71+
const isBorrowPowerBlocked =
72+
state.actionsBlocked?.[ActionsBlockedReason.INSUFFICIENT_BORROW_POWER];
73+
if (isBlockingError || isBorrowPowerBlocked) {
74+
setState({
75+
...(isBlockingError ? { error: undefined } : {}),
76+
actionsBlocked: {
77+
[ActionsBlockedReason.INSUFFICIENT_BORROW_POWER]: undefined,
78+
},
79+
});
80+
}
81+
}
82+
}, [
83+
state.sellAmountFormatted,
84+
state.buyAmountFormatted,
85+
state.destinationReserve?.reserve?.priceInUSD,
86+
state.sourceReserve?.reserve?.priceInUSD,
87+
extendedUser?.availableBorrowsUSD,
88+
state.error,
89+
state.actionsBlocked,
90+
]);
91+
92+
if (hasInsufficientBorrowPower(state, extendedUser?.availableBorrowsUSD)) {
93+
return <InsufficientBorrowPowerBlockingError sx={{ mb: !isSwapFlowSelected ? 0 : 4, ...sx }} />;
94+
}
95+
96+
return null;
97+
};

src/components/transactions/Swap/types/state.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export enum ActionsBlockedReason {
2525
INSUFFICIENT_LIQUIDITY = 'INSUFFICIENT_LIQUIDITY',
2626
FLASH_LOAN_DISABLED = 'FLASH_LOAN_DISABLED',
2727
IS_LIQUIDATABLE = 'IS_LIQUIDATABLE',
28+
INSUFFICIENT_BORROW_POWER = 'INSUFFICIENT_BORROW_POWER',
2829
}
2930

3031
/**

src/locales/el/messages.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/locales/en/messages.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/locales/en/messages.po

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3765,6 +3765,10 @@ msgstr "This asset is eligible for Kernel points incentive program. Aave Labs do
37653765
msgid "You have no rewards to claim at this time."
37663766
msgstr "You have no rewards to claim at this time."
37673767

3768+
#: src/components/transactions/Swap/errors/shared/InsufficientBorrowPowerBlockingError.tsx
3769+
msgid "Insufficient collateral to cover new borrow position. Wallet must have borrowing power remaining to perform debt switch."
3770+
msgstr "Insufficient collateral to cover new borrow position. Wallet must have borrowing power remaining to perform debt switch."
3771+
37683772
#: src/components/transactions/StakeRewardClaimRestake/StakeRewardClaimRestakeModalContent.tsx
37693773
msgid "Restaked"
37703774
msgstr "Restaked"

src/locales/es/messages.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/locales/fr/messages.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)