Every likeUser permanently removes >= 1 ETH from circulation into a contract
that has exactly one outgoing function (withdrawFees), which can only send
totalFees
Users can never recover their like payments; the promised match pool are pooled into a shared
multisig wallet is never funded, so matched users receive 0.
Even the owner cannot sweep the balance
Any matching user loses at least 1 ETH; the loss scales with
the number of likes
likeUser accepts >= 1 ETH per like but never writes it into userBalances.
The only two write sites for userBalances in the entire codebase zero it out
(matchRewards, L58–59). Because balances stay 0, every mutual match pays out
0 ETH, totalFees stays 0, and withdrawFees can never move the contract's
ETH. All like-payments are permanently stranded in LikeRegistry with no recovery
path for any actor
Can lead to loss of Funds
}
PoC evidence: after two mutual likes of 1 ETH each, LikeRegistry holds 2 ETH,
both userBalances are 0, totalFees is 0, the matched pair received 0, and
a legitimate withdrawFees() by the owner reverts with "No fees to withdraw".
Credit the payment atomically when it is taken, following CEI:
and in matchRewards, move the external call to the end (the multisig receive()
is empty today, so reentrancy risk is nil, but ordering should be hardened anyway).
## 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.