DatingDapp

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

matchRewards Sends ETH to Newly Deployed Contract Without verifying Deployment Success

Root + Impact

Description

In matchRewards(), a new MultiSigWallet is deployed via new, and ETH
is immediately sent to it. If the constructor were to revert (e.g., due to
identical addresses from a bug), the entire match transaction would revert,
but the likes and matches state has already been modified. While the
revert would roll back state changes, the users' like ETH (if F-01 is fixed)
could be stuck if there's any edge case where the MultiSig deployment
succeeds but the ETH transfer fails.

More importantly, there is no record kept of the deployed MultiSig address.
Users have no on-chain way to discover their multisig wallet address.

// src/LikeRegistry.sol:61-66
MultiSigWallet multiSigWallet = new MultiSigWallet(from, to);
// @> No event emitting the deployed multisig address
// @> No mapping storing the multisig address for matched users
(bool success,) = payable(address(multiSigWallet)).call{value: rewards}("");
require(success, "Transfer failed");

Risk

Likelihood: Certain

  • Every match deploys a multisig with no on-chain reference

Impact: Medium

  • Users cannot discover their multisig wallet address on-chain

  • Must rely on off-chain event indexing or transaction tracing

  • Funds could be effectively lost if users can't find the multisig address

Proof of Concept

This test triggers a mutual match and then proves there is no on-chain
mechanism to retrieve the deployed MultiSig address — no mapping, no
getter, and the Matched event only logs the two user addresses without
the wallet. The only evidence of the wallet is buried in the internal
CREATE trace, inaccessible to regular contract calls.

function test_F06_no_multisig_address_stored() public {
// Alice likes Bob
vm.prank(alice);
registry.likeUser{value: 1 ether}(bob);
// Bob likes Alice — triggers match, deploys MultiSig internally
vm.prank(bob);
registry.likeUser{value: 1 ether}(alice);
// Verify match happened
address matchedWith = registry.matches(alice, 0);
assertEq(matchedWith, bob, "Match exists");
// Now try to find the MultiSig address on-chain:
// - No registry.matchedMultiSig(alice, bob) exists
// - No registry.getMultiSig(alice, bob) exists
// - The Matched event only emits (user1, user2), not the wallet address
// - There is NO way for alice or bob to discover their multisig on-chain
// The only emitted event is:
// event Matched(address indexed user1, address indexed user2)
// which contains zero information about the deployed wallet address.
//
// Users must parse internal transaction traces (CREATE opcode output)
// to find the MultiSig — this is not accessible via standard RPC calls
// or contract interactions.
}

Recommended Mitigation

Store the deployed multisig address in a bidirectional mapping and emit
a dedicated event so matched users can discover their shared wallet both
on-chain (via getter) and off-chain (via event indexing).

+ mapping(address => mapping(address => address)) public matchedMultiSig;
+ event MultiSigDeployed(address indexed user1, address indexed user2, address wallet);
function matchRewards(address from, address to) internal {
// ...
MultiSigWallet multiSigWallet = new MultiSigWallet(from, to);
+ matchedMultiSig[from][to] = address(multiSigWallet);
+ matchedMultiSig[to][from] = address(multiSigWallet);
+ emit MultiSigDeployed(from, to, address(multiSigWallet));
(bool success,) = payable(address(multiSigWallet)).call{value: rewards}("");
require(success, "Transfer failed");
}
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!