@@ -6,10 +6,11 @@ use serde_with::{serde_as, DeserializeAs};
6
6
use starknet:: core:: types:: ContractClass ;
7
7
use starknet_in_rust:: {
8
8
core:: errors:: state_errors:: StateError ,
9
+ execution:: CallInfo ,
9
10
felt:: Felt252 ,
10
11
services:: api:: contract_classes:: compiled_class:: CompiledClass ,
11
12
state:: { state_api:: StateReader , state_cache:: StorageEntry } ,
12
- utils:: { Address , ClassHash , CompiledClassHash } ,
13
+ utils:: { parse_felt_array , Address , ClassHash , CompiledClassHash } ,
13
14
} ;
14
15
use std:: env;
15
16
use thiserror:: Error ;
@@ -138,6 +139,63 @@ impl RpcState {
138
139
}
139
140
}
140
141
142
+ #[ derive( Debug , Clone ) ]
143
+ pub struct TransactionTrace {
144
+ pub validate_invocation : CallInfo ,
145
+ pub function_invocation : CallInfo ,
146
+ pub fee_transfer_invocation : CallInfo ,
147
+ pub signature : Vec < Felt252 > ,
148
+ }
149
+
150
+ impl < ' de > Deserialize < ' de > for TransactionTrace {
151
+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
152
+ where
153
+ D : Deserializer < ' de > ,
154
+ {
155
+ let value: serde_json:: Value = Deserialize :: deserialize ( deserializer) ?;
156
+
157
+ let validate_invocation = value[ "validate_invocation" ] . clone ( ) ;
158
+ let function_invocation = value[ "function_invocation" ] . clone ( ) ;
159
+ let fee_transfer_invocation = value[ "fee_transfer_invocation" ] . clone ( ) ;
160
+ let signature_value = value[ "signature" ] . clone ( ) ;
161
+ let signature = parse_felt_array ( signature_value. as_array ( ) . unwrap ( ) ) ;
162
+
163
+ Ok ( TransactionTrace {
164
+ validate_invocation : serde_json:: from_value ( validate_invocation)
165
+ . map_err ( serde:: de:: Error :: custom) ?,
166
+ function_invocation : serde_json:: from_value ( function_invocation)
167
+ . map_err ( serde:: de:: Error :: custom) ?,
168
+ fee_transfer_invocation : serde_json:: from_value ( fee_transfer_invocation)
169
+ . map_err ( serde:: de:: Error :: custom) ?,
170
+ signature,
171
+ } )
172
+ }
173
+ }
174
+
175
+ #[ cfg( test) ]
176
+ impl RpcState {
177
+ pub fn get_transaction_trace ( & self , hash : Felt252 ) -> TransactionTrace {
178
+ let chain_name = self . get_chain_name ( ) ;
179
+ let response = ureq:: get ( & format ! (
180
+ "https://{}.starknet.io/feeder_gateway/get_transaction_trace" ,
181
+ chain_name
182
+ ) )
183
+ . query ( "transactionHash" , & format ! ( "0x{}" , hash. to_str_radix( 16 ) ) )
184
+ . call ( )
185
+ . unwrap ( ) ;
186
+
187
+ serde_json:: from_str ( & response. into_string ( ) . unwrap ( ) ) . unwrap ( )
188
+ }
189
+
190
+ fn get_chain_name ( & self ) -> String {
191
+ match self . chain {
192
+ RpcChain :: MainNet => "alpha-mainnet" . to_string ( ) ,
193
+ RpcChain :: TestNet => "alpha4" . to_string ( ) ,
194
+ RpcChain :: TestNet2 => "alpha4-2" . to_string ( ) ,
195
+ }
196
+ }
197
+ }
198
+
141
199
impl StateReader for RpcState {
142
200
fn get_contract_class ( & self , class_hash : & ClassHash ) -> Result < CompiledClass , StateError > {
143
201
let params = ureq:: json!( {
@@ -212,7 +270,8 @@ impl StateReader for RpcState {
212
270
213
271
#[ cfg( test) ]
214
272
mod tests {
215
- use std:: sync:: Arc ;
273
+ use cairo_vm:: vm:: runners:: cairo_runner:: ExecutionResources ;
274
+ use std:: { collections:: HashMap , sync:: Arc } ;
216
275
217
276
use super :: * ;
218
277
use starknet_in_rust:: {
@@ -658,4 +717,135 @@ mod tests {
658
717
. execute ( & mut state, & block_context, 0 )
659
718
. unwrap ( ) ;
660
719
}
720
+
721
+ // https://alpha4-2.starknet.io/feeder_gateway/get_transaction_trace?transactionHash=0x019feb888a2d53ffddb7a1750264640afab8e9c23119e648b5259f1b5e7d51bc
722
+ #[ test]
723
+ fn test_get_transaction_trace ( ) {
724
+ let state_reader = RpcState :: new (
725
+ RpcChain :: TestNet2 ,
726
+ BlockValue :: Number ( serde_json:: to_value ( 838683 ) . unwrap ( ) ) ,
727
+ ) ;
728
+
729
+ let tx_hash_str = "19feb888a2d53ffddb7a1750264640afab8e9c23119e648b5259f1b5e7d51bc" ;
730
+ let tx_hash = felt_str ! ( format!( "{}" , tx_hash_str) , 16 ) ;
731
+
732
+ let tx_trace = state_reader. get_transaction_trace ( tx_hash) ;
733
+
734
+ assert_eq ! (
735
+ tx_trace. signature,
736
+ vec![
737
+ felt_str!(
738
+ "ffab1c47d8d5e5b76bdcc4af79e98205716c36b440f20244c69599a91ace58" ,
739
+ 16
740
+ ) ,
741
+ felt_str!(
742
+ "6aa48a0906c9c1f7381c1a040c043b649eeac1eea08f24a9d07813f6b1d05fe" ,
743
+ 16
744
+ ) ,
745
+ ]
746
+ ) ;
747
+
748
+ assert_eq ! (
749
+ tx_trace. validate_invocation. calldata,
750
+ vec![
751
+ felt_str!( "1" , 16 ) ,
752
+ felt_str!(
753
+ "690c876e61beda61e994543af68038edac4e1cb1990ab06e52a2d27e56a1232" ,
754
+ 16
755
+ ) ,
756
+ felt_str!(
757
+ "1f24f689ced5802b706d7a2e28743fe45c7bfa37431c97b1c766e9622b65573" ,
758
+ 16
759
+ ) ,
760
+ felt_str!( "0" , 16 ) ,
761
+ felt_str!( "9" , 16 ) ,
762
+ felt_str!( "9" , 16 ) ,
763
+ felt_str!( "4" , 16 ) ,
764
+ felt_str!( "4254432d55534443" , 16 ) ,
765
+ felt_str!( "f02e7324ecbd65ce267" , 16 ) ,
766
+ felt_str!( "5754492d55534443" , 16 ) ,
767
+ felt_str!( "8e13050d06d8f514c" , 16 ) ,
768
+ felt_str!( "4554482d55534443" , 16 ) ,
769
+ felt_str!( "f0e4a142c3551c149d" , 16 ) ,
770
+ felt_str!( "4a50592d55534443" , 16 ) ,
771
+ felt_str!( "38bd34c31a0a5c" , 16 ) ,
772
+ ]
773
+ ) ;
774
+ assert_eq ! ( tx_trace. validate_invocation. retdata, vec![ ] ) ;
775
+ assert_eq ! (
776
+ tx_trace. validate_invocation. execution_resources,
777
+ ExecutionResources {
778
+ n_steps: 790 ,
779
+ n_memory_holes: 51 ,
780
+ builtin_instance_counter: HashMap :: from( [
781
+ ( "range_check_builtin" . to_string( ) , 20 ) ,
782
+ ( "ecdsa_builtin" . to_string( ) , 1 ) ,
783
+ ( "pedersen_builtin" . to_string( ) , 2 ) ,
784
+ ] ) ,
785
+ }
786
+ ) ;
787
+
788
+ assert_eq ! (
789
+ tx_trace. function_invocation. calldata,
790
+ vec![
791
+ felt_str!( "1" , 16 ) ,
792
+ felt_str!(
793
+ "690c876e61beda61e994543af68038edac4e1cb1990ab06e52a2d27e56a1232" ,
794
+ 16
795
+ ) ,
796
+ felt_str!(
797
+ "1f24f689ced5802b706d7a2e28743fe45c7bfa37431c97b1c766e9622b65573" ,
798
+ 16
799
+ ) ,
800
+ felt_str!( "0" , 16 ) ,
801
+ felt_str!( "9" , 16 ) ,
802
+ felt_str!( "9" , 16 ) ,
803
+ felt_str!( "4" , 16 ) ,
804
+ felt_str!( "4254432d55534443" , 16 ) ,
805
+ felt_str!( "f02e7324ecbd65ce267" , 16 ) ,
806
+ felt_str!( "5754492d55534443" , 16 ) ,
807
+ felt_str!( "8e13050d06d8f514c" , 16 ) ,
808
+ felt_str!( "4554482d55534443" , 16 ) ,
809
+ felt_str!( "f0e4a142c3551c149d" , 16 ) ,
810
+ felt_str!( "4a50592d55534443" , 16 ) ,
811
+ felt_str!( "38bd34c31a0a5c" , 16 ) ,
812
+ ]
813
+ ) ;
814
+ assert_eq ! ( tx_trace. function_invocation. retdata, vec![ 0 . into( ) ] ) ;
815
+ assert_eq ! (
816
+ tx_trace. function_invocation. execution_resources,
817
+ ExecutionResources {
818
+ n_steps: 2808 ,
819
+ n_memory_holes: 136 ,
820
+ builtin_instance_counter: HashMap :: from( [
821
+ ( "range_check_builtin" . to_string( ) , 49 ) ,
822
+ ( "pedersen_builtin" . to_string( ) , 14 ) ,
823
+ ] ) ,
824
+ }
825
+ ) ;
826
+
827
+ assert_eq ! (
828
+ tx_trace. fee_transfer_invocation. calldata,
829
+ vec![
830
+ felt_str!(
831
+ "1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8" ,
832
+ 16
833
+ ) ,
834
+ felt_str!( "2b0322a23ba4" , 16 ) ,
835
+ felt_str!( "0" , 16 ) ,
836
+ ]
837
+ ) ;
838
+ assert_eq ! ( tx_trace. fee_transfer_invocation. retdata, vec![ 1 . into( ) ] ) ;
839
+ assert_eq ! (
840
+ tx_trace. fee_transfer_invocation. execution_resources,
841
+ ExecutionResources {
842
+ n_steps: 586 ,
843
+ n_memory_holes: 42 ,
844
+ builtin_instance_counter: HashMap :: from( [
845
+ ( "range_check_builtin" . to_string( ) , 21 ) ,
846
+ ( "pedersen_builtin" . to_string( ) , 4 ) ,
847
+ ] ) ,
848
+ }
849
+ ) ;
850
+ }
661
851
}
0 commit comments