DatingDapp

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

[H] If users who were not matched wish to retrieve their ETH, there is currently no refund function available.

Root + Impact

Description

  • If no matching users are found, the user may wish to retrieve their funds; the user can recover all previously pledged funds.

  • The current contract does not provide a method for refunds.

// Root cause in the codebase with @> marks to highlight the relevant section

Risk

Likelihood: High

  • The current contract does not provide a refund function, so refunds cannot be processed if any user requests one.


Impact: High

  • Users who have not been matched for a long time cannot get refunds; their funds remain locked in the contract for extended periods, which easily prompts user suspicion and may be perceived as fund fraud.

Proof of Concept

  1. Add the test `test_Poc_H2_UnmatchedLikeFundsLockedWithoutUserWithdrawalPath` in testLikeRegistry.t.sol

  2. Observe whether the funds are still in the contract one year after a user likes and transfers (funds).

function test_Poc_H2_UnmatchedLikeFundsLockedWithoutUserWithdrawalPath() public {
vm.prank(user);
soulboundNFT.mintProfile("Alice", 25, "ipfs://alice");
vm.prank(user2);
soulboundNFT.mintProfile("Bob", 30, "ipfs://bob");
// User likes once, but no mutual like happens.
vm.prank(user);
likeRegistry.likeUser{value: 1 ether}(user2);
assertEq(likeRegistry.userBalances(user), 1 ether, "User balance should be escrowed");
assertEq(address(likeRegistry).balance, 1 ether, "Funds should remain in LikeRegistry");
// Time passes, but escrow is still locked without a matching like.
vm.warp(block.timestamp + 365 days);
assertEq(likeRegistry.userBalances(user), 1 ether, "Escrow remains locked after long time");
assertEq(address(likeRegistry).balance, 1 ether, "Contract balance remains locked");
// Only fee withdrawal exists; owner cannot release unmatched user principal.
vm.prank(owner);
vm.expectRevert("No fees to withdraw");
likeRegistry.withdrawFees();
assertEq(likeRegistry.userBalances(user), 1 ether, "User still cannot retrieve principal");
assertEq(address(likeRegistry).balance, 1 ether, "Principal remains stuck in contract");
}

Recommended Mitigation

Add a refund function so that users who are not matched can retrieve their funds at any time.

+ function refund() external {
+ uint256 balance = userBalances[msg.sender];
+ require(balance > 0, "No funds to refund");
+ require(matches[msg.sender].length == 0, "Matched users cannot refund");
+ userBalances[msg.sender] = 0;
+ (bool success,) = payable(msg.sender).call{value: balance}("");
+ require(success, "Refund transfer failed");
+ }
Updates

Lead Judging Commences

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