Vanguard

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

[HIGH-02] _resetPerAddressTracking function only resets data for address(0); per-user tracking data is not cleared on phase switch

Author Revealed upon completion

Root + Impact

  • Root Cause: The _resetPerAddressTracking function only resets the mapping value for address(0) instead of clearing data for all users.

  • Impact: Transaction volume accumulated by users in Phase 1 carries over into Phase 2, causing users to be close to or already exceed limits at the start of the new phase.

Description

  • Expected behavior: When the protocol switches from Phase 1 to Phase 2, each user's transaction tracking data (cumulative traded volume, last transaction block) should be reset so users have fresh limits in the new phase.

  • Specific issue: The implementation of _resetPerAddressTracking is completely wrong — it only clears the data for address(0)

function _resetPerAddressTracking() internal {
// @audit This only resets the data for address(0); it has no effect on other users!
addressSwappedAmount[address(0)] = 0;
addressLastSwapBlock[address(0)] = 0;
}

Risk

Likelihood:

  • This function is called on every phase transition

  • Triggered inevitably when switching Phase 1 → Phase 2 and Phase 2 → Phase 3

Impact:

  • A user's trading volume in Phase 1 is counted toward Phase 2's limit

  • Users may be flagged as over the limit at the start of Phase 2

  • This goes against the intended design of phased resets

Proof of Concept

function test_ResetTrackingNotWorking() public {
// Phase 1: User transactions have reached the limit
vm.prank(alice);
hook.swap(poolKey, maxPhase1Amount);
assertEq(hook.addressSwappedAmount(alice), maxPhase1Amount);
// Into Phase 2
vm.roll(block.number + phase1Duration + 1);
// Trigger phase switch
vm.prank(bob);
hook.swap(poolKey, smallAmount);
// Alice's data should have been reset, but it wasn't
assertEq(hook.addressSwappedAmount(alice), maxPhase1Amount); // 仍然是旧值!
// Alice is immediately penalized in Phase 2
vm.prank(alice);
// Even very small trades will trigger penalties because the cumulative amount has already exceeded the Phase 2 limit
}

Recommended Mitigation

  • Because Solidity cannot directly clear a mapping, it is recommended to use a phase/epoch flagging mechanism:

- function _resetPerAddressTracking() internal {
- addressSwappedAmount[address(0)] = 0;
- addressLastSwapBlock[address(0)] = 0;
- }
+ mapping(address => uint256) public addressLastPhase;
+ if (addressLastPhase[sender] != currentPhase) {
+ addressSwappedAmount[sender] = 0;
+ addressLastSwapBlock[sender] = 0;
+ addressLastPhase[sender] = currentPhase;
+ }

Support

FAQs

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

Give us feedback!