## 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:**
**Alice** wants to buy 600 tokens. She sends a transaction via the `UniversalRouter`.
* The Hook sees `sender` = `UniversalRouter`.
* `addressSwappedAmount[UniversalRouter]` becomes 600.
\
**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)**.
\
**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.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.