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

Missing check in `Soulmate::mintSoulmateToken` allows `msg.sender` to pair with itself

Summary

Missing check in Soulmate::mintSoulmateToken allows msg.sender to pair with itself

Vulnerability Details

Soulmate::mintSoulmateToken is used to pair 2 people who call the function. However, because there is no check that prevents somebody for entering twice in a row; it is possible for somebody to be paired with itself

Impact

Breaks the whole purpose of the contract and allows the exploiter to never get divorced (He is paired with himself, only him can get him divorced by calling the function, whereas other pairs can be divorced if the other person calls the Soulmate::getDivorced function)

Tools Used

Foundry

Proof of Concept:
1- soulmate3 calls Soulmate::mintSoulmateToken when nobody is looking for a soul
2- soulmate3 calls Soulmate::mintSoulmateToken again just after the first call, pairing with itself

Code
function testPairingWithItself() public {
address soulmate3 = makeAddr("soulmate3");
vm.prank(soulmate3);
soulmateContract.mintSoulmateToken();
vm.prank(soulmate3);
soulmateContract.mintSoulmateToken();
address owncouple = soulmateContract.soulmateOf(soulmate3);
//Both return the same address
console2.log(owncouple);
console2.log(soulmate3);
}

Recommendations

Check in Soulmate::mintSoulmateToken if the soulmate1 (idToOwners[nextID][0]) is different to msg.sender inside the if clause soulmate2 == address(0):

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)) {
+ } else if (soulmate2 == address(0) && soulmate1 != msg.sender) {
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.