Skip to content

Latest commit

 

History

History
100 lines (69 loc) · 3.5 KB

File metadata and controls

100 lines (69 loc) · 3.5 KB

Proud Punch Mouse

Medium

In Airdrop, users who do not claim immediately after payTax will pay the excess penalty amount.

Summary

In Airdrop, users who do not claim immediately after payTax will pay the excess penalty amount.

Root Cause

Both payTaxAmount and _computePenalty are calculated according to the current time, so as to avoid double payment (Tax and Penalty).

However, there is a case where the user payTaxAmount does not execute the claim immediately, but executes it one or more months later, so that the user with monthsPassed > 0 will double pay the tax and Penalty.

It's possible, and it's not the user's fault, the user has the right to decide when to claim the token(within the validity period).

    function _computePenalty(
        AirdropDistributionStorageV0 storage $,
        uint256 totalAmount,
        address account,
        uint256 monthsPassed
    ) internal returns (uint256) {
        uint256 penaltyAmount = 0;
        uint256 oneSixthAmount = totalAmount.mulDiv(1, AIRDROP_VESTING_DURATION_IN_MONTHS, Math.Rounding.Ceil);    
        for (uint256 i = 1; i <= monthsPassed; i++) {
            if ($.penaltyPercentageByMonth[account][i] == 0) {
                continue;
            } else if ($.penaltyPercentageByMonth[account][i] == BASIS_POINT_BASE) { //BASIS_POINT_BASE = 10_000;
                penaltyAmount += oneSixthAmount;
            } else {
                uint256 monthlyPenalty = oneSixthAmount.mulDiv($.penaltyPercentageByMonth[account][i], BASIS_POINT_BASE);
                penaltyAmount += monthlyPenalty;
            }
            $.penaltyPercentageByMonth[account][i] = 0;
        }
        return penaltyAmount;
    }

    function _payTaxAmount(address account) internal {
        AirdropTaxCollectorStorage storage $ = _airdropTaxCollectorStorage();

        bool isBeforeStartDate = block.timestamp < AIRDROP_INITIAL_START_TIME;
        bool isAfterEndDate = block.timestamp > AIRDROP_INITIAL_START_TIME + AIRDROP_CLAIMING_PERIOD_LENGTH;

        if (isBeforeStartDate || isAfterEndDate) {
            revert NotInClaimingPeriod();
        }

        if ($.taxedClaimers[account]) {
            revert ClaimerHasPaidTax();
        }

        // Check if the account hasn't voided it's eligibility for airdrop
        IAirdropDistribution airdropContract = IAirdropDistribution($.registryContract.getContract(CONTRACT_AIRDROP_DISTRIBUTION));
        if (airdropContract.getRagequitStatus(account)) {
            revert AirdropVoided();
        }

        uint256 claimTaxAmount = _calculateClaimTaxAmount($, account);

        $.taxedClaimers[account] = true;
        $.usd0PP.setBondEarlyUnlockDisabled(account);
        emit AirdropTaxPaid(account, claimTaxAmount);

        $.usd0PP.safeTransferFrom(account, $.treasury, claimTaxAmount);
    }

https://github.com/sherlock-audit/2024-10-usual-labs-v1/blob/4fb4a64a479e0b9b8f93934220e891c29d54df33/pegasus/packages/solidity/src/airdrop/AirdropDistribution.sol#L314-L341

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

  1. The user pays tax on month 0.
  2. The user does not claim the token at month 0, but calls the claim 6 months later.
  3. The user has paid the excess penalty amount.
  4. This is not the user's fault, the user has the right to decide when to claim the token(within the validity period)

Impact

Loss of user funds

PoC

No response

Mitigation

Calculate the amount of the penalty based on the time of payTax instead of the current time.