DatingDapp

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

Business Logic Flaw: likeUser() does not update userBalances, causing deposited ETH to become permanently locked

Root + Impact

Description

  • The LikeRegistry::likeUser function allows users to send ETH when liking another profile. According to the protocol design, the ETH sent by users should be tracked in the userBalances mapping so that when a mutual match occurs, the combined balances of both users can be distributed to a newly created multisig wallet (minus protocol fees).

  • However, the likeUser function never updates the userBalances mapping. As a result, although users send ETH to the contract, their balances remain 0. When a mutual match occurs and matchRewards is executed, the function reads zero balances for both users.

// audit - The function is not updating the mapping "userBalances" when a user likes another user. This means that the rewards for matching will always be zero, and the fees will also be zero. This could lead to a situation where users are incentivized to like each other without any actual rewards being distributed, which could undermine the purpose of 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");
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);
}
}

Risk

Likelihood:

  • The Money is lost whenever a user deposits money to the contract with LikeRegistry::likeUser function

Impact:

  • Loss of User funds: Users deposit ETH when liking another profile, but the contract never records these deposits. As a result, The deposited ETH remains permanently locked in the contract.

  • When a mutual match occurs, matchRewards reads zero balances and distributes no rewards.

  • Hence, The intended reward mechanism becomes completely non-functional: Users pay ETH to like profiles, Matches occur but No ETH is ever distributed. This breaks the core incentive mechanism of the protocol.

Proof of Concept

The foundry PoC demonstrates that:

  • Users successfully send ETH when calling likeUser.

  • userBalances remains zero.

  • ETH remains stuck inside the contract.

contract MockProfileNFT {
function profileToToken(address) external pure returns (uint256) {
return 1; // pretend every user has a profile
}
}
contract LikeRegistryTest is Test {
MockProfileNFT profileNFT;
LikeRegistry likeRegistry;
function setUp() public {
profileNFT = new MockProfileNFT();
likeRegistry = new LikeRegistry(address(profileNFT));
}
function testLikeUserDoesntRecordBalance() public {
address user1 = makeAddr("User1");
address user2 = makeAddr("User2");
//user 1 and user 2 both get a match
vm.deal(user1, 1 ether);
vm.prank(user1);
likeRegistry.likeUser{value: 1 ether}(user2);
vm.deal(user2, 1 ether);
vm.prank(user2);
likeRegistry.likeUser{value: 1 ether}(user1);
// balances remain zero due to missing update
assertEq(likeRegistry.userBalances(user1), 0);
assertEq(likeRegistry.userBalances(user2), 0);
// ETH stuck in contract
assertEq(address(likeRegistry).balance, 2 ether);
}
}

Recommended Mitigation

// Update the func function to record Eth deposited by the User
+ userBalances[msg.sender] += msg.value;
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!