MEDIUM severity: Inaccurate stake calculation due to decimal mismatch across multitoken asset classes. **Description:** Currently, when calculating the operator stake, the system iterates through all vaults associated with a specific asset class ID and sums all of the staked amounts. However, an asset class may consist of multiple token types (collaterals) that use different decimal precision...

Vulnerable code:
function getOperatorStake(
        address operator,
        uint48 epoch,
        uint96 assetClassId
    ) public view returns (uint256 stake) {
        if (totalStakeCached[epoch][assetClassId]) {
            uint256 cachedStake = operatorStakeCache[epoch][assetClassId][operator];

            return cachedStake;
        }

        uint48 epochStartTs = getEpochStartTs(epoch);

        uint256 totalVaults = vaultManager.getVaultCount();

        for (uint256 i; i < totalVaults; ++i) {
            (address vault, uint48 enabledTime, uint48 disabledTime) = vaultManager.getVaultAtWithTimes(i);

            // Skip if vault not active in the target epoch
            if (!_wasActiveAt(enabledTime, disabledTime, epochStartTs)) {
                continue;
            }

            // Skip if vault asset not in AssetClassID
            if (vaultManager.getVaultAssetClass(vault) != assetClassId) {
                continue;
            }

            uint256 vaultStake = BaseDelegator(IVaultTokenized(vault).delegator()).stakeAt(
                L1_VALIDATOR_MANAGER, assetClassId, operator, epochStartTs, new bytes(0)
            );

            stake += vaultStake;
        }

function test_operatorStakeWithoutNormalization() public {
        uint48 epoch = 1;
        uint256 uptime = 4 hours;

        // Deploy tokens with different decimals
        ERC20WithDecimals tokenA = new ERC20WithDecimals("TokenA", "TKA", 6);  // e.g., USDC
        ERC20WithDecimals tokenB = new ERC20WithDecimals("TokenB", "TKB", 18); // e.g., DAI

        // Deploy vaults and associate with asset class 1
        vm.startPrank(validatorManagerAddress);
        address vaultAddress1 = vaultFactory.create(
            1,
            bob,
            abi.encode(
                IVaultTokenized.InitParams({
                    collateral: address(tokenA),
                    burner: address(0xdEaD),
                    epochDuration: 8 hours,
                    depositWhitelist: false,
                    isDepositLimit: false,
                    depositLimit: 0,
                    defaultAdminRoleHolder: bob,
                    depositWhitelistSetRoleHolder: bob,
                    depositorWhitelistRoleHolder: bob,
                    isDepositLimitSetRoleHolder: bob,
                    depositLimitSetRoleHolder: bob,
                    name: "Test",
                    symbol: "TEST"
                })
            ),
            address(delegatorFactory),
            address(slasherFactory)
        );
        address vaultAddress2 = vaultFactory.create(
            1,
            bob,
            abi.encode(
                IVaultTokenized.InitParams({
                    collateral: address(tokenB),
                    burner: address(0xdEaD),
                    epochDuration: 8 hours,
                    depositWhitelist: false,
                    isDepositLimit: false,
                    depositLimit: 0,
                    defaultAdminRoleHolder: bob,
                    depositWhitelistSetRoleHolder: bob,
                    depositorWhitelistRoleHolder: bob,
                    isDepositLimitSetRoleHolder: bob,
                    depositLimitSetRoleHolder: bob,
                    name: "Test",
                    symbol: "TEST"
                })
            ),
            address(delegatorFactory),
            address(slasherFactory)
        );
        VaultTokenized vaultTokenA = VaultTokenized(vaultAddress1);
        VaultTokenized vaultTokenB = VaultTokenized(vaultAddress2);
        vm.startPrank(validatorManagerAddress);
        middleware.addAssetClass(2, 0, 100, address(tokenA));
        middleware.activateSecondaryAssetClass(2);
        middleware.addAssetToClass(2, address(tokenB));
        vm.stopPrank();

        address[] memory l1LimitSetRoleHolders = new address[](1);
        l1LimitSetRoleHolders[0] = bob;
        address[] memory operatorL1SharesSetRoleHolders = new address[](1);
        operatorL1SharesSetRoleHolders[0] = bob;

        address delegatorAddress2 = delegatorFactory.create(
            0,
            abi.encode(
                address(vaultTokenA),
                abi.encode(
                    IL1RestakeDelegator.InitParams({
                        baseParams: IBaseDelegator.BaseParams({
                            defaultAdminRoleHolder: bob,
                            hook: address(0),
                            hookSetRoleHolder: bob
                        }),
                        l1LimitSetRoleHolders: l1LimitSetRoleHolders,
                        operatorL1SharesSetRoleHolders: operatorL1SharesSetRoleHolders
                    })
                )
            )
        );

        L1RestakeDelegator delegator2 = L1RestakeDelegator(delegatorAddress2);

        address delegatorAddress3 = delegatorFactory.create(
            0,
            abi.encode(
                address(vaultTokenB),
                abi.encode(
                    IL1RestakeDelegator.InitParams({
                        baseParams: IBaseDelegator.BaseParams({
                            defaultAdminRoleHolder: bob,
                            hook: address(0),
                            hookSetRoleHolder: bob
                        }),
                        l1LimitSetRoleHolders: l1LimitSetRoleHolders,
                        operatorL1SharesSetRoleHolders: operatorL1SharesSetRoleHolders
                    })
                )
            )
        );
        L1RestakeDelegator delegator3 = L1RestakeDelegator(delegatorAddress3);

        vm.prank(bob);
        vaultTokenA.setDelegator(delegatorAddress2);

        // Set the delegator in vault3
        vm.prank(bob);
        vaultTokenB.setDelegator(delegatorAddress3);

        _setOperatorL1Shares(bob, validatorManagerAddress, 2, alice, 100, delegator2);
        _setOperatorL1Shares(bob, validatorManagerAddress, 2, alice, 100, delegator3);

        vm.startPrank(validatorManagerAddress);
        vaultManager.registerVault(address(vaultTokenA), 2, 3000 ether);
        vaultManager.registerVault(address(vaultTokenB), 2, 3000 ether);
        vm.stopPrank();

        _optInOperatorVault(alice, address(vaultTokenA));
        _optInOperatorVault(alice, address(vaultTokenB));
        //_optInOperatorL1(alice, validatorManagerAddress);

        _setL1Limit(bob, validatorManagerAddress, 2, 10000 * 10**6, delegator2);
        _setL1Limit(bob, validatorManagerAddress, 2, 10 * 10**18, delegator3);

        // Define stakes without normalization
        uint256 stakeA = 10000 * 10**6; // 10,000 TokenA (6 decimals)
        uint256 stakeB = 10 * 10**18;   // 10 TokenB (18 decimals)

        tokenA.transfer(staker, stakeA);
        vm.startPrank(staker);
        tokenA.approve(address(vaultTokenA), stakeA);
        vaultTokenA.deposit(staker, stakeA);
        vm.stopPrank();

        tokenB.transfer(staker, stakeB);
        vm.startPrank(staker);
        tokenB.approve(address(vaultTokenB), stakeB);
        vaultTokenB.deposit(staker, stakeB);
        vm.stopPrank();

        vm.warp((epoch + 3) * middleware.EPOCH_DURATION());


        assertNotEq(middleware.getOperatorStake(alice, 2, 2), stakeA + stakeB);

    }

function getOperatorStake(
     address operator,
     uint48 epoch,
     uint96 assetClassId
 ) public view returns (uint256 stake) {
     if (totalStakeCached[epoch][assetClassId]) {
         return operatorStakeCache[epoch][assetClassId][operator];
     }

     uint48 epochStartTs = getEpochStartTs(epoch);
     uint256 totalVaults = vaultManager.getVaultCount();

     for (uint256 i; i < totalVaults; ++i) {
         (address vault, uint48 enabledTime, uint48 disabledTime) = vaultManager.getVaultAtWithTimes(i);

         // Skip if vault not active in the target epoch
         if (!_wasActiveAt(enabledTime, disabledTime, epochStartTs)) {
             continue;
         }

         // Skip if vault asset not in AssetClassID
         if (vaultManager.getVaultAssetClass(vault) != assetClassId) {
             continue;
         }

-        uint256 vaultStake = BaseDelegator(IVaultTokenized(vault).delegator()).stakeAt(
+        uint256 rawStake = BaseDelegator(IVaultTokenized(vault).delegator()).stakeAt(
             L1_VALIDATOR_MANAGER, assetClassId, operator, epochStartTs, new bytes(0)
         );

+        // Normalize stake to 18 decimals
+        address token = IVaultTokenized(vault).underlyingToken();
+        uint8 tokenDecimals = IERC20Metadata(token).decimals();
+
+        if (tokenDecimals < 18) {
+            rawStake *= 10 ** (18 - tokenDecimals);
+        } else if (tokenDecimals > 18) {
+            rawStake /= 10 ** (tokenDecimals - 18);
+        }

-        stake += vaultStake;
+        stake += rawStake;
     }
 }