1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use std::vec::Vec;

use blockifier::execution::contract_class::ContractClass as StarknetContractClass;
use mp_felt::Felt252Wrapper;
use mp_genesis_config::ContractClass;
pub use mp_genesis_config::{GenesisData, GenesisLoader, HexFelt, PredeployedAccount};

use crate::GenesisConfig;

impl<T: crate::Config> From<GenesisLoader> for GenesisConfig<T> {
    fn from(loader: GenesisLoader) -> Self {
        let contract_classes = loader
            .data()
            .contract_classes
            .clone()
            .into_iter()
            .map(|(hash, class)| {
                let hash = Felt252Wrapper(hash.0).into();
                match class {
                    ContractClass::Path { path, version } => (
                        hash,
                        read_contract_class_from_json(
                            &std::fs::read_to_string(loader.base_path().join(path)).expect(
                                "Some contract is missing in the config folder. Try to run `madara setup` before \
                                 opening an issue.",
                            ),
                            version,
                        ),
                    ),
                }
            })
            .collect::<Vec<_>>();
        let sierra_to_casm_class_hash = loader
            .data()
            .sierra_class_hash_to_casm_class_hash
            .clone()
            .into_iter()
            .map(|(sierra_hash, casm_hash)| {
                let sierra_hash = Felt252Wrapper(sierra_hash.0).into();
                let casm_hash = Felt252Wrapper(casm_hash.0).into();
                (sierra_hash, casm_hash)
            })
            .collect::<Vec<_>>();
        let contracts = loader
            .data()
            .contracts
            .clone()
            .into_iter()
            .map(|(address, hash)| {
                let address = Felt252Wrapper(address.0).into();
                let hash = Felt252Wrapper(hash.0).into();
                (address, hash)
            })
            .collect::<Vec<_>>();
        let storage = loader
            .data()
            .storage
            .clone()
            .into_iter()
            .map(|(key, value)| {
                let key = (Felt252Wrapper(key.0.0).into(), Felt252Wrapper(key.1.0).into());
                let value = Felt252Wrapper(value.0).into();
                (key, value)
            })
            .collect::<Vec<_>>();

        let chain_id = loader
            .data()
            .chain_id
            .clone()
            .into_bytes()
            .iter()
            .as_ref()
            .try_into()
            .expect("Failed to convert chain id to felt");

        GenesisConfig {
            contracts,
            contract_classes,
            sierra_to_casm_class_hash,
            storage,
            strk_fee_token_address: Felt252Wrapper(loader.data().strk_fee_token_address.0).into(),
            eth_fee_token_address: Felt252Wrapper(loader.data().eth_fee_token_address.0).into(),
            chain_id,
            ..Default::default()
        }
    }
}

/// Create a `ContractClass` from a JSON string
///
/// This function takes a JSON string (`json_str`) containing the JSON representation of a
/// ContractClass
///
/// `ContractClassV0` can be read directly from the JSON because the Serde methods have been
/// implemented in the blockifier
///
/// `ContractClassV1` needs to be read in Casm and then converted to Contract Class V1
pub(crate) fn read_contract_class_from_json(json_str: &str, version: u8) -> StarknetContractClass {
    if version == 0 {
        return StarknetContractClass::V0(
            serde_json::from_str(json_str).expect("`json_str` should be deserializable into the correct ContracClass"),
        );
    } else if version == 1 {
        let casm_contract_class: cairo_lang_starknet_classes::casm_contract_class::CasmContractClass =
            serde_json::from_str(json_str).expect("`json_str` should be deserializable into the CasmContracClass");
        return StarknetContractClass::V1(
            casm_contract_class.try_into().expect("the CasmContractClass should produce a valid ContractClassV1"),
        );
    }
    unimplemented!("version {} is not supported to get contract class from JSON", version);
}

#[cfg(test)]
mod tests {
    use starknet_crypto::FieldElement;

    use super::*;

    #[test]
    fn test_deserialize_loader() {
        // When
        let loader: GenesisData = serde_json::from_str(include_str!("./tests/mock/genesis.json")).unwrap();

        // Then
        assert_eq!(14, loader.contract_classes.len());
    }

    #[test]
    fn test_serialize_loader() {
        // Given
        let class: ContractClass = ContractClass::Path { path: "cairo-contracts/ERC20.json".into(), version: 0 };

        let class_hash = FieldElement::from(1u8).into();
        let contract_address = FieldElement::from(2u8).into();
        let storage_key = FieldElement::from(3u8).into();
        let storage_value = FieldElement::from(4u8).into();
        let fee_token_address: HexFelt = FieldElement::from(5u8).into();

        let genesis_loader = GenesisData {
            contract_classes: vec![(class_hash, class)],
            sierra_class_hash_to_casm_class_hash: vec![(
                FieldElement::from(42u8).into(),
                FieldElement::from(1u8).into(),
            )],
            contracts: vec![(contract_address, class_hash)],
            predeployed_accounts: Vec::new(),
            storage: vec![((contract_address, storage_key), storage_value)],
            strk_fee_token_address: fee_token_address,
            eth_fee_token_address: fee_token_address,
            chain_id: String::from("MADARA"),
        };

        // When
        let serialized_loader = serde_json::to_string(&genesis_loader).unwrap();

        // Then
        let expected = r#"{"contract_classes":[["0x1",{"path":"cairo-contracts/ERC20.json","version":0}]],"sierra_class_hash_to_casm_class_hash":[["0x2a","0x1"]],"contracts":[["0x2","0x1"]],"predeployed_accounts":[],"storage":[[["0x2","0x3"],"0x4"]],"chain_id":"MADARA","strk_fee_token_address":"0x5","eth_fee_token_address":"0x5"}"#;
        assert_eq!(expected, serialized_loader);
    }
}