@@ -2083,3 +2083,141 @@ async fn test_initial_sierra_gas() {
20832083
20842084 test_output. perform_default_validations ( ) ;
20852085}
2086+
2087+ #[ rstest]
2088+ #[ tokio:: test]
2089+ async fn test_reverted_call ( ) {
2090+ let test_contract = FeatureContract :: TestContract ( CairoVersion :: Cairo1 ( RunnableCairo1 :: Casm ) ) ;
2091+ let test_class_hash = get_class_hash_of_feature_contract ( test_contract) ;
2092+ let test_contract2 = FeatureContract :: TestContract2 ;
2093+ let empty_contract = FeatureContract :: Empty ( CairoVersion :: Cairo0 ) ;
2094+ let ( mut test_manager, [ main_contract_address, test_contract2_address, empty_contract_address] ) =
2095+ TestManager :: < DictStateReader > :: new_with_default_initial_state ( [
2096+ // The `my_storage_var` cell is initialized as the sum of the ctor args in the
2097+ // constructor. This cell is also used in revert tests, so it must be
2098+ // initialized to zero.
2099+ ( test_contract, calldata ! [ Felt :: ZERO , Felt :: ZERO ] ) ,
2100+ ( test_contract2, calldata ! [ ] ) ,
2101+ ( empty_contract, calldata ! [ ] ) ,
2102+ ] )
2103+ . await ;
2104+
2105+ // Tests 1+2.
2106+
2107+ // Tell inner contract to panic.
2108+ let to_panic = true ;
2109+ for ( inner_selector, is_meta_tx) in
2110+ [ ( "test_revert_helper" , false ) , ( "bad_selector" , false ) , ( "__execute__" , true ) ]
2111+ {
2112+ // Test contract call to test_revert_helper.
2113+ let calldata = create_calldata (
2114+ main_contract_address,
2115+ "test_call_contract_revert" ,
2116+ & [
2117+ * * main_contract_address,
2118+ selector_from_name ( inner_selector) . 0 ,
2119+ Felt :: TWO ,
2120+ test_class_hash. 0 ,
2121+ to_panic. into ( ) ,
2122+ is_meta_tx. into ( ) ,
2123+ ] ,
2124+ ) ;
2125+ test_manager. add_funded_account_invoke ( invoke_tx_args ! { calldata } ) ;
2126+ }
2127+
2128+ // Test call to cairo0 contracts with 0 and more then 0 entry points.
2129+ for ( contract, address) in
2130+ [ ( empty_contract, empty_contract_address) , ( test_contract2, test_contract2_address) ]
2131+ {
2132+ let class_hash = get_class_hash_of_feature_contract ( contract) ;
2133+ let calldata = create_calldata (
2134+ main_contract_address,
2135+ "test_call_contract_revert" ,
2136+ & [
2137+ * * address,
2138+ selector_from_name ( "bad_selector" ) . 0 ,
2139+ Felt :: TWO ,
2140+ class_hash. 0 ,
2141+ to_panic. into ( ) ,
2142+ false . into ( ) ,
2143+ ] ,
2144+ ) ;
2145+ test_manager. add_funded_account_invoke ( invoke_tx_args ! { calldata } ) ;
2146+
2147+ // Test 3:
2148+ // - Contract A calls Contract B.
2149+ // - Contract B changes the storage value from 0 to 10.
2150+ // - Contract A calls Contract C.
2151+ // - Contract C changes the storage value from 10 to 17 and raises an exception.
2152+ // - Contract A checks that the storage value == 10.
2153+ let calldata = create_calldata (
2154+ main_contract_address,
2155+ "test_revert_with_inner_call_and_reverted_storage" ,
2156+ & [ * * main_contract_address, test_class_hash. 0 ] ,
2157+ ) ;
2158+ test_manager. add_funded_account_invoke ( invoke_tx_args ! { calldata } ) ;
2159+ }
2160+
2161+ // Test 4:
2162+ // - Contract A calls Contract B and asserts that the state remains unchanged.
2163+ // - Contract B calls Contract C and panics.
2164+ // - Contract C modifies the state but does not panic.
2165+
2166+ // Tell contract C not to panic:
2167+ let to_panic = false ;
2168+ let contract_c_calldata = [ test_class_hash. 0 , to_panic. into ( ) ] ;
2169+
2170+ // Create calldata recursively.
2171+ let contract_b_calldata = [
2172+ vec ! [
2173+ * * main_contract_address,
2174+ selector_from_name( "test_revert_helper" ) . 0 ,
2175+ contract_c_calldata. len( ) . into( ) ,
2176+ ] ,
2177+ contract_c_calldata. to_vec ( ) ,
2178+ ]
2179+ . concat ( ) ;
2180+ let contract_a_calldata = [
2181+ vec ! [
2182+ * * main_contract_address,
2183+ selector_from_name( "middle_revert_contract" ) . 0 ,
2184+ contract_b_calldata. len( ) . into( ) ,
2185+ ] ,
2186+ contract_b_calldata,
2187+ vec ! [ false . into( ) ] , // is_meta_tx.
2188+ ]
2189+ . concat ( ) ;
2190+
2191+ // Call contract A.
2192+ let calldata =
2193+ create_calldata ( main_contract_address, "test_call_contract_revert" , & contract_a_calldata) ;
2194+ test_manager. add_funded_account_invoke ( invoke_tx_args ! { calldata } ) ;
2195+
2196+ // Run the test and assert only the fee token contract and the OS contracts have storage
2197+ // updates.
2198+ let test_output = test_manager
2199+ . execute_test_with_default_block_contexts ( & TestParameters {
2200+ use_kzg_da : true ,
2201+ ..Default :: default ( )
2202+ } )
2203+ . await ;
2204+
2205+ test_output. perform_default_validations ( ) ;
2206+
2207+ let block_hash_contract_address = ContractAddress (
2208+ Const :: BlockHashContractAddress . fetch_from_os_program ( ) . unwrap ( ) . try_into ( ) . unwrap ( ) ,
2209+ ) ;
2210+ let expected_changed_addresses: HashSet < & ContractAddress > = HashSet :: from_iter ( [
2211+ & * STRK_FEE_TOKEN_ADDRESS ,
2212+ & * ALIAS_CONTRACT_ADDRESS ,
2213+ & block_hash_contract_address,
2214+ ] ) ;
2215+ let actual_changed_addresses =
2216+ test_output. decompressed_state_diff . storage_updates . keys ( ) . collect :: < HashSet < _ > > ( ) ;
2217+ assert ! (
2218+ actual_changed_addresses. is_subset( & expected_changed_addresses) ,
2219+ "Expected changed addresses are not subset of actual changed addresses: actual changed \
2220+ addresses are {actual_changed_addresses:#?}, expected changed addresses are \
2221+ {expected_changed_addresses:#?}"
2222+ ) ;
2223+ }
0 commit comments