#55174 [SC-Insight] over assignment of payable in claimairdropdistribution function could cause confusion regarding native token handling

Submitted on Sep 23rd 2025 at 18:43:20 UTC by @Pig46940 for Mitigation Audit | Flare | FAssets

  • Report ID: #55174

  • Report Type: Smart Contract

  • Report severity: Insight

  • Target: https://github.com/flare-foundation/fassets/commit/92e1e2bdc6e8f75f61cfd9f10ddb05df4a7c8c6b

  • Impacts: potential confusion in integrator assumptions about native token transfers

Description

Brief / Intro

The claimAirdropDistribution function casts address(this) to address payable when calling _distribution.claim, even though the _recipient parameter in IDistributionToDelegators is defined as a non-payable address. This mismatch is unnecessary and may cause confusion about whether the distribution contract expects to receive native tokens.

Vulnerability Details

IDistributionToDelegators.claim is specified as:

function claim(
    address _rewardOwner,
    address _recipient,
    uint256 _month,
    bool _wrap
) external returns (
    uint256 _rewardAmount
);
  • _rewardOwner: address of the reward owner

  • _recipient: address to transfer funds to (non-payable address)

  • _month: last month to claim for

  • _wrap: should reward be wrapped immediately

In CollateralPool.sol, the implementation uses an unnecessary payable cast:

Because the interface expects a non-payable address for _recipient, casting address(this) to address payable is unnecessary and may mislead developers.

Note that a different interface, RewardsV2Interface, defines _recipient as address payable, which may further add to confusion when comparing different claim functions:

Example of matching use elsewhere in the codebase:

Impact Details

Integrators or developers interacting with the Flare Fasset system may be confused by the discrepancy between the interface documentation and the implementation. Specifically, _recipient in IDistributionToDelegators.claim is defined as a non-payable address, but the contract casts it to address payable. This may lead integrators to incorrectly assume that the contract can receive native FLR, potentially causing misunderstandings in contract usage, reward claiming logic, or security assumptions.

References

  • https://github.com/flare-foundation/developer-hub/blob/main/docs/network/solidity-reference/IDistributionToDelegators.md

  • https://github.com/flare-foundation/developer-hub/blob/main/docs/network/solidity-reference/RewardsV2Interface.md

Proof of Concept

Proof of Concept — Removing the `payable` cast (click to expand)
  • Deleted the payable cast in the claimAirdropDistribution function in CollateralPool.sol. Tests still pass after removing the payable() cast.

Modified function:

Commands used to run tests:

Notes / Recommendation

  • Remove the unnecessary payable cast to match the IDistributionToDelegators interface and avoid misleading consumers of the contract.

  • Ensure consistent parameter types across similar claim interfaces (or clearly document differences) to reduce potential confusion.

Was this helpful?