Loss protection reduces the -100% cap on losses
Description
When a trade is closed, _currentPercentProfit
is called to get the percent profit:
function _currentPercentProfit(
uint openPrice,
uint currentPrice,
bool buy,
uint leverage
) private pure returns (int p) {
int diff = buy ? (int(currentPrice) - int(openPrice))
: (int(openPrice) - int(currentPrice));
int minPnlP = int(_PRECISION) * (-100);
int maxPnlP = int(_MAX_GAIN_P) * int(_PRECISION);
p = (diff * 100 * int(_PRECISION.mul(leverage))) / int(openPrice);
p = p < minPnlP ? minPnlP : p > maxPnlP ? maxPnlP : p;
}
Note that the minimum this can return is -100%. Then, the return value is passed as _percentProfit
to _unregisterTrade
, which calls PairInfos.getTradeValue
, which fetches the actual loss-protection percentage and then calls getTradeValuePure
:
function getTradeValuePure(
uint collateral,
int percentProfit,
uint rolloverFee,
uint closingFee,
uint lossProtection
) public pure returns (uint, int, uint) {
int pnl = (int(collateral) * percentProfit) / int(_PRECISION) / 100;
if (pnl < 0) {
pnl = (pnl * int(lossProtection)) / 100;
}
int fees = int(rolloverFee) + int(closingFee);
int value = int(collateral) + pnl - fees;
if (value <= (int(collateral) * int(100 - _LIQ_THRESHOLD_P)) / 100) {
value = 0;
}
return (value > 0 ? uint(value) : 0, pnl, uint(fees));
}
So, the loss protection is applied after the -100% minimum.
Impact
A trade with a loss-protection tier has a cap on the amount of loss that is greater than complete loss. This means that the trader will always get back an amount, no matter how much their position loses.
Recommendations
Instead of constraining the loss to -100% in _currentPercentProfit
, consider handling the case of unlimited losses in a higher-level function so that it is aware of the loss-protection tier.
Remediation
This issue has been acknowledged by Avantis Labs, Inc., and fixes were implemented in the following commits: