DatingDapp

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

Permanent Loss of User Funds due to Missing Balance Accounting

Description

  • The likeUser function is intended to facilitate matching between users by requiring a payment of at least 1 ETH. This ETH is meant to be stored as a reward pool that is distributed to the pair upon a mutual match.

  • The specific issue is that while likeUser successfully receives msg.value, it fails to update the userBalances mapping for the sender. Consequently, when matchRewards is triggered, it attempts to distribute balances that are hard-coded to 0 at the storage level, leaving the actual ETH trapped in the contract.

function likeUser(address liked) external payable {
require(msg.value >= 1 ether, "Must send at least 1 ETH");
require(!likes[msg.sender][liked], "Already liked");
// ... (other checks)
@> // BUG: userBalances[msg.sender] += msg.value; is missing here.
likes[msg.sender][liked] = true;
emit Liked(msg.sender, liked);
if (likes[liked][msg.sender]) {
// ...
matchRewards(liked, msg.sender);
}
}

Risk

Likelihood:

  • Every successful call to likeUser results in this state.

  • The logic failure is embedded in the primary entry point for value-flow within the protocol.

Impact:

  • Total Loss of Funds: Users who pay to like others can never recover their ETH, even if they match.

  • Protocol Insolvency: The matchRewards function will send 0 rewards to the MultiSig, while the actual ETH remains stuck in the LikeRegistry with no withdrawal mechanism for users.

PoC:

  1. User A calls likeUser(User B) and sends 1 ETH. LikeRegistry balance is 1 ETH. userBalances[User A] remains 0.

  2. User B calls likeUser(User A) and sends 1 ETH. LikeRegistry balance is 2 ETH. userBalances[User B] remains 0.

  3. Match is triggered. matchRewards(A, B) calculates totalRewards = 0 + 0.

  4. A MultiSig is deployed, but it receives 0 ETH.

  5. 2 ETH is now permanently trapped in LikeRegistry.

Mitigation:

function likeUser(address liked) external payable {
require(msg.value >= 1 ether, "Must send at least 1 ETH");
+ userBalances[msg.sender] += msg.value;
require(!likes[msg.sender][liked], "Already liked");
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 15 days 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!