@@ -6,10 +6,11 @@ use serde_with::{serde_as, DeserializeAs};
66use starknet:: core:: types:: ContractClass ;
77use starknet_in_rust:: {
88 core:: errors:: state_errors:: StateError ,
9+ execution:: CallInfo ,
910 felt:: Felt252 ,
1011 services:: api:: contract_classes:: compiled_class:: CompiledClass ,
1112 state:: { state_api:: StateReader , state_cache:: StorageEntry } ,
12- utils:: { Address , ClassHash , CompiledClassHash } ,
13+ utils:: { parse_felt_array , Address , ClassHash , CompiledClassHash } ,
1314} ;
1415use std:: env;
1516use thiserror:: Error ;
@@ -138,6 +139,63 @@ impl RpcState {
138139 }
139140}
140141
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+
141199impl StateReader for RpcState {
142200 fn get_contract_class ( & self , class_hash : & ClassHash ) -> Result < CompiledClass , StateError > {
143201 let params = ureq:: json!( {
@@ -212,7 +270,8 @@ impl StateReader for RpcState {
212270
213271#[ cfg( test) ]
214272mod tests {
215- use std:: sync:: Arc ;
273+ use cairo_vm:: vm:: runners:: cairo_runner:: ExecutionResources ;
274+ use std:: { collections:: HashMap , sync:: Arc } ;
216275
217276 use super :: * ;
218277 use starknet_in_rust:: {
@@ -658,4 +717,135 @@ mod tests {
658717 . execute ( & mut state, & block_context, 0 )
659718 . unwrap ( ) ;
660719 }
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+ }
661851}
0 commit comments