DatingDapp

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

Mutual Match Can Be Triggered Multiple Times, Re-Creating Multisig and Re-Distributing Rewards

Root + Impact

Description

Normal behavior:
Once two users mutually like each other and a match is formed:

  • rewards should be distributed once, and

  • the match should be finalized and immutable.

Repeated reward distribution for the same pair must not be possible.

Issue:
LikeRegistry does not mark a match as consumed or finalized after matchRewards() is executed.
As a result, the same mutual match can be processed multiple times, repeatedly deploying new multisig wallets and attempting to distribute rewards again.

There is no state variable preventing re-execution for the same (from, to) pair.

// Conceptual flow
if (likes[from][to] && likes[to][from]) {
matchRewards(from, to);
// @> no state updated to block re-entry for this match
}

Risk

Likelihood:

  • Reason 1: Any function path that checks mutual likes can trigger matchRewards again.

  • Reason 2: No attacker privileges required.

  • Reason 3: Can be triggered accidentally or intentionally.

Impact:

  • Impact 1: Duplicate multisig creation: unnecessary contract deployments.

  • Impact 2: Inconsistent accounting: rewards logic may be re-applied.

  • Impact 3: Protocol integrity risk: a single match no longer represents a one-time event.

This does not always lead to additional fund loss (depending on balances), but it breaks a core invariant of the matching system.

Proof of Concept

Explanation

Assume the following sequence:

// User A likes User B
likeUser{value: 1 ether}(userB);
// User B likes User A
likeUser{value: 1 ether}(userA);
// Mutual match occurs → matchRewards(A, B)
// Later, any code path re-checks likes[A][B] && likes[B][A]
// matchRewards(A, B) is called again

Result:

  • A new multisig wallet is deployed again

  • Reward logic is re-run

  • Match semantics are violated

Recommended Mitigation

Mark matches as finalized immediately after reward distribution.

+ mapping(address => mapping(address => bool)) public matched;
function matchRewards(address from, address to) internal {
+ require(!matched[from][to], "Match already processed");
+ matched[from][to] = true;
+ matched[to][from] = true;
...
}

Alternatively, clear the likes mapping after a successful match.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 1 day 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!