Summary
In this dating platform, users invest 1 ETH to "like" other profiles, but when an admin blocks a profile using blockProfile()
, all ETH invested in relation to that profile becomes permanently locked in the contract. This includes both the ETH spent by others liking the blocked profile and the ETH the blocked user spent liking others.
Vulnerability Details
contract SoulboundProfileNFT {
mapping(address => uint256) public profileToToken;
function blockProfile(address blockAddress) external onlyOwner {
uint256 tokenId = profileToToken[blockAddress];
require(tokenId != 0, "No profile found");
_burn(tokenId);
delete profileToToken[blockAddress];
delete _profiles[tokenId];
emit ProfileBurned(blockAddress, tokenId);
}
}
Impact
Multiple users could be affected per blocked profile
Users who liked blocked profile lose 1 ETH per like
UserBalances remain unchanged but inaccessible
Users lose funds through no fault of their own
Admin could block profiles to deliberately lock funds
Recommendations
When a user is blocked there should be a mechanism to refund users who liked the blocked profile and another mechnism to refund the user that was blocked.
contract LikeRegistry {
function handleProfileBlocked(address blockedUser) external onlyProfileNFT {
for (uint i = 0; i < likersList[blockedUser].length; i++) {
address liker = likersList[blockedUser][i];
if (likes[liker][blockedUser]) {
payable(liker).transfer(1 ether);
likes[liker][blockedUser] = false;
emit LikeRefunded(liker, blockedUser, 1 ether);
}
}
uint256 userRefund = userBalances[blockedUser];
if (userRefund > 0) {
userBalances[blockedUser] = 0;
payable(blockedUser).transfer(userRefund);
emit BlockedUserRefunded(blockedUser, userRefund);
}
}
}
contract SoulboundProfileNFT {
function blockProfile(address blockAddress) external onlyOwner {
// First handle funds in LikeRegistry
+ likeRegistry.handleProfileBlocked(blockAddress);
// Then proceed with profile removal
uint256 tokenId = profileToToken[blockAddress];
_burn(tokenId);
delete profileToToken[blockAddress];
delete _profiles[tokenId];
emit ProfileBurned(blockAddress, tokenId);
}
}