The likeUser function is designed to accumulate ETH payments into user balances, which are later used to calculate match rewards and distribute funds to shared multisig wallets.
However, the function does not update the userBalances mapping when a user sends ETH, causing all user balances to remain at zero.
Likelihood:
Occurs every time a user calls likeUser with ETH payment
The balance is never accumulated regardless of the number of likes or amounts sent
Impact:
Users cannot receive rewards from their like payments
Matched users receive multisig wallets with zero balance
Contract owner cannot withdraw platform fees as totalFees remains zero
The blockProfile function permanently blocks a user by burning their NFT and removing them from the system, intended to prevent re-entry.
However, the function uses delete on the profileToToken mapping, which resets the value to zero. The mintProfile function checks require(profileToToken[msg.sender] == 0) to prevent duplicate profiles, which means a blocked user can bypass this check by reminting a new profile.
Likelihood:
Occurs every time a blocked account attempts to mint a new profile
The deleted mapping entry provides no permanent block record
Impact:
Blocked users can circumvent platform restrictions and regain access
Platform integrity is compromised as malicious users can evade permanent bans
Previous violations by blocked users are not enforced
The matchRewards function collects all accumulated balance from two matched users and creates a shared multisig wallet with those pooled funds.
When users are matched, their balances are reset to zero. If a user matches with multiple people over time, their balance from the first match is lost and not included in subsequent matches, allowing users to receive free dates with unaccounted funds.
Likelihood:
Occurs when a user matches with multiple different users over time
Example: User A matches with User B (balance reset), then User A matches with User C (previous balance not included)
Impact:
Previously matched users lose their accumulated balance from prior matches
Subsequent matches may not receive full expected funds
Protocol reward distribution becomes inconsistent
The blockProfile function allows the app owner to permanently block any user by burning their NFT.
Users cannot interact with likeUser without a valid profile NFT. When blocked, users lose access to the platform but their ETH deposits remain locked in the contract with no withdrawal mechanism.
Likelihood:
Centralized risk: owner can block users at any time without cause
Occurs whenever the owner calls blockProfile on any address
Impact:
Users' deposited ETH becomes permanently locked in the contract
Users cannot participate in future likes or matches
No mechanism for users to recover their funds after being blocked
Abuse of centralized power creates trust issues
The mintProfile function calls _safeMint before updating the contract state variables _profiles and profileToToken.
Since _safeMint invokes onERC721Received on the recipient (if it's a contract), a malicious contract can reenter mintProfile and mint additional NFTs before the state is updated.
Likelihood:
Occurs when a contract address calls mintProfile with a malicious onERC721Received implementation
Attacker can reenter during the _safeMint call
Impact:
Single address can own multiple NFTs when only one should be allowed
Breaks protocol invariant of one profile per address
profileToToken mapping only tracks the last minted token, causing inconsistency
NFT count and profile count divergence
## Description User A calls `likeUser` and sends `value > 1` ETH. According to the design of DatingDapp, the amount for user A should be accumulated by `userBalances`. Otherwise, in the subsequent calculations, the balance for each user will be 0. ## Vulnerability Details When User A calls `likeUser`, the accumulation of `userBalances` is not performed. ```solidity 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; emit Liked(msg.sender, liked); // Check if mutual like if (likes[liked][msg.sender]) { matches[msg.sender].push(liked); matches[liked].push(msg.sender); emit Matched(msg.sender, liked); matchRewards(liked, msg.sender); } } ``` This will result in `totalRewards` always being 0, affecting all subsequent calculations: ```solidity uint256 totalRewards = matchUserOne + matchUserTwo; uint256 matchingFees = (totalRewards * FIXEDFEE ) / 100; uint256 rewards = totalRewards - matchingFees; totalFees += matchingFees; ``` ## POC ```solidity function testUserBalanceshouldIncreaseAfterLike() public { vm.prank(user1); likeRegistry.likeUser{value: 20 ether}(user2); assertEq(likeRegistry.userBalances(user1), 20 ether, "User1 balance should be 20 ether"); } ``` Then we will get an error: ```shell [FAIL: User1 balance should be 20 ether: 0 != 20000000000000000000] ``` ## Impact - Users will be unable to receive rewards. - The contract owner will also be unable to withdraw ETH from the contract. ## Recommendations Add processing for `userBalances` in the `likeUser` function: ```diff 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] += msg.value; emit Liked(msg.sender, liked); [...] } ```
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.