LikeRegistry::likeUser never updates userBalances, locking all user ETH and breaking reward distribution and fee collection
Users must send at least 1 ETH to likeUser, and these deposits are meant to be tracked in userBalances. On a mutual like, matchRewards reads both users' balances, keeps 10% (FIXEDFEE) as totalFees, and sends the remaining 90% to a newly deployed MultiSigWallet shared by the two users.
However, likeUser never adds msg.value to userBalances, so the mapping is always 0. matchRewards therefore computes totalRewards = 0, sends 0 ETH to the MultiSigWallet, and accrues 0 fees. The ETH sent by users remains in LikeRegistry, which has no function that returns it to them.
Likelihood: High
This occurs on every call to likeUser by every user. No attacker, special condition, or privileged access is required.
Impact: High
All ETH sent through likeUser is permanently locked in LikeRegistry. No function sends it back to users, and withdrawFees can only send totalFees, which is always 0. This applies to every like, including likes that are never reciprocated.
On a mutual like, the MultiSigWallet is deployed with a 0 balance instead of 90% of the two users' combined deposits (1.8 ETH for two deposits of 1 ETH).
totalFees is always 0, so the protocol never collects fees and withdrawFees always reverts.
Credit the deposit to the sender (msg.sender) before the mutual-like check, because matchRewards reads userBalances.
## 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.