A like expresses interest in a profile. A user who deletes their profile with burnProfile ("Allow users to delete their profile") leaves the app. A match should form only when both current profiles have liked each other.
LikeRegistry stores consent as likes[liker][liked] = true (L38) and partners in matches[user] (L43-L44). Both are keyed by bare address. No code ever sets a like back to false or removes a match. burnProfile (L49-L51) and blockProfile (L61-L63) clear only NFT-side state, and LikeRegistry has no hook into them. So when an address burns (or is blocked) and then mints a new profile, its old likes carry over, both those it gave and those it received. One like from the counterparty, or from the re-minted address, then completes a match against a profile that was never liked in that direction. It emits Matched and deploys a MultiSigWallet. The NFT is soulbound and identity is the address, so the stale like was paid for by the same key holder. This is stale state surviving profile deletion, not a third party forcing consent.
Likelihood:
It happens when a user who liked someone, or was liked, burns their profile (or is blocked), mints a new profile at the same address, and then one more like is sent in the other direction.
That chain needs a voluntary burn and a re-mint, so it is uncommon.
Impact:
Match state does not follow the current profiles. In the PoC, a like given from Alice's deleted profile (token 1) completes a match for her new profile (token 3, new name, age and image). That match emits Matched, adds both matches[] entries and deploys a 2-of-2 MultiSigWallet. In the reverse direction, Bob's like of Alice's old profile matches her new profile on her first like.
Several side effects follow:
Burning a profile does not withdraw interest.
getMatches() keeps listing burned and blocked partners.
A re-minted address cannot like the same person again (Already liked), even when that person has not liked it back.
Under the current code no funds move, because every match wallet receives 0 ETH.
The PoC deploys SoulboundProfileNFT and LikeRegistry. The NFT is owned by the test contract so that the test can call blockProfile. Matches are detected by counting Matched events from the registry. The baseline shows that a one-sided like creates no match and no wallet. The other tests show the following:
Stale outgoing like: Alice likes Bob, then burns her profile; likes(alice, bob) stays true. She mints a new profile (token 3, with a new name, age and image). One like from Bob then:
emits Matched;
lists the pair in both getMatches();
deploys a MultiSigWallet owned by alice and bob.
Reverse direction: Bob's like of Alice's old profile matches her new profile on her first like.
Cannot re-like: before Bob likes anyone, Alice's like of Bob from her new profile, sent with 1 ETH, reverts Already liked.
Stale matches: getMatches() keeps returning a burned partner and a blocked partner.
Blocked user: if the owner blocks Alice and she re-mints, she inherits her pre-block like.
Run from the repository root, with the file saved as test/PoC_DD-07.t.sol:
Result: Suite result: ok. 6 passed; 0 failed; 0 skipped. The logs show alice old tokenId / new tokenId: 1 3.
Key likes by profile token id instead of by address. Token ids come from a counter and are never reused, so a new profile starts with no likes, given or received. getMatches() can similarly skip partners whose profileToToken is 0.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.