Snowman Merkle Airdrop

First Flight #42
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: low
Valid

Incorrect `s_earnTimer` updating causing DoS

Description

Updating s_earnTimer must happen only in earnSnow function
Currently the it's updated and when a user is buying Snow in buySnow

function buySnow(uint256 amount) external payable canFarmSnow {
if (msg.value == (s_buyFee * amount)) {
_mint(msg.sender, amount);
} else {
i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
_mint(msg.sender, amount);
}
s_earnTimer = block.timestamp; // @> This is not supposed to be here
emit SnowBought(msg.sender, amount);
}

Risk

Likelihood: High

  • This occurs every time a user is buying snow via buySnow

Impact: High

  • When buySnow is called the timer is updates incorrectly, because of this the earnSnow function cannot serve it's purpose

Proof of Concept

actors:
ashley - regular snow collector
jerry - a snow buyer

  1. ashley collects Snow successfuly
    2.After 5 days jerry decides to buy some snow (just for fun unknowing or knowing this action will break the contract).

function testBuySnowBlocked() public { // @audit2 proof
vm.prank(ashley);
snow.earnSnow();
assert(snow.balanceOf(ashley) == 1);
// ashley collects snow
vm.warp(block.timestamp + 5 days); // @>5 days passed
vm.startPrank(jerry);// @> lets say on friday jerry decides to buy some snow
weth.approve(address(snow), FEE);
snow.buySnow(1); // @> at this point the `s_earnTimer` is reset for another 7 days
vm.stopPrank();
vm.warp(block.timestamp + 3 days); // @> 3 more days pass
// @> at this point 8 days have passed after `ashley` collected her free `Snow` and she is supposed to be able to do it again (we assume nobody collected before her)
vm.prank(ashley); //@> even after a week ashley cant get her free snow (how sad)
snow.earnSnow();
assert(snow.balanceOf(ashley) == 2); // @> can't be reached because the test fails 1 line up
}

Failing test:

image of failing test

Recommended Mitigation

Removing the line that updates the s_earnTimer will solve the problem.

function buySnow(uint256 amount) external payable canFarmSnow {
if (msg.value == (s_buyFee * amount)) {
_mint(msg.sender, amount);
} else {
i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
_mint(msg.sender, amount);
}
- s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, amount);
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge 24 days ago
Submission Judgement Published
Validated
Assigned finding tags:

buying of snow resets global timer thus affecting earning of free snow

When buySnow is successfully called, the global timer is reset. This inadvertently affects the earning of snow as that particular action also depends on the global timer.

Support

FAQs

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