DatingDapp

First Flight #33
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: medium
Valid

`LikeRegistry::userBalances` only stores the total amount per user, causing the entire balance to be transferred when a match occurs, leaving another profile matches without funds.

Description: LikeRegistry::userBalances only stores the total amount per user. Therefore, when a user likes multiple profiles and one profile gets matched, the entire deposited amount will be transferred. Subsequent matched profiles will not receive any funds from that user.

Impact: A user may match with profiles where the total deposited funds from both parties are insufficient.

Proof of Concept:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import "forge-std/Test.sol";
import {DevOpsTools} from "lib/foundry-devops/src/DevOpsTools.sol";
import "../src/SoulboundProfileNFT.sol";
import "../src/LikeRegistry.sol";
contract LikeRegistryTest is Test {
SoulboundProfileNFT soulboundNFT;
LikeRegistry likeRegistry;
address user = address(0x123);
address user2 = address(0x456);
address user3 = address(0x789);
address owner = address(this); // Test contract acts as the owner
uint256 public constant DEFAULT_DEPOSIT_VALUE = 1 ether;
function setUp() public {
soulboundNFT = new SoulboundProfileNFT();
likeRegistry = new LikeRegistry(address(soulboundNFT));
vm.deal(user, 2 ether);
vm.deal(user2, 2 ether);
vm.deal(user3, 2 ether);
}
modifier mintProfile() {
vm.prank(user);
soulboundNFT.mintProfile("Alice", 25, "ipfs://profileImage");
vm.prank(user2);
soulboundNFT.mintProfile("Bob", 30, "ipfs://profileImage");
vm.prank(user3);
soulboundNFT.mintProfile("Anna", 20, "ipfs://profileImage");
_;
}
function testWrongBalanceWhenUserMatchMultipleProfile() public mintProfile {
vm.startPrank(user);
likeRegistry.likeUser{value: DEFAULT_DEPOSIT_VALUE}(user2);
likeRegistry.likeUser{value: DEFAULT_DEPOSIT_VALUE}(user3);
vm.stopPrank();
uint256 expertIncreaseBalance = 2 * DEFAULT_DEPOSIT_VALUE * 90 / 100;
vm.prank(user2);
likeRegistry.likeUser{value: DEFAULT_DEPOSIT_VALUE}(user);
address multiSigWallet = likeRegistry.multiSigWalletAddress(user2, user);
assertEq(address(multiSigWallet).balance, expertIncreaseBalance);
}
}

Recommended Mitigation: Change the data type of LikeRegistry::userBalances to map two profiles together

- mapping(address => uint256) public userBalances;
+ mapping(address user => mapping(address liked => uint256 balance)) public userBalances;
function likeUser(address liked) external payable {
require(msg.value >= 1 ether, "Must send at least 1 ETH");
require(!likes[msg.sender][liked], "Already liked");
require(msg.sender != liked, "Cannot like yourself");
require(profileNFT.profileToToken(msg.sender) != 0, "Must have a profile NFT");
require(profileNFT.profileToToken(liked) != 0, "Liked user must have a profile NFT");
likes[msg.sender][liked] = true;
+ userBalances[msg.sender][liked] += msg.value;
emit Liked(msg.sender, liked);
// Check if mutual like
if (likes[liked][msg.sender]) { // matched
matches[msg.sender].push(liked);
matches[liked].push(msg.sender);
emit Matched(msg.sender, liked);
matchRewards(liked, msg.sender);
}
}
function matchRewards(address from, address to) internal {
- uint256 matchUserOne = userBalances[from];
- uint256 matchUserTwo = userBalances[to];
- userBalances[from] = 0;
- userBalances[to] = 0;
+ uint256 matchUserOne = userBalances[from][to];
+ uint256 matchUserTwo = userBalances[to][from];
+ userBalances[from][to] = 0;
+ userBalances[to][from] = 0;
...
}
Updates

Appeal created

n0kto Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding_several_match_lead_to_multisig_with_no_funds

Likelihood: Medium, if anyone has 2 matches or more before reliking. Impact: Medium, the user won't contribute to the wallet.

Support

FAQs

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