constructor(
IERC20 _asset,
uint256 _participationFeeBsp,
uint256 _eventStartDate,
@> address _participationFeeAddress,
uint256 _minimumAmount,
uint256 _eventEndDate
) ERC4626(_asset) ERC20("BriTechLabs", "BTT") Ownable(msg.sender) {
if (_participationFeeBsp > PARTICIPATIONFEEBSPMAX) {
revert limiteExceede();
}
participationFeeBsp = _participationFeeBsp;
eventStartDate = _eventStartDate;
eventEndDate = _eventEndDate;
@> participationFeeAddress = _participationFeeAddress;
minimumAmount = _minimumAmount;
_setWinner = false;
}
function deposit(uint256 assets, address receiver) public override returns (uint256) {
require(receiver != address(0));
if (block.timestamp >= eventStartDate) {
revert eventStarted();
}
uint256 fee = _getParticipationFee(assets);
if (minimumAmount + fee > assets) {
revert lowFeeAndAmount();
}
uint256 stakeAsset = assets - fee;
stakedAsset[receiver] = stakeAsset;
uint256 participantShares = _convertToShares(stakeAsset);
@> IERC20(asset()).safeTransferFrom(msg.sender, participationFeeAddress, fee);
IERC20(asset()).safeTransferFrom(msg.sender, address(this), stakeAsset);
_mint(msg.sender, participantShares);
emit deposited(receiver, stakeAsset);
return participantShares;
}
pragma solidity ^0.8.24;
import {Test, console} from "forge-std/Test.sol";
import {BriVault} from "../src/briVault.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {MockERC20} from "./MockErc20.t.sol";
* @title C-02: Zero Address Fee Recipient Causes Complete DoS
* @notice Demonstrates that address(0) fee recipient blocks ALL deposits
*/
contract C02_ZeroAddressFeeRecipient is Test {
MockERC20 public mockToken;
BriVault public vault;
address owner = makeAddr("owner");
address user1 = makeAddr("user1");
address user2 = makeAddr("user2");
address user3 = makeAddr("user3");
address user4 = makeAddr("user4");
address user5 = makeAddr("user5");
uint256 participationFeeBsp = 300;
uint256 eventStartDate;
uint256 eventEndDate;
uint256 minimumAmount = 0.0002 ether;
string[48] countries = [
"United States", "Canada", "Mexico", "Argentina", "Brazil",
"Ecuador", "Uruguay", "Colombia", "Peru", "Chile",
"Japan", "South Korea", "Australia", "Iran", "Saudi Arabia",
"Qatar", "Uzbekistan", "Jordan", "France", "Germany",
"Spain", "Portugal", "England", "Netherlands", "Italy",
"Croatia", "Belgium", "Switzerland", "Denmark", "Poland",
"Serbia", "Sweden", "Austria", "Morocco", "Senegal",
"Nigeria", "Cameroon", "Egypt", "South Africa", "Ghana",
"Algeria", "Tunisia", "Ivory Coast", "New Zealand", "Costa Rica",
"Panama", "United Arab Emirates", "Iraq"
];
function setUp() public {
eventStartDate = block.timestamp + 2 days;
eventEndDate = eventStartDate + 31 days;
mockToken = new MockERC20("Mock Token", "MTK");
mockToken.mint(user1, 100 ether);
mockToken.mint(user2, 200 ether);
mockToken.mint(user3, 50 ether);
mockToken.mint(user4, 150 ether);
mockToken.mint(user5, 75 ether);
}
function test_ZeroAddressFeeRecipient_CompleteDoS() public {
console.log("=== POC: Zero Address Fee Recipient - Complete DoS ===");
console.log("\n--- Step 1: Deployment ---");
vm.startPrank(owner);
vault = new BriVault(
IERC20(address(mockToken)),
participationFeeBsp,
eventStartDate,
address(0),
minimumAmount,
eventEndDate
);
vm.stopPrank();
console.log("Vault deployed successfully");
console.log("Fee recipient: address(0)");
console.log("Participation fee:", (participationFeeBsp * 100) / 10000, "%");
console.log("\n--- Step 2: Multiple Users Attempt Deposits ---");
uint256 totalAttemptedDeposits = 0;
console.log("\nUser1 attempting to deposit 10 ETH...");
uint256 deposit1 = 10 ether;
totalAttemptedDeposits += deposit1;
vm.startPrank(user1);
mockToken.approve(address(vault), deposit1);
vm.expectRevert();
vault.deposit(deposit1, user1);
vm.stopPrank();
console.log("Result: REVERTED");
console.log("\nUser2 attempting to deposit 50 ETH...");
uint256 deposit2 = 50 ether;
totalAttemptedDeposits += deposit2;
vm.startPrank(user2);
mockToken.approve(address(vault), deposit2);
vm.expectRevert();
vault.deposit(deposit2, user2);
vm.stopPrank();
console.log("Result: REVERTED");
console.log("\nUser3 attempting to deposit 25 ETH...");
uint256 deposit3 = 25 ether;
totalAttemptedDeposits += deposit3;
vm.startPrank(user3);
mockToken.approve(address(vault), deposit3);
vm.expectRevert();
vault.deposit(deposit3, user3);
vm.stopPrank();
console.log("Result: REVERTED");
console.log("\nUser4 attempting to deposit 100 ETH...");
uint256 deposit4 = 100 ether;
totalAttemptedDeposits += deposit4;
vm.startPrank(user4);
mockToken.approve(address(vault), deposit4);
vm.expectRevert();
vault.deposit(deposit4, user4);
vm.stopPrank();
console.log("Result: REVERTED");
console.log("\nUser5 attempting to deposit 30 ETH...");
uint256 deposit5 = 30 ether;
totalAttemptedDeposits += deposit5;
vm.startPrank(user5);
mockToken.approve(address(vault), deposit5);
vm.expectRevert();
vault.deposit(deposit5, user5);
vm.stopPrank();
console.log("Result: REVERTED");
console.log("\n--- Step 3: Impact Verification ---");
uint256 vaultBalance = mockToken.balanceOf(address(vault));
uint256 numberOfParticipants = vault.numberOfParticipants();
console.log("Total users attempted: 5");
console.log("Successful deposits: 0");
console.log("Attempted deposit amount:", totalAttemptedDeposits / 1 ether, "ETH");
console.log("Actual vault balance:", vaultBalance);
console.log("Participants registered:", numberOfParticipants);
assertEq(vaultBalance, 0, "Vault should be empty");
assertEq(numberOfParticipants, 0, "No participants");
console.log("\n=== IMPACT SUMMARY ===");
console.log("Users affected: 5/5 (100%)");
console.log("Deposits blocked:", totalAttemptedDeposits / 1 ether, "ETH");
console.log("Contract status: UNUSABLE - REQUIRES REDEPLOYMENT");
}
}
[PASS] test_ZeroAddressFeeRecipient_CompleteDoS() (gas: 4001217)
Logs:
=== POC: Zero Address Fee Recipient - Complete DoS ===
--- Step 1: Deployment ---
Vault deployed successfully
Fee recipient: address(0)
Participation fee: 3 %
Minimum deposit: 200000000000000
--- Step 2: Multiple Users Attempt Deposits ---
User1 attempting to deposit 10 ETH...
Result: REVERTED
User1 balance unchanged: true
User2 attempting to deposit 50 ETH...
Result: REVERTED
User2 balance unchanged: true
User3 attempting to deposit 25 ETH...
Result: REVERTED
User3 balance unchanged: true
User4 attempting to deposit 100 ETH...
Result: REVERTED
User4 balance unchanged: true
User5 attempting to deposit 30 ETH...
Result: REVERTED
User5 balance unchanged: true
--- Step 3: DoS Impact Analysis ---
Total users attempted to deposit: 5
Successful deposits: 0
Total attempted deposit amount: 215 ETH
Actual vault balance: 0
Number of participants: 0
--- Step 4: Financial Impact ---
Expected total deposits: 215 ETH
Expected protocol fees: 6 ETH
Expected vault balance: 208 ETH
Actual vault balance: 0 ETH
Actual participants: 0
=== IMPACT SUMMARY ===
Vulnerability: Fee recipient set to address(0)
Consequence: ERC20 transfer to address(0) reverts
DoS Statistics:
- Users affected: 5/5 (100%)
- Deposits blocked: 215 ETH
- Successful deposits: 0 ETH
- Contract participants: 0
Business Impact:
- Lost user deposits: 215 ETH
- Lost protocol fees: 6 ETH
- Contract status: UNUSABLE
- Required action: REDEPLOY CONTRACT
Severity: HIGH
Impact: Complete Denial of Service
User Experience: All deposit attempts fail