# AirdropDesk Contract Internal Security Review

**Review date:** 2026-07-12  
**Scope:** `contracts/BulkAirdropWithFee.remix.sol`, `contracts/BulkAirdropWithFee.sol`, frontend transaction flow, and four deployed EVM contracts.  
**Review type:** Internal engineering review. This is not an independent third-party audit or a guarantee that the contract is vulnerability-free.

## Executive Summary

No critical issue was found in the reviewed transfer loop. The contract uses atomic transactions, safe ERC-20 calls, a reentrancy guard, zero-value checks, matching-array checks, and a bounded recipient count. It does not custody distribution tokens during normal operation.

The main trust risk is mutable pricing without a user-supplied fee cap. The owner can change the service token, price, and recipient immediately. A user's transaction reads the latest price when mined, so a previously approved high allowance could expose the user to a higher fee than the frontend preview if the owner changes pricing before inclusion.

## Findings

### M-01: No maximum-fee protection in `airdropERC20`

**Severity:** Medium  
**Status:** Open

`airdropERC20` calculates `quoteFee(recipients.length)` at execution time. The transaction does not include an expected fee or `maxServiceFee` parameter. Because the owner can call `setPricing`, the mined fee can differ from the value shown before signing.

**Impact:** If a user has granted a historical large or unlimited service-token allowance, an owner price change before transaction inclusion can collect more than the previewed amount. The current frontend now approves exactly the previewed fee, so a higher fee will fail against that new allowance; users should still revoke older or larger allowances.

**Recommendation:** Add a `maxServiceFee` argument and require `serviceFeePaid <= maxServiceFee`. A stronger version should also include an expected service-token address and a deadline.

### M-02: Immediate centralized pricing and recipient controls

**Severity:** Medium  
**Status:** Acknowledged

The owner can immediately change the service token, price per recipient, fee recipient, and maximum batch size. There is no multisig requirement, timelock, or two-step pricing activation.

**Recommendation:** Transfer ownership to a multisig and consider a timelock or delayed pricing updates. Publish every admin change and keep the frontend's on-chain mismatch checks enabled.

### L-01: No pause mechanism

**Severity:** Low  
**Status:** Open

The owner cannot pause new airdrops if a token integration or operational issue is discovered.

**Recommendation:** Add an explicit pause state controlled by a multisig. Do not add an unrestricted withdrawal or transfer function.

### L-02: Tokens sent directly to the contract cannot be recovered

**Severity:** Low  
**Status:** Open

The intended flow uses `transferFrom` directly from the sender to recipients. If someone transfers ERC-20 tokens directly to the contract address, there is no recovery function.

**Recommendation:** Document that users must not transfer tokens to the contract. If a recovery function is added, restrict it to a multisig and ensure it cannot access user-approved funds.

### I-01: Duplicate recipients are not rejected on-chain

**Severity:** Informational  
**Status:** Accepted

The frontend merges duplicates, but direct callers can submit the same recipient multiple times. The contract bills and transfers per array entry.

**Recommendation:** Keep duplicate handling in the frontend for gas efficiency and disclose that direct contract calls are billed per entry.

### I-02: Non-standard token behavior

**Severity:** Informational  
**Status:** Accepted

The safe transfer helper supports tokens that return `true` or no value. Fee-on-transfer, rebasing, blocked-address, or callback-enabled tokens may produce different recipient balances or revert.

**Recommendation:** State that standard ERC-20 behavior is expected and test unusual tokens before large distributions.

## Controls That Passed Review

- `nonReentrant` protects the fee and distribution flow from reentrant entry.
- The fee transfer and every distribution transfer are atomic; any failed transfer reverts the whole transaction, including the fee.
- Token, recipient, and amount zero checks are present.
- Recipient and amount array lengths must match.
- The default batch limit is 300 and the owner cannot raise it above 500.
- Arithmetic uses Solidity 0.8 checked math.
- The safe transfer helper accepts both standard boolean-returning ERC-20 tokens and tokens that return no data.
- The contract has no arbitrary owner withdrawal, arbitrary user transfer, `delegatecall`, proxy, or upgrade function.
- Current live service token, fee recipient, price, and batch limit were read directly from all four deployed contracts.
- The single-file contract compiles successfully with Solidity 0.8.34, optimization disabled.

## Upgradeability and Admin Summary

The four contracts are non-upgradeable deployments. Logic can only be changed by deploying a replacement contract. The owner address is `0xfD148d89eF7D87FFbB18183DbE50EA3baD67FF7c` on every reviewed chain.

Owner-only functions:

```solidity
setPricing(address newServiceToken, uint256 newPricePerRecipient, address newFeeRecipient)
setMaxRecipientsPerTx(uint256 newMaxRecipientsPerTx)
transferOwnership(address newOwner)
```

## Recommended Next Contract Version

Before a major volume increase, deploy a new non-proxy version with:

1. `maxServiceFee`, expected fee-token, and deadline parameters;
2. multisig ownership and delayed pricing updates;
3. a narrowly scoped pause mechanism;
4. tests for fee changes, fee-on-transfer tokens, malicious token callbacks, 500-recipient gas limits, and duplicate direct calls.
