Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
areActionsBlocked,
ExpiryToSecondsMap,
isCowProtocolRates,
isShieldBlocked,
OrderType,
SwapParams,
SwapState,
Expand Down Expand Up @@ -360,6 +361,7 @@ export const CollateralSwapActionsViaCowAdapters = ({
}}
fetchingData={state.actionsLoading || loadingPermitData}
blocked={areActionsBlocked(state) || !precalculatedInstanceAddress}
blockedText={isShieldBlocked(state) ? <Trans>Blocked by Shield</Trans> : undefined}
tryPermit={tryPermit}
permitInUse={disablePermitDueToActiveOrder}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
areActionsBlocked,
isParaswapRates,
isProtocolSwapState,
isShieldBlocked,
SwapParams,
SwapState,
} from '../../types';
Expand Down Expand Up @@ -286,6 +287,7 @@ export const CollateralSwapActionsViaParaswapAdapters = ({
requiresAmount
amount={state.processedSide === 'sell' ? state.sellAmountFormatted : state.buyAmountFormatted}
blocked={areActionsBlocked(state) || approvalTxState.loading}
blockedText={isShieldBlocked(state) ? <Trans>Blocked by Shield</Trans> : undefined}
handleApproval={approval}
requiresApproval={requiresApproval}
actionText={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
ExpiryToSecondsMap,
isCowProtocolRates,
isProtocolSwapState,
isShieldBlocked,
OrderType,
SwapParams,
SwapState,
Expand Down Expand Up @@ -387,6 +388,7 @@ export const DebtSwapActionsViaCoW = ({
}}
fetchingData={state.actionsLoading || loadingPermitData}
blocked={areActionsBlocked(state) || !precalculatedInstanceAddress}
blockedText={isShieldBlocked(state) ? <Trans>Blocked by Shield</Trans> : undefined}
tryPermit={tryPermit}
permitInUse={disablePermitDueToActiveOrder}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { useSwapGasEstimation } from '../../hooks/useSwapGasEstimation';
import {
areActionsBlocked,
isParaswapRates,
isShieldBlocked,
ProtocolSwapParams,
ProtocolSwapState,
SwapState,
Expand Down Expand Up @@ -255,6 +256,7 @@ export const DebtSwapActionsViaParaswap = ({
handleClick: action,
}}
blocked={areActionsBlocked(state)}
blockedText={isShieldBlocked(state) ? <Trans>Blocked by Shield</Trans> : undefined}
tryPermit={tryPermit}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
areActionsBlocked,
ExpiryToSecondsMap,
isCowProtocolRates,
isShieldBlocked,
OrderType,
SwapParams,
SwapState,
Expand Down Expand Up @@ -391,6 +392,7 @@ export const RepayWithCollateralActionsViaCoW = ({
}}
fetchingData={state.actionsLoading || loadingPermitData}
blocked={areActionsBlocked(state) || !precalculatedInstanceAddress}
blockedText={isShieldBlocked(state) ? <Trans>Blocked by Shield</Trans> : undefined}
tryPermit={tryPermit}
permitInUse={disablePermitDueToActiveOrder}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { useSwapGasEstimation } from '../../hooks/useSwapGasEstimation';
import {
areActionsBlocked,
isParaswapRates,
isShieldBlocked,
ProtocolSwapParams,
ProtocolSwapState,
SwapState,
Expand Down Expand Up @@ -279,6 +280,7 @@ export const RepayWithCollateralActionsViaParaswap = ({
requiresApproval={requiresApproval}
isWrongNetwork={state.isWrongNetwork}
blocked={areActionsBlocked(state)}
blockedText={isShieldBlocked(state) ? <Trans>Blocked by Shield</Trans> : undefined}
handleAction={action}
handleApproval={approval}
actionText={<Trans>Repay {state.sourceReserve.reserve.symbol}</Trans>}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
areActionsBlocked,
ExpiryToSecondsMap,
isCowProtocolRates,
isShieldBlocked,
OrderType,
SwapParams,
SwapState,
Expand Down Expand Up @@ -422,6 +423,7 @@ export const SwapActionsViaCoW = ({
}}
fetchingData={state.actionsLoading || loadingPermitData}
blocked={areActionsBlocked(state)}
blockedText={isShieldBlocked(state) ? <Trans>Blocked by Shield</Trans> : undefined}
tryPermit={tryPermit}
permitInUse={disablePermitDueToActiveOrder}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ import { useShallow } from 'zustand/shallow';
import { TrackAnalyticsHandlers } from '../../analytics/useTrackAnalytics';
import { APP_CODE_PER_SWAP_TYPE } from '../../constants/shared.constants';
import { useSwapGasEstimation } from '../../hooks/useSwapGasEstimation';
import { areActionsBlocked, isParaswapRates, SwapParams, SwapState } from '../../types';
import {
areActionsBlocked,
isParaswapRates,
isShieldBlocked,
SwapParams,
SwapState,
} from '../../types';
import { useSwapTokenApproval } from '../approval/useSwapTokenApproval';

/**
Expand Down Expand Up @@ -237,6 +243,7 @@ export const SwapActionsViaParaswap = ({
}}
fetchingData={state.actionsLoading || loadingPermitData}
blocked={areActionsBlocked(state)}
blockedText={isShieldBlocked(state) ? <Trans>Blocked by Shield</Trans> : undefined}
tryPermit={tryPermit}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { useSwapGasEstimation } from '../../hooks/useSwapGasEstimation';
import {
areActionsBlocked,
isParaswapRates,
isShieldBlocked,
ProtocolSwapParams,
ProtocolSwapState,
SwapState,
Expand Down Expand Up @@ -231,6 +232,7 @@ export const WithdrawAndSwapActionsViaParaswap = ({
}}
fetchingData={state.actionsLoading || loadingPermitData}
blocked={areActionsBlocked(state)}
blockedText={isShieldBlocked(state) ? <Trans>Blocked by Shield</Trans> : undefined}
tryPermit={tryPermit}
/>
);
Expand Down
5 changes: 5 additions & 0 deletions src/components/transactions/Swap/types/state.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export enum ActionsBlockedReason {
FLASH_LOAN_DISABLED = 'FLASH_LOAN_DISABLED',
IS_LIQUIDATABLE = 'IS_LIQUIDATABLE',
INSUFFICIENT_BORROW_POWER = 'INSUFFICIENT_BORROW_POWER',
SHIELD_BLOCKED = 'SHIELD_BLOCKED',
}

/**
Expand Down Expand Up @@ -186,6 +187,10 @@ export const actionsBlockedReasonsAmount = (state: SwapState): number => {
return Object.values(state.actionsBlocked).filter((blocked) => blocked === true).length;
};

export const isShieldBlocked = (state: SwapState): boolean => {
return state.actionsBlocked[ActionsBlockedReason.SHIELD_BLOCKED] === true;
};

/**
* State for protocol-aware flows. Includes reserve context for computing
* HF effects, borrow/collateral semantics, and determining flashloan usage.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
LiquidationCriticalWarning,
LowHealthFactorWarning,
SafetyModuleSwapWarning,
ShieldSwapWarning,
SlippageWarning,
USDTResetWarning,
} from './postInputs';
Expand Down Expand Up @@ -41,6 +42,7 @@ export const SwapPostInputWarnings = ({
<LiquidationCriticalWarning params={params} state={state} setState={setState} />
<LowHealthFactorWarning params={params} state={state} setState={setState} />
<HighPriceImpactWarning state={state} setState={setState} />
<ShieldSwapWarning state={state} setState={setState} />
<LimitOrderAmountWarning state={state} />
<SafetyModuleSwapWarning state={state} />
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export function HighPriceImpactWarning({

return (
<Warning
severity="warning"
severity={lostValue > 0.3 ? 'error' : 'warning'}
icon={false}
sx={{
mt: 2,
Expand All @@ -67,11 +67,15 @@ export function HighPriceImpactWarning({
>
<Typography variant="caption">
<Trans>
High price impact ({(lostValue * 100).toFixed(1)}%). This route may return{' '}
{state.isInvertedSwap ? 'more' : 'less'} due to low liquidity or small order size.
High price impact (<strong>{(lostValue * 100).toFixed(1)}%</strong>)! This route will
return {state.isInvertedSwap ? 'more' : 'less'} due to low liquidity or small order size.
</Trans>
</Typography>

<Typography variant="caption" sx={{ mt: 1 }}>
<Trans>Please review the swap values before confirming.</Trans>
</Typography>

{requireConfirmation && (
<Box
sx={{
Expand All @@ -83,8 +87,8 @@ export function HighPriceImpactWarning({
>
<Typography variant="caption">
<Trans>
I confirm the swap with a potential {(lostValue * 100).toFixed(0)}% value{' '}
{state.isInvertedSwap ? 'increase' : 'loss'}
I confirm the swap knowing that I could lose up to{' '}
<strong>{(lostValue * 100).toFixed(0)}%</strong> on this swap.
</Trans>
</Typography>
<Checkbox
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { ShieldExclamationIcon } from '@heroicons/react/outline';
import { Trans } from '@lingui/macro';
import { Box, SvgIcon, Typography } from '@mui/material';
import { Dispatch, useEffect, useMemo } from 'react';
import { Warning } from 'src/components/primitives/Warning';
import { useRootStore } from 'src/store/root';

import { ActionsBlockedReason, SwapState } from '../../types';
import { valueLostPercentage } from '../helpers';

const SHIELD_PRICE_IMPACT_THRESHOLD = 0.25;

export function ShieldSwapWarning({
state,
setState,
}: {
state: SwapState;
setState: Dispatch<Partial<SwapState>>;
}) {
const shieldEnabled = useRootStore((store) => store.shieldEnabled);

const lostValue = useMemo(() => {
if (!state.swapRate) return 0;
const sell = Number(state.sellAmountUSD);
const buy = Number(state.buyAmountUSD);
// Skip when sell amount isn't populated yet (no quote).
// Zero buy with nonzero sell is a real outcome (total value loss) - don't skip it.
if (!sell) return 0;
return valueLostPercentage(buy, sell);
}, [state.buyAmountUSD, state.sellAmountUSD, state.swapRate]);

const shouldBlock = shieldEnabled && lostValue > SHIELD_PRICE_IMPACT_THRESHOLD;

useEffect(() => {
setState({
actionsBlocked: {
[ActionsBlockedReason.SHIELD_BLOCKED]: shouldBlock || undefined,
},
});
}, [shouldBlock, state.quoteLastUpdatedAt]);

if (!shouldBlock) return null;

return (
<Warning
severity="error"
icon={false}
sx={{
mt: 2,
mb: 2,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1 }}>
<SvgIcon sx={{ fontSize: 20 }}>
<ShieldExclamationIcon />
</SvgIcon>
<Typography variant="subheader1">
<Trans>Aave Shield: Transaction blocked</Trans>
</Typography>
</Box>
<Typography variant="caption">
<Trans>
This swap has a price impact of {(lostValue * 100).toFixed(1)}%, which exceeds the 25%
safety threshold. To proceed, disable Aave Shield in the settings menu.
</Trans>
</Typography>
</Warning>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export * from './LimitOrderAmountWarning';
export * from './LiquidationCriticalWarning';
export * from './LowHealthFactorWarning';
export * from './SafetyModuleSwapWarning';
export * from './ShieldSwapWarning';
export * from './SlippageWarning';
export * from './USDTResetWarning';
4 changes: 3 additions & 1 deletion src/components/transactions/TxActionsWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface TxActionsWrapperProps extends BoxProps {
requiresApprovalReset?: boolean;
symbol?: string;
blocked?: boolean;
blockedText?: ReactNode;
fetchingData?: boolean;
errorParams?: {
loading: boolean;
Expand Down Expand Up @@ -53,6 +54,7 @@ export const TxActionsWrapper = ({
sx,
symbol,
blocked,
blockedText,
fetchingData = false,
errorParams,
tryPermit,
Expand All @@ -67,7 +69,7 @@ export const TxActionsWrapper = ({
const isAmountMissing = requiresAmount && requiresAmount && Number(amount) === 0;

function getMainParams() {
if (blocked) return { disabled: true, content: actionText };
if (blocked) return { disabled: true, content: blockedText || actionText };
if (
(txError?.txAction === TxAction.GAS_ESTIMATION ||
txError?.txAction === TxAction.MAIN_ACTION) &&
Expand Down
2 changes: 2 additions & 0 deletions src/layouts/MobileMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { DrawerWrapper } from './components/DrawerWrapper';
import { LanguageListItem, LanguagesList } from './components/LanguageSwitcher';
import { MobileCloseButton } from './components/MobileCloseButton';
import { NavItems } from './components/NavItems';
import { ShieldSwitcher } from './components/ShieldSwitcher';
import { TestNetModeSwitcher } from './components/TestNetModeSwitcher';

interface MobileMenuProps {
Expand Down Expand Up @@ -83,6 +84,7 @@ export const MobileMenu = ({ open, setOpen, headerHeight }: MobileMenuProps) =>
<MenuItemsWrapper title={<Trans>Global settings</Trans>}>
<List>
<DarkModeSwitcher />
<ShieldSwitcher />
{PROD_ENV && <TestNetModeSwitcher />}
<LanguageListItem onClick={() => setIsLanguagesListOpen(true)} />
</List>
Expand Down
2 changes: 2 additions & 0 deletions src/layouts/SettingsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { PROD_ENV } from 'src/utils/marketsAndNetworksConfig';

import { DarkModeSwitcher } from './components/DarkModeSwitcher';
import { LanguageListItem, LanguagesList } from './components/LanguageSwitcher';
import { ShieldSwitcher } from './components/ShieldSwitcher';
import { TestNetModeSwitcher } from './components/TestNetModeSwitcher';

export const LANG_MAP = {
Expand Down Expand Up @@ -95,6 +96,7 @@ export function SettingsMenu() {
</MenuItem>

<DarkModeSwitcher component={MenuItem} />
<ShieldSwitcher component={MenuItem} />
{PROD_ENV && <TestNetModeSwitcher />}
<LanguageListItem onClick={handleLanguageClick} component={MenuItem} />
<MenuItem onClick={handleOpenReadMode}>
Expand Down
Loading
Loading