#55230 [SC-Insight] there is a sub gwei executor fee can be bypass and freezes eth in redemptionrequests
Submitted on Sep 25th 2025 at 01:43:26 UTC by @XDZIBECX for Mitigation Audit | Flare | FAssets
Report ID: #55230
Report Type: Smart Contract
Report severity: Insight
Target: https://github.com/flare-foundation/fassets/commit/7dd1ddd574989c44b3057ce426ff188bc69743d1
Impacts: Permanent freezing of funds
Description
Brief / Intro
The new fix is unsafe because the redemption guard validates the executor fee after rounding msg.value
down to gwei. Any sub-gwei ETH (for example 1 wei) sent with no executor bypasses the check, creating a redemption with zero executor/fee, and that ETH becomes permanently stuck on the contract.
CollateralPool and CollateralReservations handle refunds correctly; the flaw is isolated to RedemptionRequests. The recommended validation is to check raw msg.value
before rounding:
If
_executor == address(0)
, requiremsg.value == 0
.If
_executor != address(0)
, optionally requiremsg.value % 1 gwei == 0
to ban hidden dust.
Vulnerability Details
The problematic line:
require(_executorFeeNatGWei == 0 || _executor != address(0), "executor fee without executor");
In RedemptionRequests the guard is applied after computing the executor fee by truncating msg.value
to gwei:
uint256 executorFeeNatGWei = msg.value / Conversion.GWEI; // rounds down
Any 0 < msg.value < 1 gwei
becomes 0 after rounding. The require then passes for _executor == address(0)
and executorFeeNatGWei == 0
, allowing the call to succeed. This creates a redemption request with executor = 0
and executorFee = 0
, while the dust ETH remains on the contract with no refund path — permanently stranded.
Root cause: validating the gwei-rounded executor fee instead of the raw msg.value
allows sub-gwei amounts to bypass the "no ETH unless executor is set" policy.
Other parts of the patch are safe:
CollateralPool forwards
msg.value
only when an executor is provided; otherwise it refunds at the end.CollateralReservations records an executor fee only when an executor exists; if none, it refunds the excess (or burns on refund failure).
The fix in RedemptionRequests must validate raw msg.value
before rounding to prevent dust bypass.
Impact Details
Because the guard validates the rounded fee instead of raw msg.value
, calls with _executor == address(0)
and 0 < msg.value < 1 gwei
succeed and create redemption requests with zero executor fee while the sent ETH is neither refunded nor consumed. This permanently traps ETH in the AssetManager.
Consequences:
Users can lose small amounts if wallets/UIs accidentally attach dust ETH without specifying an executor.
Attackers can grief by repeatedly calling redeem to strand arbitrary ETH on the contract. Each call can trap up to 999,999,999 wei (~1 gwei). Repetition can accumulate meaningful balances limited only by attacker resources and available fAssets to redeem.
There is no protocol path to reclaim this ETH for the sender (refund does not occur and the fee recorded is zero).
References
https://github.com/flare-foundation/fassets/commit/7dd1ddd574989c44b3057ce426ff188bc69743d1?utm_source=immunefi
Recommended Validation (as stated in report)
If
_executor == address(0)
, requiremsg.value == 0
.If
_executor != address(0)
, optionally requiremsg.value % 1 gwei == 0
to ban hidden dust.
Proof of Concept
Was this helpful?