The normal behavior should validate that fees set by the owner are within reasonable bounds. Uniswap V4 uses basis points where 1,000,000 = 100%, so fees should not exceed this maximum.
pragma solidity ^0.8.26;
import "forge-std/Test.sol";
import "forge-std/console.sol";
contract FeeValidationTest is Test {
uint24 public buyFee;
uint24 public sellFee;
uint24 constant MAX_VALID_FEE = 1000000;
uint24 constant MAX_UINT24 = type(uint24).max;
function changeFee_BUGGY(
bool _isBuyFee,
uint24 _buyFee,
bool _isSellFee,
uint24 _sellFee
) public {
if (_isBuyFee) buyFee = _buyFee;
if (_isSellFee) sellFee = _sellFee;
}
function changeFee_SAFE(
bool _isBuyFee,
uint24 _buyFee,
bool _isSellFee,
uint24 _sellFee
) public {
if (_isBuyFee) {
require(_buyFee <= MAX_VALID_FEE, "Buy fee exceeds maximum");
buyFee = _buyFee;
}
if (_isSellFee) {
require(_sellFee <= MAX_VALID_FEE, "Sell fee exceeds maximum");
sellFee = _sellFee;
}
}
function test_BuggyAllowsExcessiveFees() public {
console.log("\n=== Testing Buggy Implementation ===");
console.log("Maximum valid fee (100%):", MAX_VALID_FEE);
console.log("Maximum uint24 value:", MAX_UINT24);
uint24 wrongFee = 30000;
changeFee_BUGGY(false, 0, true, wrongFee);
console.log("\nScenario 1: Typo with extra zero");
console.log("Intended fee: 3000 (0.3%)");
console.log("Actual fee set:", sellFee);
console.log("Fee percentage:", uint256(sellFee) * 100 / MAX_VALID_FEE, "%");
assertEq(sellFee, 30000, "Wrong fee accepted");
uint24 percentageMistake = 30;
changeFee_BUGGY(false, 0, true, percentageMistake);
console.log("\nScenario 2: Unit confusion");
console.log("Owner thinks: 30%");
console.log("Actual fee set:", sellFee);
console.log("Actual percentage:", uint256(sellFee) * 100 / MAX_VALID_FEE, "%");
changeFee_BUGGY(false, 0, true, MAX_VALID_FEE);
console.log("\nScenario 3: Maximum valid fee");
console.log("Fee set:", sellFee, "(100%)");
assertEq(sellFee, MAX_VALID_FEE, "100% fee allowed");
uint24 excessiveFee = MAX_VALID_FEE + 1;
changeFee_BUGGY(false, 0, true, excessiveFee);
console.log("\nScenario 4: Exceeds 100%");
console.log("Fee set:", sellFee);
console.log("Percentage:", uint256(sellFee) * 100 / MAX_VALID_FEE, "%");
assertGt(sellFee, MAX_VALID_FEE, "Excessive fee accepted");
changeFee_BUGGY(false, 0, true, MAX_UINT24);
console.log("\nScenario 5: Maximum uint24 value");
console.log("Fee set:", sellFee);
console.log("Percentage:", uint256(sellFee) * 100 / MAX_VALID_FEE, "%");
assertEq(sellFee, MAX_UINT24, "Maximum uint24 accepted");
console.log("\n❌ BUG CONFIRMED: All excessive fees were accepted without validation!");
}
function test_SafeRejectsExcessiveFees() public {
console.log("\n=== Testing Safe Implementation ===");
changeFee_SAFE(false, 0, true, MAX_VALID_FEE);
console.log("Boundary test: MAX_VALID_FEE accepted");
assertEq(sellFee, MAX_VALID_FEE, "Boundary fee accepted");
console.log("\nTesting fees beyond 100%...");
vm.expectRevert("Sell fee exceeds maximum");
changeFee_SAFE(false, 0, true, MAX_VALID_FEE + 1);
console.log("✅ Correctly rejected fee:", MAX_VALID_FEE + 1);
vm.expectRevert("Sell fee exceeds maximum");
changeFee_SAFE(false, 0, true, MAX_UINT24);
console.log("✅ Correctly rejected max uint24:", MAX_UINT24);
vm.expectRevert("Buy fee exceeds maximum");
changeFee_SAFE(true, MAX_VALID_FEE + 1, false, 0);
console.log("✅ Correctly rejected excessive buy fee");
}
function test_RealWorldScenarios() public {
console.log("\n=== Real-World Scenarios ===");
console.log("\nOwner wants: 0.3% sell fee");
console.log("Correct value: 3000 basis points");
changeFee_BUGGY(false, 0, true, 30000);
console.log("Typo (30000):", sellFee, "basis points =", uint256(sellFee) / 100, "% ❌");
changeFee_BUGGY(false, 0, true, 3);
console.log("Unit confusion (3):", sellFee, "basis points = 0.003% ❌");
changeFee_BUGGY(false, 0, true, 3000);
console.log("Correct (3000):", sellFee, "basis points = 0.3% ✅");
console.log("\n=== Potential Exploit Scenario ===");
console.log("Malicious owner sets 99% sell fee to extract value");
changeFee_BUGGY(false, 0, true, 990000);
console.log("Sell fee set to:", sellFee, "basis points");
console.log("Effective fee: 99%");
console.log("Impact: Users selling 1000 tokens receive only 10 tokens");
console.log("LPs receive 990 tokens as fees (potential rugpull vector)");
}
function test_ImpactOnSwaps() public {
console.log("\n=== Impact on Swap Calculations ===");
uint256 swapAmount = 1000 ether;
console.log("Swap amount:", swapAmount / 1 ether, "tokens");
sellFee = 3000;
uint256 normalFeeAmount = (swapAmount * sellFee) / MAX_VALID_FEE;
console.log("\nNormal fee (0.3%):", normalFeeAmount / 1 ether, "tokens");
console.log("User receives:", (swapAmount - normalFeeAmount) / 1 ether, "tokens");
sellFee = 100000;
uint256 excessiveFeeAmount = (swapAmount * sellFee) / MAX_VALID_FEE;
console.log("\nExcessive fee (10%):", excessiveFeeAmount / 1 ether, "tokens");
console.log("User receives:", (swapAmount - excessiveFeeAmount) / 1 ether, "tokens");
sellFee = MAX_VALID_FEE;
uint256 extremeFeeAmount = (swapAmount * sellFee) / MAX_VALID_FEE;
console.log("\nExtreme fee (100%):", extremeFeeAmount / 1 ether, "tokens");
console.log("User receives:", (swapAmount - extremeFeeAmount) / 1 ether, "tokens (nothing!)");
sellFee = MAX_VALID_FEE + 500000;
uint256 invalidFeeAmount = (swapAmount * sellFee) / MAX_VALID_FEE;
console.log("\nInvalid fee (>100%):", invalidFeeAmount / 1 ether, "tokens");
console.log("Fee amount exceeds swap amount - undefined behavior!");
}
}