Setting the strategy cap to "0" does not update the total shares held or the withdrawal queue
Removing or setting the strategy cap to 0 will not decrease the shares held in the system. Additionally, it will not update the withdrawal queue, which means users can request withdrawals, and the withdrawals will exceed the allocated amount when rebalance occurs.\\nLet's go over the issue with an example:\\nAssume there is 1 strategy and 2 operators active in an LSR with total strategy shares holding is 1000 * 1e18 where both operators shares 500-500 the assets.\\nWhen the owner decides to inactivate or just simply sets one of the operators cap to "0" the operator will withdraw all its assets as follows:\\n```\\nfunction setOperatorStrategyCap(\\n        RioLRTOperatorRegistryStorageV1.StorageV1 storage s,\\n        uint8 operatorId,\\n        IRioLRTOperatorRegistry.StrategyShareCap memory newShareCap\\n    ) internal {\\n        . \\n        // @review this "if" will be executed\\n        -> if (currentShareDetails.cap > 0 && newShareCap.cap == 0) {\\n            // If the operator has allocations, queue them for exit.\\n            if (currentShareDetails.allocation > 0) {\\n                -> operatorDetails.queueOperatorStrategyExit(operatorId, newShareCap.strategy);\\n            }\\n            // Remove the operator from the utilization heap.\\n            utilizationHeap.removeByID(operatorId);\\n        } else if (currentShareDetails.cap == 0 && newShareCap.cap > 0) {\\n            // If the current cap is 0 and the new cap is greater than 0, insert the operator into the heap.\\n            utilizationHeap.insert(OperatorUtilizationHeap.Operator(operatorId, 0));\\n        } else {\\n            // Otherwise, update the operator's utilization in the heap.\\n            utilizationHeap.updateUtilizationByID(operatorId, currentShareDetails.allocation.divWad(newShareCap.cap));\\n        }\\n        .\\n    }\\n```\\n\\n```\\nfunction queueOperatorStrategyExit(IRioLRTOperatorRegistry.OperatorDetails storage operator, uint8 operatorId, address strategy) internal {\\n        .\\n        // @review asks delegator to exit\\n        -> bytes32 withdrawalRoot = delegator.queueWithdrawalForOperatorExit(strategy, sharesToExit);\\n        emit IRioLRTOperatorRegistry.OperatorStrategyExitQueued(operatorId, strategy, sharesToExit, withdrawalRoot);\\n    }\\n```\\n\\nThen the operator delegator contract calls the EigenLayer to withdraw all its balance as follows:\\n```\\nfunction _queueWithdrawalForOperatorExitOrScrape(address strategy, uint256 shares) internal returns (bytes32 root) {\\n       . // @review jumps to internal function\\n        -> root = _queueWithdrawal(strategy, shares, address(depositPool()));\\n    }\\n\\nfunction _queueWithdrawal(address strategy, uint256 shares, address withdrawer) internal returns (bytes32 root) {\\n        IDelegationManager.QueuedWithdrawalParams[] memory withdrawalParams = new IDelegationManager.QueuedWithdrawalParams[](1);\\n        withdrawalParams[0] = IDelegationManager.QueuedWithdrawalParams({\\n            strategies: strategy.toArray(),\\n            shares: shares.toArray(),\\n            withdrawer: withdrawer\\n        });\\n        // @review calls Eigen layer to queue all the balance and returns the root\\n        -> root = delegationManager.queueWithdrawals(withdrawalParams)[0];\\n    }\\n```\\n\\nWhich we can observe from the above snippet the EigenLayer is called for the withdrawal and then the entire function execution ends. The problem is `assetRegistry` still thinks there are 1000 * 1e18 EigenLayer shares in the operators. Also, the `withdrawalQueue` is not aware of this withdrawal request which means that users can call `requestWithdrawal` to withdraw up to 1000 * 1e18 EigenLayer shares worth LRT but in reality the 500 * 1e18 portion of it already queued in withdrawal by the owner of operator registry.\\nCoded PoC:\\n```\\nfunction test_SettingStrategyCapZero_WithdrawalsAreDoubleCountable() public {\\n        IRioLRTOperatorRegistry.StrategyShareCap[] memory zeroStrategyShareCaps =\\n            new IRioLRTOperatorRegistry.StrategyShareCap[](2);\\n        zeroStrategyShareCaps[0] = IRioLRTOperatorRegistry.StrategyShareCap({strategy: RETH_STRATEGY, cap: 0});\\n        zeroStrategyShareCaps[1] = IRioLRTOperatorRegistry.StrategyShareCap({strategy: CBETH_STRATEGY, cap: 0});\\n\\n        uint8 operatorId = addOperatorDelegator(reLST.operatorRegistry, address(reLST.rewardDistributor));\\n\\n        uint256 AMOUNT = 111e18;\\n\\n        // Allocate to cbETH strategy.\\n        cbETH.approve(address(reLST.coordinator), type(uint256).max);\\n        uint256 lrtAmount = reLST.coordinator.deposit(CBETH_ADDRESS, AMOUNT);\\n\\n        // Push funds into EigenLayer.\\n        vm.prank(EOA, EOA);\\n        reLST.coordinator.rebalance(CBETH_ADDRESS);\\n\\n        vm.recordLogs();\\n        reLST.operatorRegistry.setOperatorStrategyShareCaps(operatorId, zeroStrategyShareCaps);\\n\\n        Vm.Log[] memory entries = vm.getRecordedLogs();\\n        assertGt(entries.length, 0);\\n\\n        for (uint256 i = 0; i < entries.length; i++) {\\n            if (entries[i].topics[0] == keccak256('OperatorStrategyExitQueued(uint8,address,uint256,bytes32)')) {\\n                uint8 emittedOperatorId = abi.decode(abi.encodePacked(entries[i].topics[1]), (uint8));\\n                (address strategy, uint256 sharesToExit, bytes32 withdrawalRoot) =\\n                    abi.decode(entries[i].data, (address, uint256, bytes32));\\n\\n                assertEq(emittedOperatorId, operatorId);\\n                assertEq(strategy, CBETH_STRATEGY);\\n                assertEq(sharesToExit, AMOUNT);\\n                assertNotEq(withdrawalRoot, bytes32(0));\\n\\n                break;\\n            }\\n            if (i == entries.length - 1) fail('Event not found');\\n        }\\n\\n        // @review add these\\n        // @review all the eigen layer shares are already queued as we checked above, now user requestWithdrawal\\n        // of the same amount of EigenLayer share worth of LRT which there will be double counting when epoch is settled.\\n        uint256 queuedShares = reLST.coordinator.requestWithdrawal(address(cbETH), lrtAmount);\\n        console.log("Queued shares", queuedShares);\\n    }\\n```\\n
Update the withdrawal queue when the operator registry admin changes the EigenLayer shares amount by either removing an operator or setting its strategy cap to "0".
High, because the users withdrawals will never go through in rebalancing because of double counting of the same share withdrawals.
```\\nfunction setOperatorStrategyCap(\\n        RioLRTOperatorRegistryStorageV1.StorageV1 storage s,\\n        uint8 operatorId,\\n        IRioLRTOperatorRegistry.StrategyShareCap memory newShareCap\\n    ) internal {\\n        . \\n        // @review this "if" will be executed\\n        -> if (currentShareDetails.cap > 0 && newShareCap.cap == 0) {\\n            // If the operator has allocations, queue them for exit.\\n            if (currentShareDetails.allocation > 0) {\\n                -> operatorDetails.queueOperatorStrategyExit(operatorId, newShareCap.strategy);\\n            }\\n            // Remove the operator from the utilization heap.\\n            utilizationHeap.removeByID(operatorId);\\n        } else if (currentShareDetails.cap == 0 && newShareCap.cap > 0) {\\n            // If the current cap is 0 and the new cap is greater than 0, insert the operator into the heap.\\n            utilizationHeap.insert(OperatorUtilizationHeap.Operator(operatorId, 0));\\n        } else {\\n            // Otherwise, update the operator's utilization in the heap.\\n            utilizationHeap.updateUtilizationByID(operatorId, currentShareDetails.allocation.divWad(newShareCap.cap));\\n        }\\n        .\\n    }\\n```\\n