In the Dating DApp (Web3 Audit) protocol, the owner is permanently locked out of withdrawing accumulated protocol fees. The withdrawFees() function correctly restricts access using the onlyOwner modifier and requires that totalFees > 0. However, because of the previously identified broken accounting in the likeUser() function, userBalances always evaluate to zero. When matchRewards() executes, it calculates the matchingFees as a percentage of 0. Consequently, totalFees always increases by 0, leaving the state variable permanently empty.
Likelihood:
Because the root cause lies in a guaranteed mathematical failure during the matching process, totalFees will never increment under any circumstances. Therefore, every single attempt by the owner to call withdrawFees() will fail.
Impact:
The protocol suffers a 100% loss of its intended revenue. The Ether that should have been collected as fees remains permanently trapped inside the LikeRegistry contract, alongside the users' stuck funds, with no alternative administrative function available to sweep or recover it.
The accompanying Foundry test demonstrates the downstream failure of the fee accounting system. The test simulates a successful match where Alice and Spider each deposit 1 Ether (totaling 2 Ether held by the contract). When the protocol owner attempts to call withdrawFees(), the transaction reverts with the specific error "No fees to withdraw". The final assertions definitively prove that the contract balance remains exactly at 2 Ether and the owner successfully retrieves nothing.
Because this is a cascading failure, resolving this issue requires fixing the root cause upstream rather than changing the withdrawFees() function itself.
## Description User A calls `likeUser` and sends `value > 1` ETH. According to the design of DatingDapp, the amount for user A should be accumulated by `userBalances`. Otherwise, in the subsequent calculations, the balance for each user will be 0. ## Vulnerability Details When User A calls `likeUser`, the accumulation of `userBalances` is not performed. ```solidity 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); // 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); } } ``` This will result in `totalRewards` always being 0, affecting all subsequent calculations: ```solidity uint256 totalRewards = matchUserOne + matchUserTwo; uint256 matchingFees = (totalRewards * FIXEDFEE ) / 100; uint256 rewards = totalRewards - matchingFees; totalFees += matchingFees; ``` ## POC ```solidity function testUserBalanceshouldIncreaseAfterLike() public { vm.prank(user1); likeRegistry.likeUser{value: 20 ether}(user2); assertEq(likeRegistry.userBalances(user1), 20 ether, "User1 balance should be 20 ether"); } ``` Then we will get an error: ```shell [FAIL: User1 balance should be 20 ether: 0 != 20000000000000000000] ``` ## Impact - Users will be unable to receive rewards. - The contract owner will also be unable to withdraw ETH from the contract. ## Recommendations Add processing for `userBalances` in the `likeUser` function: ```diff 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; + userBalances[msg.sender] += msg.value; emit Liked(msg.sender, liked); [...] } ```
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.