Vanguard

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

[High-01]The sender parameter is the router address rather than the actual user, causing the anti-bot mechanism to fail completely

Author Revealed upon completion

Root + Impact

Root Cause: The sender parameter in the _beforeSwap function is the contract address calling the PoolManager (typically the router), not the actual user address that initiated the transaction.

Impact: All users transacting through the same router share the same limit and cooldown period, rendering the anti-bot protection completely ineffective.

Description

  • Normal behavior: Hook should track each real user's trading activity, including trade volume and cooldown periods, to prevent bots from mass-snatching tokens during the early stages of a token launch.

  • Specific issue: In the Uniswap V4 architecture, users interact with PoolManager through a router contract. When PoolManager calls the Hook's _beforeSwap, the sender parameter passed in is the router address, not the actual user.

    This causes:

    • addressSwappedAmount[sender] records the router's cumulative trading volume

    • addressLastSwapBlock[sender] records the router's last trading block

    • All users' limits are aggregated and calculated together

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 real user!
// ...
addressSwappedAmount[sender] += swapAmount; // Error: tracking the router
addressLastSwapBlock[sender] = block.number;
}

Risk

Likelihood:

  • 100% of user transactions go through the router contract

  • This is the standard architecture of Uniswap V4 and cannot be avoided

Impact:

  • Anti-bot mechanism completely failed, unable to distinguish between different users

  • The protocol’s core functionality is lost

  • Early investors cannot receive the expected protection

Proof of Concept

function test_SenderIsRouterNotUser() public {
vm.prank(alice);
router.swap(poolKey, swapParams, "");
vm.prank(bob);
router.swap(poolKey, swapParams, "");
assertEq(hook.addressSwappedAmount(alice), 0);
assertEq(hook.addressSwappedAmount(address(router)), aliceAmount + bobAmount);
}

Recommended Mitigation

- addressSwappedAmount[sender] += swapAmount;
- addressLastSwapBlock[sender] = block.number;
+ address realUser;
+ try IMsgSender(sender).msgSender() returns (address user) {
+ realUser = user;
+ } catch {
+ revert("Must use compatible router");
+ }
+ addressSwappedAmount[realUser] += swapAmount;
+ addressLastSwapBlock[realUser] = block.number;

Support

FAQs

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

Give us feedback!