Funds available for distribution are burned if a user paid for more rewards, resulting in a loss of rewards
When NXL rewards are insufficient for the requested amount, the protocol reverts and burns remaining tokens instead of distributing the partial amount available.
Description
When users purchase raffle tickets in NexumManager, they are entitled to receive NXL token rewards proportional to the number of tickets purchased. However, the current implementation follows an "all-or-nothing" approach: if the NXL contract doesn't have enough tokens to fulfill the entire reward amount, the user receives nothing at all, despite having paid for their tickets.
The issue occurs in the _distributeNXL() function which uses a try-catch block. When nxlToken.distributeReward() reverts due to insufficient rewards, the catch block calls _handleNXLExhaustion() which deactivates the product and attempts to burn remaining tokens, but the user who just purchased tickets receives zero NXL tokens.
Vulnerable Scenario:
-
The NXL token contract has 5 NXL tokens remaining in its rewards pool (after accounting for vesting reserves).
-
Alice purchases 10 tickets for the BLACKBLOK product, which should reward her with
10 tickets * 1 NXL per ticket = 10 NXLtotal. -
The
_distributeNXL()function is called withamount = 10 NXL. -
The
distributeReward()function in NXLToken checks available rewards and finds only 5 NXL available. -
The function reverts with "Insufficient rewards available" because it requires exactly 10 NXL but only 5 are available.
-
The catch block in
_distributeNXL()triggers_handleNXLExhaustion(), which:- Deactivates the BLACKBLOK product
- Attempts to burn remaining tokens
- Alice receives 0 NXL despite paying full price for 10 tickets
- If she had paid the price for 5 tickets, she would have gotten 5 NXL
-
Alice has paid for 10 tickets but received no NXL rewards, while the contract still holds 5 NXL that could have been partially distributed to her.
Impact
Users who purchase tickets when the NXL rewards pool is running low receive zero NXL tokens despite paying full price. This creates severe unfairness as users pay full stablecoin price but receive 0% of expected NXL rewards when partial rewards are available.
For example, with 5 NXL remaining, a user expecting 10 NXL receives nothing instead of the available 5 NXL (100% loss vs 50% distribution).
Recommendation
Modify the reward distribution logic to support partial distributions when full rewards are unavailable:
Update NXLToken::distributeReward() to accept partial distributions:
function distributeReward(address recipient, uint256 amount) external {require(msg.sender == nexumManager, "Only NexumManager");require(recipient != address(0), "Invalid recipient");require(amount > 0, "Amount must be > 0");uint256 availableRewards = getAvailableRewards();// Distribute available amount (full or partial)uint256 amountToDistribute = availableRewards >= amount ? amount : availableRewards;require(amountToDistribute > 0, "No rewards available");rewardsDistributed += amountToDistribute;_transfer(address(this), recipient, amountToDistribute);emit RewardDistributed(recipient, amountToDistribute);}
Resolution
Nexalo: Fixed.
Zealynx: Verified. Fixed.

