@@ -190,6 +190,8 @@ pub enum RawBundleConvertError {
190190 UnsupportedVersion ( String ) ,
191191 #[ error( "Field {0} not supported by version {1:?}" ) ]
192192 FieldNotSupportedByVersion ( String , BundleVersion ) ,
193+ #[ error( "More than one refund tx hash not supported" ) ]
194+ MoreThanOneRefundTxHash ,
193195}
194196
195197/// Since we use the same API (eth_sendBundle) to get new bundles and also to cancel them we need this struct.
@@ -333,13 +335,22 @@ impl RawBundle {
333335 if let Some ( percent) = refund_percent {
334336 // Refund can be configured only if bundle is not empty.
335337 // If bundle contains only one transaction, first == last.
338+ // If refund_tx_hashes is empty we use the last tx.
336339 if let Some ( ( first_tx, last_tx) ) = txs. first ( ) . zip ( txs. last ( ) ) {
340+ let tx_hash = if let Some ( refund_tx_hashes) = refund_tx_hashes {
341+ if refund_tx_hashes. len ( ) > 1 {
342+ return Err ( RawBundleConvertError :: MoreThanOneRefundTxHash ) ;
343+ }
344+ refund_tx_hashes. first ( ) . copied ( )
345+ } else {
346+ None
347+ }
348+ . unwrap_or ( last_tx. hash ( ) ) ;
349+
337350 refund = Some ( BundleRefund {
338351 percent,
339352 recipient : refund_recipient. unwrap_or_else ( || first_tx. signer ( ) ) ,
340- tx_hashes : refund_tx_hashes
341- . filter ( |tx_hashes| !tx_hashes. is_empty ( ) )
342- . unwrap_or_else ( || Vec :: from ( [ last_tx. hash ( ) ] ) ) ,
353+ tx_hash,
343354 } ) ;
344355 }
345356 }
@@ -411,7 +422,7 @@ impl RawBundle {
411422 replacement_nonce,
412423 refund_percent : value. refund . as_ref ( ) . map ( |br| br. percent ) ,
413424 refund_recipient : value. refund . as_ref ( ) . map ( |br| br. recipient ) ,
414- refund_tx_hashes : value. refund . map ( |br| br. tx_hashes ) ,
425+ refund_tx_hashes : value. refund . map ( |br| vec ! [ br. tx_hash ] ) ,
415426 first_seen_at : None ,
416427 version : Some ( Self :: encode_version ( value. version ) ) ,
417428 }
@@ -1128,21 +1139,100 @@ mod tests {
11281139 . clone ( )
11291140 . decode_new_bundle ( TxEncoding :: WithBlobData )
11301141 . expect ( "failed to convert bundle request to bundle" ) ;
1131-
1142+ println ! ( "{}" , bundle . txs [ 0 ] . hash ( ) ) ;
11321143 assert_eq ! ( bundle. block, None ) ;
11331144 assert_eq ! (
11341145 bundle. refund,
11351146 Some ( BundleRefund {
11361147 percent: 1 ,
11371148 recipient: Address :: from_str( "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5" ) . unwrap( ) ,
1138- tx_hashes : Vec :: from ( [ b256!(
1149+ tx_hash : b256!(
11391150 "0x75662ab9cb6d1be7334723db5587435616352c7e581a52867959ac24006ac1fe"
1140- ) ] ) ,
1151+ ) ,
11411152 } )
11421153 ) ;
11431154 assert_eq ! ( bundle. uuid, uuid!( "e2bdb8cd-9473-5a1b-b425-57fa7ecfe2c1" ) ) ;
11441155 }
11451156
1157+ /// If refundTxHashes is missing it should use the last tx and the id should be the same.
1158+ #[ test]
1159+ fn test_correct_bundle_decoding_refund_hash_missing ( ) {
1160+ // raw json string
1161+ let base_bundle_json = r#"
1162+ {
1163+ "version": "v2",
1164+ "txs": [
1165+ "0x02f86b83aa36a780800982520894f24a01ae29dec4629dfb4170647c4ed4efc392cd861ca62a4c95b880c080a07d37bb5a4da153a6fbe24cf1f346ef35748003d1d0fc59cf6c17fb22d49e42cea02c231ac233220b494b1ad501c440c8b1a34535cdb8ca633992d6f35b14428672"
1166+ ],
1167+ "blockNumber": 0,
1168+ "minTimestamp": 123,
1169+ "maxTimestamp": 1234,
1170+ "revertingTxHashes": ["0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"],
1171+ "droppingTxHashes": ["0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],
1172+ "refundPercent": 1,
1173+ "refundRecipient": "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5"
1174+ "# ;
1175+ let bundles = [
1176+ base_bundle_json. to_owned ( ) + "}" ,
1177+ base_bundle_json. to_owned ( )
1178+ + r#","refundTxHashes": ["0x84310f7f7860f0cd65407fe340d471ca008d0c58976746a560312d4aebba3f4a"]}"# ,
1179+ ] ;
1180+
1181+ for bundle_json in bundles {
1182+ let bundle_request: RawBundle =
1183+ serde_json:: from_str ( & bundle_json) . expect ( "failed to decode bundle" ) ;
1184+
1185+ let bundle = bundle_request
1186+ . clone ( )
1187+ . decode_new_bundle ( TxEncoding :: WithBlobData )
1188+ . expect ( "failed to convert bundle request to bundle" ) ;
1189+ assert_eq ! ( bundle. block, None ) ;
1190+ assert_eq ! (
1191+ bundle. refund,
1192+ Some ( BundleRefund {
1193+ percent: 1 ,
1194+ recipient: Address :: from_str( "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5" )
1195+ . unwrap( ) ,
1196+ tx_hash: b256!(
1197+ "0x84310f7f7860f0cd65407fe340d471ca008d0c58976746a560312d4aebba3f4a"
1198+ ) ,
1199+ } )
1200+ ) ;
1201+ assert_eq ! ( bundle. uuid, uuid!( "ea9954e1-b7be-5af0-9c39-6b11c9d24c05" ) ) ;
1202+ }
1203+ }
1204+
1205+ /// More than 1 refundTxHashes should fail.
1206+ #[ test]
1207+ fn test_fail_bundle_decoding_2_refund_hashes ( ) {
1208+ // raw json string
1209+ let bundle_json = r#"
1210+ {
1211+ "version": "v2",
1212+ "txs": [
1213+ "0x02f86b83aa36a780800982520894f24a01ae29dec4629dfb4170647c4ed4efc392cd861ca62a4c95b880c080a07d37bb5a4da153a6fbe24cf1f346ef35748003d1d0fc59cf6c17fb22d49e42cea02c231ac233220b494b1ad501c440c8b1a34535cdb8ca633992d6f35b14428672"
1214+ ],
1215+ "blockNumber": 0,
1216+ "minTimestamp": 123,
1217+ "maxTimestamp": 1234,
1218+ "revertingTxHashes": ["0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"],
1219+ "droppingTxHashes": ["0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],
1220+ "refundPercent": 1,
1221+ "refundRecipient": "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5",
1222+ "refundTxHashes": ["0x84310f7f7860f0cd65407fe340d471ca008d0c58976746a560312d4aebba3f4a","0x84310f7f7860f0cd65407fe340d471ca008d0c58976746a560312d4aebba3f4a"]
1223+ }
1224+ "# ;
1225+ let bundle_request: RawBundle =
1226+ serde_json:: from_str ( bundle_json) . expect ( "failed to decode bundle" ) ;
1227+
1228+ assert ! ( matches!(
1229+ bundle_request
1230+ . clone( )
1231+ . decode_new_bundle( TxEncoding :: WithBlobData ) ,
1232+ Err ( RawBundleConvertError :: MoreThanOneRefundTxHash )
1233+ ) ) ;
1234+ }
1235+
11461236 /// Should default to last version.
11471237 #[ test]
11481238 fn test_correct_bundle_decoding_no_version ( ) {
0 commit comments