1use super::*;
19use alloc::vec::Vec;
20use codec::{Decode, Encode};
21use scale_info::TypeInfo;
22use sp_core::{H160, U256};
23use sp_crypto_hashing::keccak_256;
24
25#[derive(Debug, Encode, TypeInfo, Clone)]
75pub struct DryRunConfig<Moment> {
76 pub timestamp_override: Option<Moment>,
78 pub perform_balance_checks: Option<bool>,
80 pub state_overrides: Option<StateOverrideSet>,
84}
85
86impl<Moment> Default for DryRunConfig<Moment> {
87 fn default() -> Self {
88 Self { timestamp_override: None, perform_balance_checks: Some(true), state_overrides: None }
89 }
90}
91
92impl<Moment: Decode> Decode for DryRunConfig<Moment> {
101 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
102 let timestamp_override = Option::<Moment>::decode(input)?;
103 let perform_balance_checks = Option::<bool>::decode(input)?;
104 let state_overrides = Option::<StateOverrideSet>::decode(input).unwrap_or_default();
105 Ok(Self { timestamp_override, perform_balance_checks, state_overrides })
106 }
107}
108
109impl<Moment> DryRunConfig<Moment> {
110 pub fn new() -> Self {
114 Self::default()
115 }
116
117 pub fn with_timestamp_override(
119 mut self,
120 timestamp_override: impl Into<Option<Moment>>,
121 ) -> Self {
122 self.timestamp_override = timestamp_override.into();
123 self
124 }
125
126 pub fn with_perform_balance_checks(
128 mut self,
129 perform_balance_checks: impl Into<Option<bool>>,
130 ) -> Self {
131 self.perform_balance_checks = perform_balance_checks.into();
132 self
133 }
134
135 pub fn with_state_overrides(
137 mut self,
138 state_overrides: impl Into<Option<StateOverrideSet>>,
139 ) -> Self {
140 self.state_overrides = state_overrides.into();
141 self
142 }
143}
144
145#[derive(Debug, Default, Encode, TypeInfo, Clone)]
171pub struct TracingConfig {
172 pub state_overrides: Option<StateOverrideSet>,
176}
177
178impl Decode for TracingConfig {
187 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
188 let state_overrides = Option::<StateOverrideSet>::decode(input).unwrap_or_default();
189 Ok(Self { state_overrides })
190 }
191}
192
193impl TracingConfig {
194 pub fn new() -> Self {
196 Self::default()
197 }
198
199 pub fn with_state_overrides(
201 mut self,
202 state_overrides: impl Into<Option<StateOverrideSet>>,
203 ) -> Self {
204 self.state_overrides = state_overrides.into();
205 self
206 }
207}
208
209impl From<BlockNumberOrTag> for BlockNumberOrTagOrHash {
210 fn from(b: BlockNumberOrTag) -> Self {
211 match b {
212 BlockNumberOrTag::U256(n) => BlockNumberOrTagOrHash::BlockNumber(n),
213 BlockNumberOrTag::BlockTag(t) => BlockNumberOrTagOrHash::BlockTag(t),
214 }
215 }
216}
217
218impl From<TransactionSigned> for TransactionUnsigned {
219 fn from(tx: TransactionSigned) -> Self {
220 use TransactionSigned::*;
221 match tx {
222 Transaction7702Signed(tx) => tx.transaction_7702_unsigned.into(),
223 Transaction4844Signed(tx) => tx.transaction_4844_unsigned.into(),
224 Transaction1559Signed(tx) => tx.transaction_1559_unsigned.into(),
225 Transaction2930Signed(tx) => tx.transaction_2930_unsigned.into(),
226 TransactionLegacySigned(tx) => tx.transaction_legacy_unsigned.into(),
227 }
228 }
229}
230
231impl TransactionInfo {
232 pub fn new(receipt: &ReceiptInfo, transaction_signed: TransactionSigned) -> Self {
234 Self {
235 block_hash: receipt.block_hash,
236 block_number: receipt.block_number,
237 from: receipt.from,
238 hash: receipt.transaction_hash,
239 transaction_index: receipt.transaction_index,
240 transaction_signed,
241 }
242 }
243}
244
245impl ReceiptInfo {
246 pub fn new(
248 block_hash: H256,
249 block_number: U256,
250 contract_address: Option<Address>,
251 from: Address,
252 logs: Vec<Log>,
253 to: Option<Address>,
254 effective_gas_price: U256,
255 gas_used: U256,
256 success: bool,
257 transaction_hash: H256,
258 transaction_index: U256,
259 r#type: Byte,
260 ) -> Self {
261 let logs_bloom = Self::logs_bloom(&logs);
262 ReceiptInfo {
263 block_hash,
264 block_number,
265 contract_address,
266 from,
267 logs,
268 logs_bloom,
269 to,
270 effective_gas_price,
271 gas_used,
272 status: Some(if success { U256::one() } else { U256::zero() }),
273 transaction_hash,
274 transaction_index,
275 r#type: Some(r#type),
276 ..Default::default()
277 }
278 }
279
280 pub fn is_success(&self) -> bool {
282 self.status.map_or(false, |status| status == U256::one())
283 }
284
285 fn logs_bloom(logs: &[Log]) -> Bytes256 {
287 let mut bloom = [0u8; 256];
288 for log in logs {
289 m3_2048(&mut bloom, &log.address.as_ref());
290 for topic in &log.topics {
291 m3_2048(&mut bloom, topic.as_ref());
292 }
293 }
294 bloom.into()
295 }
296}
297fn m3_2048(bloom: &mut [u8; 256], bytes: &[u8]) {
304 let hash = keccak_256(bytes);
305 for i in [0, 2, 4] {
306 let bit = (hash[i + 1] as usize + ((hash[i] as usize) << 8)) & 0x7FF;
307 bloom[256 - 1 - bit / 8] |= 1 << (bit % 8);
308 }
309}
310
311#[test]
312fn can_deserialize_input_or_data_field_from_generic_transaction() {
313 let cases = [
314 ("with input", r#"{"input": "0x01"}"#),
315 ("with data", r#"{"data": "0x01"}"#),
316 ("with both", r#"{"data": "0x01", "input": "0x01"}"#),
317 ];
318
319 for (name, json) in cases {
320 let tx = serde_json::from_str::<GenericTransaction>(json).unwrap();
321 assert_eq!(tx.input.to_vec(), vec![1u8], "{}", name);
322 }
323
324 let err = serde_json::from_str::<GenericTransaction>(r#"{"data": "0x02", "input": "0x01"}"#)
325 .unwrap_err();
326 assert!(
327 err.to_string().starts_with(
328 "Both \"data\" and \"input\" are set and not equal. Please use \"input\" to pass transaction call data"
329 )
330 );
331}
332
333#[test]
334fn test_block_number_or_tag_or_hash_deserialization() {
335 let val: BlockNumberOrTagOrHash = serde_json::from_str("\"latest\"").unwrap();
336 assert_eq!(val, BlockTag::Latest.into());
337
338 for s in ["\"0x1a\"", r#"{ "blockNumber": "0x1a" }"#] {
339 let val: BlockNumberOrTagOrHash = serde_json::from_str(s).unwrap();
340 assert!(matches!(val, BlockNumberOrTagOrHash::BlockNumber(n) if n == 26u64.into()));
341 }
342
343 for s in [
344 "\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"",
345 r#"{ "blockHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }"#,
346 ] {
347 let val: BlockNumberOrTagOrHash = serde_json::from_str(s).unwrap();
348 assert_eq!(val, BlockNumberOrTagOrHash::BlockHash(H256([0xaau8; 32])));
349 }
350}
351
352#[test]
353fn logs_bloom_works() {
354 let receipt: ReceiptInfo = serde_json::from_str(
355 r#"
356 {
357 "blockHash": "0x835ee379aaabf4802a22a93ad8164c02bbdde2cc03d4552d5c642faf4e09d1f3",
358 "blockNumber": "0x2",
359 "contractAddress": null,
360 "cumulativeGasUsed": "0x5d92",
361 "effectiveGasPrice": "0x2dcd5c2d",
362 "from": "0xb4f1f9ecfe5a28633a27f57300bda217e99b8969",
363 "gasUsed": "0x5d92",
364 "logs": [
365 {
366 "address": "0x82bdb002b9b1f36c42df15fbdc6886abcb2ab31d",
367 "topics": [
368 "0x1585375487296ff2f0370daeec4214074a032b31af827c12622fa9a58c16c7d0",
369 "0x000000000000000000000000b4f1f9ecfe5a28633a27f57300bda217e99b8969"
370 ],
371 "data": "0x00000000000000000000000000000000000000000000000000000000000030390000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20776f726c64000000000000000000000000000000000000000000",
372 "blockNumber": "0x2",
373 "transactionHash": "0xad0075127962bdf73d787f2944bdb5f351876f23c35e6a48c1f5b6463a100af4",
374 "transactionIndex": "0x0",
375 "blockHash": "0x835ee379aaabf4802a22a93ad8164c02bbdde2cc03d4552d5c642faf4e09d1f3",
376 "logIndex": "0x0",
377 "removed": false
378 }
379 ],
380 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000008000000000000000000000000000000000000000000000000800000000040000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000004000000000000000800000000000000000080000000000000000000000000000000000000000000",
381 "status": "0x1",
382 "to": "0x82bdb002b9b1f36c42df15fbdc6886abcb2ab31d",
383 "transactionHash": "0xad0075127962bdf73d787f2944bdb5f351876f23c35e6a48c1f5b6463a100af4",
384 "transactionIndex": "0x0",
385 "type": "0x2"
386 }
387 "#,
388 )
389 .unwrap();
390 assert_eq!(receipt.logs_bloom, ReceiptInfo::logs_bloom(&receipt.logs));
391}
392
393impl GenericTransaction {
394 pub fn from_signed(tx: TransactionSigned, base_gas_price: U256, from: Option<H160>) -> Self {
396 Self::from_unsigned(tx.into(), base_gas_price, from)
397 }
398
399 pub fn effective_gas_price(&self, base_gas_price: U256) -> Option<U256> {
401 let effective_gas_price = if let Some(prio_price) = self.max_priority_fee_per_gas {
402 let max_price = self.max_fee_per_gas?;
403 Some(max_price.min(base_gas_price.saturating_add(prio_price)))
404 } else {
405 self.gas_price
406 };
407
408 effective_gas_price.map(|e| e.min(base_gas_price))
411 }
412
413 pub fn from_unsigned(
415 tx: TransactionUnsigned,
416 base_gas_price: U256,
417 from: Option<H160>,
418 ) -> Self {
419 use TransactionUnsigned::*;
420 let mut tx = match tx {
421 TransactionLegacyUnsigned(tx) => GenericTransaction {
422 from,
423 r#type: Some(tx.r#type.as_byte()),
424 chain_id: tx.chain_id,
425 input: tx.input.into(),
426 nonce: Some(tx.nonce),
427 value: Some(tx.value),
428 to: tx.to,
429 gas: Some(tx.gas),
430 gas_price: Some(tx.gas_price),
431 ..Default::default()
432 },
433 Transaction4844Unsigned(tx) => GenericTransaction {
434 from,
435 r#type: Some(tx.r#type.as_byte()),
436 chain_id: Some(tx.chain_id),
437 input: tx.input.into(),
438 nonce: Some(tx.nonce),
439 value: Some(tx.value),
440 to: Some(tx.to),
441 gas: Some(tx.gas),
442 access_list: Some(tx.access_list),
443 blob_versioned_hashes: tx.blob_versioned_hashes,
444 max_fee_per_blob_gas: Some(tx.max_fee_per_blob_gas),
445 max_fee_per_gas: Some(tx.max_fee_per_gas),
446 max_priority_fee_per_gas: Some(tx.max_priority_fee_per_gas),
447 ..Default::default()
448 },
449 Transaction1559Unsigned(tx) => GenericTransaction {
450 from,
451 r#type: Some(tx.r#type.as_byte()),
452 chain_id: Some(tx.chain_id),
453 input: tx.input.into(),
454 nonce: Some(tx.nonce),
455 value: Some(tx.value),
456 to: tx.to,
457 gas: Some(tx.gas),
458 access_list: Some(tx.access_list),
459 max_fee_per_gas: Some(tx.max_fee_per_gas),
460 max_priority_fee_per_gas: Some(tx.max_priority_fee_per_gas),
461 ..Default::default()
462 },
463 Transaction2930Unsigned(tx) => GenericTransaction {
464 from,
465 r#type: Some(tx.r#type.as_byte()),
466 chain_id: Some(tx.chain_id),
467 input: tx.input.into(),
468 nonce: Some(tx.nonce),
469 value: Some(tx.value),
470 to: tx.to,
471 gas: Some(tx.gas),
472 gas_price: Some(tx.gas_price),
473 access_list: Some(tx.access_list),
474 ..Default::default()
475 },
476 Transaction7702Unsigned(tx) => GenericTransaction {
477 from,
478 r#type: Some(tx.r#type.as_byte()),
479 chain_id: Some(tx.chain_id),
480 input: tx.input.into(),
481 nonce: Some(tx.nonce),
482 value: Some(tx.value),
483 to: Some(tx.to),
484 gas: Some(tx.gas),
485 access_list: Some(tx.access_list),
486 authorization_list: tx.authorization_list,
487 max_fee_per_gas: Some(tx.max_fee_per_gas),
488 max_priority_fee_per_gas: Some(tx.max_priority_fee_per_gas),
489 ..Default::default()
490 },
491 };
492 tx.gas_price = tx.effective_gas_price(base_gas_price);
493 tx
494 }
495
496 pub fn try_into_unsigned(self) -> Result<TransactionUnsigned, ()> {
498 match self.r#type.unwrap_or_default().0 {
499 TYPE_LEGACY => Ok(TransactionLegacyUnsigned {
500 r#type: TypeLegacy {},
501 chain_id: self.chain_id,
502 input: self.input.to_bytes(),
503 nonce: self.nonce.unwrap_or_default(),
504 value: self.value.unwrap_or_default(),
505 to: self.to,
506 gas: self.gas.unwrap_or_default(),
507 gas_price: self.gas_price.unwrap_or_default(),
508 }
509 .into()),
510 TYPE_EIP1559 => Ok(Transaction1559Unsigned {
511 r#type: TypeEip1559 {},
512 chain_id: self.chain_id.unwrap_or_default(),
513 input: self.input.to_bytes(),
514 nonce: self.nonce.unwrap_or_default(),
515 value: self.value.unwrap_or_default(),
516 to: self.to,
517 gas: self.gas.unwrap_or_default(),
518 gas_price: self.max_fee_per_gas.unwrap_or_default(),
519 access_list: self.access_list.unwrap_or_default(),
520 max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(),
521 max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(),
522 }
523 .into()),
524 TYPE_EIP2930 => Ok(Transaction2930Unsigned {
525 r#type: TypeEip2930 {},
526 chain_id: self.chain_id.unwrap_or_default(),
527 input: self.input.to_bytes(),
528 nonce: self.nonce.unwrap_or_default(),
529 value: self.value.unwrap_or_default(),
530 to: self.to,
531 gas: self.gas.unwrap_or_default(),
532 gas_price: self.gas_price.unwrap_or_default(),
533 access_list: self.access_list.unwrap_or_default(),
534 }
535 .into()),
536 TYPE_EIP4844 => Ok(Transaction4844Unsigned {
537 r#type: TypeEip4844 {},
538 chain_id: self.chain_id.unwrap_or_default(),
539 input: self.input.to_bytes(),
540 nonce: self.nonce.unwrap_or_default(),
541 value: self.value.unwrap_or_default(),
542 to: self.to.unwrap_or_default(),
543 gas: self.gas.unwrap_or_default(),
544 max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(),
545 max_fee_per_blob_gas: self.max_fee_per_blob_gas.unwrap_or_default(),
546 max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(),
547 access_list: self.access_list.unwrap_or_default(),
548 blob_versioned_hashes: self.blob_versioned_hashes,
549 }
550 .into()),
551 TYPE_EIP7702 => Ok(Transaction7702Unsigned {
552 r#type: TypeEip7702 {},
553 chain_id: self.chain_id.unwrap_or_default(),
554 input: self.input.to_bytes(),
555 nonce: self.nonce.unwrap_or_default(),
556 value: self.value.unwrap_or_default(),
557 to: self.to.unwrap_or_default(),
558 gas: self.gas.unwrap_or_default(),
559 max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(),
560 max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(),
561 access_list: self.access_list.unwrap_or_default(),
562 authorization_list: self.authorization_list,
563 }
564 .into()),
565 _ => Err(()),
566 }
567 }
568}
569
570#[test]
571fn from_unsigned_works_for_legacy() {
572 let base_gas_price = U256::from(10);
573 let tx = TransactionUnsigned::from(TransactionLegacyUnsigned {
574 chain_id: Some(U256::from(1)),
575 input: Bytes::from(vec![1u8]),
576 nonce: U256::from(1),
577 value: U256::from(1),
578 to: Some(H160::zero()),
579 gas: U256::from(1),
580 gas_price: U256::from(10),
581 ..Default::default()
582 });
583
584 let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
585 assert_eq!(generic.gas_price, Some(U256::from(10)));
586
587 let tx2 = generic.try_into_unsigned().unwrap();
588 assert_eq!(tx, tx2);
589}
590
591#[test]
592fn from_unsigned_works_for_1559() {
593 let base_gas_price = U256::from(10);
594 let tx = TransactionUnsigned::from(Transaction1559Unsigned {
595 chain_id: U256::from(1),
596 input: Bytes::from(vec![1u8]),
597 nonce: U256::from(1),
598 value: U256::from(1),
599 to: Some(H160::zero()),
600 gas: U256::from(1),
601 gas_price: U256::from(20),
602 max_fee_per_gas: U256::from(20),
603 max_priority_fee_per_gas: U256::from(1),
604 ..Default::default()
605 });
606
607 let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
608 assert_eq!(generic.gas_price, Some(U256::from(10)));
609
610 let tx2 = generic.try_into_unsigned().unwrap();
611 assert_eq!(tx, tx2);
612}
613
614#[test]
615fn from_unsigned_works_for_7702() {
616 let base_gas_price = U256::from(10);
617 let tx = TransactionUnsigned::from(Transaction7702Unsigned {
618 chain_id: U256::from(1),
619 input: Bytes::from(vec![1u8]),
620 nonce: U256::from(1),
621 value: U256::from(1),
622 to: H160::zero(),
623 gas: U256::from(1),
624 max_fee_per_gas: U256::from(20),
625 max_priority_fee_per_gas: U256::from(1),
626 authorization_list: vec![AuthorizationListEntry {
627 chain_id: U256::from(1),
628 address: H160::from_low_u64_be(42),
629 nonce: U256::from(0),
630 y_parity: U256::from(1),
631 r: U256::from(1),
632 s: U256::from(2),
633 }],
634 ..Default::default()
635 });
636
637 let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
638 assert_eq!(generic.gas_price, Some(U256::from(10)));
639
640 let tx2 = generic.try_into_unsigned().unwrap();
641 assert_eq!(tx, tx2);
642}