@@ -329,6 +329,68 @@ pub struct BroadcastedTxWithChainId {
329329 pub chain : ChainId ,
330330}
331331
332+ impl BroadcastedTxWithChainId {
333+ /// Compute the hash of the transaction.
334+ pub fn calculate_hash ( & self ) -> TxHash {
335+ let is_query = self . tx . is_query ( ) ;
336+ match & self . tx {
337+ BroadcastedTx :: Invoke ( tx) => {
338+ let invoke_tx = InvokeTx :: V3 ( InvokeTxV3 {
339+ chain_id : self . chain ,
340+ tip : tx. tip . into ( ) ,
341+ nonce : tx. nonce ,
342+ calldata : tx. calldata . clone ( ) ,
343+ signature : tx. signature . clone ( ) ,
344+ sender_address : tx. sender_address ,
345+ paymaster_data : tx. paymaster_data . clone ( ) ,
346+ resource_bounds : tx. resource_bounds . clone ( ) ,
347+ account_deployment_data : tx. account_deployment_data . clone ( ) ,
348+ fee_data_availability_mode : tx. fee_data_availability_mode ,
349+ nonce_data_availability_mode : tx. nonce_data_availability_mode ,
350+ } ) ;
351+ invoke_tx. calculate_hash ( is_query)
352+ }
353+
354+ BroadcastedTx :: Declare ( tx) => {
355+ let declare_tx = DeclareTx :: V3 ( DeclareTxV3 {
356+ chain_id : self . chain ,
357+ class_hash : tx. contract_class . hash ( ) . expect ( "failed to compute class hash" ) ,
358+ tip : tx. tip . into ( ) ,
359+ nonce : tx. nonce ,
360+ signature : tx. signature . clone ( ) ,
361+ paymaster_data : tx. paymaster_data . clone ( ) ,
362+ sender_address : tx. sender_address ,
363+ resource_bounds : tx. resource_bounds . clone ( ) ,
364+ compiled_class_hash : tx. compiled_class_hash ,
365+ account_deployment_data : tx. account_deployment_data . clone ( ) ,
366+ fee_data_availability_mode : tx. fee_data_availability_mode ,
367+ nonce_data_availability_mode : tx. nonce_data_availability_mode ,
368+ } ) ;
369+ declare_tx. calculate_hash ( is_query)
370+ }
371+
372+ BroadcastedTx :: DeployAccount ( tx) => {
373+ let contract_address = tx. contract_address ( ) ;
374+ let deploy_account_tx = DeployAccountTx :: V3 ( DeployAccountTxV3 {
375+ chain_id : self . chain ,
376+ tip : tx. tip . into ( ) ,
377+ nonce : tx. nonce ,
378+ contract_address,
379+ signature : tx. signature . clone ( ) ,
380+ class_hash : tx. class_hash ,
381+ paymaster_data : tx. paymaster_data . clone ( ) ,
382+ contract_address_salt : tx. contract_address_salt ,
383+ constructor_calldata : tx. constructor_calldata . clone ( ) ,
384+ resource_bounds : tx. resource_bounds . clone ( ) ,
385+ fee_data_availability_mode : tx. fee_data_availability_mode ,
386+ nonce_data_availability_mode : tx. nonce_data_availability_mode ,
387+ } ) ;
388+ deploy_account_tx. calculate_hash ( is_query)
389+ }
390+ }
391+ }
392+ }
393+
332394/// A broadcasted transaction.
333395#[ derive( Debug , Clone , Serialize ) ]
334396#[ serde( untagged) ]
@@ -1216,6 +1278,76 @@ mod tests {
12161278 similar_asserts:: assert_eq!( actual, expected) ;
12171279 }
12181280
1281+ #[ test]
1282+ fn broadcasted_tx_with_chain_id_calculate_hash ( ) {
1283+ use katana_primitives:: chain:: ChainId ;
1284+
1285+ // Test with an Invoke transaction
1286+ let invoke_json = json ! ( {
1287+ "type" : "INVOKE" ,
1288+ "version" : "0x3" ,
1289+ "signature" : [ ] ,
1290+ "tip" : "0x0" ,
1291+ "paymaster_data" : [ ] ,
1292+ "resource_bounds" : {
1293+ "l1_gas" : {
1294+ "max_amount" : "0x100" ,
1295+ "max_price_per_unit" : "0x200"
1296+ } ,
1297+ "l2_gas" : {
1298+ "max_amount" : "0x0" ,
1299+ "max_price_per_unit" : "0x0"
1300+ }
1301+ } ,
1302+ "nonce_data_availability_mode" : "L1" ,
1303+ "fee_data_availability_mode" : "L1" ,
1304+ "sender_address" : "0x123" ,
1305+ "calldata" : [ "0x1" , "0x2" ] ,
1306+ "nonce" : "0x0" ,
1307+ "account_deployment_data" : [ ]
1308+ } ) ;
1309+
1310+ let invoke_tx: BroadcastedTx = serde_json:: from_value ( invoke_json) . unwrap ( ) ;
1311+ let tx_with_chain = BroadcastedTxWithChainId { tx : invoke_tx, chain : ChainId :: SEPOLIA } ;
1312+
1313+ // The hash should be computed successfully
1314+ let hash = tx_with_chain. calculate_hash ( ) ;
1315+ assert_ne ! ( hash, Felt :: ZERO ) ;
1316+
1317+ // Test with a DeployAccount transaction
1318+ let deploy_account_json = json ! ( {
1319+ "type" : "DEPLOY_ACCOUNT" ,
1320+ "version" : "0x3" ,
1321+ "signature" : [ "0x1" , "0x2" , "0x3" ] ,
1322+ "tip" : "0x1" ,
1323+ "paymaster_data" : [ ] ,
1324+ "resource_bounds" : {
1325+ "l1_gas" : {
1326+ "max_amount" : "0x100" ,
1327+ "max_price_per_unit" : "0x200"
1328+ } ,
1329+ "l2_gas" : {
1330+ "max_amount" : "0x123" ,
1331+ "max_price_per_unit" : "0x1337"
1332+ }
1333+ } ,
1334+ "nonce_data_availability_mode" : "L1" ,
1335+ "fee_data_availability_mode" : "L1" ,
1336+ "nonce" : "0x0" ,
1337+ "contract_address_salt" : "0xabc" ,
1338+ "constructor_calldata" : [ "0x1" , "0x2" ] ,
1339+ "class_hash" : "0xdef"
1340+ } ) ;
1341+
1342+ let deploy_account_tx: BroadcastedTx = serde_json:: from_value ( deploy_account_json) . unwrap ( ) ;
1343+ let tx_with_chain =
1344+ BroadcastedTxWithChainId { tx : deploy_account_tx, chain : ChainId :: SEPOLIA } ;
1345+
1346+ // The hash should be computed successfully
1347+ let hash = tx_with_chain. calculate_hash ( ) ;
1348+ assert_ne ! ( hash, Felt :: ZERO ) ;
1349+ }
1350+
12191351 // This will currently fail because the l2 gas on legacy resource bounds format are not
12201352 // preserved throughout the serialization process.
12211353 #[ test]
0 commit comments