Summary
Incorrect validation check in the Airdrop.sol::claim()
function will allow divorced users to claim airdrop tokens.
Vulnerability Details
In the Airdrop.sol::claim()
function, the following code block checks the divorce status of soulmate and reverts the transaction if soulmate is divorced:
if (soulmateContract.isDivorced()) revert Airdrop__CoupleIsDivorced();
The Soulmate.sol::isDivorced()
is implemented as follows:
function isDivorced() public view returns (bool) {
@> return divorced[msg.sender];
}
Divorce status is checked for the msg.sender
. When checking the divorce status in the Airdrop.sol::claim()
function, the divorce status is checked for the Airdrop.sol
contract instead of the user wanting to claim airdrop tokens.
Impact
Divorced users are able to claim airdrop tokens.
Proof of Concept (PoC)
Add the following test in AirdropTest.t.sol
:
function test_divorcedSoulmatesCanClaimTokens() public {
_mintOneTokenForBothSoulmates();
uint256 airdropVaultBalanceBeforeClaim = loveToken.balanceOf(address(airdropVault));
uint256 divorcedUserBalanceBeforeClaim = loveToken.balanceOf(soulmate1);
assertEq(divorcedUserBalanceBeforeClaim, 0);
vm.warp(block.timestamp + 1 days);
vm.startPrank(soulmate1);
soulmateContract.getDivorced();
assertTrue(soulmateContract.isDivorced());
airdropContract.claim();
vm.stopPrank();
uint256 airdropVaultBalanceAfterClaim = loveToken.balanceOf(address(airdropVault));
uint256 divorcedUserBalanceAfterClaim = loveToken.balanceOf(soulmate1);
assertGt(divorcedUserBalanceAfterClaim, divorcedUserBalanceBeforeClaim);
assertEq(airdropVaultBalanceAfterClaim, airdropVaultBalanceBeforeClaim - divorcedUserBalanceAfterClaim);
}
Run a test with forge test --mt test_divorcedSoulmatesCanClaimTokens
.
Tools Used
Recommendations
Airdrop.sol::claim()
should check the divorce status for the user wanting to claim tokens.
Recommended changes to the ISoulmate.sol::isDivorced()
function:
-function isDivorced() external view returns (bool);
+function isDivorced(address soulmate) external view returns (bool);
Recommended changes to the Soulmate.sol::isDivorced()
function:
-function isDivorced() public view returns (bool) {
+function isDivorced(address soulmate) public view returns (bool) {
- return divorced[msg.sender];
+ return divorced[soulmate];
}
Recommended changes to the Airdrop.sol::claim()
function:
function claim() public {
// No LoveToken for people who don't love their soulmates anymore.
- if (soulmateContract.isDivorced()) revert Airdrop__CoupleIsDivorced();
+ if (soulmateContract.isDivorced(msg.sender)) revert Airdrop__CoupleIsDivorced();
// 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
);
}
Add the following test in AirdropTest.t.sol
:
function test_claimTokensRevertsWhenSoulmateIsDivorced() public {
_mintOneTokenForBothSoulmates();
uint256 airdropVaultBalanceBeforeClaim = loveToken.balanceOf(address(airdropVault));
uint256 divorcedUserBalanceBeforeClaim = loveToken.balanceOf(soulmate1);
assertEq(divorcedUserBalanceBeforeClaim, 0);
vm.warp(block.timestamp + 1 days);
vm.startPrank(soulmate1);
soulmateContract.getDivorced();
assertTrue(soulmateContract.isDivorced(soulmate1));
vm.expectRevert(Airdrop.Airdrop__CoupleIsDivorced.selector);
airdropContract.claim();
vm.stopPrank();
}
Run a test with forge test --mt test_claimTokensRevertsWhenSoulmateIsDivorced
.