diff --git a/src/components/transactions/Swap/actions/CollateralSwap/CollateralSwapActionsViaCoWAdapters.tsx b/src/components/transactions/Swap/actions/CollateralSwap/CollateralSwapActionsViaCoWAdapters.tsx
index 9aaa91dbcc..011ed0b0bb 100644
--- a/src/components/transactions/Swap/actions/CollateralSwap/CollateralSwapActionsViaCoWAdapters.tsx
+++ b/src/components/transactions/Swap/actions/CollateralSwap/CollateralSwapActionsViaCoWAdapters.tsx
@@ -28,6 +28,7 @@ import {
areActionsBlocked,
ExpiryToSecondsMap,
isCowProtocolRates,
+ isShieldBlocked,
OrderType,
SwapParams,
SwapState,
@@ -360,6 +361,7 @@ export const CollateralSwapActionsViaCowAdapters = ({
}}
fetchingData={state.actionsLoading || loadingPermitData}
blocked={areActionsBlocked(state) || !precalculatedInstanceAddress}
+ blockedText={isShieldBlocked(state) ? Blocked by Shield : undefined}
tryPermit={tryPermit}
permitInUse={disablePermitDueToActiveOrder}
/>
diff --git a/src/components/transactions/Swap/actions/CollateralSwap/CollateralSwapActionsViaParaswapAdapters.tsx b/src/components/transactions/Swap/actions/CollateralSwap/CollateralSwapActionsViaParaswapAdapters.tsx
index 958e435464..c8567ed466 100644
--- a/src/components/transactions/Swap/actions/CollateralSwap/CollateralSwapActionsViaParaswapAdapters.tsx
+++ b/src/components/transactions/Swap/actions/CollateralSwap/CollateralSwapActionsViaParaswapAdapters.tsx
@@ -18,6 +18,7 @@ import {
areActionsBlocked,
isParaswapRates,
isProtocolSwapState,
+ isShieldBlocked,
SwapParams,
SwapState,
} from '../../types';
@@ -286,6 +287,7 @@ export const CollateralSwapActionsViaParaswapAdapters = ({
requiresAmount
amount={state.processedSide === 'sell' ? state.sellAmountFormatted : state.buyAmountFormatted}
blocked={areActionsBlocked(state) || approvalTxState.loading}
+ blockedText={isShieldBlocked(state) ? Blocked by Shield : undefined}
handleApproval={approval}
requiresApproval={requiresApproval}
actionText={
diff --git a/src/components/transactions/Swap/actions/DebtSwap/DebtSwapActionsViaCoW.tsx b/src/components/transactions/Swap/actions/DebtSwap/DebtSwapActionsViaCoW.tsx
index 97ce2633d2..1a5866fd51 100644
--- a/src/components/transactions/Swap/actions/DebtSwap/DebtSwapActionsViaCoW.tsx
+++ b/src/components/transactions/Swap/actions/DebtSwap/DebtSwapActionsViaCoW.tsx
@@ -34,6 +34,7 @@ import {
ExpiryToSecondsMap,
isCowProtocolRates,
isProtocolSwapState,
+ isShieldBlocked,
OrderType,
SwapParams,
SwapState,
@@ -387,6 +388,7 @@ export const DebtSwapActionsViaCoW = ({
}}
fetchingData={state.actionsLoading || loadingPermitData}
blocked={areActionsBlocked(state) || !precalculatedInstanceAddress}
+ blockedText={isShieldBlocked(state) ? Blocked by Shield : undefined}
tryPermit={tryPermit}
permitInUse={disablePermitDueToActiveOrder}
/>
diff --git a/src/components/transactions/Swap/actions/DebtSwap/DebtSwapActionsViaParaswap.tsx b/src/components/transactions/Swap/actions/DebtSwap/DebtSwapActionsViaParaswap.tsx
index 89faa2a671..0c8f21bbdd 100644
--- a/src/components/transactions/Swap/actions/DebtSwap/DebtSwapActionsViaParaswap.tsx
+++ b/src/components/transactions/Swap/actions/DebtSwap/DebtSwapActionsViaParaswap.tsx
@@ -18,6 +18,7 @@ import { useSwapGasEstimation } from '../../hooks/useSwapGasEstimation';
import {
areActionsBlocked,
isParaswapRates,
+ isShieldBlocked,
ProtocolSwapParams,
ProtocolSwapState,
SwapState,
@@ -255,6 +256,7 @@ export const DebtSwapActionsViaParaswap = ({
handleClick: action,
}}
blocked={areActionsBlocked(state)}
+ blockedText={isShieldBlocked(state) ? Blocked by Shield : undefined}
tryPermit={tryPermit}
/>
);
diff --git a/src/components/transactions/Swap/actions/RepayWithCollateral/RepayWithCollateralActionsViaCoW.tsx b/src/components/transactions/Swap/actions/RepayWithCollateral/RepayWithCollateralActionsViaCoW.tsx
index 3cc8bc461c..93dba1ba9a 100644
--- a/src/components/transactions/Swap/actions/RepayWithCollateral/RepayWithCollateralActionsViaCoW.tsx
+++ b/src/components/transactions/Swap/actions/RepayWithCollateral/RepayWithCollateralActionsViaCoW.tsx
@@ -32,6 +32,7 @@ import {
areActionsBlocked,
ExpiryToSecondsMap,
isCowProtocolRates,
+ isShieldBlocked,
OrderType,
SwapParams,
SwapState,
@@ -391,6 +392,7 @@ export const RepayWithCollateralActionsViaCoW = ({
}}
fetchingData={state.actionsLoading || loadingPermitData}
blocked={areActionsBlocked(state) || !precalculatedInstanceAddress}
+ blockedText={isShieldBlocked(state) ? Blocked by Shield : undefined}
tryPermit={tryPermit}
permitInUse={disablePermitDueToActiveOrder}
/>
diff --git a/src/components/transactions/Swap/actions/RepayWithCollateral/RepayWithCollateralActionsViaParaswap.tsx b/src/components/transactions/Swap/actions/RepayWithCollateral/RepayWithCollateralActionsViaParaswap.tsx
index d8b6625dec..95a190db2c 100644
--- a/src/components/transactions/Swap/actions/RepayWithCollateral/RepayWithCollateralActionsViaParaswap.tsx
+++ b/src/components/transactions/Swap/actions/RepayWithCollateral/RepayWithCollateralActionsViaParaswap.tsx
@@ -18,6 +18,7 @@ import { useSwapGasEstimation } from '../../hooks/useSwapGasEstimation';
import {
areActionsBlocked,
isParaswapRates,
+ isShieldBlocked,
ProtocolSwapParams,
ProtocolSwapState,
SwapState,
@@ -279,6 +280,7 @@ export const RepayWithCollateralActionsViaParaswap = ({
requiresApproval={requiresApproval}
isWrongNetwork={state.isWrongNetwork}
blocked={areActionsBlocked(state)}
+ blockedText={isShieldBlocked(state) ? Blocked by Shield : undefined}
handleAction={action}
handleApproval={approval}
actionText={Repay {state.sourceReserve.reserve.symbol}}
diff --git a/src/components/transactions/Swap/actions/SwapActions/SwapActionsViaCoW.tsx b/src/components/transactions/Swap/actions/SwapActions/SwapActionsViaCoW.tsx
index 959d0f9a74..0ad3501a80 100644
--- a/src/components/transactions/Swap/actions/SwapActions/SwapActionsViaCoW.tsx
+++ b/src/components/transactions/Swap/actions/SwapActions/SwapActionsViaCoW.tsx
@@ -35,6 +35,7 @@ import {
areActionsBlocked,
ExpiryToSecondsMap,
isCowProtocolRates,
+ isShieldBlocked,
OrderType,
SwapParams,
SwapState,
@@ -422,6 +423,7 @@ export const SwapActionsViaCoW = ({
}}
fetchingData={state.actionsLoading || loadingPermitData}
blocked={areActionsBlocked(state)}
+ blockedText={isShieldBlocked(state) ? Blocked by Shield : undefined}
tryPermit={tryPermit}
permitInUse={disablePermitDueToActiveOrder}
/>
diff --git a/src/components/transactions/Swap/actions/SwapActions/SwapActionsViaParaswap.tsx b/src/components/transactions/Swap/actions/SwapActions/SwapActionsViaParaswap.tsx
index e8b9f258ee..ae89549dd6 100644
--- a/src/components/transactions/Swap/actions/SwapActions/SwapActionsViaParaswap.tsx
+++ b/src/components/transactions/Swap/actions/SwapActions/SwapActionsViaParaswap.tsx
@@ -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';
/**
@@ -237,6 +243,7 @@ export const SwapActionsViaParaswap = ({
}}
fetchingData={state.actionsLoading || loadingPermitData}
blocked={areActionsBlocked(state)}
+ blockedText={isShieldBlocked(state) ? Blocked by Shield : undefined}
tryPermit={tryPermit}
/>
);
diff --git a/src/components/transactions/Swap/actions/WithdrawAndSwap/WithdrawAndSwapActionsViaParaswap.tsx b/src/components/transactions/Swap/actions/WithdrawAndSwap/WithdrawAndSwapActionsViaParaswap.tsx
index df69e53bad..60c62fa3dc 100644
--- a/src/components/transactions/Swap/actions/WithdrawAndSwap/WithdrawAndSwapActionsViaParaswap.tsx
+++ b/src/components/transactions/Swap/actions/WithdrawAndSwap/WithdrawAndSwapActionsViaParaswap.tsx
@@ -17,6 +17,7 @@ import { useSwapGasEstimation } from '../../hooks/useSwapGasEstimation';
import {
areActionsBlocked,
isParaswapRates,
+ isShieldBlocked,
ProtocolSwapParams,
ProtocolSwapState,
SwapState,
@@ -231,6 +232,7 @@ export const WithdrawAndSwapActionsViaParaswap = ({
}}
fetchingData={state.actionsLoading || loadingPermitData}
blocked={areActionsBlocked(state)}
+ blockedText={isShieldBlocked(state) ? Blocked by Shield : undefined}
tryPermit={tryPermit}
/>
);
diff --git a/src/components/transactions/Swap/types/state.types.ts b/src/components/transactions/Swap/types/state.types.ts
index 6b8889a3cd..f3bff46de9 100644
--- a/src/components/transactions/Swap/types/state.types.ts
+++ b/src/components/transactions/Swap/types/state.types.ts
@@ -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',
}
/**
@@ -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.
diff --git a/src/components/transactions/Swap/warnings/SwapPostInputWarnings.tsx b/src/components/transactions/Swap/warnings/SwapPostInputWarnings.tsx
index 83105a49a0..f71a2553b0 100644
--- a/src/components/transactions/Swap/warnings/SwapPostInputWarnings.tsx
+++ b/src/components/transactions/Swap/warnings/SwapPostInputWarnings.tsx
@@ -12,6 +12,7 @@ import {
LiquidationCriticalWarning,
LowHealthFactorWarning,
SafetyModuleSwapWarning,
+ ShieldSwapWarning,
SlippageWarning,
USDTResetWarning,
} from './postInputs';
@@ -41,6 +42,7 @@ export const SwapPostInputWarnings = ({
+
diff --git a/src/components/transactions/Swap/warnings/postInputs/HighPriceImpactWarning.tsx b/src/components/transactions/Swap/warnings/postInputs/HighPriceImpactWarning.tsx
index 698890710b..29a282c8a6 100644
--- a/src/components/transactions/Swap/warnings/postInputs/HighPriceImpactWarning.tsx
+++ b/src/components/transactions/Swap/warnings/postInputs/HighPriceImpactWarning.tsx
@@ -55,7 +55,7 @@ export function HighPriceImpactWarning({
return (
0.3 ? 'error' : 'warning'}
icon={false}
sx={{
mt: 2,
@@ -67,11 +67,15 @@ export function HighPriceImpactWarning({
>
- 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 ({(lostValue * 100).toFixed(1)}%)! This route will
+ return {state.isInvertedSwap ? 'more' : 'less'} due to low liquidity or small order size.
+
+ Please review the swap values before confirming.
+
+
{requireConfirmation && (
- 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{' '}
+ {(lostValue * 100).toFixed(0)}% on this swap.
>;
+}) {
+ 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 (
+
+
+
+
+
+
+ Aave Shield: Transaction blocked
+
+
+
+
+ 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.
+
+
+
+ );
+}
diff --git a/src/components/transactions/Swap/warnings/postInputs/index.ts b/src/components/transactions/Swap/warnings/postInputs/index.ts
index d79bf5530c..01d3ff70ea 100644
--- a/src/components/transactions/Swap/warnings/postInputs/index.ts
+++ b/src/components/transactions/Swap/warnings/postInputs/index.ts
@@ -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';
diff --git a/src/components/transactions/TxActionsWrapper.tsx b/src/components/transactions/TxActionsWrapper.tsx
index d3f92eb4d2..bc8dc3d2ba 100644
--- a/src/components/transactions/TxActionsWrapper.tsx
+++ b/src/components/transactions/TxActionsWrapper.tsx
@@ -25,6 +25,7 @@ interface TxActionsWrapperProps extends BoxProps {
requiresApprovalReset?: boolean;
symbol?: string;
blocked?: boolean;
+ blockedText?: ReactNode;
fetchingData?: boolean;
errorParams?: {
loading: boolean;
@@ -53,6 +54,7 @@ export const TxActionsWrapper = ({
sx,
symbol,
blocked,
+ blockedText,
fetchingData = false,
errorParams,
tryPermit,
@@ -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) &&
diff --git a/src/layouts/MobileMenu.tsx b/src/layouts/MobileMenu.tsx
index e3bb0ecadd..711b0f0984 100644
--- a/src/layouts/MobileMenu.tsx
+++ b/src/layouts/MobileMenu.tsx
@@ -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 {
@@ -83,6 +84,7 @@ export const MobileMenu = ({ open, setOpen, headerHeight }: MobileMenuProps) =>
Global settings}>
+
{PROD_ENV && }
setIsLanguagesListOpen(true)} />
diff --git a/src/layouts/SettingsMenu.tsx b/src/layouts/SettingsMenu.tsx
index c43d2c98e7..cd5a6a98f0 100644
--- a/src/layouts/SettingsMenu.tsx
+++ b/src/layouts/SettingsMenu.tsx
@@ -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 = {
@@ -95,6 +96,7 @@ export function SettingsMenu() {
+
{PROD_ENV && }