Assessment reports>Avantis>High findings>Referrer rebates must not decrease ,totalRewards
Category: Business Logic

Referrer rebates must not decrease totalRewards

High Severity
High Impact
High Likelihood

Description

When a trade is opened, if the trader has set their referral code, the referrer would get a rebate. This rebate is added as rebates in the function applyReferralOpen when a trade is opened. Additionally, part of the remaining fee, after the rebate, is allocated as rewards in the vault manager.

function handleDevGovFees(
    address _trader,
    uint _pairIndex,
    uint _leveragedPositionSize,
    bool _usdc,
    bool _fullFee,
    bool _buy
) external override onlyTrading returns (uint feeAfterRebate) {
    //...
    feeAfterRebate = applyReferralOpen(_trader, fee, _leveragedPositionSize);

    uint vaultAllocation =
        (feeAfterRebate * (100 - _callbacks.vaultFeeP())) / 100;
    uint govFees = (feeAfterRebate * _callbacks.vaultFeeP()) / 100 / 2;

    if (_usdc) usdc.transfer(address(vaultManager), vaultAllocation);

    vaultManager.allocateRewards(vaultAllocation);
    //...
}

function applyReferralOpen(
    address _trader,
    uint _fees,
    uint _leveragedPosition
) public override onlyTrading returns (uint) {
    (uint traderFeePostDiscount, address referrer, uint referrerRebate) =
        referral.traderReferralDiscount(_trader, _fees);

    if (referrer != address(0)) {
        rebates[referrer] += referrerRebate;
        emit TradeReferred(
            _trader, 
            referrer, 
            _leveragedPosition, 
            traderFeePostDiscount, 
            _fees - traderFeePostDiscount,
            referrerRebate
        );
        return traderFeePostDiscount - referrerRebate;
    }
    return _fees;
}

When the trade is closed, the function applyReferralClose returns the referrerRebate, which is then subtracted from totalRewards in the function sendReferrerRebateToStorage as shown:

function _unregisterTrade(
    ITradingStorage.Trade memory _trade,
    int _percentProfit,
    uint _collateral,
    uint _feeAmountToken, // executor reward
    uint _lpFeeToken,
    uint _tier
) private returns (uint usdcSentToTrader) {
    //Scoping Local Variables to avoid stack too deep
    uint totalFees;
    {
        (uint feeAfterRebate, uint referrerRebate) =
            storageT.applyReferralClose(
                _trade.trader,
                _lpFeeToken,
                _trade.initialPosToken.mul(_trade.leverage)
            );

        //...
        if (referrerRebate > 0) {
            storageT.vaultManager()
                .sendReferrerRebateToStorage(referrerRebate);
        }
    }
}
function sendReferrerRebateToStorage(uint _amount)
external override onlyCallbacks {
    require(_amount > 0, "NO_REWARDS_ALLOCATED");
    require(totalRewards >= _amount, "UNDERFLOW_DETECTED");

    totalRewards -= _amount;
    IERC20(junior.asset()).transfer(address(storageT), _amount);

    emit ReferralRebateAwarded(_amount);
}

As the rewards do not include the referral rebate amount, these should not be subtracted from totalRewards.

Impact

The totalRewards distributed will be less than the total rewards available in the vault manager.

Recommendations

We recommend not subtracting the referral rebate from totalRewards.

Remediation

This issue has been acknowledged by Avantis Labs, Inc., and a fix was implemented in commit 6e335dc2.

Zellic © 2025Back to top ↑