DatingDapp

First Flight #33
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: high
Valid

Untracked Deposits Vulnerability in LikeRegistry Contract

Description

The matchRewards function in LikeRegistry is failing to properly pool users' like payments into their shared MultiSig wallet because the contract doesn't track user deposits. The userBalances mapping is never updated when users make like payments, causing the reward calculation to always use zero balances.

Current implementation has two critical issues:

  • Deposits are never tracked when users like:

function likeUser(address liked) external payable {
require(msg.value >= 1 ether, "Must send at least 1 ETH");
// msg.value is never added to userBalances
likes[msg.sender][liked] = true;
// ...
}
  • Rewards function uses uninitialized balances:

function matchRewards(address from, address to) internal {
uint256 matchUserOne = userBalances[from]; // Always 0
uint256 matchUserTwo = userBalances[to]; // Always 0
userBalances[from] = 0;
userBalances[to] = 0;
uint256 totalRewards = matchUserOne + matchUserTwo; // Always 0
uint256 matchingFees = (totalRewards * FIXEDFEE) / 100; // Always 0
uint256 rewards = totalRewards - matchingFees; // Always 0
// MultiSig gets created but receives 0 ETH
MultiSigWallet multiSigWallet = new MultiSigWallet(from, to);
(bool success,) = payable(address(multiSigWallet)).call{value: rewards}("");
require(success, "Transfer failed");
}

Impact:

  • Like payments never reach MultiSig wallet

  • Users lose all deposited ETH

  • Platform fees not collected

  • Empty MultiSig wallets created

  • Violation of core platform functionality

  • Protocol doesn't match documentation

  • funds permanently locked in the contract.

Proof Of Concept

function testUnTrackedDeposits() public {
vm.deal(user, 2 ether);
vm.deal(user2, 2 ether);
// Create profiles
vm.prank(user);
soulboundNFT.mintProfile("Alice", 25, "ipfs://alice");
vm.prank(user2);
soulboundNFT.mintProfile("Bob", 28, "ipfs://user2");
// Track starting balances
uint256 aliceStartBalance = user.balance;
uint256 bobStartBalance = user2.balance;
uint256 contractStartBalance = address(likeRegistry).balance;
// Alice likes Bob
vm.prank(user);
likeRegistry.likeUser{value: 1 ether}(user2);
// Bob likes Alice
vm.prank(user2);
likeRegistry.likeUser{value: 1 ether}(user);
// Verify deposits were made but not tracked
assertEq(user.balance, aliceStartBalance - 1 ether);
assertEq(user2.balance, bobStartBalance - 1 ether);
assertEq(address(likeRegistry).balance, contractStartBalance + 2 ether);
// But userBalances weren't updated
assertEq(likeRegistry.userBalances(user), 0);
assertEq(likeRegistry.userBalances(user2), 0);
// Find created MultiSig address through events
vm.recordLogs();
Vm.Log[] memory entries = vm.getRecordedLogs();
// MultiSig received no funds despite 2 ETH in deposits
for (uint i = 0; i < entries.length; i++) {
if (entries[i].topics[0] == keccak256("MultiSigCreated(address,address,address)")) {
address multiSig = address(uint160(uint256(entries[i].topics[3])));
assertEq(address(multiSig).balance, 0);
}
}
// Contract still holds the funds
assertEq(address(likeRegistry).balance, 2 ether);
}

Fix Recommendation

  • Track deposits when liking:

add code snippet to the likeUser function

userBalances[msg.sender] += msg.value;

Tools Used

  • Foundry Testing Framework

  • Manual Review

Updates

Appeal created

n0kto Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding_likeUser_no_userBalances_updated

Likelihood: High, always. Impact: High, loss of funds

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.