DatingDapp

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

Deployed Multisig Wallets Are Not Trackable by Matched Users

[L-1] Deployed Multisig Wallets Are Not Trackable by Matched Users

Description:

  • When a mutual like occurs, the contract deploys a new MultiSigWallet instance inside matchRewards and transfers ETH rewards to it:

    MultiSigWallet multiSigWallet = new MultiSigWallet(from, to);
  • However, the address of the deployed multisig wallet is neither stored in contract state nor emitted in any event.
    The matches mapping only records counterpart user addresses:

    mapping(address => address[]) public matches;
  • As a result, matched users have no on-chain method to discover the multisig wallet that holds their rewards.
    Users must manually inspect transaction traces or rely on off-chain indexing to locate the deployed multisig address, which introduces friction and reduces composability with other smart contracts or automation systems.

Risk:

Impact: Low

  • No funds are lost and the rewards are correctly sent to the deployed multisig wallet. However, users cannot programmatically retrieve the multisig address associated with a match, making it difficult to access or manage their rewards and preventing trustless integration with other on-chain systems.

Likelihood: High

  • This behavior occurs for every successful match, since a multisig wallet is deployed on each match and its address is never persisted or emitted. All matched users will encounter this limitation.

Proof of Concept:

  1. User A and User B both hold valid profile NFTs.

  2. User A likes User B by calling likeUser(UserB) and sending the required ETH.
    Since the like is not mutual yet, no match is created and no multisig wallet is deployed.

  3. User B later likes User A by calling likeUser(UserA) and sending the required ETH.
    This time, the mutual-like condition is met and:

  4. A new multisig wallet is deployed internally.

  5. ETH rewards are transferred to the deployed multisig wallet.

  6. The Matched event is emitted.

  7. Both users are added to each other’s matches list.

  8. After the match, User A or User B attempts to determine the multisig wallet address that holds their rewards.
    They can:

  • Call getMatches() to retrieve matched counterpart addresses.

  • Inspect emitted Matched events.

=> Neither getMatches() nor any event provides the multisig wallet address.
The contract also does not store the multisig address in state.

=> As a result, the users have no on-chain or in-contract method to retrieve the multisig wallet address associated with their match.

=> The only way to find it is by manually inspecting transaction traces or using off-chain tooling to infer the deployment address.

Recommended Mitigation

  • Put this following mitigation in contract LikeRegistry:

    • Introduce a new MatchInfo struct to store match metadata, including the matched user and the associated multisig contract address.

    • Change the matches mapping from address => address[] to address => MatchInfo[] to persist multisig information on-chain.

    • Include the multisig contract address as a parameter in the Matched event.

    • Update match data is stored inside the matchReward function instead.

    • Emit the Matched event from the matchRewards function after the multisig wallet is deployed, ensuring the emitted address reflects the actual deployed contract.

+ struct MatchInfo {
+ address user;
+ address multisig;
+ }
- mapping(address => address[]) public matches;
+ mapping(address => MatchInfo[]) public matches;
- event Matched(address indexed user1, address indexed user2);
+ event Matched(address indexed user1, address indexed user2,address indexed multisigContract);
function likeUser(address liked) external payable {
//other codes
if (likes[liked][msg.sender]) {
- matches[msg.sender].push(liked);
- matches[liked].push(msg.sender);
- emit Matched(msg.sender, liked);
matchRewards(liked, msg.sender);
}
}
function matchRewards(address from, address to) internal {
//other codes
// Deploy a MultiSig contract for the matched users
MultiSigWallet multiSigWallet = new MultiSigWallet(from, to);
+ matches[from].push(
+ MatchInfo({ user: to, multisig: address(multiSigWallet) })
+ );
+ matches[to].push(
+ MatchInfo({ user: from, multisig: address(multiSigWallet) })
+ );
// Send ETH to the deployed multisig wallet
(bool success,) = payable(address(multiSigWallet)).call{value: rewards}("");
require(success, "Transfer failed");
+ emit Matched(from, to, address(multiSigWallet));
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 15 days 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!