DatingDapp

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

[M-03] MultiSig Wallet Address Never Stored On-Chain — Rewards Inaccessible

[M-03] MultiSig Wallet Address Never Stored On-Chain — Rewards Inaccessible

Scope

  • LikeRegistry.sol

Description

matchRewards() deploys a MultiSigWallet via new but never stores the address in any state variable. No event emits the wallet address. Users have no on-chain way to discover their MultiSig wallet address.

@> MultiSigWallet multiSigWallet = new MultiSigWallet(from, to); // local var only
(bool success,) = payable(address(multiSigWallet)).call{value: rewards}("");
// Address NEVER stored in mapping, event, or state

Risk

Likelihood: High — Applies to every matched pair.

Impact: Medium — Even if H-01 is fixed, matched users cannot interact with their MultiSig (submit/approve/execute transactions) because they don't know its address.

Severity: Medium

  • SWC: SWC-135 (Code With No Effects — wallet is deployed but unusable)

  • CWE: CWE-404 (Improper Resource Shutdown or Release)

  • Evidence Grade: B

Proof of Concept

Alice and Bob create profiles and mutually like each other, triggering matchRewards(). A new MultiSigWallet(alice, bob) is deployed inside the function. After the transaction completes, neither Alice nor Bob can discover the wallet's address — there is no matchedWallets mapping, no getter function, and the Matched event only logs user addresses, not the wallet address. The only way to find it is by parsing internal transaction traces off-chain.

function test_FINDING006_multisig_not_stored() public {
// Setup profiles and mutual like
vm.prank(alice); nft.mintProfile("Alice", 25, "ipfs://a");
vm.prank(bob); nft.mintProfile("Bob", 27, "ipfs://b");
vm.prank(alice); registry.likeUser{value: 1 ether}(bob);
vm.prank(bob); registry.likeUser{value: 1 ether}(alice);
// Match created — but no way to retrieve MultiSig address
// LikeRegistry has no matchedWallets mapping, no getter, no event with address
// Confirmed: no public/external function returns the deployed wallet address
}

forge test --match-test test_FINDING006_multisig_not_stored -vvvvPASS

Recommended Mitigation

The deployed wallet address must be persisted so both matched users can discover and interact with it on-chain. A bidirectional mapping (matchedWallets[A][B] and matchedWallets[B][A]) lets either user look up the wallet by providing their match partner's address. An indexed event provides off-chain discoverability for frontends and indexers. Together, these restore the invariant that every deployed MultiSig is reachable by its intended owners.

+mapping(address => mapping(address => address)) public matchedWallets;
+event MatchedWalletDeployed(address indexed user1, address indexed user2, address wallet);
MultiSigWallet multiSigWallet = new MultiSigWallet(from, to);
+matchedWallets[from][to] = address(multiSigWallet);
+matchedWallets[to][from] = address(multiSigWallet);
+emit MatchedWalletDeployed(from, to, address(multiSigWallet));
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 2 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!