DatingDapp

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

Incorrect Operation Order in likeUser() Leads to Broken Matching System and Lost Balance Tracking

Summary

Incorrect order of operations in likeUser function leads to broken matching logic and untracked user balances.

Vulnerability Details

In LikeRegistry.sol, the likeUser function:

  1. Updates the likes mapping and emits the Liked event before checking for a match

  2. Doesn't track user balances when ETH is sent

  3. Has no else branch for non-matching cases

function likeUser(address liked) external payable {
// ... checks ...
likes[msg.sender][liked] = true;
emit Liked(msg.sender, liked); // Event emitted too early
if (likes[liked][msg.sender]) { // Match check after state update
matches[msg.sender].push(liked);
matches[liked].push(msg.sender);
emit Matched(msg.sender, liked);
matchRewards(liked, msg.sender);
}
// No else branch to handle non-match case
}

Impact

  1. ETH sent by users is not tracked in userBalances

  2. Events are emitted in wrong order, breaking external integrations

  3. Matching logic fails as demonstrated by test failure

Severity: MEDIUM

Tools Used

  • Foundry for testing

  • Manual code review

  • PoC test demonstrating both issues:

    • testIncorrectEventOrderAndBalances: Proves matching failure

    • testLikeWithoutMatch: Proves untracked balances

Proof of Concept

contract LikeRegistryTest is Test {
LikeRegistry public likeRegistry;
SoulboundProfileNFT public profileNFT;
address public alice = address(1);
address public bob = address(2);
event Liked(address indexed liker, address indexed liked);
event Matched(address indexed user1, address indexed user2);
function setUp() public {
profileNFT = new SoulboundProfileNFT();
likeRegistry = new LikeRegistry(address(profileNFT));
// Mint NFTs for Alice and Bob
vm.prank(alice);
profileNFT.mintProfile("Alice", 25, "alice_uri");
vm.prank(bob);
profileNFT.mintProfile("Bob", 28, "bob_uri");
}
function testIncorrectEventOrderAndBalances() public {
vm.deal(alice, 2 ether);
vm.deal(bob, 2 ether);
// Alice likes Bob
vm.startPrank(alice);
likeRegistry.likeUser{value: 1 ether}(bob);
// Verify userBalances not updated
assertEq(likeRegistry.userBalances(alice), 0, "User balance should be updated");
vm.stopPrank();
// Bob likes Alice (creating match)
vm.startPrank(bob);
// Events in wrong order
vm.expectEmit(true, true, false, true);
emit Liked(bob, alice);
vm.expectEmit(true, true, false, true);
emit Matched(bob, alice);
likeRegistry.likeUser{value: 1 ether}(alice);
vm.stopPrank();
// Verify matching failed
address[] memory aliceMatches = likeRegistry.getMatches();
assertEq(aliceMatches.length, 1, "Alice should have one match"); // This fails
}
function testLikeWithoutMatch() public {
vm.deal(alice, 2 ether);
uint256 initialBalance = address(likeRegistry).balance;
// Alice likes Bob
vm.prank(alice);
likeRegistry.likeUser{value: 1 ether}(bob);
// Verify ETH received but not tracked
assertEq(address(likeRegistry).balance, initialBalance + 1 ether, "Contract should receive ETH");
assertEq(likeRegistry.userBalances(alice), 0, "User balance should be updated");
}
}

Recommendations

  1. Update the function structure:

function likeUser(address liked) external payable {
// ... checks ...
userBalances[msg.sender] += msg.value; // Track ETH
if (likes[liked][msg.sender]) {
likes[msg.sender][liked] = true;
matches[msg.sender].push(liked);
matches[liked].push(msg.sender);
emit Matched(msg.sender, liked);
matchRewards(liked, msg.sender);
} else {
likes[msg.sender][liked] = true;
emit Liked(msg.sender, liked);
}
}
Updates

Appeal created

n0kto Lead Judge 6 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.