Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: high
Invalid

Incorrect calculation of `RealizedDebtUsd`

Summary

The calculation of RealizedDebtUsd in marketMakingEngin is incorrect.

Vulnerability Details

function convertMarketsCreditDepositsToUsdc(
...
)
external
onlyRegisteredSystemKeepers
{
...
for (uint256 i; i < assets.length; i++) {
// revert if the market hasn't received any fees for the given asset
(bool exists, uint256 creditDeposits) = market.creditDeposits.tryGet(assets[i]);
if (!exists) revert Errors.MarketDoesNotContainTheAsset(assets[i]);
if (creditDeposits == 0) revert Errors.AssetAmountIsZero(assets[i]);
// cache usdc address
address usdc = MarketMakingEngineConfiguration.load().usdc;
// creditDeposits in zaros internal precision so convert to native token decimals
ctx.creditDepositsNativeDecimals =
Collateral.load(assets[i]).convertUd60x18ToTokenAmount(ud60x18(creditDeposits));
// convert the assets to USDC; both input and outputs in native token decimals
uint256 usdcOut = _convertAssetsToUsdc(
dexSwapStrategyIds[i], assets[i], ctx.creditDepositsNativeDecimals, paths[i], address(this), usdc
);
// sanity check to ensure we didn't somehow give away the input tokens
if (usdcOut == 0) revert Errors.ZeroOutputTokens();
// settles the credit deposit for the amount of USDC received
// updating storage so convert from native token decimals to zaros internal precision
373: market.settleCreditDeposit(assets[i], Collateral.load(usdc).convertTokenAmountToUd60x18(usdcOut));
// emit an event
emit LogConvertMarketCreditDepositsToUsdc(marketId, assets[i], creditDeposits, usdcOut);
}
}
443:function settleCreditDeposit(Data storage self, address settledAsset, UD60x18 netUsdcReceivedX18) internal {
// removes the credit deposit asset that has just been settled for usdc
self.creditDeposits.remove(settledAsset);
// calculate the usdc that has been accumulated per usd of credit delegated to the market
UD60x18 addedUsdcPerCreditShareX18 = netUsdcReceivedX18.div(ud60x18(self.totalDelegatedCreditUsd));
// add the usdc acquired to the accumulated usdc credit variable
self.usdcCreditPerVaultShare =
ud60x18(self.usdcCreditPerVaultShare).add(addedUsdcPerCreditShareX18).intoUint128();
// deduct the amount of usdc credit from the realized debt per vault share, so we don't double count it
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) {
// prepare the credit deposits usd value variable;
UD60x18 creditDepositsValueUsdX18;
// if the credit deposits usd value cache is up to date, return the stored value
if (block.timestamp <= self.lastCreditDepositsValueRehydration) {
creditDepositsValueUsdX18 = ud60x18(self.creditDepositsValueCacheUsd);
} else {
// otherwise, we'll need to loop over credit deposits to calculate it
creditDepositsValueUsdX18 = getCreditDepositsValueUsd(self);
}
// finally after determining the market's latest credit deposits usd value, sum it with the stored net usd
// token issuance to return the net realized debt usd value
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.

Updates

Lead Judging Commences

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

Support

FAQs

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