Vanguard

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

Anti-Bot Protection Bypassed: Hook Tracks Router Address Instead of User Address

Author Revealed upon completion

Root + Impact

Description

The _beforeSwap function tracks swap amounts and cooldowns using the sender parameter. However, in Uniswap V4's architecture, sender is the address that called PoolManager.swap(), which is typically a SwapRouter contract, NOT the end user.

Vulnerable Code (lines 171-172):

addressSwappedAmount[sender] += swapAmount; // sender = SwapRouter, NOT user!
addressLastSwapBlock[sender] = block.number; // sender = SwapRouter, NOT user!

Call flow for typical swaps:

User (Alice) -> SwapRouter.swap() -> PoolManager.swap() -> Hook._beforeSwap(sender = SwapRouter)

This means:

  1. ALL users using the same SwapRouter share the same tracking limits

  2. Individual per-user limits are completely bypassed

  3. Bots can trivially bypass all limits by deploying their own router contracts

Impact

Complete bypass of anti-bot protection:

  1. Shared Limits Problem:

    • Alice swaps 0.5% through SwapRouter

    • Bob swaps 0.6% through same SwapRouter

    • Bob gets PENALIZED even though his individual limit isn't hit

    • Because: Router's tracked amount (0.5%) + Bob's swap (0.6%) > 1% limit

  2. Bot Bypass Attack:

    • Bot deploys custom router contracts (cheap via CREATE2)

    • Routes each swap through a fresh router address

    • Each router starts with 0 tracked amount

    • Bot can swap unlimited amounts with NO penalties

Proof of Concept

// Attack demonstration:
// Setup: phase1LimitBps = 100 (1%), initialLiquidity = 1000 ETH
// Max swap per address = 10 ETH
// LEGITIMATE USER EXPERIENCE:
// Alice swaps 5 ETH via SwapRouter
// addressSwappedAmount[SwapRouter] = 5 ETH
// Bob swaps 6 ETH via same SwapRouter
// Check: 5 + 6 = 11 ETH > 10 ETH limit
// Result: Bob gets PENALIZED (unfair!)
// BOT ATTACK:
// Bot wants to swap 100 ETH (10x the limit)
// Bot deploys 10 router contracts
// Routes 10 ETH through each router
// Each router: addressSwappedAmount[RouterN] = 10 ETH (under limit)
// Result: Bot swaps 100 ETH with ZERO penalties!

Risk

Severity: HIGH/CRITICAL

  • Complete bypass of the hook's core anti-bot functionality

  • Legitimate users unfairly penalized when sharing routers

  • Bots can trivially circumvent all protections

Recommended Mitigation

Option 1: Encode actual user in hookData

function _beforeSwap(
address sender,
PoolKey calldata key,
SwapParams calldata params,
bytes calldata hookData
) internal override returns (bytes4, BeforeSwapDelta, uint24) {
// Decode actual user from hookData
address actualUser = abi.decode(hookData, (address));
// Use actualUser instead of sender for tracking
addressSwappedAmount[actualUser] += swapAmount;
addressLastSwapBlock[actualUser] = block.number;
}

Option 2: Track by tx.origin (has other security tradeoffs)

Option 3: Require users to interact directly (poor UX)

Support

FAQs

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

Give us feedback!