Beginner FriendlyFoundryNFT
100 EXP
View results
Submission Details
Severity: medium
Valid

`Soulmate::mintSoulmateToken` missing self-soulmate check allows minting a Soulmate token without a soulmate

Summary

The mintSoulmateToken function in the Soulmate contract doesn't include a check to prevent a user from being their own soulmate. This allows a single user to call mintSoulmateToken twice, resulting in the minting of a Soulmate token where both 'soulmates' are the same person.

Vulnerability Details

Copy-paste the function in SoulmateTest.t.sol, then run forge test --mt test_UserCanBeHisOwnSoulmate -vv in the terminal

POC:

function test_UserCanBeHisOwnSoulmate() public {
vm.startPrank(soulmate1);
soulmateContract.mintSoulmateToken(); // soulmate1 calls mintSoulmateToken
console2.log("Soulmate tokens minted (first `mintSoulmateToken()` call): %s", soulmateContract.totalSupply()); // but there is no Soulmate token minted
console2.log("soulmate of soulmate1: %s", soulmateContract.soulmateOf(soulmate1)); // as soulmate1 doesn't have a soulmate yet
soulmateContract.mintSoulmateToken(); // soulmate1 calls mintSoulmateToken again
console2.log("\n Soulmate tokens minted (second `mintSoulmateToken()` call): %s", soulmateContract.totalSupply()); // now there is a Soulmate token minted
console2.log("soulmate1 address : %s \n soulmate of soulmate1: %s", soulmate1, soulmateContract.soulmateOf(soulmate1)); // and soulmate1 is his own soulmate
}

Logs:

Soulmate tokens minted (first `mintSoulmateToken()` call): 0
soulmate of soulmate1: 0x0000000000000000000000000000000000000000
Soulmate tokens minted (second `mintSoulmateToken()` call): 1
soulmate1 address : 0x65629adcc2F9C857Aeb285100Cc00Fb41E78DC2f
soulmate of soulmate1: 0x65629adcc2F9C857Aeb285100Cc00Fb41E78DC2f

Impact

A single user can be his own soulmate and mint a Soulmate token without the need for a soulmate.

Tools Used

Manual review

Recommendations

function mintSoulmateToken() public returns (uint256) {
// Check if people already have a soulmate, which means already have a token
address soulmate = soulmateOf[msg.sender];
if (soulmate != address(0))
revert Soulmate__alreadyHaveASoulmate(soulmate);
address soulmate1 = idToOwners[nextID][0];
address soulmate2 = idToOwners[nextID][1];
if (soulmate1 == address(0)) {
idToOwners[nextID][0] = msg.sender;
ownerToId[msg.sender] = nextID;
emit SoulmateIsWaiting(msg.sender);
} else if (soulmate2 == address(0)) {
+ if (msg.sender == soulmate1) revert("Soulmate cannot be his own soulmate");
idToOwners[nextID][1] = msg.sender;
// Once 2 soulmates are reunited, the token is minted
ownerToId[msg.sender] = nextID;
soulmateOf[msg.sender] = soulmate1;
soulmateOf[soulmate1] = msg.sender;
idToCreationTimestamp[nextID] = block.timestamp;
emit SoulmateAreReunited(soulmate1, soulmate2, nextID);
_mint(msg.sender, nextID++);
}
return ownerToId[msg.sender];
}
Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-self-soulmate

- Given the native anonymous nature of blockchain in general, this issue cannot be avoided unless an explicit whitelist is implemented. Even then we can only confirm soulmates are distinct individuals via kyc. I believe finding a soulmate is intended to be permisionless. - However, even though sufficient (500_000_000e18 in each vault) tokens are minted to claim staking and airdrop rewards, it would take 500_000_000 / 2 combined weeks for airdrop vault to be drained which is not unreasonable given there are [80+ million existing wallets](https://coinweb.com/trends/how-many-crypto-wallets-are-there/). Given there is no option to mint new love tokens, this would actually ruin the functionality of the protocol of finding soulmates and shift the focus to abusing a sybil attack to farming airdrops instead. Assigning medium severity for now but am open for appeals otherwise, since most if not all issues lack indepth analysis of the issue.

Support

FAQs

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