LikeRegistry is designed so that when two users mutually like each other, they are recorded as a match exactly once and rewarded through a shared MultiSigWallet.
After a match is recorded, likes[msg.sender][liked] and likes[liked][msg.sender] are never reset to false. If a user burns and re-mints their profile, the stale like flag persists. A subsequent likeUser call immediately re-triggers the match condition, pushing duplicate entries into both matches arrays and deploying another MultiSigWallet.
Likelihood:
A user burns their profile with burnProfile(), re-mints it with mintProfile(), and any counterparty who previously liked them calls likeUser again — the stale flag causes an instant duplicate match.
A user deliberately cycles profiles to farm multiple MultiSig wallets from the same counterparty.
Impact:
Multiple MultiSigWallet contracts are deployed for the same user pair, fragmenting ETH rewards across duplicate wallets.
matches arrays grow with duplicate entries, corrupting getMatches() output and all front-end match displays.
This test walks through the exact burn-and-remint exploit path that causes a duplicate match:
First match — Alice and Bob like each other. A match is recorded and matches[alice] = [bob], matches[bob] = [alice]. Crucially, likes[bob][alice] remains true — it is never cleared.
Alice resets her profile — Alice calls burnProfile() which deletes her profileToToken entry. She then calls mintProfile() and gets a fresh profile with a new token ID. Note that likeRegistry has no concept of this — likes[bob][alice] is still true.
Alice likes Bob again — Alice calls likeUser{value: 1 ether}(bob). The require(!likes[alice][bob]) check passes because likes[alice][bob] was not reset. The mutual-match branch fires again because likes[bob][alice] is still true.
Duplicate match — matches[alice].push(bob) is called a second time. getMatches() for Alice now returns [bob, bob].
To run: forge test --match-test test_duplicateMatchAfterProfileReset -vvvv
Reset both like flags immediately after the match is recorded:
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.