DatingDapp

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

Unaccounted msg.value in likeUser() — user ETH permanently stuck, matchRewards always pays out zero

Description

The likeUser() function accepts an ETH payment but never credits it to
userBalances, the mapping matchRewards() relies on to calculate match
payouts. As a result, every match distributes 0 ETH regardless of how
much either party paid, and all ETH sent via likeUser() becomes
permanently stuck in the contract with no function able to recover it.


Risk → Likelihood

- Every call to likeUser() sends ETH but the contract never records it against the sender — this is unconditional, not an edge case; it happens on 100% of likes.
- userBalances has no write path anywhere else in the contract, so there's no code path that avoids the bug.

Risk → Impact

- All ETH sent via likeUser() becomes permanently stuck with no recovery path — withdrawFees() only drains totalFees, which is itself always 0 since it's derived from userBalances inside matchRewards.
- Every match pays out 0 ETH via matchRewards, even though both users paid the required stake — the core value proposition of the protocol (pooling like-payments into a shared multisig on match) silently never happens.
​
​
PoC Foundry (Proof of concept):
​
function test_LikeUser_DoesNotCreditUserBalance() public {
vm.prank(alice);
likeRegistry.likeUser{value: 1 ether}(bob);
​
// Contract holds the ETH...
assertEq(address(likeRegistry).balance, 1 ether);
​
// ...but userBalances was never credited
assertEq(likeRegistry.userBalances(alice), 0);
​
// No function in the contract can move this ETH back to alice:
// withdrawFees() only drains totalFees, which is always 0.
}

Recommended Mitigation

function likeUser(address liked) external payable {
- require(msg.value >= 1 ether, "Must send at least 1 ETH");
+ require(msg.value == 1 ether, "Must send exactly 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);
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 1 hour 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!