sherlock-2025-07-mellow-h05

[H-05] Incorrect performance fee calculation in FeeManager

보고서

Summary

수수료 계산 공식이 잘못되어 수수료를 실제보다 많이 뗄 수 있다.

Keyword

arithmetic error, logic flaw, fee

Vulnerability

function calculateFee(address vault, address asset, uint256 priceD18, uint256 totalShares)
    public
    view
    returns (uint256 shares)
{
    FeeManagerStorage storage $ = _feeManagerStorage();
    if (asset == $.baseAsset[vault]) {
        uint256 minPriceD18_ = $.minPriceD18[vault];
        if (priceD18 < minPriceD18_ && minPriceD18_ != 0) {
@>          shares = Math.mulDiv(minPriceD18_ - priceD18, $.performanceFeeD6 * totalShares, 1e24);
        }
    }
    uint256 timestamp = $.timestamps[vault];
    if (timestamp != 0 && block.timestamp > timestamp) {
        shares += Math.mulDiv(totalShares, $.protocolFeeD6 * (block.timestamp - timestamp), 365e6 days);
    }
}

이 공식은 가격 차이(minPriceD18_ - priceD18)totalShares와 곱하여 수수료 주식 수를 계산하고 있다. 이 공식 자체가 잘못되었다.

예를 들어 다음과 같은 상황이라고 가정하자.

  • 기존 최소 가격 minPriceD18 = 100e18, 새로운 가격 priceD18 = 90e18, 수수료 비율 performanceFee = 1e5(10%) 라고 하자.
  • feeShare = 10e18 * 1e5 * totalShare / 1e24 = totalShare 이다.
  • 즉, 수수료 share가 total share 와 같게 된다.

Impact

수수료가 잘못 계산된다.

Mitigation

가격 변화로 인한 share 를 계산하기 위해서는 분모에 priceD18 를 포함시켜야 한다. 가격 변화량 * (수수료 % / 1e6) * 전체 share / 가격 으로 계산해야 한다.

-  shares = Math.mulDiv(minPriceD18_ - priceD18, $.performanceFeeD6 * totalShares, 1e24);
+  shares = Math.mulDiv(minPriceD18_ - priceD18, $.performanceFeeD6 * totalShares, 1e6 * priceD18);

Memo

가격을 1e18로 잘못 가정한 것이 문제인 것 같다. (기존 1e24로 나눔)


tags: bughunting, mellow, smart contract, solidity, severity high, arithmetic error, logic flaw, fee