DatingDapp

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

Reentrancy Vulnerability in LikeRegistry's matchRewards Function

Summary

A critical reentrancy vulnerability exists in the LikeRegistry contract's matchRewards function where state changes occur after external contract calls, violating the checks-effects-interactions pattern.

Vulnerability Details

The matchRewards function performs an external call to transfer ETH to a newly created MultiSigWallet before updating critical state variables:

function matchRewards(address from, address to) internal {
uint256 matchUserOne = userBalances[from];
uint256 matchUserTwo = userBalances[to];
userBalances[from] = 0;
userBalances[to] = 0;
uint256 totalRewards = matchUserOne + matchUserTwo;
uint256 matchingFees = (totalRewards * FIXEDFEE) / 100;
uint256 rewards = totalRewards - matchingFees;
totalFees += matchingFees;
// Deploy a MultiSig contract for the matched users
MultiSigWallet multiSigWallet = new MultiSigWallet(from, to);
// Send ETH to the deployed multisig wallet
(bool success,) = payable(address(multiSigWallet)).call{value: rewards}("");
require(success, "Transfer failed");
}

An attacker could create a malicious contract that implements a fallback function to re-enter the LikeRegistry contract before the state changes are completed.

Impact

  • Potential double-spending of rewards

  • Manipulation of user balances

  • Draining of contract funds

  • Breaking of core matching logic

Tools Used

  • Manual code review

  • Slither static analyzer

  • Historical reentrancy patterns analysis

Recommendations

  1. Implement the checks-effects-interactions pattern by moving all state changes before external calls:

function matchRewards(address from, address to) internal {
uint256 matchUserOne = userBalances[from];
uint256 matchUserTwo = userBalances[to];
// Update state before external calls
userBalances[from] = 0;
userBalances[to] = 0;
uint256 totalRewards = matchUserOne + matchUserTwo;
uint256 matchingFees = (totalRewards * FIXEDFEE) / 100;
uint256 rewards = totalRewards - matchingFees;
totalFees += matchingFees;
// Create MultiSig after state updates
MultiSigWallet multiSigWallet = new MultiSigWallet(from, to);
// Consider using OpenZeppelin's ReentrancyGuard
(bool success,) = payable(address(multiSigWallet)).call{value: rewards}("");
require(success, "Transfer failed");
}

2. Add OpenZeppelin's ReentrancyGuard modifier to sensitive functions

3. Consider implementing a pull-payment pattern instead of push-payment

Updates

Appeal created

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

invalid_reentrancy_with_no_impact

matchRewards: Contract is created just before and is the one called. No impact. executeTransaction: CEI is followed. Emitting an event in disorder is informational in that context. withdraw: CEI is followed.

Support

FAQs

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