Summary
The vault's current state is not updated before price adjustments hence the present state of the contract is not track and the first user will successfully bypass the price/margin adjustment.
Vulnerability Details
Lack of an update call to update the state of the marketed before statement adjusts the price
if (ctx.pnlUsdX18.gt(SD59x18_ZERO)) {
IMarketMakingEngine marketMakingEngine = IMarketMakingEngine(perpsEngineConfiguration.marketMakingEngine);
@audit>> ctx.marginToAddX18 =
@audit>> marketMakingEngine.getAdjustedProfitForMarketId(marketId, ctx.pnlUsdX18.intoUD60x18().intoUint256());
tradingAccount.deposit(perpsEngineConfiguration.usdToken, ctx.marginToAddX18);
marketMakingEngine.withdrawUsdTokenFromMarket(marketId, ctx.marginToAddX18.intoUint256());
}
This update cannot be done in price adjustment because it is a view function
function getAdjustedProfitForMarketId(
uint128 marketId,
uint256 profitUsd
)
public
view
returns (UD60x18 adjustedProfitUsdX18)
{
Market.Data storage market = Market.loadLive(marketId);
@audit >>. SD59x18 marketTotalDebtUsdX18 = market.getTotalDebt();
@audit >>. UD60x18 delegatedCreditUsdX18 = market.getTotalDelegatedCreditUsd();
@audit>> SD59x18 creditCapacityUsdX18 = Market.getCreditCapacityUsd(delegatedCreditUsdX18, marketTotalDebtUsdX18);
@audit>> if (creditCapacityUsdX18.lte(SD59x18_ZERO)) {
revert Errors.InsufficientCreditCapacity(marketId, creditCapacityUsdX18.intoInt256());
}
adjustedProfitUsdX18 = ud60x18(profitUsd);
@audit>> bypassed>>> if (market.isAutoDeleverageTriggered(delegatedCreditUsdX18, marketTotalDebtUsdX18)) {
adjustedProfitUsdX18 =
market.getAutoDeleverageFactor(delegatedCreditUsdX18, marketTotalDebtUsdX18).mul(adjustedProfitUsdX18);
}
}
Diving into the getcreditcapacityusd call
@audit>>>. /
@audit>>>
audit>>> function getCreditCapacityForMarketId(uint128 marketId) public view returns (SD59x18) {
Market.Data storage market = Market.loadExisting(marketId);
@audit>>> return Market.getCreditCapacityUsd(market.getTotalDelegatedCreditUsd(), market.getTotalDebt());
}
Missing check
@audit>>>
function updateMarketCreditDelegations(uint128 marketId) public {
Vault.recalculateVaultsCreditCapacity(Market.loadLive(marketId).getConnectedVaultsIds());
}
Impact
Price adjustment can be bypassed and the system is placed in a more imbalanced state.
Tools Used
Manual review
Recommendations
call the update market credit delegation before adjusting all deposits.
if (ctx.pnlUsdX18.gt(SD59x18_ZERO)) {
IMarketMakingEngine marketMakingEngine = IMarketMakingEngine(perpsEngineConfiguration.marketMakingEngine);
++ marketMakingEngine.updateMarketCreditDelegations(marketId);
ctx.marginToAddX18 =
marketMakingEngine.getAdjustedProfitForMarketId(marketId, ctx.pnlUsdX18.intoUD60x18().intoUint256());
tradingAccount.deposit(perpsEngineConfiguration.usdToken, ctx.marginToAddX18);
marketMakingEngine.withdrawUsdTokenFromMarket(marketId, ctx.marginToAddX18.intoUint256());
}