DeFiFoundry
60,000 USDC
View results
Submission Details
Severity: medium
Invalid

No incentive for the user to bring skew back to 0

Summary

No incentive for the user to bring skew back to 0

Vulnerability Details

Whenever a position is created, the trader has to pay an order fee. This is how it gets calculated:

function getOrderFeeUsd(
Data storage self,
SD59x18 sizeDelta,
UD60x18 markPriceX18
)
internal
view
returns (UD60x18 orderFeeUsd)
{
// isSkewGtZero = true, isBuyOrder = true, skewIsZero = false -> taker
// isSkewGtZero = true, isBuyOrder = false, skewIsZero = false -> maker
// isSkewGtZero = false, isBuyOrder = true, skewIsZero = true -> taker
// isSkewGtZero = false, isBuyOrder = false, skewIsZero = true -> taker
// isSkewGtZero = false, isBuyOrder = true, skewIsZero = false -> maker
// get current skew int128 -> SD59x18
SD59x18 skew = sd59x18(self.skew);
// is current skew > 0 ?
bool isSkewGtZero = skew.gt(SD59x18_ZERO);
// is this order a buy/long ?
bool isBuyOrder = sizeDelta.gt(SD59x18_ZERO);
// apply new order's skew to current skew
SD59x18 newSkew = skew.add(sizeDelta);
// does the new order result in the skew remaining on the same side?
// true if:
// the new skew is 0 OR
// original skew is 0 OR
// new skew > 0 == skew > 0 (new and old skew on the same side)
bool sameSide =
newSkew.eq(SD59x18_ZERO) || skew.eq(SD59x18_ZERO) || newSkew.gt(SD59x18_ZERO) == skew.gt(SD59x18_ZERO);
if (sameSide) {
// charge (typically) lower maker fee when:
// current skew > 0 AND order is sell/short AND current skew != 0
// current skew < 0 AND order is buy/long AND current skew != 0
UD60x18 feeBps = isSkewGtZero != isBuyOrder && !skew.isZero()
? ud60x18(self.configuration.orderFees.makerFee)
: ud60x18(self.configuration.orderFees.takerFee);
// output order fee
orderFeeUsd = markPriceX18.mul(sizeDelta.abs().intoUD60x18()).mul(feeBps);
}
// special logic for trades that flip the skew; trader should receive:
// makerFee for portion of size that returns skew to 0
// takerFee for portion of size that flips skew in other direction
else {
// convert new skew abs(SD59x18) -> uint256
uint256 takerSize = newSkew.abs().intoUint256();
// abs( abs(orderSize) - abs(newSkew) ) -> uint256
uint256 makerSize = sizeDelta.abs().sub(sd59x18(int256(takerSize))).abs().intoUint256();
// calculate corresponding fees for maker and taker portions
// of this trade which flipped the skew
UD60x18 takerFee =
markPriceX18.mul(ud60x18(takerSize)).mul(ud60x18(self.configuration.orderFees.takerFee));
UD60x18 makerFee =
markPriceX18.mul(ud60x18(makerSize)).mul(ud60x18(self.configuration.orderFees.makerFee));
// output order fee
orderFeeUsd = takerFee.add(makerFee);
}
}

Let's say the current skew is 10. The user wants to create a short position with a size of 9. Thus, the skew will change to 1. He will end up on the sameSide block of the if statement and his fee will be calculated as 9 * makerFee (ignoring decimals for simplicity purposes). If the user is thinking whether to help the protocol by bringing the skew to 0 instead even though he only wants a position of 9, he will consider whether he is in some way incentivized to make his decision. He will see that instead of being incentivized, his fee will actually be bigger and will be 10 x makerFee instead. Thus, he will choose to bring it to 1 as he is not incentivized to bring it to 0 and 9 is the size of the position he actually wants.

Impact

No incentive for the user to bring skew back to 0

Tools Used

Manual Review

Recommendations

If the user brings skew to 0, do not charge him any fees to incentivize users to do so

Updates

Lead Judging Commences

inallhonesty Lead Judge
10 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.