DatingDapp

AI First Flight #6
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

LikeRegistry::likeUser allows liking users whose profiles have been burned or blocked — ETH sent with no valid counterparty

Root + Impact

Description

  • The likeUser function checks profileNFT.profileToToken(liked) != 0 to verify the target user has a profile. However, if the target user burns their profile or gets blocked between the time the liker sees their profile and the time the likeUser transaction is mined, the check will fail and the transaction reverts.

    More importantly, the check only validates at the time of the like. Consider this scenario:

    1. Alice likes Bob (both have profiles, check passes).

    2. Bob burns his profile.

    3. Carol likes Alice (Alice still has a profile).

    4. Alice's accumulated balance (if H-01 were fixed) could include ETH from her like of Bob — but Bob no longer exists on the platform.

    The likes mapping retains stale entries for burned profiles. If a burned user re-mints (see H-03), the old likes[burned_user][target] entries persist, creating ghost state. A user could:

    1. Like someone, paying 1 ETH.

    2. Burn their profile.

    3. Re-mint.

    4. The old like mapping is still true, so they can't re-like the same person (or can exploit state mismatches).

// Root cause in LikeRegistry.sol
// likes mapping is never cleaned up when profiles are burned
mapping(address => mapping(address => bool)) public likes;
// matches mapping also retains stale entries
mapping(address => address[]) public matches;

Risk

Likelihood:

  • This occurs whenever a user burns or gets blocked and has existing likes or matches in the LikeRegistry.

Impact:

  • Stale like/match state persists, creating inconsistencies between the NFT profile state and the LikeRegistry state.

  • Ghost matches with non-existent profiles pollute the matches array.

Proof of Concept

This test shows Alice liking Bob, then Bob burning his profile. The likes[alice][bob] mapping remains true even after the burn. When Bob re-mints a new profile, Alice cannot re-like him because the stale "Already liked" check blocks her, creating permanent ghost state.

function testM02_StaleLikesAfterBurn() public {
vm.prank(alice);
profileNFT.mintProfile("Alice", 25, "ipfs://alice");
vm.prank(bob);
profileNFT.mintProfile("Bob", 27, "ipfs://bob");
// Alice likes Bob
vm.prank(alice);
likeRegistry.likeUser{value: 1 ether}(bob);
assertTrue(likeRegistry.likes(alice, bob));
// Bob burns his profile
vm.prank(bob);
profileNFT.burnProfile();
// Alice's like of Bob still exists in the mapping — stale state
assertTrue(likeRegistry.likes(alice, bob)); // Ghost like!
// Bob re-mints — the old like persists
vm.prank(bob);
profileNFT.mintProfile("Bob2", 27, "ipfs://bob2");
// Bob cannot be liked again by Alice (already liked = true)
vm.prank(alice);
vm.expectRevert("Already liked");
likeRegistry.likeUser{value: 1 ether}(bob);
}

Recommended Mitigation

Add a callback mechanism or require re-validation of both profiles at match time. Consider allowing users to withdraw ETH from unmatched likes if the target profile is burned.

function matchRewards(address from, address to) internal {
+ require(profileNFT.profileToToken(from) != 0, "From user has no profile");
+ require(profileNFT.profileToToken(to) != 0, "To user has no profile");
// ...
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 3 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!