DatingDapp

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

`likeUser()` does not credit deposited ETH, causing matched rewards to remain locked in `LikeRegistry`

Severity

Medium

Likelihood

High

Root + Impact

likeUser() never credits deposited ETH, so matched funds remain locked in LikeRegistry.

Description

  • The expected behavior is that each paid like is credited to the sender so that, upon a mutual match, both users' deposited ETH can be combined, fee-adjusted, andforwarded to the newly deployed multisig wallet.

  • Instead, likeUser() records the like but never increments userBalances[msg.sender], so matchRewards() reads 0 for both matched users, deploys the multisig with no rewards, and leaves the actual ETH trapped inside LikeRegistry.

Risk

Likelihood: High

The issue occurs in the normal protocol flow and requires no special conditions beyond two users mutually liking each other with the required payment.

Impact: Medium

Users' paid likes are never forwarded to the match wallet and remain locked in LikeRegistry, breaking the protocol's core ETH distribution flow.

Proof of Concept

The following test shows that likeUser() accepts ETH from both users and records the mutual match, but never credits those payments into userBalances. As a result, matchRewards() deploys the multisig wallet with 0 ETH, while the full 2 ether paid by both users remains trapped in LikeRegistry.

function test_likePaymentsAreNotCredited_and_multisigReceivesZero() public {
vm.prank(user);
soulboundNFT.mintProfile("Jorx", 25, "foto");
vm.prank(user2);
soulboundNFT.mintProfile("Pepa", 22, "fotoPepa");
vm.prank(user);
registry.likeUser{value: 1 ether}(user2);
// Predict the next contract address created by LikeRegistry.
uint64 nonceBefore = vm.getNonce(address(registry));
address predictedMultiSig = vm.computeCreateAddress(address(registry), nonceBefore);
vm.prank(user2);
registry.likeUser{value: 1 ether}(user);
vm.prank(user);
address[] memory userMatches = registry.getMatches();
assertEq(userMatches.length, 1);
assertEq(userMatches[0], user2);
vm.prank(user2);
address[] memory user2Matches = registry.getMatches();
assertEq(user2Matches.length, 1);
assertEq(user2Matches[0], user);
// The multisig was deployed.
assertGt(predictedMultiSig.code.length, 0);
// But no rewards were transferred to it.
assertEq(predictedMultiSig.balance, 0);
// Both paid likes remain inside LikeRegistry.
assertEq(address(registry).balance, 2 ether);
// Internal accounting was never updated.
assertEq(registry.userBalances(user), 0);
assertEq(registry.userBalances(user2), 0);
assertEq(registry.totalFees(), 0);
}

Recommended Mitigation

Credit each user's payment when the like is submitted so that matchRewards() can account for deposited ETH correctly andforward the expected amount to the match wallet.

function likeUser(address liked) external payable {
...
require(profileNFT.profileToToken(liked) != 0, "Liked user must have a profile NFT");
+ userBalances[msg.sender] += msg.value;
likes[msg.sender][liked] = true;
emit Liked(msg.sender, liked);
...
}
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!