pub(crate) fn load_plonk_proof_from_bytes(
    buffer: &[u8],
    num_bsb22_commitments: usize,
) -> Result<PlonkProof, PlonkError> {
    if buffer.len() <
        PLONK_CLAIMED_VALUES_OFFSET +
            PLONK_CLAIMED_VALUES_COUNT * 32 +
            PLONK_Z_SHIFTED_OPENING_VALUE_OFFSET +
            PLONK_Z_SHIFTED_OPENING_H_OFFSET +
            num_bsb22_commitments * 32 +
            num_bsb22_commitments * 64
    {
        return Err(PlonkError::GeneralError(Error::InvalidData));
    }

    let lro0 = uncompressed_bytes_to_g1_point(&buffer[..64])?;
    let lro1 = uncompressed_bytes_to_g1_point(&buffer[64..128])?;
    let lro2 = uncompressed_bytes_to_g1_point(&buffer[128..192])?;
    let h0 = uncompressed_bytes_to_g1_point(&buffer[192..256])?;
    let h1 = uncompressed_bytes_to_g1_point(&buffer[256..320])?;
    let h2 = uncompressed_bytes_to_g1_point(&buffer[320..384])?;

    // Stores l_at_zeta, r_at_zeta, o_at_zeta, s 1_at_zeta, s2_at_zeta, bsb22_commitments
    let mut claimed_values = Vec::with_capacity(PLONK_CLAIMED_VALUES_COUNT + num_bsb22_commitments);
    let mut offset = PLONK_CLAIMED_VALUES_OFFSET;
    for _ in 0..PLONK_CLAIMED_VALUES_COUNT {
        let value = Fr::from_slice(&buffer[offset..offset + 32])
            .map_err(|e| PlonkError::GeneralError(Error::Field(e)))?;
        claimed_values.push(value);
        offset += 32;
    }

    let z = uncompressed_bytes_to_g1_point(&buffer[offset..offset + 64])?;
    let z_shifted_opening_value = Fr::from_slice(&buffer[offset + 64..offset + 96])
        .map_err(|e| PlonkError::GeneralError(Error::Field(e)))?;
    offset += PLONK_Z_SHIFTED_OPENING_VALUE_OFFSET;

    let batched_proof_h = uncompressed_bytes_to_g1_point(&buffer[offset..offset + 64])?;
    let z_shifted_opening_h = uncompressed_bytes_to_g1_point(&buffer[offset + 64..offset + 128])?;
    offset += PLONK_Z_SHIFTED_OPENING_H_OFFSET;

    for _ in 0..num_bsb22_commitments {
        let commitment = Fr::from_slice(&buffer[offset..offset + 32])
            .map_err(|e| PlonkError::GeneralError(Error::Field(e)))?;
        claimed_values.push(commitment);
        offset += 32;
    }

    let mut bsb22_commitments = Vec::with_capacity(num_bsb22_commitments);
    for _ in 0..num_bsb22_commitments {
        let commitment = uncompressed_bytes_to_g1_point(&buffer[offset..offset + 64])?;
        bsb22_commitments.push(commitment);
        offset += 64;
    }

    let result = PlonkProof {
        lro: [lro0, lro1, lro2],
        z,
        h: [h0, h1, h2],
        bsb22_commitments,
        batched_proof: BatchOpeningProof { h: batched_proof_h, claimed_values },
        z_shifted_opening: OpeningProof {
            h: z_shifted_opening_h,
            claimed_value: z_shifted_opening_value,
        },
    };

    Ok(result)
}
See https://github.com/jtguibas/gnark/blob/26e3df73fc223292be8b7fc0b7451caa4059a649/backend/plonk/bn254/solidity.go
for how the proof is serialized.
crates/verifier/src/plonk/converter.rs