@@ -58,3 +58,379 @@ pub struct L1ToL2Message {
5858 pub payload : Vec < Felt > ,
5959 pub nonce : Option < Nonce > ,
6060}
61+
62+ ////////////////////////////////////////////////////////////////////////////////
63+ // Conversion to Katana RPC types.
64+ ////////////////////////////////////////////////////////////////////////////////
65+
66+ impl From < ExecutionStatus > for katana_rpc_types:: ExecutionResult {
67+ fn from ( value : ExecutionStatus ) -> Self {
68+ match value {
69+ ExecutionStatus :: Succeeded => katana_rpc_types:: ExecutionResult :: Succeeded ,
70+ ExecutionStatus :: Reverted => {
71+ // When converting from gateway ExecutionStatus::Reverted, we don't have the
72+ // revert reason here. The caller should use the revert_error field from
73+ // ReceiptBody if available.
74+ katana_rpc_types:: ExecutionResult :: Reverted {
75+ reason : String :: from ( "Transaction reverted" ) ,
76+ }
77+ }
78+ }
79+ }
80+ }
81+
82+ impl From < ExecutionResources > for katana_rpc_types:: ExecutionResources {
83+ fn from ( value : ExecutionResources ) -> Self {
84+ let gas = value. total_gas_consumed . unwrap_or_default ( ) ;
85+ katana_rpc_types:: ExecutionResources {
86+ l1_gas : gas. l1_gas ,
87+ l1_data_gas : gas. l1_data_gas ,
88+ l2_gas : gas. l2_gas ,
89+ }
90+ }
91+ }
92+
93+ impl ReceiptBody {
94+ /// Convert the receipt body to an RPC execution result.
95+ ///
96+ /// This uses the `execution_status` field if available, otherwise falls back to checking
97+ /// the `revert_error` field. If `revert_error` is present, the result is `Reverted`.
98+ pub fn to_execution_result ( & self ) -> katana_rpc_types:: ExecutionResult {
99+ if let Some ( revert_error) = & self . revert_error {
100+ katana_rpc_types:: ExecutionResult :: Reverted { reason : revert_error. clone ( ) }
101+ } else if let Some ( status) = & self . execution_status {
102+ match status {
103+ ExecutionStatus :: Succeeded => katana_rpc_types:: ExecutionResult :: Succeeded ,
104+ ExecutionStatus :: Reverted => {
105+ // Reverted status without error message
106+ katana_rpc_types:: ExecutionResult :: Reverted {
107+ reason : String :: from ( "Transaction reverted" ) ,
108+ }
109+ }
110+ }
111+ } else {
112+ // If no status is provided, assume success
113+ katana_rpc_types:: ExecutionResult :: Succeeded
114+ }
115+ }
116+
117+ /// Convert to an RPC FeePayment with the given price unit.
118+ pub fn to_fee_payment (
119+ & self ,
120+ unit : katana_primitives:: fee:: PriceUnit ,
121+ ) -> katana_rpc_types:: FeePayment {
122+ katana_rpc_types:: FeePayment { amount : self . actual_fee , unit }
123+ }
124+ }
125+
126+ impl ConfirmedReceipt {
127+ /// Create an RPC Invoke receipt from this gateway receipt.
128+ ///
129+ /// # Arguments
130+ /// * `finality_status` - The finality status of the transaction
131+ /// * `fee_unit` - The price unit for the fee
132+ pub fn to_rpc_invoke_receipt (
133+ self ,
134+ finality_status : katana_primitives:: block:: FinalityStatus ,
135+ fee_unit : katana_primitives:: fee:: PriceUnit ,
136+ ) -> katana_rpc_types:: RpcInvokeTxReceipt {
137+ let execution_result = self . body . to_execution_result ( ) ;
138+ let actual_fee = self . body . to_fee_payment ( fee_unit) ;
139+
140+ katana_rpc_types:: RpcInvokeTxReceipt {
141+ actual_fee,
142+ finality_status,
143+ messages_sent : self . body . l2_to_l1_messages ,
144+ events : self . body . events ,
145+ execution_resources : self . body . execution_resources . unwrap_or_default ( ) . into ( ) ,
146+ execution_result,
147+ }
148+ }
149+
150+ /// Create an RPC Declare receipt from this gateway receipt.
151+ ///
152+ /// # Arguments
153+ /// * `finality_status` - The finality status of the transaction
154+ /// * `fee_unit` - The price unit for the fee
155+ pub fn to_rpc_declare_receipt (
156+ self ,
157+ finality_status : katana_primitives:: block:: FinalityStatus ,
158+ fee_unit : katana_primitives:: fee:: PriceUnit ,
159+ ) -> katana_rpc_types:: RpcDeclareTxReceipt {
160+ let execution_result = self . body . to_execution_result ( ) ;
161+ let actual_fee = self . body . to_fee_payment ( fee_unit) ;
162+
163+ katana_rpc_types:: RpcDeclareTxReceipt {
164+ actual_fee,
165+ finality_status,
166+ messages_sent : self . body . l2_to_l1_messages ,
167+ events : self . body . events ,
168+ execution_resources : self . body . execution_resources . unwrap_or_default ( ) . into ( ) ,
169+ execution_result,
170+ }
171+ }
172+
173+ /// Create an RPC Deploy receipt from this gateway receipt.
174+ ///
175+ /// # Arguments
176+ /// * `finality_status` - The finality status of the transaction
177+ /// * `fee_unit` - The price unit for the fee
178+ /// * `contract_address` - The deployed contract address
179+ pub fn to_rpc_deploy_receipt (
180+ self ,
181+ finality_status : katana_primitives:: block:: FinalityStatus ,
182+ fee_unit : katana_primitives:: fee:: PriceUnit ,
183+ contract_address : ContractAddress ,
184+ ) -> katana_rpc_types:: RpcDeployTxReceipt {
185+ let execution_result = self . body . to_execution_result ( ) ;
186+ let actual_fee = self . body . to_fee_payment ( fee_unit) ;
187+
188+ katana_rpc_types:: RpcDeployTxReceipt {
189+ actual_fee,
190+ finality_status,
191+ messages_sent : self . body . l2_to_l1_messages ,
192+ events : self . body . events ,
193+ execution_resources : self . body . execution_resources . unwrap_or_default ( ) . into ( ) ,
194+ contract_address,
195+ execution_result,
196+ }
197+ }
198+
199+ /// Create an RPC DeployAccount receipt from this gateway receipt.
200+ ///
201+ /// # Arguments
202+ /// * `finality_status` - The finality status of the transaction
203+ /// * `fee_unit` - The price unit for the fee
204+ /// * `contract_address` - The deployed account contract address
205+ pub fn to_rpc_deploy_account_receipt (
206+ self ,
207+ finality_status : katana_primitives:: block:: FinalityStatus ,
208+ fee_unit : katana_primitives:: fee:: PriceUnit ,
209+ contract_address : ContractAddress ,
210+ ) -> katana_rpc_types:: RpcDeployAccountTxReceipt {
211+ let execution_result = self . body . to_execution_result ( ) ;
212+ let actual_fee = self . body . to_fee_payment ( fee_unit) ;
213+
214+ katana_rpc_types:: RpcDeployAccountTxReceipt {
215+ actual_fee,
216+ finality_status,
217+ messages_sent : self . body . l2_to_l1_messages ,
218+ events : self . body . events ,
219+ execution_resources : self . body . execution_resources . unwrap_or_default ( ) . into ( ) ,
220+ contract_address,
221+ execution_result,
222+ }
223+ }
224+
225+ /// Create an RPC L1Handler receipt from this gateway receipt.
226+ ///
227+ /// # Arguments
228+ /// * `finality_status` - The finality status of the transaction
229+ /// * `fee_unit` - The price unit for the fee
230+ /// * `message_hash` - The L1 to L2 message hash
231+ pub fn to_rpc_l1_handler_receipt (
232+ self ,
233+ finality_status : katana_primitives:: block:: FinalityStatus ,
234+ fee_unit : katana_primitives:: fee:: PriceUnit ,
235+ message_hash : katana_primitives:: B256 ,
236+ ) -> katana_rpc_types:: RpcL1HandlerTxReceipt {
237+ let execution_result = self . body . to_execution_result ( ) ;
238+ let actual_fee = self . body . to_fee_payment ( fee_unit) ;
239+
240+ katana_rpc_types:: RpcL1HandlerTxReceipt {
241+ actual_fee,
242+ finality_status,
243+ messages_sent : self . body . l2_to_l1_messages ,
244+ events : self . body . events ,
245+ execution_resources : self . body . execution_resources . unwrap_or_default ( ) . into ( ) ,
246+ message_hash,
247+ execution_result,
248+ }
249+ }
250+ }
251+
252+ impl Default for ExecutionResources {
253+ fn default ( ) -> Self {
254+ Self {
255+ vm_resources : Default :: default ( ) ,
256+ data_availability : None ,
257+ total_gas_consumed : Some ( Default :: default ( ) ) ,
258+ }
259+ }
260+ }
261+
262+ #[ cfg( test) ]
263+ mod tests {
264+ use katana_primitives:: block:: FinalityStatus ;
265+ use katana_primitives:: fee:: PriceUnit ;
266+ use katana_primitives:: receipt:: GasUsed ;
267+ use katana_primitives:: { address, felt} ;
268+
269+ use super :: * ;
270+
271+ fn create_test_receipt_body ( ) -> ReceiptBody {
272+ ReceiptBody {
273+ execution_resources : Some ( ExecutionResources {
274+ vm_resources : Default :: default ( ) ,
275+ data_availability : None ,
276+ total_gas_consumed : Some ( GasUsed { l1_gas : 100 , l1_data_gas : 50 , l2_gas : 200 } ) ,
277+ } ) ,
278+ l1_to_l2_consumed_message : None ,
279+ l2_to_l1_messages : vec ! [ ] ,
280+ events : vec ! [ ] ,
281+ actual_fee : felt ! ( "0x1234" ) ,
282+ execution_status : Some ( ExecutionStatus :: Succeeded ) ,
283+ revert_error : None ,
284+ }
285+ }
286+
287+ #[ test]
288+ fn test_execution_status_to_execution_result ( ) {
289+ let succeeded: katana_rpc_types:: ExecutionResult = ExecutionStatus :: Succeeded . into ( ) ;
290+ assert_eq ! ( succeeded, katana_rpc_types:: ExecutionResult :: Succeeded ) ;
291+
292+ let reverted: katana_rpc_types:: ExecutionResult = ExecutionStatus :: Reverted . into ( ) ;
293+ match reverted {
294+ katana_rpc_types:: ExecutionResult :: Reverted { reason } => {
295+ assert_eq ! ( reason, "Transaction reverted" ) ;
296+ }
297+ _ => panic ! ( "Expected Reverted result" ) ,
298+ }
299+ }
300+
301+ #[ test]
302+ fn test_execution_resources_conversion ( ) {
303+ let gateway_resources = ExecutionResources {
304+ vm_resources : Default :: default ( ) ,
305+ data_availability : None ,
306+ total_gas_consumed : Some ( GasUsed { l1_gas : 100 , l1_data_gas : 50 , l2_gas : 200 } ) ,
307+ } ;
308+
309+ let rpc_resources: katana_rpc_types:: ExecutionResources = gateway_resources. into ( ) ;
310+ assert_eq ! ( rpc_resources. l1_gas, 100 ) ;
311+ assert_eq ! ( rpc_resources. l1_data_gas, 50 ) ;
312+ assert_eq ! ( rpc_resources. l2_gas, 200 ) ;
313+ }
314+
315+ #[ test]
316+ fn test_receipt_body_to_execution_result_with_revert_error ( ) {
317+ let body = ReceiptBody {
318+ execution_resources : None ,
319+ l1_to_l2_consumed_message : None ,
320+ l2_to_l1_messages : vec ! [ ] ,
321+ events : vec ! [ ] ,
322+ actual_fee : felt ! ( "0x0" ) ,
323+ execution_status : Some ( ExecutionStatus :: Succeeded ) ,
324+ revert_error : Some ( "Out of gas" . to_string ( ) ) ,
325+ } ;
326+
327+ let result = body. to_execution_result ( ) ;
328+ match result {
329+ katana_rpc_types:: ExecutionResult :: Reverted { reason } => {
330+ assert_eq ! ( reason, "Out of gas" ) ;
331+ }
332+ _ => panic ! ( "Expected Reverted result" ) ,
333+ }
334+ }
335+
336+ #[ test]
337+ fn test_receipt_body_to_execution_result_succeeded ( ) {
338+ let body = create_test_receipt_body ( ) ;
339+ let result = body. to_execution_result ( ) ;
340+ assert_eq ! ( result, katana_rpc_types:: ExecutionResult :: Succeeded ) ;
341+ }
342+
343+ #[ test]
344+ fn test_to_rpc_invoke_receipt ( ) {
345+ let gateway_receipt = ConfirmedReceipt {
346+ transaction_hash : felt ! ( "0xabc" ) ,
347+ transaction_index : 5 ,
348+ body : create_test_receipt_body ( ) ,
349+ } ;
350+
351+ let rpc_receipt = gateway_receipt
352+ . clone ( )
353+ . to_rpc_invoke_receipt ( FinalityStatus :: AcceptedOnL2 , PriceUnit :: Wei ) ;
354+
355+ assert_eq ! ( rpc_receipt. actual_fee. amount, felt!( "0x1234" ) ) ;
356+ assert_eq ! ( rpc_receipt. actual_fee. unit, PriceUnit :: Wei ) ;
357+ assert_eq ! ( rpc_receipt. finality_status, FinalityStatus :: AcceptedOnL2 ) ;
358+ assert_eq ! ( rpc_receipt. execution_resources. l1_gas, 100 ) ;
359+ assert_eq ! ( rpc_receipt. execution_resources. l2_gas, 200 ) ;
360+ assert_eq ! ( rpc_receipt. execution_result, katana_rpc_types:: ExecutionResult :: Succeeded ) ;
361+ }
362+
363+ #[ test]
364+ fn test_to_rpc_declare_receipt ( ) {
365+ let gateway_receipt = ConfirmedReceipt {
366+ transaction_hash : felt ! ( "0xdef" ) ,
367+ transaction_index : 10 ,
368+ body : create_test_receipt_body ( ) ,
369+ } ;
370+
371+ let rpc_receipt = gateway_receipt
372+ . clone ( )
373+ . to_rpc_declare_receipt ( FinalityStatus :: AcceptedOnL1 , PriceUnit :: Fri ) ;
374+
375+ assert_eq ! ( rpc_receipt. actual_fee. amount, felt!( "0x1234" ) ) ;
376+ assert_eq ! ( rpc_receipt. actual_fee. unit, PriceUnit :: Fri ) ;
377+ assert_eq ! ( rpc_receipt. finality_status, FinalityStatus :: AcceptedOnL1 ) ;
378+ }
379+
380+ #[ test]
381+ fn test_to_rpc_deploy_receipt ( ) {
382+ let gateway_receipt = ConfirmedReceipt {
383+ transaction_hash : felt ! ( "0x123" ) ,
384+ transaction_index : 1 ,
385+ body : create_test_receipt_body ( ) ,
386+ } ;
387+
388+ let contract_address = address ! ( "0x456" ) ;
389+ let rpc_receipt = gateway_receipt. clone ( ) . to_rpc_deploy_receipt (
390+ FinalityStatus :: AcceptedOnL2 ,
391+ PriceUnit :: Wei ,
392+ contract_address,
393+ ) ;
394+
395+ assert_eq ! ( rpc_receipt. contract_address, contract_address) ;
396+ assert_eq ! ( rpc_receipt. actual_fee. amount, felt!( "0x1234" ) ) ;
397+ }
398+
399+ #[ test]
400+ fn test_to_rpc_deploy_account_receipt ( ) {
401+ let gateway_receipt = ConfirmedReceipt {
402+ transaction_hash : felt ! ( "0x789" ) ,
403+ transaction_index : 2 ,
404+ body : create_test_receipt_body ( ) ,
405+ } ;
406+
407+ let contract_address = address ! ( "0xabc" ) ;
408+ let rpc_receipt = gateway_receipt. clone ( ) . to_rpc_deploy_account_receipt (
409+ FinalityStatus :: AcceptedOnL2 ,
410+ PriceUnit :: Wei ,
411+ contract_address,
412+ ) ;
413+
414+ assert_eq ! ( rpc_receipt. contract_address, contract_address) ;
415+ assert_eq ! ( rpc_receipt. execution_result, katana_rpc_types:: ExecutionResult :: Succeeded ) ;
416+ }
417+
418+ #[ test]
419+ fn test_to_rpc_l1_handler_receipt ( ) {
420+ let gateway_receipt = ConfirmedReceipt {
421+ transaction_hash : felt ! ( "0xfff" ) ,
422+ transaction_index : 3 ,
423+ body : create_test_receipt_body ( ) ,
424+ } ;
425+
426+ let message_hash = katana_primitives:: B256 :: from ( [ 1u8 ; 32 ] ) ;
427+ let rpc_receipt = gateway_receipt. clone ( ) . to_rpc_l1_handler_receipt (
428+ FinalityStatus :: AcceptedOnL2 ,
429+ PriceUnit :: Wei ,
430+ message_hash,
431+ ) ;
432+
433+ assert_eq ! ( rpc_receipt. message_hash, message_hash) ;
434+ assert_eq ! ( rpc_receipt. actual_fee. amount, felt!( "0x1234" ) ) ;
435+ }
436+ }
0 commit comments