DatingDapp

First Flight #33
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: low
Invalid

UNMATCHED USERS' ETH PERMANENTLY LOCKED IN CONTRACT

M-1 UNMATCHED USERS' ETH PERMANENTLY LOCKED IN CONTRACT

Description:
In the LikeRegistry contract, when users like another user through the likeUser() function, their ETH is stored in userBalances but can only be retrieved if they receive a mutual like. If a user never receives a mutual like, their ETH becomes permanently locked in the contract with no mechanism to withdraw it.

Impact:
This has several concerning impacts:

  1. Users who don't receive mutual likes have their ETH permanently locked

  2. No refund mechanism exists for unmatched users

  3. Could lead to significant loss of user funds

  4. Creates poor user experience and potential trust issues

Proof of Concept

Test demonstrating ETH getting locked for unmatched users:

function test_unmatched_users_eth_locked() public {
// Arrange
_mintSoulNFT(USER1, "User 1", 20, "https://example.com/image.png");
_mintSoulNFT(USER2, "User 2", 20, "https://example.com/image.png");
_mintSoulNFT(USER3, "User 3", 20, "https://example.com/image.png");
uint256 likeAmount = 1 ether;
// Act
vm.prank(USER1);
likeRegistry.likeUser{value: likeAmount}(USER2);
vm.prank(USER2);
likeRegistry.likeUser{value: likeAmount}(USER1);
//user3 likes user1 but user3 was never liked
vm.prank(USER3);
likeRegistry.likeUser{value: likeAmount}(USER1);
//admin withdraws fees
vm.prank(likeRegistry.owner());
likeRegistry.withdrawFees();
// Assert
assertEq(address(likeRegistry).balance, 1 ether); //eth is stuck in the contract
}

Code Mitigation

To prevent ETH from being permanently locked, we can implement a time-based withdrawal mechanism that allows users to retrieve their funds if they don't receive a match within a certain period.

LikeRegistry::lockPeriods add this line of code to contract state variables:

+ mapping(address => uint256) public lockPeriods;

LikeRegistry::likeUser add this line of code to track when users deposit funds:

function likeUser(address liked) external payable {
// ... existing code ...
+ lockPeriods[msg.sender] = block.timestamp + 30 days; // Set 30-day lock
}
function withdrawUnmatchedFunds() external {
require(block.timestamp >= lockPeriods[msg.sender], "Lock period not expired");
require(userBalances[msg.sender] > 0, "No balance to withdraw");
uint256 amount = userBalances[msg.sender];
userBalances[msg.sender] = 0;
(bool success,) = payable(msg.sender).call{value: amount}("");
require(success, "Transfer failed");
}
Updates

Appeal created

n0kto Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelyhood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.