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

A user without a soulmate can mint Soulmate NFT

Summary

The Soulmate protocol is designed for two people to form a soulmate pair and jointly hold a Soulbound NFT. However, a vulnerability has been discovered that allows anyone to form a soulmate pair alone and abuse the protocol, such as minting an NFT token.

Vulnerability Details

When assigning a soulmate2 in Soulmate::mintSoulmateToken, the absence of verification regarding whether msg.sender is already soulmate1 allows the same address to be assigned to both soulmate1 and soulmate2

function test_SingleSoulCanMint() public {
uint tokenIdMinted = 0;
address singleSoul = makeAddr("singleSoul");
vm.startPrank(singleSoul);
soulmateContract.mintSoulmateToken();
soulmateContract.mintSoulmateToken();
vm.stopPrank();
assertTrue(soulmateContract.totalSupply() == 1);
assertTrue(soulmateContract.soulmateOf(singleSoul) == singleSoul);
assertTrue(soulmateContract.ownerToId(soulmate1) == tokenIdMinted);
}

Impact

The impact of this vulnerability is HIGH, as it results in the bypassing of the originally intended protocol. The Soulmate protocol, by design, incorporates uncertainties such as unknown soulmate assignment and allows anyone in a soulmate pair to call Soulmate::getDivorced regardless of the other party. A self-paired soulmate ignores all of these factors. Furthermore, Soulmate::totalSouls could return an inaccurate value, since a single soul could form a soulmate pair.

Tools Used

Foundry

Recommendations

Before assigning a soulmate2, ensure that msg.sender is not already soulmate1. Define an error, such as error Soulmate__doesNotHaveASoulmate(), to prevent this vulnerability.

In Soulmate::mintSoulmateToken,

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__doesNotHaveASoulmate();
idToOwners[nextID][1] = msg.sender;
// Once 2 soulmates are reunited, the token is minted
ownerToId[msg.sender] = nextID;
...
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.