Normal behavior: when two users match via likeUser(), matchRewards() should pool the ETH each user deposited (tracked in userBalances) and forward ~90% to a newly deployed MultiSigWallet for the matched pair, keeping ~10% as protocol fee.
Specific issue: userBalances[address] is never written with a positive value anywhere in the contract. likeUser() receives msg.value but never credits it into userBalances[msg.sender], so every reward computed from it is 0.
Likelihood:
Triggers on every single likeUser() call — the ETH sent (min 1 ETH, no cap) is immediately unaccounted for the moment the function returns, with no special precondition required.
Also affects any ETH sent directly via the contract's receive() fallback, which has no accounting logic at all.
Impact:
Every ETH ever deposited into LikeRegistry accumulates permanently in the contract balance with zero function anywhere able to move it back out — not to matched users, not to the contract owner.
withdrawFees()'s require(totalFees > 0, "No fees to withdraw") always reverts as a direct downstream consequence, since totalFees can never exceed 0.
This test has two users mutually like each other (2 ETH deposited total, matching the mutual-like trigger for matchRewards()), then asserts the entire 2 ETH is sitting in LikeRegistry's own balance rather than in either user's MultiSigWallet, and that the owner's only withdrawal function reverts — proving there is no path, for any party, to recover the funds.
The root cause is a missing state write: likeUser() receives msg.value but never records it against the sender. Adding a single line — crediting userBalances[msg.sender] with the deposited ETH at the point it's received — closes the gap, since matchRewards() already correctly reads from userBalances on both sides; it was only ever missing a writer.
## 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.