Vanguard

First Flight #56
Beginner FriendlyDeFiFoundry
0 EXP
Submission Details
Impact: high
Likelihood: high

Denial of Service (DoS) via Shared Router Address Limit Collisions

Author Revealed upon completion

ROOT + IMPACT

## Description

The `TokenLaunchHook` is designed to enforce swap limits and penalties on individual users during the launch phases. It tracks user activity using the `sender` parameter provided by the `_beforeSwap` function.

However, in the Uniswap V4 architecture, the sender address calling the PoolManager is typically a **Swap Router contract** (such as the Universal Router), not the actual end-user's wallet address (EOA).

Because the hook relies on addressSwappedAmount[sender] to calculate limits, it treats the Router contract as a single "user." As soon as the aggregate volume of all users swapping through that Router exceeds the limit, the Router itself is flagged as having reached the cap. Consequently, **all subsequent users** attempting to swap through that Router will be unfairly penalized or blocked, resulting in a Denial of Service for legitimate participants.

```solidity

function _beforeSwap(address sender, PoolKey calldata key, SwapParams calldata params, bytes calldata)

internal

override

returns (bytes4, BeforeSwapDelta, uint24)

{

// ...

// @audit 'sender' is the Router address, not the User address

addressSwappedAmount[sender] += swapAmount;

addressLastSwapBlock[sender] = block.number;

```

## Risk

**Likelihood**: High

* The vast majority of Uniswap trades are executed via Router contracts to handle multi-hop swaps and slippage protection. Direct calls to `PoolManager` by users are rare.

**Impact**: Critical

* **Protocol Broken:** The core functionality (individual limits) fails immediately.

* **Denial of Service:** Legitimate users are unable to swap without paying penalties intended for whales/bots because the Router's shared limit is exhausted instantly.

* **Unfair Penalties:** Innocent users will be charged the "Penalty Fee" simply because they used a popular Router.

## Proof of Concept

**Scenario Parameters:**

* **Phase 1 Limit:** 1,000 Tokens.

* **Phase 1 Penalty:** 5%.

**The Exploit Flow:**

  1. **Alice** wants to buy 600 tokens. She sends a transaction via the `UniversalRouter`.

* The Hook sees `sender` = `UniversalRouter`.

* `addressSwappedAmount[UniversalRouter]` becomes 600.

\

  1. **Bob** wants to buy 500 tokens. He sends a transaction via the `UniversalRouter`.

* The Hook sees `sender` = `UniversalRouter`.

* The Hook checks: `addressSwappedAmount[UniversalRouter]` (600) + `currentSwap` (500) = 1,100.

* **1,100 > 1,000 (Limit)**.

\

  1. **Result:** The Hook flags the transaction as "Over Limit."

* Bob is charged the **5% Penalty Fee** even though he individually only bought 500 tokens (well under the limit).

* If the limit was a "Hard Cap" (reverting), Bob's transaction would simply fail.


\

## Recommended Mitigation

The hook must identify the origin of the transaction rather than the immediate caller.

**Option 1: Use `tx.origin` (Simplest)**

Replace `sender` with `tx.origin` to track the EOA that signed the transaction.

*Note: This may not work correctly for Account Abstraction / Smart Contract Wallets, but it fixes the Router issue for standard users.*

```diff

- if (addressLastSwapBlock[sender] > 0) {

+ if (addressLastSwapBlock[tx.origin] > 0) {

- uint256 blocksSinceLastSwap = block.number - addressLastSwapBlock[sender];

+ uint256 blocksSinceLastSwap = block.number - addressLastSwapBlock[tx.origin];

if (blocksSinceLastSwap < phaseCooldown) {

applyPenalty = true;

}

}

- if (!applyPenalty && addressSwappedAmount[sender] + swapAmount > maxSwapAmount) {

+ if (!applyPenalty && addressSwappedAmount[tx.origin] + swapAmount > maxSwapAmount) {

applyPenalty = true;

}

- addressSwappedAmount[sender] += swapAmount;

+ addressSwappedAmount[tx.origin] += swapAmount;

- addressLastSwapBlock[sender] = block.number;

+ addressLastSwapBlock[tx.origin] = block.number;

```

**Option 2: Require User Address via HookData (Robust)**

Modify the hook to decode the actual user's address passed via the `bytes calldata` parameter (HookData) from the Router. This requires the frontend/Router to support passing this data.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!