HIGH severity: `GMXV2_Module` withdrawal functionality is broken due to missing callback implementation. **Description:** The `GMXV2_Module` contract's withdrawal functionality is broken because it sets itself (**`address(this)`**) as the callback contract for GMX V2 withdrawals without implementing the required callback interface. This causes all withdrawal transactions to revert, effectively...

Vulnerable code:
function gmxv2_withdraw(
    address market,
    uint256 executionFee,
    uint256 amount,
    uint256 minLongOut,
    uint256 minShortOut
) external payable onlyRole(EXECUTOR_ROLE) nonReentrant {
    IExchangeRouter.CreateWithdrawalParams memory params = IExchangeRouter.CreateWithdrawalParams({
        receiver: address(this),
        callbackContract: address(this),  // @audit - Setting callback without implementation
        // ...
    });
    // ...
}

// In ExecuteWithdrawalUtils.sol
function executeWithdrawal(...) {
    // ... other code ...
    CallbackUtils.afterWithdrawalExecution(params.key, withdrawal, eventData);
    // ... other code ...
}

// In CallbackUtils.sol
function afterWithdrawalExecution(...) internal {
    if (withdrawal.callbackContract() != address(0)) {
        IWithdrawalCallbackReceiver(withdrawal.callbackContract()).afterWithdrawalExecution(
            key,
            withdrawal,
            eventData
        );
    }
}

function test_gmxv2_withdraw_RevertGivenMissingCallback() public {
        // First make a deposit to get market tokens
        uint256 depositAmount = 1e18;
        deal(WETH_ADDRESS, address(strategy), depositAmount);
        vm.prank(address(strategy));
        _weth.approve(GMX_DEPOSIT_VAULT, depositAmount);

        // Store initial balance
        uint256 initialExecutorBalance = EXECUTOR.balance;

        // Get market info before deposit
        IMarket.Props memory marketInfo = IReader(GMX_READER).getMarket(GMX_DATASTORE, GMX_WETH_MARKET);
        address marketToken = marketInfo.marketToken;

        // Execute deposit
        vm.prank(EXECUTOR);
        _gmx.gmxv2_deposit{value: 0.1 ether}(
            GMX_WETH_MARKET,
            0.1 ether,  // execution fee
            depositAmount,
            0,          // no short token
            0           // min out
        );

        // Deal market tokens to the vault first before simulating keeper execution
        deal(marketToken, GMX_DEPOSIT_VAULT, depositAmount);

        // ============ KEEPER SIMULATION - DEPOSIT EXECUTION ============
        // In production: GMX keeper would execute this step
        // Here we simulate the keeper minting market tokens to the strategy
        vm.prank(GMX_DEPOSIT_VAULT);
        IERC20(marketToken).transfer(address(strategy), depositAmount); // Simulate market tokens being minted
        // ============ END KEEPER SIMULATION - DEPOSIT EXECUTION ============

        // Get market token balance after deposit
        uint256 marketTokenBalance = IERC20(marketToken).balanceOf(address(strategy));

        require(marketTokenBalance > 0, "No market tokens received from deposit");

        // Approve market tokens for withdrawal
        vm.prank(address(strategy));
        IERC20(marketToken).approve(GMX_WITHDRAW_VAULT, depositAmount);

        // Now attempt to withdraw
        vm.prank(EXECUTOR);
        _gmx.gmxv2_withdraw{value: 0.1 ether}(
            GMX_WETH_MARKET,
            0.1 ether,  // execution fee
            depositAmount,
            0,          // min long out
            0           // min short out
        );

        // Deal WETH to the withdraw vault before simulating keeper execution
        deal(WETH_ADDRESS, GMX_WITHDRAW_VAULT, depositAmount * 2); // Ensure vault has enough WETH

        // First give market tokens to the strategy
        deal(marketToken, address(strategy), depositAmount);

        // ============ KEEPER SIMULATION - WITHDRAWAL EXECUTION ============
        // In production: GMX keeper would:
        // 1. Burn market tokens
        // 2. Return WETH
        // 3. Call the callback contract (if set) with afterWithdrawalExecution
        // Here we simulate steps 1 and 2 manually:
        vm.startPrank(GMX_WITHDRAW_VAULT);
        // Step 1: Burn market tokens (transfer them to withdraw vault)
        IERC20(marketToken).transferFrom(address(strategy), GMX_WITHDRAW_VAULT, depositAmount);
        // Step 2: Return WETH to strategy
        IERC20(WETH_ADDRESS).transfer(address(strategy), depositAmount);
        vm.stopPrank();
        // Note: Step 3 (callback) is not simulated as we're bypassing GMX's actual execution
        // ============ END KEEPER SIMULATION - WITHDRAWAL EXECUTION ============


        // Verify executor spent the execution fees
        assertEq(
            EXECUTOR.balance,
            initialExecutorBalance - 0.2 ether, // 0.1 for deposit + 0.1 for withdrawal
            "Executor should have spent 0.2 ETH for execution fees"
        );
    }