Part 2

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

Reentrancy in convertMarketsCreditDepositsToUsdc

Summary

The convertMarketsCreditDepositsToUsdc function is vulnerable to reentrancy attacks due to external DEX interactions occurring before state updates.

Vulnerability Details

The convertMarketsCreditDepositsToUsdc function in the CreditDelegationBranch contract interacts with external DEX adapters to convert market credit deposits into USDC. This interaction occurs before the function updates the state with market.settleCreditDeposit. This sequence violates the Checks-Effects-Interactions pattern, a critical security practice designed to prevent reentrancy attacks. An attacker could exploit this by re-entering the function during the external call, potentially manipulating the state or repeatedly converting the same assets before the state is properly updated.

Impact

I'm rating this as LOW because the onlyRegisteredSystemKeepers is already in place, but the function doesn't fully address the possibility of a reentrant call. Reentrancy also depends on the security of the external DEX adapters and the ability of an attacker to craft a reentrant call. If the DEX adapters are secure and don't allow reentrant calls, the risk is reduced but not eliminated.

Tools Used

Recommendations

Adhere to the Checks-Effects-Interactions pattern. Update the market state before performing any external calls. Alternatively, apply a nonReentrant modifier to the function to block reentrant calls.

function convertMarketsCreditDepositsToUsdc(
uint128 marketId,
address[] calldata assets,
uint128[] calldata dexSwapStrategyIds,
bytes[] calldata paths
)
external
onlyRegisteredSystemKeepers
nonReentrant // Apply nonReentrant modifier
{
if (assets.length != dexSwapStrategyIds.length || assets.length != paths.length) {
revert Errors.ArrayLengthMismatch(0, 0);
}
Market.Data storage market = Market.loadExisting(marketId);
ConvertMarketsCreditDepositsToUsdcContext memory ctx;
address usdc = MarketMakingEngineConfiguration.load().usdc;
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]);
ctx.creditDepositsNativeDecimals = Collateral.load(assets[i])
.convertUd60x18ToTokenAmount(ud60x18(creditDeposits));
// Update the market state immediately to settle credit deposit before external calls
market.settleCreditDeposit(
assets[i],
Collateral.load(usdc).convertTokenAmountToUd60x18(ctx.creditDepositsNativeDecimals)
);
uint256 usdcOut = _convertAssetsToUsdc(
dexSwapStrategyIds[i],
assets[i],
ctx.creditDepositsNativeDecimals,
paths[i],
address(this),
usdc
);
if (usdcOut == 0) revert Errors.ZeroOutputTokens();
// Optionally, adjust the state based on actual USDC output
market.settleCreditDeposit(
assets[i],
Collateral.load(usdc).convertTokenAmountToUd60x18(usdcOut)
);
emit LogConvertMarketCreditDepositsToUsdc(marketId, assets[i], creditDeposits, usdcOut);
}
}
Updates

Lead Judging Commences

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

Support

FAQs

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

Give us feedback!