Summary
external/
└── market-making/
└── branches/
└── VaultRouterBranch.sol
Vulnerability details
**1. Front-Running in deposit()
and redeem()
**
Description:
The deposit
and redeem
functions are susceptible to front-running attacks, where an attacker could manipulate the price or state before the transaction is confirmed.
Code Affected
deposit
, redeem
.
Full redeem() function
function redeem(uint128 vaultId, uint128 withdrawalRequestId, uint256 minAssets) external {
Vault.Data storage vault = Vault.loadLive(vaultId);
WithdrawalRequest.Data storage withdrawalRequest =
WithdrawalRequest.loadExisting(vaultId, msg.sender, withdrawalRequestId);
if (withdrawalRequest.fulfilled) revert Errors.WithdrawalRequestAlreadyFulfilled();
if (withdrawalRequest.timestamp + vault.withdrawalDelay > block.timestamp) {
revert Errors.WithdrawDelayNotPassed();
}
uint256[] memory vaultsIds = new uint256[](1);
vaultsIds[0] = uint256(vaultId);
Vault.recalculateVaultsCreditCapacity(vaultsIds);
RedeemContext memory ctx;
ctx.shares = withdrawalRequest.shares;
ctx.expectedAssetsX18 = getIndexTokenSwapRate(vaultId, ctx.shares, false);
MarketMakingEngineConfiguration.Data storage marketMakingEngineConfiguration =
MarketMakingEngineConfiguration.load();
ctx.redeemFee = vault.redeemFee;
ctx.expectedAssetsMinusRedeemFeeX18 =
ctx.expectedAssetsX18.sub(ctx.expectedAssetsX18.mul(ud60x18(ctx.redeemFee)));
ctx.sharesMinusRedeemFeesX18 =
getVaultAssetSwapRate(vaultId, ctx.expectedAssetsMinusRedeemFeeX18.intoUint256(), false);
ctx.sharesFees = ctx.shares - ctx.sharesMinusRedeemFeesX18.intoUint256();
ctx.creditCapacityBeforeRedeemUsdX18 = vault.getTotalCreditCapacityUsd();
ctx.lockedCreditCapacityBeforeRedeemUsdX18 = vault.getLockedCreditCapacityUsd();
address indexToken = vault.indexToken;
uint256 assets =
IERC4626(indexToken).redeem(ctx.sharesMinusRedeemFeesX18.intoUint256(), msg.sender, address(this));
if (ctx.sharesFees > 0) {
IERC4626(indexToken).redeem(
ctx.sharesFees, marketMakingEngineConfiguration.vaultDepositAndRedeemFeeRecipient, address(this)
);
}
if (assets < minAssets) revert Errors.SlippageCheckFailed(minAssets, assets);
if (assets == 0) revert Errors.RedeemMustReceiveAssets();
if (
ctx.creditCapacityBeforeRedeemUsdX18.sub(vault.getTotalCreditCapacityUsd()).lte(
ctx.lockedCreditCapacityBeforeRedeemUsdX18.intoSD59x18()
)
) {
revert Errors.NotEnoughUnlockedCreditCapacity();
}
withdrawalRequest.fulfilled = true;
emit LogRedeem(vaultId, msg.sender, ctx.sharesMinusRedeemFeesX18.intoUint256());
}
Most vulnerable part > redeem() function
ctx.creditCapacityBeforeRedeemUsdX18 = vault.getTotalCreditCapacityUsd();
ctx.lockedCreditCapacityBeforeRedeemUsdX18 = vault.getLockedCreditCapacityUsd();
address indexToken = vault.indexToken;
uint256 assets =
IERC4626(indexToken).redeem(ctx.sharesMinusRedeemFeesX18.intoUint256(), msg.sender, address(this));
if (ctx.sharesFees > 0) {
IERC4626(indexToken).redeem(
ctx.sharesFees, marketMakingEngineConfiguration.vaultDepositAndRedeemFeeRecipient, address(this)
);
}`
Deposit () function
```
function deposit(
uint128 vaultId,
uint128 assets,
uint128 minShares,
bytes memory referralCode,
bool isCustomReferralCode
)
external
{
if (assets == 0) revert Errors.ZeroInput("assets");
MarketMakingEngineConfiguration.Data storage marketMakingEngineConfiguration =
MarketMakingEngineConfiguration.load();
address whitelistCache = marketMakingEngineConfiguration.whitelist;
if (whitelistCache != address(0)) {
if (!Whitelist(whitelistCache).verifyIfUserIsAllowed(msg.sender)) {
revert Errors.UserIsNotAllowed(msg.sender);
}
}
Vault.Data storage vault = Vault.loadLive(vaultId);
if (!vault.collateral.isEnabled) revert Errors.VaultDoesNotExist(vaultId);
DepositContext memory ctx;
ctx.vaultAsset = vault.collateral.asset;
uint256[] memory vaultsIds = new uint256[](1);
vaultsIds[0] = uint256(vaultId);
Vault.recalculateVaultsCreditCapacity(vaultsIds);
ctx.referralModule = IReferral(marketMakingEngineConfiguration.referralModule);
if (referralCode.length != 0) {
ctx.referralModule.registerReferral(
abi.encode(msg.sender), msg.sender, referralCode, isCustomReferralCode
);
}
ctx.vaultAssetDecimals = vault.collateral.decimals;
ctx.assetsX18 = Math.convertTokenAmountToUd60x18(ctx.vaultAssetDecimals, assets);
ctx.vaultDepositFee = ud60x18(vault.depositFee);
if (ctx.vaultDepositFee.isZero()) {
ctx.assetsMinusFees = assets;
} else {
ctx.assetFeesX18 = ctx.assetsX18.mul(ctx.vaultDepositFee);
ctx.assetFees = Math.convertUd60x18ToTokenAmount(ctx.vaultAssetDecimals, ctx.assetFeesX18);
if (ctx.assetFees == 0) revert Errors.ZeroFeeNotAllowed();
ctx.assetsMinusFees = assets - ctx.assetFees;
if (ctx.assetsMinusFees == 0) revert Errors.DepositTooSmall();
}
IERC20(ctx.vaultAsset).safeTransferFrom(msg.sender, address(this), ctx.assetsMinusFees);
if (ctx.assetFees > 0) {
IERC20(ctx.vaultAsset).safeTransferFrom(
msg.sender, marketMakingEngineConfiguration.vaultDepositAndRedeemFeeRecipient, ctx.assetFees
);
}
address indexTokenCache = vault.indexToken;
IERC20(ctx.vaultAsset).approve(indexTokenCache, ctx.assetsMinusFees);
ctx.shares = IERC4626(indexTokenCache).deposit(ctx.assetsMinusFees, msg.sender);
if (ctx.shares < minShares) revert Errors.SlippageCheckFailed(minShares, ctx.shares);
if (ctx.shares == 0) revert Errors.DepositMustReceiveShares();
emit LogDeposit(vaultId, msg.sender, ctx.assetsMinusFees);
}
Most vulnerable part > deposit() function
```
IERC20(ctx.vaultAsset).safeTransferFrom(msg.sender, address(this), ctx.assetsMinusFees);
if (ctx.assetFees > 0) {
IERC20(ctx.vaultAsset).safeTransferFrom(
msg.sender, marketMakingEngineConfiguration.vaultDepositAndRedeemFeeRecipient, ctx.assetFees
);
}
address indexTokenCache = vault.indexToken;
IERC20(ctx.vaultAsset).approve(indexTokenCache, ctx.assetsMinusFees);
ctx.shares = IERC4626(indexTokenCache).deposit(ctx.assetsMinusFees, msg.sender);
Impact
Tools Used
Recommendations
Implement commit-reveal schemes or minimum execution delays to mitigate front-running risks.
Use off-chain signatures for transactions to prevent visibility in the mempool.
Conduct MEV (Miner Extractable Value) simulations to identify exploitability.
**2. Gas Limit Issues**
Description:
Functions like deposit
, redeem
, and unstake
perform multiple state updates and external calls, which could exceed the gas limit for a single transaction.
Code Affected:
deposit() function
, redeem() function
, unstake() function
.
Impact
Tools Used
Recommendations
-
Optimize gas usage by reducing unnecessary state updates and external calls.
-
Batch multiple operations into a single transaction to reduce gas costs.
-
Cache frequently accessed storage variables to minimize redundant reads.
3 . Dependency Risks
Description
The contract relies heavily on external libraries and contracts (Vault
, Collateral
, Distribution
). If any of these dependencies are compromised, the contract could be vulnerable.
Code Affected:
All external library imports.
Impact
Tools Used
Recommendations
Use verified and audited versions of external dependencies.
Implement fallback mechanisms in case external dependencies fail.
Maintain access control measures to prevent unauthorized dependency modifications.
**4. Unitialized Varibles**
Description
The contract assumes that external dependencies (Vault
, Collateral
) are properly initialized. If not, it could lead to unexpected behavior.
Code Affected:
All functions relying on external dependencies.
Impact
Tools Used
Recommendations
Enforce explicit initialization of external dependencies.
Implement constructor-based dependency injection for safer setup.
Add validation checks to ensure dependencies are correctly set before execution.