starknet_rust_providers/sequencer/models/
contract.rs

1use std::io::Write;
2
3use flate2::{write::GzEncoder, Compression};
4use serde::{Deserialize, Deserializer, Serialize};
5use serde_with::serde_as;
6use starknet_rust_core::{
7    serde::{byte_array::base64::serialize as base64_ser, unsigned_field_element::UfeHex},
8    types::{
9        contract::{
10            legacy::{LegacyContractClass, RawLegacyAbiEntry, RawLegacyEntryPoints},
11            CompressProgramError,
12        },
13        EntryPointsByType, Felt, FlattenedSierraClass,
14    },
15};
16
17#[derive(Debug, Serialize)]
18#[serde(untagged)]
19#[allow(clippy::large_enum_variant)]
20pub enum DeployedClass {
21    SierraClass(FlattenedSierraClass),
22    LegacyClass(LegacyContractClass),
23}
24
25#[serde_as]
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[cfg_attr(feature = "no_unknown_fields", serde(deny_unknown_fields))]
28pub struct CompressedSierraClass {
29    #[serde(serialize_with = "base64_ser")]
30    pub sierra_program: Vec<u8>,
31    pub contract_class_version: String,
32    pub entry_points_by_type: EntryPointsByType,
33    pub abi: String,
34}
35
36/// This type exists because of an `offset` issue. Without this type declaration of pre 0.11.0
37/// contracts against the sequencer gateway won't function properly.
38#[derive(Debug, Serialize, Clone)]
39pub struct CompressedLegacyContractClass {
40    #[serde(serialize_with = "base64_ser")]
41    pub program: Vec<u8>,
42    pub entry_points_by_type: RawLegacyEntryPoints,
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub abi: Option<Vec<RawLegacyAbiEntry>>,
45}
46
47#[derive(Debug, thiserror::Error)]
48pub enum DecompressProgramError {
49    #[error("json deserialization error: {0}")]
50    Json(serde_json::Error),
51    #[error("decompression io error: {0}")]
52    Io(std::io::Error),
53}
54
55// We need to manually implement this because `raw_value` doesn't work with `untagged`:
56//   https://github.com/serde-rs/serde/issues/1183
57impl<'de> Deserialize<'de> for DeployedClass {
58    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
59    where
60        D: Deserializer<'de>,
61    {
62        let temp_value = serde_json::Value::deserialize(deserializer)?;
63        if let Ok(value) = FlattenedSierraClass::deserialize(&temp_value) {
64            return Ok(Self::SierraClass(value));
65        }
66        if let Ok(value) = LegacyContractClass::deserialize(&temp_value) {
67            return Ok(Self::LegacyClass(value));
68        }
69        Err(serde::de::Error::custom(
70            "data did not match any variant of enum DeployedClass",
71        ))
72    }
73}
74
75impl CompressedSierraClass {
76    pub fn from_flattened(
77        flattened_class: &FlattenedSierraClass,
78    ) -> Result<Self, DecompressProgramError> {
79        #[serde_as]
80        #[derive(Serialize)]
81        struct SierraProgram<'a>(#[serde_as(as = "Vec<UfeHex>")] &'a Vec<Felt>);
82
83        let program_json = serde_json::to_string(&SierraProgram(&flattened_class.sierra_program))
84            .map_err(DecompressProgramError::Json)?;
85
86        // Use best compression level to optimize for payload size
87        let mut gzip_encoder = GzEncoder::new(Vec::new(), Compression::best());
88        gzip_encoder
89            .write_all(program_json.as_bytes())
90            .map_err(DecompressProgramError::Io)?;
91
92        let compressed_program = gzip_encoder.finish().map_err(DecompressProgramError::Io)?;
93
94        Ok(Self {
95            sierra_program: compressed_program,
96            contract_class_version: flattened_class.contract_class_version.clone(),
97            entry_points_by_type: flattened_class.entry_points_by_type.clone(),
98            abi: flattened_class.abi.clone(),
99        })
100    }
101}