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

There is a lack of check that either people have soulmate or not in Airdrop :: claim() so that any person which have not any soulmate can also claim love token.

Summary

In Airdrop :: claim() function both partner of a couple can claim their own token every days on the basis of their Soulmate NFT token.

Vulnerability Details

But this function is only checked for the mapping is either couple divorced or not.It will not check that is the soulmate of the msg.sender exist or not. The result is this anyone who not mint an SoulmateToken can also claim love token from this function.

Code Snippet

     function claim() public {
    // No LoveToken for people who don't love their soulmates anymore.
    // @audit-issue : Lack of check that either it have soulmate or not
    if (soulmateContract.isDivorced()) revert Airdrop__CoupleIsDivorced();
    // Calculating since how long soulmates are reunited
    uint256 numberOfDaysInCouple = (block.timestamp -
        soulmateContract.idToCreationTimestamp(0
            soulmateContract.ownerToId(msg.sender)
        )) / daysInSecond;

    uint256 amountAlreadyClaimed = _claimedBy[msg.sender];

    if (
        amountAlreadyClaimed >=
        numberOfDaysInCouple * 10 ** loveToken.decimals()
    ) revert Airdrop__PreviousTokenAlreadyClaimed();

    uint256 tokenAmountToDistribute = (numberOfDaysInCouple *
        10 ** loveToken.decimals()) - amountAlreadyClaimed;

    // Dust collector
    if (
        tokenAmountToDistribute >=
        loveToken.balanceOf(address(airdropVault))
    ) {
        tokenAmountToDistribute = loveToken.balanceOf(
            address(airdropVault)
        );
    }
    _claimedBy[msg.sender] += tokenAmountToDistribute;

    emit TokenClaimed(msg.sender, tokenAmountToDistribute);

    loveToken.transferFrom(
        address(airdropVault),
        msg.sender,
        tokenAmountToDistribute
    );
}

Impact

The impact is this the people who have not soulmates can claim love token.

POC

    function test_singlescanClaim() public {
    vm.prank(attacker);

    vm.warp(block.timestamp + 200 days + 1 seconds);

    vm.prank(attacker);
    airdropContract.claim();
    assertTrue(loveToken.balanceOf(attacker) == 200 ether);
}

Tools Used

Foundry

Recommendations

Its recommended to add an additional check in claim function which check for soulmate of msg.sender is exist or not.If it will return the address(0) then the function is reverted.

    function claim() public {
    // No LoveToken for people who don't love their soulmates anymore.
    if (soulmateContract.isDivorced()) revert Airdrop__CoupleIsDivorced();
       // @audit - recommended
      address soulmate2 = soulmateContract.soulmateOf(msg.sender);
      require(soulmate2!=address(0));
    // Calculating since how long soulmates are reunited
    uint256 numberOfDaysInCouple = (block.timestamp -
        soulmateContract.idToCreationTimestamp(
            soulmateContract.ownerToId(msg.sender)
        )) / daysInSecond;

    uint256 amountAlreadyClaimed = _claimedBy[msg.sender];

    if (
        amountAlreadyClaimed >=
        numberOfDaysInCouple * 10 ** loveToken.decimals()
    ) revert Airdrop__PreviousTokenAlreadyClaimed();

    uint256 tokenAmountToDistribute = (numberOfDaysInCouple *
        10 ** loveToken.decimals()) - amountAlreadyClaimed;

    // Dust collector
    if (
        tokenAmountToDistribute >=
        loveToken.balanceOf(address(airdropVault))
    ) {
        tokenAmountToDistribute = loveToken.balanceOf(
            address(airdropVault)
        );
    }
    _claimedBy[msg.sender] += tokenAmountToDistribute;

    emit TokenClaimed(msg.sender, tokenAmountToDistribute);

    loveToken.transferFrom(
        address(airdropVault),
        msg.sender,
        tokenAmountToDistribute
    );
}

POC :

    function test_youcannotClaimlove_token_if_youaresingle() public {
    vm.prank(attacker);
 
    vm.warp(block.timestamp + 200 days + 1 seconds);

    vm.prank(attacker);
    vm.expectRevert();
    airdropContract.claim();
}
Updates

Lead Judging Commences

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

finding-claim-airdrop-without-owning-NFT

High severity, This issue is separated from the flawed `isDivorced()` check presented in issue #168 as even if that is fixed, if ownership is not checked, isDivorced would still default to false and allow bypass to claim airdrops by posing as tokenId 0 in turn resulting in this [important check for token claim is bypassed.](https://github.com/Cyfrin/2024-02-soulmate/blob/b3f9227942ffd5c443ce6bccaa980fea0304c38f/src/Airdrop.sol#L61-L66). #220 is the most comprehensive issue as it correctly recognizes both issues existing within the same function.

Support

FAQs

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