buySnow When Sending ETH Incorrectly; collectFee Sweeps Overcharged FundsIntended Behavior: The Snow.sol contract allows users to buy Snow tokens. Payment can be made either entirely in native ETH (if msg.value exactly matches the total fee) or entirely in WETH (if msg.value is not the exact ETH fee, implying a WETH payment). The collectFee function allows a designated collector to withdraw accumulated ETH and WETH fees from the contract.
Specific Vulnerability: The buySnow function has a logical flaw in its payment processing. If a user sends some native ETH (i.e., msg.value > 0) but this amount does not exactly match the total fee calculated as (s_buyFee * amount), the contract enters an else block. In this block:
The native ETH sent by the user (the msg.value) remains with the Snow.sol contract.
The contract then proceeds to execute i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount)), transferring the full fee amount in WETH from the user.
This results in the user being charged twice: once with the native ETH they sent, and again with the full WETH fee for the same Snow tokens. The collectFee function subsequently allows the collector to withdraw these overcharged funds (both the erroneously kept ETH and the WETH).
Likelihood: Medium
A user might mistakenly send some ETH while intending a WETH payment, or send an incorrect ETH amount, triggering the overcharge. This depends on user interface and user understanding.
Impact: High
Direct Loss of User Funds: Users are charged more than the intended fee, losing both the incorrectly sent ETH and the full WETH fee.
Unjust Enrichment of Collector: The collectFee function transfers these overcharged funds to the collector.
Damage to Trust: Users who are overcharged will lose trust in the protocol.
The testBuySnow_UserOverchargeAndCollectorWithdrawal function in test/Snow.t.sol demonstrates this vulnerability.
(The constructor _buyFee argument is set to 1, which due to s_buyFee = _buyFee * PRECISION makes the actual s_buyFee per Snow token equal to 1 WETH).
Setup: Alice has ETH and WETH. The Snow contract (instance named snow) is deployed with _buyFee = 1 (so 1 Snow token costs 1 WETH).
Overcharge Transaction: Alice calls snow.buySnow{value: 0.1 ether}(1). She intends to buy 1 Snow token.
Since 0.1 ether is not equal to the total fee in ETH (which would be 1 ETH if s_buyFee is 1 WETH), the else block in buySnow is triggered.
The Snow contract receives and keeps the 0.1 ether.
The Snow contract then transfers 1 WETH (the s_buyFee * 1) from Alice.
Alice effectively pays 0.1 ETH + 1 WETH for 1 Snow token.
Fee Collection: The collector calls snow.collectFee().
The 0.1 ETH and 1 WETH (which includes the overcharged components) held by the Snow contract are transferred to the collector.
The test logs and assertions confirm that Alice is charged both the ETH she sent and the full WETH fee, and these funds are subsequently withdrawable by the collector.
Modify the buySnow function to handle payments more robustly and prevent overcharging:
Clear Payment Paths: Enforce clearer conditions for ETH vs. WETH payments.
If msg.value > 0, it should be treated as an attempt for a full ETH payment. If msg.value is not the exact amount, the transaction should revert.
If msg.value == 0, it should be treated as a WETH payment, and only WETH should be transferred.
Clarify _buyFee and s_buyFee: Review the s_buyFee = _buyFee * PRECISION; logic. If _buyFee is intended to be the price in wei, the multiplication by PRECISION is incorrect and will lead to an extremely inflated s_buyFee. s_buyFee should accurately reflect the intended price per Snow token in wei. For example, if _buyFee from the constructor represents the fee in wei, then it should be s_buyFee = _buyFee;.
By implementing these changes, users will not be accidentally overcharged, and the collectFee function will only collect legitimately earned fees.
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.