pub fn get_amortized_overhead(
    total_gas_limit: u32,
    gas_per_pubdata_byte_limit: u32,
    encoded_len: usize,
    coefficients: OverheadCoefficients,
) -> u32 {
    // Using large U256 type to prevent overflows.
    let overhead_for_block_gas = U256::from(block_overhead_gas(gas_per_pubdata_byte_limit));
    let total_gas_limit = U256::from(total_gas_limit);
    let encoded_len = U256::from(encoded_len);

    // Derivation of overhead consists of 4 parts:
    // 1. The overhead for taking up a transaction's slot. `(O1): O1 = 1 / MAX_TXS_IN_BLOCK`
    // 2. The overhead for taking up the bootloader's memory `(O2): O2 = encoded_len / BOOTLOADER_TX_ENCODING_SPACE`
    // 3. The overhead for possible usage of pubdata. `(O3): O3 = max_pubdata_in_tx / MAX_PUBDATA_PER_BLOCK`
    // 4. The overhead for possible usage of all the single-instance circuits. `(O4): O4 = gas_limit / MAX_TX_ERGS_LIMIT`
    //
    // The maximum of these is taken to derive the part of the block's overhead to be paid by the users:
    //
    // `max_overhead = max(O1, O2, O3, O4)`
    // `overhead_gas = ceil(max_overhead * overhead_for_block_gas)`. Thus, `overhead_gas` is a function of
    // `tx_gas_limit`, `gas_per_pubdata_byte_limit` and `encoded_len`.
    //
    // While it is possible to derive the overhead with binary search in `O(log n)`, it is too expensive to be done
    // on L1, so here is a reference implementation of finding the overhead for transaction in O(1):
    //
    // Given `total_gas_limit = tx_gas_limit + overhead_gas`, we need to find `overhead_gas` and `tx_gas_limit`, such that:
    // 1. `overhead_gas` is maximal possible (the operator is paid fairly)
    // 2. `overhead_gas(tx_gas_limit, gas_per_pubdata_byte_limit, encoded_len) >= overhead_gas` (the user does not overpay)
    // The third part boils to the following 4 inequalities (at least one of these must hold):
    // `ceil(O1 * overhead_for_block_gas) >= overhead_gas`
    // `ceil(O2 * overhead_for_block_gas) >= overhead_gas`
    // `ceil(O3 * overhead_for_block_gas) >= overhead_gas`
    // `ceil(O4 * overhead_for_block_gas) >= overhead_gas`
    //
    // Now, we need to solve each of these separately:

    // 1. The overhead for occupying a single tx slot is a constant:
    let tx_slot_overhead = {
        let tx_slot_overhead =
            ceil_div_u256(overhead_for_block_gas, MAX_TXS_IN_BLOCK.into()).as_u32();
        (coefficients.slot_overhead_coeficient * tx_slot_overhead as f64).floor() as u32
    };

    // 2. The overhead for occupying the bootloader memory can be derived from `encoded_len`
    let overhead_for_length = {
        let overhead_for_length = ceil_div_u256(
            encoded_len * overhead_for_block_gas,
            BOOTLOADER_TX_ENCODING_SPACE.into(),
        )
        .as_u32();

        (coefficients.bootloader_memory_overhead_coeficient * overhead_for_length as f64).floor()
            as u32
    };

    // TODO (EVM-67): possibly include the overhead for pubdata. The formula below has not been properly maintained,
    // since the pubdata is not published. If decided to use the pubdata overhead, it needs to be updated.
    // ```
    // 3. ceil(O3 * overhead_for_block_gas) >= overhead_gas
    // O3 = max_pubdata_in_tx / MAX_PUBDATA_PER_BLOCK = ceil(gas_limit / gas_per_pubdata_byte_limit) / MAX_PUBDATA_PER_BLOCK
    // >= (gas_limit / (gas_per_pubdata_byte_limit * MAX_PUBDATA_PER_BLOCK).
    // ```
    // Throwing off the `ceil`, while may provide marginally lower
    // overhead to the operator, provides substantially easier formula to work with.
    //
    // For better clarity, let's denote `gas_limit = GL, MAX_PUBDATA_PER_BLOCK = MP, gas_per_pubdata_byte_limit = EP, overhead_for_block_gas = OB, total_gas_limit = TL, overhead_gas = OE`
    // `ceil(OB * (TL - OE) / (EP * MP)) >= OE`
    // ```
    // OB * (TL - OE) / (MP * EP) > OE - 1
    // OB * (TL - OE) > (OE - 1) * EP * MP
    // OB * TL + EP * MP > OE * EP * MP + OE * OB
    // (OB * TL + EP * MP) / (EP * MP + OB) > OE
    // OE = floor((OB * TL + EP * MP) / (EP * MP + OB)) with possible -1 if the division is without remainder
    // let overhead_for_pubdata = {
    //     let numerator: U256 = overhead_for_block_gas * total_gas_limit
    //         + gas_per_pubdata_byte_limit * U256::from(MAX_PUBDATA_PER_BLOCK);
    //     let denominator =
    //         gas_per_pubdata_byte_limit * U256::from(MAX_PUBDATA_PER_BLOCK) + overhead_for_block_gas;
    //
    //     // Corner case: if `total_gas_limit` = `gas_per_pubdata_byte_limit` = 0
    //     // then the numerator will be 0 and subtracting 1 will cause a panic, so we just return a zero.
    //     if numerator.is_zero() {
    //         0.into()
    //     } else {
    //         (numerator - 1) / denominator
    //     }
    // };
    //
    // 4. K * ceil(O4 * overhead_for_block_gas) >= overhead_gas, where K is the discount
    // O4 = gas_limit / MAX_TX_ERGS_LIMIT. Using the notation from the previous equation:
    // ceil(OB * GL / MAX_TX_ERGS_LIMIT) >= (OE / K)
    // ceil(OB * (TL - OE) / MAX_TX_ERGS_LIMIT) >= (OE/K)
    // OB * (TL - OE) / MAX_TX_ERGS_LIMIT > (OE/K) - 1
    // OB * (TL - OE) > (OE/K) * MAX_TX_ERGS_LIMIT - MAX_TX_ERGS_LIMIT
    // OB * TL + MAX_TX_ERGS_LIMIT > OE * ( MAX_TX_ERGS_LIMIT/K + OB)
    // OE = floor(OB * TL + MAX_TX_ERGS_LIMIT / (MAX_TX_ERGS_LIMIT/K + OB)), with possible -1 if the division is without remainder
    // ```
    let overhead_for_gas = {
        let numerator = overhead_for_block_gas * total_gas_limit + U256::from(MAX_TX_ERGS_LIMIT);
        let denominator: U256 = U256::from(
            (MAX_TX_ERGS_LIMIT as f64 / coefficients.ergs_limit_overhead_coeficient) as u64,
        ) + overhead_for_block_gas;

        let overhead_for_gas = (numerator - 1) / denominator;

        overhead_for_gas.as_u32()
    };

    let overhead = vec![tx_slot_overhead, overhead_for_length, overhead_for_gas]
        .into_iter()
        .max()
        // For the sake of consistency making sure that `total_gas_limit >= max_overhead`
        .map(|max_overhead| std::cmp::min(max_overhead, total_gas_limit.as_u32()))
        .unwrap();

    let limit_after_deducting_overhead = total_gas_limit - overhead;

    // During double checking of the overhead, the bootloader will assume that the
    // body of the transaction does not have any more than `MAX_L2_TX_GAS_LIMIT` ergs available to it.
    if limit_after_deducting_overhead.as_u64() > MAX_L2_TX_GAS_LIMIT {
        // We derive the same overhead that would exist for the `MAX_L2_TX_GAS_LIMIT` ergs
        derive_overhead(
            MAX_L2_TX_GAS_LIMIT,
            gas_per_pubdata_byte_limit,
            encoded_len.as_usize(),
            coefficients,
        )
    } else {
        overhead
    }
}
This method returns the overhead for processing the block
core/lib/multivm/src/versions/vm_m6/transaction_data.rs