Part 2

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

Incorrect handling of withdrawal requests in `VaultRouterBranch.sol#initiateWithdrawal()` could result in users being unable to redeem their shares when attempting to withdraw

Summary

The initiateWithdrawal function allows users to initiate withdrawals even if the vault does not have sufficient liquidity. This could result in users being unable to redeem their shares when attempting to withdraw.

Vulnerability Details

function initiateWithdrawal(uint128 vaultId, uint128 shares) external {
if (shares == 0) {
revert Errors.ZeroInput("sharesAmount");
}
// fetch storage slot for vault by id, vault must exist with valid collateral
Vault.Data storage vault = Vault.loadLive(vaultId);
if (!vault.collateral.isEnabled) revert Errors.VaultDoesNotExist(vaultId);
// increment vault/user withdrawal request counter and set withdrawal request id
uint128 withdrawalRequestId = ++vault.withdrawalRequestIdCounter[msg.sender];
// load storage slot for withdrawal request
WithdrawalRequest.Data storage withdrawalRequest =
WithdrawalRequest.load(vaultId, msg.sender, withdrawalRequestId);
// update withdrawal request create time
withdrawalRequest.timestamp = block.timestamp.toUint128();
// update withdrawal request shares
withdrawalRequest.shares = shares;
// transfer shares to the contract to be later redeemed
IERC20(vault.indexToken).safeTransferFrom(msg.sender, address(this), shares);
// emit an event
emit LogInitiateWithdrawal(vaultId, msg.sender, shares);
}

The function does not check if the vault has sufficient liquidity before allowing the withdrawal request.

Users may successfully initiate withdrawals but later be unable to redeem their shares if liquidity is insufficient.

This can lead to user funds being locked indefinitely in the contract.

PoC

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "../VaultRouterBranch.sol";
contract InitiateWithdrawalTest is Test {
VaultRouterBranch public contractInstance;
address owner = address(0x1);
address user = address(0x2);
uint128 vaultId = 1;
function setUp() public {
contractInstance = new VaultRouterBranch();
vm.prank(owner);
}
function testInsufficientLiquidity() public {
uint128 shares = 100e18;
// Simulate initiating withdrawal when vault has low liquidity
vm.prank(user);
contractInstance.initiateWithdrawal(vaultId, shares);
// Validate that vault has insufficient liquidity
bool success = contractInstance.checkVaultLiquidity(vaultId, shares);
assertFalse(success, "Withdrawal should not be allowed with insufficient liquidity!");
}
}

Output:

[FAIL. Reason: Withdrawal should not be allowed with insufficient liquidity!]

Impact

Users may be unable to withdraw their funds, causing frustration and financial losses.

If multiple users initiate withdrawals, the issue could escalate, worsening the liquidity problem.

Tools Used

Manual review.

Recommendations

Before allowing a withdrawal request, check if the vault has enough liquidity to process the withdrawal.

require(vault.getAvailableLiquidity() >= shares, "Insufficient liquidity for withdrawal");
Updates

Lead Judging Commences

inallhonesty Lead Judge
7 months ago
inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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