Skip to content

Unprotected Swaps

Decentralized exchanges (DEXs) provide a core functionality of the decentralized finance (DeFi) ecosystem: programmatic swapping of one asset for another. This feature enables a broad range of products, from lending platforms to derivatives, forming the foundation of the DeFi landscape.

Despite their utility, asset swaps on DEXs are prone to several pitfalls, particularly related to pricing inefficiencies, also called slippage. These vulnerabilities can lead to suboptimal trade outcomes or, worse, exploitation by attackers.

Slippage

Slippage refers to the difference between the expected price of a trade and the actual price at which it is executed. This issue arises due to the inherent mechanics of automated market makers (AMMs) where depending on the total amount of liquidity in the pool and the size of the trade, the price might be moving more than expected, leading to unfavorable price changes. This effect can be amplified by the delay between when a transaction is signed and when it is included in a block, especially during periods of network congestion or high gas fees.

DEX smart contracts generally allow users to define the maximum tolerable slippage for their trades. This is implemented by specifying the minimum number of tokens the user expects to receive, such that if the trade cannot meet this condition, the transaction reverts. However, poor implementation or omission of slippage protection can lead to disastrous outcomes.

Consider the following example of a vulnerability in a smart contract integrating Uniswap V2:

1
2
3
4
5
6
7
IUniswapRouterV2(ROUTER).swapExactTokensForTokens(
    toSwap,
    0,
    path,
    address(this),
    now
);

In this snippet, the second parameter, amountOutMin, specifies the minimum number of output tokens the trader expects to receive. When amountOutMin is set to 0, the trade can proceed regardless of the output amount, effectively tolerating unlimited slippage. Another example:

1
2
3
4
5
6
7
8
9
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
 tokenIn: inToken,
 tokenOut: getTokenisedAddr(_outToken),
 fee: 3000,
 recipient: address(this),
 deadline: block.timestamp + 60,
 amountIn: _amount - swapFee,
 amountOutMinimum: 0
 });

In this case, setting amountOutMinimum to zero again allows for unlimited slippage. Additionally, the hardcoded fee can result in swaps failing if the pool's fee increases above the 3000 threshold.

These oversights allow an attacker to manipulate prices and execute a sandwich attack. By artificially inflating or deflating the price before the vulnerable trade, the attacker can extract significant profits at the protocol or user's expense.

Exploit Scenario

The following example illustrates how an attacker can exploit a protocol that swaps collateral between two tokens, A and Y:

An attacker can exploit a protocol by first opening a collateralized debt position (CDP) using 10 A tokens as collateral to mint 100 Y tokens. They then trigger the protocol to perform a manipulated swap in the A/Y pool, taking advantage of the lack of slippage protection, which allows for infinite price movement. By leveraging flash loans and other price manipulation techniques, the attacker artificially inflates or deflates the price, ultimately draining the pool's liquidity for token A. As a result, the protocol is left with bad debt because it cannot recover sufficient collateral, while the attacker sells the minted Y tokens at a profit, leaving the protocol and its users to bear the loss.

This exploit severely harms the protocol, transferring losses directly to the protocol or its users.

Limitations of Slippage Protection

Even with slippage protection (e.g., 0.5% maximum price deviation), trades remain susceptible to sandwiching attacks:

  1. An attacker front-runs the target transaction by placing a buy order, increasing the price of the asset to be purchased.
  2. The target trade executes at the inflated price, incurring a loss due to slippage.
  3. The attacker finalizes the attack by selling the asset in a back-running transaction, profiting from the price difference.

Although slippage limits reduce potential attacker profits, they cannot fully eliminate the attack vector, especially for highly volatile or low-liquidity assets. A malicious actor can still profit off the difference between the original trade's actual slippage and the maximum slippage amount stipulated by the order. It is absolutely essential that slippage values are sourced from outside the current transaction's context. No slippage-related calculations should be made within the swap transaction as the data might already have been manipulated by a frontrunning transaction.

Additional Mitigations

A robust solution involves a two-step transaction process as a commit-reveal scheme:

  1. Commit Phase: Users submit a cryptographic commitment to their trade details (e.g., asset pair, amount, and slippage tolerance). This hides critical information from attackers.
  2. Reveal Phase: After the commit phase is finalized, users disclose the trade details, and the transaction is executed.

This mechanism makes it significantly harder for attackers to anticipate and exploit transactions, mitigating sandwiching and slippage-based attacks.

Additionally, protocols can implement further mitigations to enhance security. Dynamic slippage limits can be employed to adjust tolerances based on market conditions and trade size, reducing vulnerabilities in volatile markets. The use of time-weighted average price (TWAP) feeds helps counteract short-term price manipulation, ensuring more accurate pricing. Private transactions through solutions like MEV-protected mempools can conceal transaction details from public view until they are included in a block, preventing attackers from preemptively targeting trades. Finally, conducting liquidity analysis ensures that trading pools maintain sufficient liquidity, minimizing the price impact of large trades and reducing the risk of manipulation.