Summary
The calculation of RealizedDebtUsd
in marketMakingEngin is incorrect.
Vulnerability Details
function convertMarketsCreditDepositsToUsdc(
...
)
external
onlyRegisteredSystemKeepers
{
...
for (uint256 i; i < assets.length; i++) {
(bool exists, uint256 creditDeposits) = market.creditDeposits.tryGet(assets[i]);
if (!exists) revert Errors.MarketDoesNotContainTheAsset(assets[i]);
if (creditDeposits == 0) revert Errors.AssetAmountIsZero(assets[i]);
address usdc = MarketMakingEngineConfiguration.load().usdc;
ctx.creditDepositsNativeDecimals =
Collateral.load(assets[i]).convertUd60x18ToTokenAmount(ud60x18(creditDeposits));
uint256 usdcOut = _convertAssetsToUsdc(
dexSwapStrategyIds[i], assets[i], ctx.creditDepositsNativeDecimals, paths[i], address(this), usdc
);
if (usdcOut == 0) revert Errors.ZeroOutputTokens();
373: market.settleCreditDeposit(assets[i], Collateral.load(usdc).convertTokenAmountToUd60x18(usdcOut));
emit LogConvertMarketCreditDepositsToUsdc(marketId, assets[i], creditDeposits, usdcOut);
}
}
443:function settleCreditDeposit(Data storage self, address settledAsset, UD60x18 netUsdcReceivedX18) internal {
self.creditDeposits.remove(settledAsset);
UD60x18 addedUsdcPerCreditShareX18 = netUsdcReceivedX18.div(ud60x18(self.totalDelegatedCreditUsd));
self.usdcCreditPerVaultShare =
ud60x18(self.usdcCreditPerVaultShare).add(addedUsdcPerCreditShareX18).intoUint128();
self.realizedDebtUsdPerVaultShare = sd59x18(self.realizedDebtUsdPerVaultShare).sub(
addedUsdcPerCreditShareX18.intoSD59x18()
).intoInt256().toInt128();
}
As we can see, creditDeposits
are swapped and settled by the keeper.
At this time, only self.usdcCreditPerVaultShare
and self.realizedDebtUsdPerVaultShare
are changed.
These values pertain to the zlpVault
and are not used to calculate the debt or credit in this market.
234:function getRealizedDebtUsd(Data storage self) internal view returns (SD59x18 realizedDebtUsdX18) {
UD60x18 creditDepositsValueUsdX18;
if (block.timestamp <= self.lastCreditDepositsValueRehydration) {
creditDepositsValueUsdX18 = ud60x18(self.creditDepositsValueCacheUsd);
} else {
creditDepositsValueUsdX18 = getCreditDepositsValueUsd(self);
}
realizedDebtUsdX18 = creditDepositsValueUsdX18.intoSD59x18().add(sd59x18(self.netUsdTokenIssuance));
}
As a result, RealizedDebtUsd
is changed after keeper call convertMarketsCreditDepositsToUsdc
function.
Impact
Markets don't work as intended.
Incorrect accounting results in losses for providers or traders.
Recommendations
Consider memorizing the settled and unsettled amounts in the creditDeposits
asset.