Summary
Vulnerability Details
The withdrawFees
function is permanently disabled due to the missing balance recording issue. Since user balances are never updated, the fee calculation always results in zero, preventing the protocol from collecting any fees.
Impact
Protocol revenue stream completely disabled and owner unable to withdraw legitimate fees
Proof of concept
function testWithdrawalFeeDosAttack() public {
vm.deal(user, 10 ether);
vm.deal(user2, 10 ether);
vm.prank(user);
soulboundNFT.mintProfile("Alice", 25, "ipfs://profileImage");
vm.prank(user2);
soulboundNFT.mintProfile("Bob", 30, "ipfs://profileImage");
vm.prank(user);
likeRegistry.likeUser{value: 1 ether}(user2);
vm.prank(user2);
likeRegistry.likeUser{value: 1 ether}(user);
vm.expectRevert("No fees to withdraw");
likeRegistry.withdrawFees();
}
Tools Used
Manual Review
Recommendations
Fix the balance recording in likeUser function and ensure proper fee calculation in matchRewards and update user Balance:
function likeUser(address liked) external payable {
require(msg.value >= 1 ether, "Must send at least 1 ETH");
require(!likes[msg.sender][liked], "Already liked");
require(msg.sender != liked, "Cannot like yourself");
require(profileNFT.profileToToken(msg.sender) != 0, "Must have a profile NFT");
require(profileNFT.profileToToken(liked) != 0, "Liked user must have a profile NFT");
likes[msg.sender][liked] = true;
emit Liked(msg.sender, liked);
+ userBalances[msg.sender] = msg.value;
// Check if mutual like
if (likes[liked][msg.sender]) {
matches[msg.sender].push(liked);
matches[liked].push(msg.sender);
emit Matched(msg.sender, liked);
matchRewards(liked, msg.sender);
}
}