DeFiFoundrySolidity
16,653 OP
View results
Submission Details
Severity: medium
Invalid

Funds Loss During withdraw() Call Due to Insufficient Available Funds and Race Condition

Summary

When a user attempts to withdraw funds via tokenizedstrategy.withdraw(), the method internally calls _freeFunds() to free the necessary unexchanged assets from the transmuter. However, if insufficient funds are available at the time of the withdrawal, _freeFunds() transfers the available amount only, resulting in partial loss of funds and burning of all user shares

Vulnerability Details

1. A user queries StrategyMainnet.availableWithdrawLimit to determine the available withdrawal limit.

2. The user sends a withdraw() request, assuming the limit is sufficient.

https://github.com/yearn/tokenized-strategy/blob/9ef68041bd034353d39941e487499d111c3d3901/src/TokenizedStrategy.sol#L547

3. Race Condition: A malicious actor or other users front-run the request and withdraw the available funds first.

4. By the time the initial user’s transaction is processed:

  • The available funds are insufficient.

  • _freeFunds() transfers only the remaining amount, if any.

  • The user’s shares are burned regardless of the incomplete withdrawal.

https://github.com/Cyfrin/2024-12-alchemix/blob/82798f4891e41959eef866bd1d4cb44fc1e26439/src/StrategyMainnet.sol#L136-L143

5. The user ends up losing their funds entirely.

This happens everytime that multiple users simultaneously call withdraw() with amounts that collectively exceed the available unexchanged balance.

Impact

Users can lose all their funds if the available balance is insufficient during withdrawal

Tools Used

vscode

Recommendations

Try one of these:

1- If partial funds are transferred due to insufficient availability, burn shares proportional to the amount actually received.

2- in _withdraw() function, revert when there is less available assets for withdraw:

require(assets - idle <= transmuter.getUnexchangedBalance(address(this)), "Insufficient funds available for withdrawal.");

3- In the _freeFunds() function, remove the logic that caps the funds to totalAvailable and instead send the full _amount to the transmuter. If there are insufficient funds, the transaction will automatically revert.

function _freeFunds(uint256 _amount) internal override {
// Directly request the full amount from the transmuter.
// If the amount exceeds the available balance, the call will revert.
transmuter.withdraw(_amount, address(this));
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
8 months ago

Appeal created

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

Support

FAQs

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