Skip to content

Latest commit

 

History

History
134 lines (109 loc) · 6.04 KB

File metadata and controls

134 lines (109 loc) · 6.04 KB

Mysterious Red Grasshopper

High

Incorrect Penalty Application for Tax-Paid Users in Top 80% of Airdrop Distribution

Summary

In the Airdrop Distribution Module, the penalty calculation for Top 80% users is incorrectly applied to users who have paid the tax, which contradicts the business logic of the system. Tax-paying users in the Top 80% should not incur any penalties as they are entitled to claim the full amount upfront. This issue leads to incorrect claimable amounts for tax-paying users, potentially causing financial loss and undermining the airdrop system's intended flexibility.

Vulnerability

The vulnerability lies in the logic of the _available function where penalty calculations are applied indiscriminately to all Top 80% users, regardless of whether the tax has been paid. According to the intended airdrop rules, users who have paid the tax should be able to skip the vesting schedule and claim the full amount immediately, without facing any penalties. However, the current implementation incorrectly applies the same penalty calculation to tax-paying users as those who have not paid the tax.

if (isTop80) {
    penaltyAmount = _computePenalty($, totalAmount, account, monthsPassed); // Applies penalty to all Top 80% users
}

This logic results in penalties being applied to tax-paying users, violating the intended functionality and leading to incorrect distribution.

POC

Assuming the following data:

  • User in the Top 80% and has paid the tax.
  • Total Amount: 1000 USUAL tokens.
  • Claimable Amount: 1000 tokens (since tax is paid).
  • Penalty: Incorrectly calculated as 50 tokens (or any other arbitrary amount based on the _computePenalty logic).
  • Steps to reproduce:

Call the _available function with a user who is in the Top 80% and has paid the tax. The function should return 1000 tokens without any penalties, but due to the bug, it returns 950 tokens (after incorrectly applying the penalty). This behavior contradicts the expected outcome, where the tax-paying user should receive the full 1000 tokens with no penalty.

Impact

The core functionality of the airdrop distribution system involves different rules for users who have paid the tax versus those who have not. By applying penalties to users who have already paid the tax (and are supposed to claim the full amount immediately), the contract violates the business logic of the system and unfairly penalizes users who are entitled to receive their full airdrop without delays. This directly impacts the user experience and could lead to users not receiving the expected amount of tokens, or facing unnecessary penalties.

Tax-paying users in the Top 80% are unfairly penalized, leading to incorrect claimable amounts and potential financial loss.

Recommendation

The logic for penalty calculation should be adjusted to ensure that tax-paying Top 80% users are not penalized. The fix should involve skipping the penalty calculation for users who have paid the tax, or ensuring that the penalty is set to 0 for those users.

function _available(
        AirdropDistributionStorageV0 storage $,
        address account,
        uint256 totalAmount,
        bool isTop80
    ) internal returns (uint256, uint256) {
        if (block.timestamp < AIRDROP_INITIAL_START_TIME) {
            revert NotClaimableYet();
        }

        uint256 claimableAmount = totalAmount;
        uint256 monthsPassed = _calculateMonthsPassed();
        uint256 totalClaimed = $.claimed[account];
        uint256 penaltyAmount = 0;
        bool hasPaidTax = $.airdropTaxCollector.hasPaidTax(account);

        if (isTop80 && !hasPaidTax) {
            // slither-disable-next-line incorrect-equality
            if (monthsPassed == 0) {
                revert NotClaimableYet();
            }
            claimableAmount = totalAmount.mulDiv(monthsPassed, AIRDROP_VESTING_DURATION_IN_MONTHS);
        }

-        // Penalty is computed only if the account is in the top 80%
-        if (isTop80) {
-            penaltyAmount = _computePenalty(
-                $, totalAmount, account, hasPaidTax ? monthsPassed + 1 : monthsPassed
-            );
-        }

        // Subtract penalties from the claimable amount
        if (penaltyAmount > claimableAmount) {
            penaltyAmount = claimableAmount;
        }

        claimableAmount -= penaltyAmount;

        if (claimableAmount <= totalClaimed) {
            revert NothingToClaim();
        }

        return (claimableAmount - totalClaimed, penaltyAmount);
    }
     function _available(
        AirdropDistributionStorageV0 storage $,
        address account,
        uint256 totalAmount,
        bool isTop80
    ) internal returns (uint256, uint256) {
        if (block.timestamp < AIRDROP_INITIAL_START_TIME) {
            revert NotClaimableYet();
        }

        uint256 claimableAmount = totalAmount;
        uint256 monthsPassed = _calculateMonthsPassed();
        uint256 totalClaimed = $.claimed[account];
        uint256 penaltyAmount = 0;
        bool hasPaidTax = $.airdropTaxCollector.hasPaidTax(account);

        if (isTop80 && !hasPaidTax) {
            // slither-disable-next-line incorrect-equality
            if (monthsPassed == 0) {
                revert NotClaimableYet();
            }
            claimableAmount = totalAmount.mulDiv(monthsPassed, AIRDROP_VESTING_DURATION_IN_MONTHS);
        }

+           if (isTop80) {
+    if (!hasPaidTax) {
+        penaltyAmount = _computePenalty(
+           $, totalAmount, account, monthsPassed
+        );
+    } else {
+        penaltyAmount = 0; // No penalty for tax-paid users
+    }
+  }
        // Subtract penalties from the claimable amount
        if (penaltyAmount > claimableAmount) {
            penaltyAmount = claimableAmount;
        }

        claimableAmount -= penaltyAmount;

        if (claimableAmount <= totalClaimed) {
            revert NothingToClaim();
        }

        return (claimableAmount - totalClaimed, penaltyAmount);
    }