57060 sc medium unconditional subsidy withdrawal in paytovenue leads to dos when venue s long pool is depleted

Submitted on Oct 23rd 2025 at 06:46:41 UTC by @cholakovvv for Audit Comp | Belongarrow-up-right

  • Report ID: #57060

  • Report Type: Smart Contract

  • Report severity: Medium

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

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

Description

Brief/Intro

BelongCheckIn.payToVenue() always attempts to withdraw a per-venue LONG subsidy from Escrow before delivering funds. When the venue’s LONG subsidy bucket (Escrow.venueDeposits[venue].longDeposits) is depleted, this withdrawal reverts, making all LONG payments for that venue fail until the venue redeposits (and pays the convenience fee again). USDC payments still work, but the LONG path is bricked, creating a venue-level denial of service that can occur naturally with usage or be forced cheaply by an adversary.

Vulnerability Details

When a customer pays a venue in LONG, the contract first calculates a subsidy amount derived from the platform’s configuration and attempts to withdraw it from the venue’s LONG pool in Escrow. The withdrawn subsidy is then combined with the customer’s own LONG payment to form the total amount transferred or staked for the venue. The subsidy is meant to be sourced from the venue’s previously deposited LONG balance, which is finite and decreases with every LONG payment.

In the LONG payment branch of BelongCheckIn::payToVenue(), the function computes the subsidy and immediately requests it from the escrow contract:

 } else {
            // platform subsidy - processing fee
            uint256 subsidyMinusFees = _storage
                .fees
                .platformSubsidyPercentage
                .calculateRate(customerInfo.amount) -
                _storage.fees.processingFeePercentage.calculateRate(
                    customerInfo.amount
                );
            _storage.contracts.escrow.distributeLONGDiscount(
                customerInfo.venueToPayFor,
                address(this),
                subsidyMinusFees
            );

The call to Escrow::distributeLONGDiscount() reverts if the venue’s LONG balance is insufficient to cover the requested amount:

Once the venue’s longDeposits reach zero, this require statement fails, reverting the entire transaction. There is no fallback behavior to cap the subsidy at the remaining balance, nor to skip subsidy withdrawal entirely when the pool is empty. Because of this, any subsequent LONG payments to the venue will consistently revert until new funds are deposited to replenish its LONG balance in escrow.

This logic makes LONG payments depend entirely on the venue’s available subsidy balance. Once that balance is depleted, every LONG transaction to the venue fails, effectively disabling this payment method until the subsidy is replenished. Meanwhile, USDC payments remain unaffected and continue to work as normal.

Impact Details

Selected impact: Smart contract unable to operate due to lack of token funds.

Once a venue’s Escrow.venueDeposits[venue].longDeposits reaches zero, all subsequent LONG payments to that venue revert with NotEnoughLONGs. This disables one of the two accepted payment paths and effectively halts all LONG-based activity for that venue until a redeposit occurs.

The issue can appear naturally after enough LONG payments deplete the subsidy pool or be triggered intentionally by an attacker repeatedly making LONG payments to exhaust the pool (a cheap griefing vector). Although USDC payments remain functional, the protocol fails to deliver on its intended functionality for LONG payments, which makes the LONG payment path unusable for that venue.

References

  • https://github.com/immunefi-team/audit-comp-belong/blob/main/contracts/v2/platform/BelongCheckIn.sol#L471C8-L477C102

  • https://github.com/immunefi-team/audit-comp-belong/blob/main/contracts/v2/periphery/Escrow.sol#L113C3-L126C6

Proof of Concept

chevron-rightPoC test (open to expand)hashtag

To reproduce the issue, add the following test case to belong-check-in.test.ts, inside the existing test suite. Then, run the test with:

PoC:

Expected output (example):

Was this helpful?