Summary
In the LikeRegistry
contract, the likeUser()
function requires users to send at least 1 ETH, but it does not update the userBalances
mapping. As a result, when a match occurs, matchRewards()
retrieves a balance of 0
for both users, leading to no funds being pooled into the multisig wallet. This makes the payment mechanism ineffective and prevents users from accessing their intended match funds.
Vulnerability Details
Affected Code in likeUser()
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);
if (likes[liked][msg.sender]) {
matches[msg.sender].push(liked);
matches[liked].push(msg.sender);
emit Matched(msg.sender, liked);
matchRewards(liked, msg.sender);
}
}
Affected Code in matchRewards()
function matchRewards(address from, address to) internal {
uint256 matchUserOne = userBalances[from];
uint256 matchUserTwo = userBalances[to];
userBalances[from] = 0;
userBalances[to] = 0;
uint256 totalRewards = matchUserOne + matchUserTwo;
uint256 matchingFees = (totalRewards * FIXEDFEE) / 100;
uint256 rewards = totalRewards - matchingFees;
totalFees += matchingFees;
MultiSigWallet multiSigWallet = new MultiSigWallet(from, to);
(bool success,) = payable(address(multiSigWallet)).call{value: rewards}("");
require(success, "Transfer failed");
}
Root Cause:
userBalances[msg.sender]
is never updated when sending ETH in likeUser()
.
When matchRewards()
runs, it pulls the user’s balance, which is always 0, preventing fund distribution.
Impact
High Severity: This completely breaks the core payment logic of the DatingDapp.
Users pay 1 ETH per like, but their funds are never recorded, so the match rewards system never transfers ETH to the multisig wallet.
Funds remain stuck in the contract, meaning users are effectively paying ETH for nothing.
Tools Used
Foundry POC
pragma solidity ^0.8.19;
import "../src/LikeRegistry.sol";
import {Test} from "forge-std/Test.sol";
import {console} from "forge-std/console.sol";
import "../src/SoulboundProfileNFT.sol";
contract LikeRegistryTest is Test {
LikeRegistry target;
SoulboundProfileNFT soulboundNFT;
address LikeRegistryAddress;
address User1 = address(0x1);
address User2 = address(0x2);
function setUp() public {
soulboundNFT = new SoulboundProfileNFT();
target = new LikeRegistry(address(soulboundNFT));
vm.deal(User1, 5 ether);
vm.deal(User2, 5 ether);
vm.startPrank(User1);
soulboundNFT.mintProfile("x", 20, "png://test.png");
vm.stopPrank();
vm.startPrank(User2);
soulboundNFT.mintProfile("godwin", 20, "png://test.png");
vm.stopPrank();
}
function testLikeUser() public {
uint256 amount = 1 ether;
vm.startPrank(User1);
target.likeUser{value: amount}(User2);
uint256 balance1 = target.userBalances(User1);
assertEq(balance1, amount, "User1 balance1 should be updated to 1 ether");
vm.stopPrank();
vm.startPrank(User2);
target.likeUser{value: amount}(User1);
uint256 balance2 = target.userBalances(User2);
assertEq(balance2, amount, "User2 balance2 should be updated to 1 ether");
}
}
The Test code fails in the section when we expect user balance to update after succesfully calling the likeUser
function, but it does not.
Recommendations
✅ Fix: Properly Update userBalances
in likeUser()
Modify the function to store the ETH sent by the user:
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");
userBalances[msg.sender] += msg.value;
likes[msg.sender][liked] = true;
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);
}
}
✅ Additional Fix: Ensure totalRewards
is Greater Than 0 Before Transfer
function matchRewards(address from, address to) internal {
uint256 matchUserOne = userBalances[from];
uint256 matchUserTwo = userBalances[to];
uint256 totalRewards = matchUserOne + matchUserTwo;
require(totalRewards > 0, "No funds to distribute");
uint256 matchingFees = (totalRewards * FIXEDFEE) / 100;
uint256 rewards = totalRewards - matchingFees;
totalFees += matchingFees;
userBalances[from] = 0;
userBalances[to] = 0;
MultiSigWallet multiSigWallet = new MultiSigWallet(from, to);
(bool success,) = payable(address(multiSigWallet)).call{value: rewards}("");
require(success, "Transfer failed");
}