Snowman Merkle Airdrop

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

[H-3] `Snow::buySnow()` Updates Shared `s_earnTimer` Variable Causing Denial Of Service Within `Snow::earnSnow()`

[H-3] Snow::buySnow() Updates Shared s_earnTimer Variable Causing Denial Of Service Within Snow::earnSnow()

Description

  • Snow::buySnow() should allow Snow tokens to be bought at anytime, while Snow::earnSnow() should allow users to earn Snow tokens for free once a week.

  • The issue here is that Snow::buySnow() updates the s_earnTimer variable which is also used within Snow::earnSnow() to check if a week has passed before allowing users to earn Snow tokens. This results in a Denial Of Service for users to call Snow::earnSnow()

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; // variable updated here
emit SnowBought(msg.sender, amount);
}
function earnSnow() external canFarmSnow {
@> if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) { // variable checked here to check if 1 week has passed since s_earnTimer
revert S__Timer();
}
_mint(msg.sender, 1);
s_earnTimer = block.timestamp;
}

Risk

Likelihood: High

  • Snow::buySnow() is an advertised component which would be used often by users

Impact: High

  • Users will not be able to earn Snow tokens which is an advertised component of the airdrop.

Proof of Concept

The following test added to TestSnow.t.sol demonstrates the Denial Of Service for the earnSnow() function when buySnow() is called by anyone.

function testCannotEarnSnowAfterBuySnow() public {
vm.prank(victory);
snow.buySnow{value: FEE}(1);
assert(victory.balance == 0);
assert(address(snow).balance == FEE);
assert(snow.balanceOf(victory) == 1);
vm.prank(ashley);
vm.expectRevert(); // Should not revert as Ashley has not earned free Snow token yet, but reverts
snow.earnSnow();
vm.warp(block.timestamp + 1 weeks);
vm.prank(ashley);
snow.earnSnow();
assert(snow.balanceOf(ashley) == 1);
}

Recommended Mitigation

This issue can be mitigated by removing the line that updates s_earnTimer within Snow::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;
emit SnowBought(msg.sender, amount);
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge 3 months 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.