Summary
Executing LikeRegistry::likeUser
requires a payment, but the payment is not recorded in userBalances
. As a result, any other functions that rely on userBalances
will have incorrect logic.
Vulnerability Details
LikeRegistry::likeUser
requires 1 ETH or more to be sent to the contract, but it is not recorded in the balance variable.
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);
}
}
In LikeRegistry::matchRewards
, since the balance is not recorded, the matchUserOne
and matchUserTwo
variables are always 0.
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");
}
POC
function testlikeUserBalance() public {
vm.prank(user1);
soulboundNFT.mintProfile("user1", 10, "ipfs://profileImage");
vm.prank(user2);
soulboundNFT.mintProfile("user2", 20, "ipfs://profileImage");
hoax(user1, 1 ether);
likeRegistry.likeUser{value: 1 ether}(user2);
hoax(user2, 1 ether);
likeRegistry.likeUser{value: 1 ether}(user1);
uint256 balance1 = likeRegistry.userBalances(user1);
assertEq(balance1, 0 , "Balance1 is not equal to 0");\
uint256 balance2 = likeRegistry.userBalances(user2);
assertEq(balance2, 0 , "Balance2 is not equal to 0");
}
Impact
In LikeRegistry::matchRewards
, the balances of the matched users are always 0, preventing the correct amount of funds from being transferred to the shared multisig wallet. This severely impacts the contract's functionality.
Tools Used
Manual review
Foundry for POC
Recommendations
Record userBalances
when calling LikeRegistry::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");
+ userBalances[msg.sender] += msg.value;
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);
}
}