[H-1] Incorrect ETH and Weth Fee Calculations in Snow::buySnow
Function.
Description
The fee Charged to users in terms of ETH and WETH in Snow::buySnow
Function contains incorrect calculations,rounding errors,precision error and
unimaginary amounts,which leads to minting excessively large amount of tokens.
Impact:
1.Users can mint astronomically large amount of snow tokens due to mismatch of eth and weth calculations.
2.Excessive minting inflates the total supply of snow tokens which devalues the token and can result
in overflow/underflow issues.
3.Malicious user can exploit this issue and can mint large amount of tokens.
Proof of Concept
1.Despite of minting correct tokens as Bob.Bob cannot buy snow tokens due to math error.
2.Bob minted 1e18 tokens, but the contract logic is trying to transfer 1e36 tokens which results
in ERC20insufficientbalance error.
POC With Weth-Payment:
function test_buysnowExploitwithWETH() public {
vm.startPrank(bob);
weth.mint(bob, 1e18);
weth.approve(address(snow), type(uint).max);
snow.buySnow{value: 0}(1e18);
vm.stopPrank();
OwnTest::test_buysnowExploit()
├─ [0] VM::startPrank(bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e])
│ └─ ← [Return]
├─ [49997] MockWETH::mint(bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e], 1000000000000000000 [1e18])
│ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e], value: 1000000000000000000 [1e18])
│ └─ ← [Return]
├─ [26927] MockWETH::approve(Snow: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77])
│ ├─ emit Approval(owner: bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e], spender: Snow: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], value: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77])
│ └─ ← [Return] true
├─ [10932] Snow::buySnow(1000000000000000000 [1e18])
│ ├─ [3911] MockWETH::transferFrom(bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e], Snow: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], 1000000000000000000000000000000000000 [1e36])
│ │ └─ ← [Revert] ERC20InsufficientBalance(0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e, 1000000000000000000 [1e18], 1000000000000000000000000000000000000 [1e36])
│ └─ ← [Revert] ERC20InsufficientBalance(0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e, 1000000000000000000 [1e18], 1000000000000000000000000000000000000 [1e36])
└─ ← [Revert] ERC20InsufficientBalance(0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e, 1000000000000000000 [1e18], 1000000000000000000000000000000000000 [1e36])
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 21.45ms (4.91ms CPU time)*/
}
3.Due to incorrect calculations ,the if statement of ETH payment will never gets exceuted.
4.This will result making LHS(msg.value) ==1e18 ,But due to wrong calculations
RHS will be 1e36 due to which this if (msg.value == (s_buyFee * amount))
condition
will not get satisfied ,resulting into weth condition execution
POC with ETH-Payment:
function test_buysnowExploitwithETH() public {
vm.deal(bob, 10 ether);
vm.startPrank(bob);
snow.buySnow{value: 1e18}(1e18);
vm.stopPrank();
}
Recommended Mitigation
Use Correct precision calculations:
function buySnow(uint256 amount) external payable canFarmSnow {
require(amount > 0, "Amount must be greater than zero");
uint256 scaledAmount = amount / 1e18;
uint256 totalCost = s_buyFee * scaledAmount;
if (msg.value > 0) {
require(msg.value == totalCost, "Incorrect ETH sent");
} else {
i_weth.safeTransferFrom(msg.sender, address(this), totalCost);
}
_mint(msg.sender, scaledAmount);
s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, scaledAmount);