Summary
In the settleVaultsDebt
function, the vault in the debt state was assessed incorrectly.
Vulnerability Details
function settleVaultsDebt(uint256[] calldata vaultsIds) external onlyRegisteredSystemKeepers {
Vault.recalculateVaultsCreditCapacity(vaultsIds);
SettleVaultDebtContext memory ctx;
ctx.usdc = MarketMakingEngineConfiguration.load().usdc;
Collateral.Data storage usdcCollateralConfig = Collateral.load(ctx.usdc);
for (uint256 i; i < vaultsIds.length; i++) {
Vault.Data storage vault = Vault.loadExisting(vaultsIds[i].toUint128());
ctx.vaultUnsettledRealizedDebtUsdX18 = vault.getUnsettledRealizedDebt();
if (ctx.vaultUnsettledRealizedDebtUsdX18.isZero()) continue;
ctx.vaultAsset = vault.collateral.asset;
DexSwapStrategy.Data storage dexSwapStrategy =
DexSwapStrategy.loadExisting(vault.swapStrategy.assetDexSwapStrategyId);
436: if (ctx.vaultUnsettledRealizedDebtUsdX18.lt(SD59x18_ZERO)) {
ctx.swapAmount = calculateSwapAmount(
dexSwapStrategy.dexAdapter,
ctx.usdc,
ctx.vaultAsset,
usdcCollateralConfig.convertSd59x18ToTokenAmount(ctx.vaultUnsettledRealizedDebtUsdX18.abs())
);
ctx.usdcOut = _convertAssetsToUsdc(
vault.swapStrategy.usdcDexSwapStrategyId,
ctx.vaultAsset,
ctx.swapAmount,
vault.swapStrategy.usdcDexSwapPath,
address(this),
ctx.usdc
);
if (ctx.usdcOut == 0) revert Errors.ZeroOutputTokens();
ctx.usdcOutX18 = usdcCollateralConfig.convertTokenAmountToUd60x18(ctx.usdcOut);
466: vault.marketsRealizedDebtUsd -= ctx.usdcOutX18.intoUint256().toInt256().toInt128();
...
} ...
}
function getUnsettledRealizedDebt(Data storage self)
internal
view
returns (SD59x18 unsettledRealizedDebtUsdX18)
{
244: unsettledRealizedDebtUsdX18 =
sd59x18(self.marketsRealizedDebtUsd).add(unary(ud60x18(self.depositedUsdc).intoSD59x18()));
}
As we can see, in the settleVaultsDebt
function, if vaultUnsettledRealizedDebtUsdX18 < 0
, the vault's assets is swaped to usdc.
At this time, vault.marketsRealizedDebtUsd
is decreased by the amount of the swap.
Therefore, vaultUnsettledRealizedDebtUsd
is more decreased.
The opposite case works similarly.
Impact
Markets don't work as intended.
When the vault is in a credit state, the keeper's calls will result in the exchange of all the vault's assets.
The usdToken owners could not withdraw their assets, due to insufficient assets in the vault.
Recommendations
ctx.vaultUnsettledRealizedDebtUsdX18 = vault.getUnsettledRealizedDebt();
if (ctx.vaultUnsettledRealizedDebtUsdX18.isZero()) continue;
...
// if the vault is in debt, swap its assets to USDC
-436: if (ctx.vaultUnsettledRealizedDebtUsdX18.lt(SD59x18_ZERO)) {
+436: if (ctx.vaultUnsettledRealizedDebtUsdX18.gt(SD59x18_ZERO)) {