DatingDapp

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

burnProfile() does not clear the likes mapping, leaving stale like entries that permanently lock ETH and block future match attempts

Root + Impact

Description

  • burnProfile() deletes profileToToken[msg.sender] and burns the NFT but does not clear any entries in LikeRegistry's likes mapping. Outgoing likes (likes[burner][*]) and incoming likes (likes[*][burner]) remain set to true indefinitely.

  • This creates a permanent lock on ETH: if Alice liked Bob before burning her profile, likes[alice][bob] remains true. Bob can no longer like Alice (the profileToToken(alice) != 0 check fails), so a match can never be triggered and Alice's like ETH is permanently unreachable — even if Alice remints.

// src/SoulboundProfileNFT.sol — burnProfile()
function burnProfile() external {
uint256 tokenId = profileToToken[msg.sender];
require(tokenId != 0, "No profile found");
_burn(tokenId);
delete profileToToken[msg.sender]; // @> only clears NFT ownership
delete _profiles[tokenId];
// @> likes[msg.sender][*] and likes[*][msg.sender] remain set
}
// src/LikeRegistry.sol — likeUser() check blocks recovery after remint
require(!likes[msg.sender][liked], "Already liked"); // @> Alice cannot re-like Bob after burn+remint

Risk

Likelihood:

  • Any user who burns their profile after sending a like permanently loses access to that ETH, even if they remint immediately.

Impact:

  • ETH locked via likeUser becomes unrecoverable after profile burn because the stale likes entry prevents re-liking, and the counterparty cannot like the reminted profile due to profileToToken reset. The lock is permanent.

Proof of Concept

Alice likes Bob (1 ETH), then burns her profile. After the burn, likes[alice][bob] is still true. Alice remints and attempts to re-like Bob — the "Already liked" revert blocks her. The test ends with 1 ETH still in the contract and no path to recover it, confirming the stale mapping permanently locks the funds.

function testBurnLeavesStalelikesAndLocksEth() public {
// Alice likes Bob — sends 1 ETH
vm.prank(alice);
likeRegistry.likeUser{value: 1 ether}(bob);
assertTrue(likeRegistry.likes(alice, bob));
// Alice burns her profile
vm.prank(alice);
profileNFT.burnProfile();
assertEq(profileNFT.profileToToken(alice), 0);
// likes[alice][bob] still true — stale entry persists
assertTrue(likeRegistry.likes(alice, bob));
// Alice remints
vm.prank(alice);
profileNFT.mintProfile("Alice", 25, "ipfs://alice");
// Bob tries to like Alice — profileToToken(alice) now != 0, passes
// But if Bob had already liked Alice before, likes[bob][alice] still true → match fires
// If Bob hasn't liked Alice: Bob likes Alice → match check reads likes[alice][bob] = true → match
// BUT: Alice cannot re-like Bob to trigger match herself ("Already liked" reverts)
// Either way, Alice's ETH from the original like is locked in the contract
// matchRewards reads userBalances[alice] = 0 (H-01) → match distributes 0
assertEq(address(likeRegistry).balance, 1 ether);
}

Recommended Mitigation

burnProfile cannot directly clear LikeRegistry entries without a cross-contract call, but LikeRegistry should expose a clearLikes function, or the burn logic should route through a registry-aware function:

// In LikeRegistry.sol
+ function clearLikesOnBurn(address user) external {
+ require(msg.sender == address(profileNFT), "Only profileNFT");
+ // Clear is gas-intensive for large like sets; at minimum reset userBalances
+ userBalances[user] = 0;
+ }
// In SoulboundProfileNFT.sol — burnProfile()
function burnProfile() external {
...
delete profileToToken[msg.sender];
delete _profiles[tokenId];
+ likeRegistry.clearLikesOnBurn(msg.sender);
}

For full cleanup, consider iterating stored like arrays or using a generation counter to invalidate stale like entries without on-chain iteration.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 4 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!