Root + Impact
matchRewards deploys a MultiSigWallet into a local variable and discards it. Neither storage nor events record the address, so matched users cannot discover the contract that holds their pooled funds — even once the funding bug is fixed, the reward is effectively unclaimable.
Description
function matchRewards(address from, address to) internal {
...
@> MultiSigWallet multiSigWallet = new MultiSigWallet(from, to);
@>
(bool success,) = payable(address(multiSigWallet)).call{value: rewards}("");
require(success, "Transfer failed");
}
Risk
Likelihood: High — happens for every match.
Impact: Matched users have no on-chain way to locate or operate their reward wallet; funds become unusable.
Proof of Concept
function test_WalletAddressNotRecoverable() public {
_mint(alice); _mint(bob);
vm.deal(alice, 1 ether); vm.deal(bob, 1 ether);
vm.prank(alice); registry.likeUser{value: 1 ether}(bob);
vm.prank(bob); registry.likeUser{value: 1 ether}(alice);
vm.prank(alice);
address[] memory m = registry.getMatches();
assertEq(m[0], bob);
}
Recommended Mitigation
+mapping(address => mapping(address => address)) public matchWallet;
+
function matchRewards(address from, address to) internal {
MultiSigWallet multiSigWallet = new MultiSigWallet(from, to);
+ matchWallet[from][to] = address(multiSigWallet);
+ matchWallet[to][from] = address(multiSigWallet);
+ emit MatchWalletCreated(from, to, address(multiSigWallet));
...
}