DatingDapp

AI First Flight #6
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Severity: high
Valid

Matched users receive nothing and all deposited ETH is stranded in LikeRegistry because the deposit accounting (userBalances) is never written on a like

Every liker permanently loses their ETH and matched couples receive nothing

Description

Because likeUser records no deposit into userBalances (the mapping at line 22 is read in matchRewards but never incremented), the 1+ ETH each user sends is trapped in LikeRegistry forever, and matched couples are paid 0. The escrow that should fund rewards is never built up.

function matchRewards(address from, address to) internal {
uint256 matchUserOne = userBalances[from]; // @> always 0
uint256 matchUserTwo = userBalances[to]; // @> always 0
userBalances[from] = 0;
userBalances[to] = 0;
uint256 totalRewards = matchUserOne + matchUserTwo; // @> always 0

Risk

Likelihood: Guaranteed on every interaction. Any user who calls likeUser sends real ETH that is never accounted for, and every mutual match deploys a multisig funded with 0.

Impact: Direct, permanent loss of user funds. The sum of all 1-ETH likes is locked in LikeRegistry with no reachable withdrawal path: matchRewards forwards rewards = totalRewards - matchingFees = 0 (line 58/65), and withdrawFees (lines 73-80) only sends totalFees, which is also 0 since matchingFees is computed from a 0 totalRewards. Neither users nor the owner can recover the stranded ETH. This is a complete loss-of-funds condition affecting the entire user base.

Proof of Concept

Fund several likes, then show the contract balance is stuck and no party can withdraw it.

function test_allUserEthStranded() public {
vm.prank(alice); registry.likeUser{value: 1 ether}(bob);
vm.prank(bob); registry.likeUser{value: 1 ether}(alice); // match
// couple's multisig got nothing
assertEq(address(registry).balance, 2 ether);
// owner cannot rescue: totalFees == 0
vm.prank(owner);
vm.expectRevert("No fees to withdraw");
registry.withdrawFees();
}

Recommended Mitigation

Record deposits on like so escrow accumulates and the reward/fee math operates on real balances.

function likeUser(address liked) external payable {
require(msg.value >= 1 ether, "Must send at least 1 ETH");
+ userBalances[msg.sender] += msg.value;
// ... existing checks and like recording ...
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 2 hours ago
Submission Judgement Published
Validated
Assigned finding tags:

[H-01] After the user calls the `likeUser` function, the userBalance does not increase by the corresponding value.

## 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); [...] } ```

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!