The description states that the point of the Soulmate Protocol is to "mint your shared Soulbound NFT with an unknown person, and get LoveToken as a reward for staying with your soulmate." However, the ability to deploy smart contracts allows you to ensure that the person that you are married to is yourself. This destroys the risk of a "divorce" and you can sit still and collect LoveTokens with no risk.
This vulnerability stems from the fact that partners are assigned to each other just in the order that they call the minSoulmateToken function. A user can deploy two smart contracts back to back to ensure that their smart contracts end up being married to each other.
Here is a simple smart contract designed to call mintSoulmateToken()
contract SelfMarriage{
Soulmate public target;
constructor(address _target){
target = Soulmate(_target);
target.mintSoulmateToken();
}
}
Here is a proof of concept for using the protocol to marry yourself.
function testSelfMarriage() public {
SelfMarriage contract1 = new SelfMarriage(address(soulmate));
SelfMarriage contract2 = new SelfMarriage(address(soulmate));
address soulmate_address = soulmate.soulmateOf(address(contract1));
assertEq(soulmate_address, address(contract2));
soulmate_address = soulmate.soulmateOf(address(contract2));
assertEq(soulmate_address, address(contract1));
}
Although there is no risk of funds being drained, the integrity of the protocol is at stake since its reward mechanism can be gamed.
Foundry
You could fix this issue by making sure that mintSoulmateToken() function can only be called by EOAs and not smart contracts. One way to check this would be to check the size of the code at a specific address:
function isContract(address _addr) private returns (bool isContract){
uint32 size;
assembly {
size := extcodesize(_addr)
}
return (size > 0);
}
However, this function returns false for a contract in construction, and as the proof of concept shows, the function can easily be called in a contract constructor. Therefore, we could ensure that this does not happen by requiring that the function only be called once per block as well as only by EOAs. However, this would create significant latency issues for the users. Instead, the best way to fix this issue would be through significant reengineering that changes the methodology in which partners are matched. A potential solution could be using Oracles as a source of randomness and creating couples in such manner.
- 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.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.