Vanguard

First Flight #56
Beginner FriendlyDeFiFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

User tracking metrics are not reset during phase transitions

User tracking metrics are not reset during phase transitions

Description

The protocol tracks user swap amounts and cooldowns to enforce limits that vary by phase. The design intends for these limits to reset when the protocol transitions from Phase 1 to Phase 2.

The function _resetPerAddressTracking is called during a phase transition to achieve this. However, it incorrectly resets the mapping for address(0) instead of msg.sender or iterating through users (which is not possible in this architecture). As a result, a user's addressSwappedAmount accumulates across phases. A user who swapped 1% in Phase 1 will immediately be at 1% usage in Phase 2, potentially triggering penalties prematurely if they were expected to have a fresh limit.

// src/TokenLaunchHook.sol
function _resetPerAddressTracking() internal {
@> addressSwappedAmount[address(0)] = 0;
@> addressLastSwapBlock[address(0)] = 0;
}

Risk

Likelihood:

  • Occurs for every user active across the phase boundary.

Impact:

  • Users are unfairly penalized or restricted in Phase 2 based on their Phase 1 activity, contradicting the intended fresh start for the new phase.

Proof of Concept

function test_Bug_ResetTrackingFailed() public {
SwapParams memory params = SwapParams({
zeroForOne: true,
amountSpecified: -0.001 ether,
sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1
});
PoolSwapTest.TestSettings memory testSettings =
PoolSwapTest.TestSettings({takeClaims: false, settleUsingBurn: false});
// 1. Swap in Phase 1
vm.deal(user1, 1 ether);
vm.prank(user1);
swapRouter.swap{value: 0.001 ether}(key, params, testSettings, ZERO_BYTES);
uint256 amountPhase1 = antiBotHook.addressSwappedAmount(address(swapRouter));
// 2. Move to Phase 2
vm.roll(block.number + phase1Duration + 1);
// 3. Next swap triggers phase transition
vm.prank(user1);
swapRouter.swap{value: 0.001 ether}(key, params, testSettings, ZERO_BYTES);
// The total amount is accumulated instead of being reset
uint256 amountPhase2 = antiBotHook.addressSwappedAmount(address(swapRouter));
assertEq(amountPhase2, amountPhase1 + 0.001 ether, "BUG: Address tracking not reset on phase transition");
}

Recommended Mitigation

Update state variables to map Phase ID -> User Address -> Data instead of reset.

- mapping(address => uint256) public addressSwappedAmount;
- mapping(address => uint256) public addressLastSwapBlock;
+ // Mapping: PhaseID => UserAddress => Data
+ mapping(uint256 => mapping(address => uint256)) public addressSwappedAmount;
+ mapping(uint256 => mapping(address => uint256)) public addressLastSwapBlock;
function _beforeSwap(...) {
// ... phase calculation ...
- uint256 currentAmount = addressSwappedAmount[sender];
+ uint256 currentAmount = addressSwappedAmount[currentPhase][sender];
Updates

Appeal created

chaossr Lead Judge 17 days ago
Submission Judgement Published
Validated
Assigned finding tags:

_resetPerAddressTracking() Only Resets address(0) Mappings

Support

FAQs

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

Give us feedback!