57061 sc high retroactive share recalculation causes royalty distribution failure

Submitted on Oct 23rd 2025 at 06:47:27 UTC by @preview for Audit Comp | Belongarrow-up-right

  • Report ID: #57061

  • Report Type: Smart Contract

  • Report severity: High

  • Target: https://github.com/immunefi-team/audit-comp-belong/blob/main/contracts/v2/periphery/RoyaltiesReceiverV2.sol

Impacts:

  • Smart contract unable to operate due to lack of token funds

Description

Brief/Intro

RoyaltiesReceiverV2 recalculates payment distributions using current Factory referral rates instead of the rates active when funds were received. When referral tiers change, releaseAll() reverts with TransferFailed(), permanently locking all future royalty distributions.

Vulnerability Details

The contract calculates pending payments as:

uint256 payment = ((balance + releases.totalReleased) * shares(account)) / TOTAL_SHARES;

Where shares(account) queries the Factory's current referral rate:

referralShare = _factory.getReferralRate(
    _royaltiesReceivers.creator, referralCode, royaltiesParameters.amountToPlatform
);

Because getReferralRate is called at payout time, the contract uses the Factory's current referral tier to compute distributions for all historical funds. If referral tiers change, recalculating historical entitlements with new rates can cause the contract to attempt transfers that exceed its balance.

Attack Scenario

1

Initial State

Receiver deployed with referral tier 1 (50% of platform share = 10% total).

2

First Distribution (1000 tokens)

  • Creator: 800 (80%)

  • Platform: 100 (10%)

  • Referral: 100 (10%)

All funds distributed.

3

Tier Change

Creator uses referral code again → tier 2 (30% of platform = 6% total).

4

Second Distribution (500 new tokens)

  • Contract recalculates: 1500 tokens * 6% = 90 for referral

  • Already paid: 100 to referral

  • Pending: 90 - 100 = -10 (underflow!)

  • Platform pending: 210 - 100 = 110

  • Creator pending: 1200 - 800 = 400

  • Total pending: 510, Balance: 500

releaseAll() reverts with TransferFailed().

Proof of Concept

chevron-rightShow PoC test (Hardhat / TypeScript)hashtag

Was this helpful?