Root + Impact
Description
@> using SafeERC20 for IERC20;
function collectFee() external onlyCollector {
@> i_weth.transfer(s_collector, collection);
}
Risk
Likelihood: Occurs every time collector withdraws fees; standard WETH works but non-standard tokens or upgrades could break
Impact: Fee transfers could silently fail, permanently trapping protocol revenue in the contract
Proof of Concept
The inconsistency is evident in the codebase. buySnow() properly uses safe transfers while collectFee() does not, indicating developer oversight.
pragma solidity ^0.8.24;
import {Test} from "forge-std/Test.sol";
import {Snow} from "../src/Snow.sol";
contract NonCompliantToken {
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
function mint(address to, uint256 amount) external {
balanceOf[to] += amount;
}
function approve(address spender, uint256 amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
return true;
}
function transferFrom(address from, address to, uint256 amount) external {
allowance[from][msg.sender] -= amount;
balanceOf[from] -= amount;
balanceOf[to] += amount;
}
function transfer(address to, uint256 amount) external {
balanceOf[msg.sender] -= amount;
balanceOf[to] += amount;
}
}
contract UnsafeTransferTest is Test {
function testInconsistentSafetyPattern() public {
}
}
Recommended Mitigation
Simply replace the raw transfer() call with safeTransfer() to maintain consistency with the rest of the codebase and ensure proper error handling.
function collectFee() external onlyCollector {
uint256 collection = i_weth.balanceOf(address(this));
- i_weth.transfer(s_collector, collection);
+ i_weth.safeTransfer(s_collector, collection);
(bool collected,) = payable(s_collector).call{value: address(this).balance}("");
require(collected, "Fee collection failed!!!");
+
+ emit FeeCollected();
}