Summary
In the `LikeRegistry` Contract, there is a mapping of address to uint256, the `LikeRegistry::userBalances`. However, when we call the `LikeRegistry::likeUser`, the `LikeRegistry::userBalances` variable is not updated.
Vulnerability Details
Proof of Concept:
1. Users like other users profile.
2. On call of `LikeRegistry::likeUser` function, the `msg.sender` loses ETH but the `LikeRegistry::userBalances` is not updated. So, the balance of the liked user is still zero.
3. The ETH sent by `msg.sender` is locked in the smartcontract with no way of retrieving it.
Proof of Code:
<details>
<summary>Code</summary>
Add the following code to the `testSoulboundProfileNFT.t.sol` file.
```javascript
function testUserBalanceNotUpdatedWhenUserIsLiked() public {
vm.prank(user); // Simulates user calling the function
soulboundNFT.mintProfile("Alice", 25, "ipfs://profileImage");
vm.prank(user2); // Simulates user2 calling the function
soulboundNFT.mintProfile("Ochuko", 24, "ipfs://profileImage");
uint256 TRANSFER_AMOUNT = 1 ether;
// Create LikeRegistry instances with the correct profile NFT address
LikeRegistry userRegistry = new LikeRegistry(address(soulboundNFT));
uint256 userAddressBalanceBefore = address(user).balance;
uint256 user2BalanceBefore = userRegistry.userBalances(user2);
uint256 userRegistryContractBalanceBefore = address(userRegistry).balance;
vm.deal(user, 2 * TRANSFER_AMOUNT);
vm.prank(user);
userRegistry.likeUser{value: TRANSFER_AMOUNT}(user2);
uint256 userAddressBalanceAfter = address(user).balance;
uint256 user2BalanceAfter = userRegistry.userBalances(user2);
uint256 userRegistryContractBalanceAfter = address(userRegistry).balance;
assertEq(user2BalanceBefore, user2BalanceAfter);
assert(userAddressBalanceAfter - userAddressBalanceBefore == TRANSFER_AMOUNT);
assert(userRegistryContractBalanceAfter - userRegistryContractBalanceBefore == TRANSFER_AMOUNT);
}
```
</details>
Impact
The `LikeRegistry::userBalances` variable is not updated. All users' balance will remain zero irrespective of the amount of likes they have. This means that money will be lost in the contract because the `LikeRegistry::totalFees` which is the amount withdrawable by the owner is dependent on `LikeRegistry::userBalances`. The implication is that all users will have zero balances and be unable to withdraw any funds even if they are matched. Also, the owner of the contract will not be able to withdraw any funds because the `LikeRegistry::totalFees` variable is also zero. This means that every ETH sent via the `LikeRegistry::likeUser` function is going to be lost in the smartcontract.
Recommendations
To fix this, we should have the `LikeRegistry::likeUser` function update the `LikeRegistry::userBalances` mapping. The whole issue stems from here [LikeRegistry::likeUser](https://github.com/CodeHawks-Contests/2025-02-datingdapp/blob/878bd34ef6607afe01f280cd5aedf3184fc4ca7b/src/LikeRegistry.sol#L31)
```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[liked] += msg.value;
emit Liked(msg.sender, liked);
if (likes[liked][msg.sender]) {
matches[msg.sender].push(liked);
matches[liked].push(msg.sender);
emit Matched(msg.sender, liked);
matchRewards(liked, msg.sender);
}
}
```