cairo_lang_starknet_classes/
contract_class.rs

1use cairo_lang_sierra as sierra;
2use cairo_lang_utils::bigint::{BigUintAsHex, deserialize_big_uint, serialize_big_uint};
3use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
4use num_bigint::BigUint;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use thiserror::Error;
8
9use crate::abi::Contract;
10use crate::allowed_libfuncs::{AllowedLibfuncsError, ListSelector, lookup_allowed_libfuncs_list};
11use crate::compiler_version::{VersionId, current_compiler_version_id, current_sierra_version_id};
12use crate::felt252_serde::{
13    Felt252SerdeError, sierra_from_felt252s, sierra_to_felt252s, version_id_from_felt252s,
14};
15
16#[cfg(test)]
17#[path = "contract_class_test.rs"]
18mod test;
19
20#[derive(Error, Debug, Eq, PartialEq)]
21pub enum StarknetCompilationError {
22    #[error("Invalid entry point.")]
23    EntryPointError,
24    #[error(transparent)]
25    AllowedLibfuncsError(#[from] AllowedLibfuncsError),
26}
27
28/// Represents a contract in the Starknet network.
29#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
30pub struct ContractClass {
31    pub sierra_program: Vec<BigUintAsHex>,
32    pub sierra_program_debug_info: Option<sierra::debug_info::DebugInfo>,
33    pub contract_class_version: String,
34    pub entry_points_by_type: ContractEntryPoints,
35    pub abi: Option<Contract>,
36}
37impl ContractClass {
38    /// Extracts the contract class from the given contract declaration.
39    pub fn new(
40        program: &sierra::program::Program,
41        entry_points_by_type: ContractEntryPoints,
42        abi: Option<Contract>,
43        annotations: OrderedHashMap<String, Value>,
44    ) -> Result<Self, Felt252SerdeError> {
45        let mut sierra_program_debug_info = sierra::debug_info::DebugInfo::extract(program);
46        sierra_program_debug_info.annotations.extend(annotations);
47
48        Ok(Self {
49            sierra_program: sierra_to_felt252s(
50                current_sierra_version_id(),
51                current_compiler_version_id(),
52                program,
53            )?,
54            sierra_program_debug_info: Some(sierra_program_debug_info),
55            contract_class_version: DEFAULT_CONTRACT_CLASS_VERSION.into(),
56            entry_points_by_type,
57            abi,
58        })
59    }
60
61    /// Extracts Sierra program from the ContractClass and populates it with debug info if
62    /// available.
63    pub fn extract_sierra_program(&self) -> Result<sierra::program::Program, Felt252SerdeError> {
64        let (_, _, mut sierra_program) = sierra_from_felt252s(&self.sierra_program)?;
65        if let Some(info) = &self.sierra_program_debug_info {
66            info.populate(&mut sierra_program);
67        }
68        Ok(sierra_program)
69    }
70
71    /// Sanity checks the contract class.
72    /// Currently only checks that if ABI exists, its counts match the entry points counts.
73    pub fn sanity_check(&self) {
74        if let Some(abi) = &self.abi {
75            abi.sanity_check(
76                self.entry_points_by_type.external.len(),
77                self.entry_points_by_type.l1_handler.len(),
78                self.entry_points_by_type.constructor.len(),
79            );
80        }
81    }
82
83    /// Checks that all the used libfuncs in the contract class are allowed in the contract class
84    /// Sierra version.
85    pub fn validate_version_compatible(
86        self: &ContractClass,
87        list_selector: ListSelector,
88    ) -> Result<(), AllowedLibfuncsError> {
89        let list_name = list_selector.to_string();
90        let allowed_libfuncs = lookup_allowed_libfuncs_list(list_selector)?;
91        let (_, _, sierra_program) = sierra_from_felt252s(&self.sierra_program)
92            .map_err(|_| AllowedLibfuncsError::SierraProgramError)?;
93        for libfunc in &sierra_program.libfunc_declarations {
94            if !allowed_libfuncs.allowed_libfuncs.contains(&libfunc.long_id.generic_id) {
95                return Err(AllowedLibfuncsError::UnsupportedLibfunc {
96                    invalid_libfunc: libfunc.long_id.generic_id.to_string(),
97                    allowed_libfuncs_list_name: list_name,
98                });
99            }
100        }
101        Ok(())
102    }
103}
104
105const DEFAULT_CONTRACT_CLASS_VERSION: &str = "0.1.0";
106
107#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
108pub struct ContractEntryPoints {
109    #[serde(rename = "EXTERNAL")]
110    pub external: Vec<ContractEntryPoint>,
111    #[serde(rename = "L1_HANDLER")]
112    pub l1_handler: Vec<ContractEntryPoint>,
113    #[serde(rename = "CONSTRUCTOR")]
114    pub constructor: Vec<ContractEntryPoint>,
115}
116
117#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
118pub struct ContractEntryPoint {
119    /// A field element that encodes the signature of the called function.
120    #[serde(serialize_with = "serialize_big_uint", deserialize_with = "deserialize_big_uint")]
121    pub selector: BigUint,
122    /// The index of the user function declaration in the Sierra program.
123    pub function_idx: usize,
124}
125
126/// Deserializes the versions from the header of a Sierra program represented as a slice of
127/// felt252s.
128///
129/// Returns (sierra_version_id, compiler_version_id).
130/// See [crate::compiler_version].
131pub fn version_id_from_serialized_sierra_program(
132    sierra_program: &[BigUintAsHex],
133) -> Result<(VersionId, VersionId), Felt252SerdeError> {
134    let (sierra_version_id, compiler_version_id, _) = version_id_from_felt252s(sierra_program)?;
135    Ok((sierra_version_id, compiler_version_id))
136}