Assessment reports>Deltaswap>High findings>Incorrect trade-volume calculation
Category: Business Logic

Incorrect trade-volume calculation

High Severity
High Impact
High Likelihood

Description

DeltaSwap is intended to charge swap fees only if the recent trade volume is greater than a configurable percentage of the recent average liquidity. The two averages are computed as exponential moving averages (EMAs), giving more weight to more recent data, based on the difference between the block number when the EMA was last updated and the current block number.

The swap function determines the fee to be applied (in basis points) with the following line of code (reformatted for readability)

fee = calcTradingFee(
    _updateLiquidityTradedEMA(
        DSMath.calcTradeLiquidity(amount0In, amount1In, _reserve0, _reserve1)
    ),
    liquidityEMA
)

The line also computes and updates the trade liquidity EMA value, which is then passed to calcTradingFee to compute the trading fee.

The problem lies in how calcTradeLiquidity determines the trade liquidity amount, since it is assuming only one of amount0In and amount1In are greater than zero:

function calcTradeLiquidity(uint256 amount0, uint256 amount1, uint256 reserve0, uint256 reserve1) internal pure returns(uint256) {
    if(amount0 > 0) {
        return calcSingleSideLiquidity(amount0, reserve0, reserve1);
    } else if(amount1 > 0) {
        return calcSingleSideLiquidity(amount1, reserve1, reserve0);
    }
    return 0;
}

function calcSingleSideLiquidity(uint256 amount, uint256 reserve0, uint256 reserve1) internal pure returns(uint256) {
    uint256 amount0 = amount / 2;
    uint256 amount1 = amount0 * reserve1 / reserve0;
    return DSMath.sqrt(amount0 * amount1);
}

Impact

A user could leverage this behavior to artificially lower the traded volume when trading token1 for token0. To do so, they can perform a swap, making sure to supply an amount0Out value that is slightly less than the maximum value that would still satisfy the K-invariant. Alternatively, they could also use the callback to transfer a small amount of token0 to the contract.

This would cause calcTradeLiquidity to consider the small amount of input token0 to calculate the trade volume, returning an artificially low trade volume and also influencing the trade EMA less than intended.

Recommendations

Consider updating function DeltaSwapPair.swap() to charge a trading fee that depends on max(amount0, amount1) instead of only one of the values being greater than zero in function DSMath.calcTradeLiquidity().

Remediation

This issue has been acknowledged by GammaSwap Protocol, and a fix was implemented in commit 07e3464c..

The calcTradeLiquidity function was changed to return the maximum trade of the two individually computed single sided trade liquidities. In addition, calcTradingFee was updated to consider the greater number between the liquidity EMA and the current trade liquidity when determining whether fees should be charged.

Zellic © 2024Back to top ↑