Normal Behavior: The swap functions are designed to charge a 0.3% LP fee (3/1000) on the output amount of each swap operation. This fee should be calculated as (amount_out * 3) / 1000
, deducted from the user's output, and effectively retained in the pool to benefit liquidity providers.
Specific Issue: Due to integer division with floor behavior in the fee calculation (amount_out as u128 * 3).div_floor(&1000)
, when the output amount is small enough that amount_out * 3 < 1000
(i.e., amount_out < 333
), the fee calculation results in zero. This allows attackers to perform completely fee-free swaps on small amounts, bypassing the intended fee mechanism and reducing LP revenue.
The issue occurs in both branches of the swap logic where div_floor
on integer division causes lp_fees
to be zero when amount_out * 3 < 1000
, effectively making small swaps fee-free.
Likelihood:
Reason 1: This vulnerability occurs automatically on every swap where the output amount is less than 333 tokens (since 333 * 3 = 999 < 1000), which is common for small trades or low-value token swaps
Reason 2: Attackers can deliberately structure swap amounts to stay below the 333 token threshold, making exploitation predictable and repeatable without any special conditions
Impact:
Impact 1: Direct revenue loss to liquidity providers as fees that should be collected (0.3% of swap output) are completely bypassed, reducing the economic incentives for providing liquidity to the pool
Impact 2: Systematic exploitation through automated small swaps can accumulate significant fee losses over time, potentially making the pool economically unviable for legitimate liquidity providers
The first solution uses ceiling division to ensure fees are always rounded up, preventing zero fees. The second solution enforces a minimum fee of 1 token unit for any non-zero swap output.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.