1#![allow(missing_docs)]
19
20use super::{
21 Byte, Bytes, TYPE_EIP1559, TYPE_EIP2930, TYPE_EIP4844, TYPE_EIP7702, TYPE_LEGACY, TypeEip1559,
22 TypeEip2930, TypeEip4844, TypeEip7702, TypeLegacy,
23};
24use alloc::vec::Vec;
25use codec::{Decode, DecodeWithMemTracking, Encode};
26use derive_more::{From, TryInto};
27use ethereum_types::*;
28use scale_info::TypeInfo;
29use serde::{Deserialize, Deserializer, Serialize};
30
31#[derive(
33 Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq,
34)]
35#[serde(rename_all = "camelCase")]
36pub struct GenericTransaction {
37 #[serde(skip_serializing_if = "Option::is_none")]
40 pub access_list: Option<AccessList>,
41 #[serde(default, skip_serializing_if = "Vec::is_empty")]
44 pub authorization_list: Vec<AuthorizationListEntry>,
45 #[serde(default)]
48 pub blob_versioned_hashes: Vec<H256>,
49 #[serde(default, skip_serializing_if = "Vec::is_empty")]
52 pub blobs: Vec<Bytes>,
53 #[serde(skip_serializing_if = "Option::is_none")]
56 pub chain_id: Option<U256>,
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub from: Option<Address>,
60 #[serde(skip_serializing_if = "Option::is_none")]
62 pub gas: Option<U256>,
63 #[serde(skip_serializing_if = "Option::is_none")]
66 pub gas_price: Option<U256>,
67 #[serde(flatten, deserialize_with = "deserialize_input_or_data")]
69 pub input: InputOrData,
70 #[serde(skip_serializing_if = "Option::is_none")]
73 pub max_fee_per_blob_gas: Option<U256>,
74 #[serde(skip_serializing_if = "Option::is_none")]
78 pub max_fee_per_gas: Option<U256>,
79 #[serde(skip_serializing_if = "Option::is_none")]
82 pub max_priority_fee_per_gas: Option<U256>,
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub nonce: Option<U256>,
86 pub to: Option<Address>,
88 #[serde(skip_serializing_if = "Option::is_none")]
90 pub r#type: Option<Byte>,
91 #[serde(skip_serializing_if = "Option::is_none")]
93 pub value: Option<U256>,
94}
95
96impl GenericTransaction {
97 pub fn from_signed(tx: TransactionSigned, base_gas_price: U256, from: Option<H160>) -> Self {
99 Self::from_unsigned(tx.into(), base_gas_price, from)
100 }
101
102 pub fn has_simple_transfer_fields(&self) -> bool {
106 self.input.is_empty() &&
107 self.access_list.as_ref().is_none_or(|list| list.is_empty()) &&
108 self.authorization_list.is_empty() &&
109 self.blob_versioned_hashes.is_empty() &&
110 self.blobs.is_empty() &&
111 self.max_fee_per_blob_gas.is_none()
112 }
113
114 pub fn effective_gas_price(&self, base_gas_price: U256) -> Option<U256> {
116 let effective_gas_price = if let Some(prio_price) = self.max_priority_fee_per_gas {
117 let max_price = self.max_fee_per_gas?;
118 Some(max_price.min(base_gas_price.saturating_add(prio_price)))
119 } else {
120 self.gas_price
121 };
122
123 effective_gas_price.map(|e| e.min(base_gas_price))
126 }
127
128 pub fn from_unsigned(
130 tx: TransactionUnsigned,
131 base_gas_price: U256,
132 from: Option<H160>,
133 ) -> Self {
134 use TransactionUnsigned::*;
135 let mut tx = match tx {
136 TransactionLegacyUnsigned(tx) => GenericTransaction {
137 from,
138 r#type: Some(tx.r#type.as_byte()),
139 chain_id: tx.chain_id,
140 input: tx.input.into(),
141 nonce: Some(tx.nonce),
142 value: Some(tx.value),
143 to: tx.to,
144 gas: Some(tx.gas),
145 gas_price: Some(tx.gas_price),
146 ..Default::default()
147 },
148 Transaction4844Unsigned(tx) => GenericTransaction {
149 from,
150 r#type: Some(tx.r#type.as_byte()),
151 chain_id: Some(tx.chain_id),
152 input: tx.input.into(),
153 nonce: Some(tx.nonce),
154 value: Some(tx.value),
155 to: Some(tx.to),
156 gas: Some(tx.gas),
157 access_list: Some(tx.access_list),
158 blob_versioned_hashes: tx.blob_versioned_hashes,
159 max_fee_per_blob_gas: Some(tx.max_fee_per_blob_gas),
160 max_fee_per_gas: Some(tx.max_fee_per_gas),
161 max_priority_fee_per_gas: Some(tx.max_priority_fee_per_gas),
162 ..Default::default()
163 },
164 Transaction1559Unsigned(tx) => GenericTransaction {
165 from,
166 r#type: Some(tx.r#type.as_byte()),
167 chain_id: Some(tx.chain_id),
168 input: tx.input.into(),
169 nonce: Some(tx.nonce),
170 value: Some(tx.value),
171 to: tx.to,
172 gas: Some(tx.gas),
173 access_list: Some(tx.access_list),
174 max_fee_per_gas: Some(tx.max_fee_per_gas),
175 max_priority_fee_per_gas: Some(tx.max_priority_fee_per_gas),
176 ..Default::default()
177 },
178 Transaction2930Unsigned(tx) => GenericTransaction {
179 from,
180 r#type: Some(tx.r#type.as_byte()),
181 chain_id: Some(tx.chain_id),
182 input: tx.input.into(),
183 nonce: Some(tx.nonce),
184 value: Some(tx.value),
185 to: tx.to,
186 gas: Some(tx.gas),
187 gas_price: Some(tx.gas_price),
188 access_list: Some(tx.access_list),
189 ..Default::default()
190 },
191 Transaction7702Unsigned(tx) => GenericTransaction {
192 from,
193 r#type: Some(tx.r#type.as_byte()),
194 chain_id: Some(tx.chain_id),
195 input: tx.input.into(),
196 nonce: Some(tx.nonce),
197 value: Some(tx.value),
198 to: Some(tx.to),
199 gas: Some(tx.gas),
200 access_list: Some(tx.access_list),
201 authorization_list: tx.authorization_list,
202 max_fee_per_gas: Some(tx.max_fee_per_gas),
203 max_priority_fee_per_gas: Some(tx.max_priority_fee_per_gas),
204 ..Default::default()
205 },
206 };
207 tx.gas_price = tx.effective_gas_price(base_gas_price);
208 tx
209 }
210
211 pub fn try_into_unsigned(self) -> Result<TransactionUnsigned, ()> {
213 match self.r#type.unwrap_or_default().0 {
214 TYPE_LEGACY => Ok(TransactionLegacyUnsigned {
215 r#type: TypeLegacy {},
216 chain_id: self.chain_id,
217 input: self.input.to_bytes(),
218 nonce: self.nonce.unwrap_or_default(),
219 value: self.value.unwrap_or_default(),
220 to: self.to,
221 gas: self.gas.unwrap_or_default(),
222 gas_price: self.gas_price.unwrap_or_default(),
223 }
224 .into()),
225 TYPE_EIP1559 => Ok(Transaction1559Unsigned {
226 r#type: TypeEip1559 {},
227 chain_id: self.chain_id.unwrap_or_default(),
228 input: self.input.to_bytes(),
229 nonce: self.nonce.unwrap_or_default(),
230 value: self.value.unwrap_or_default(),
231 to: self.to,
232 gas: self.gas.unwrap_or_default(),
233 gas_price: self.max_fee_per_gas.unwrap_or_default(),
234 access_list: self.access_list.unwrap_or_default(),
235 max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(),
236 max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(),
237 }
238 .into()),
239 TYPE_EIP2930 => Ok(Transaction2930Unsigned {
240 r#type: TypeEip2930 {},
241 chain_id: self.chain_id.unwrap_or_default(),
242 input: self.input.to_bytes(),
243 nonce: self.nonce.unwrap_or_default(),
244 value: self.value.unwrap_or_default(),
245 to: self.to,
246 gas: self.gas.unwrap_or_default(),
247 gas_price: self.gas_price.unwrap_or_default(),
248 access_list: self.access_list.unwrap_or_default(),
249 }
250 .into()),
251 TYPE_EIP4844 => Ok(Transaction4844Unsigned {
252 r#type: TypeEip4844 {},
253 chain_id: self.chain_id.unwrap_or_default(),
254 input: self.input.to_bytes(),
255 nonce: self.nonce.unwrap_or_default(),
256 value: self.value.unwrap_or_default(),
257 to: self.to.unwrap_or_default(),
258 gas: self.gas.unwrap_or_default(),
259 max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(),
260 max_fee_per_blob_gas: self.max_fee_per_blob_gas.unwrap_or_default(),
261 max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(),
262 access_list: self.access_list.unwrap_or_default(),
263 blob_versioned_hashes: self.blob_versioned_hashes,
264 }
265 .into()),
266 TYPE_EIP7702 => Ok(Transaction7702Unsigned {
267 r#type: TypeEip7702 {},
268 chain_id: self.chain_id.unwrap_or_default(),
269 input: self.input.to_bytes(),
270 nonce: self.nonce.unwrap_or_default(),
271 value: self.value.unwrap_or_default(),
272 to: self.to.unwrap_or_default(),
273 gas: self.gas.unwrap_or_default(),
274 max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(),
275 max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(),
276 access_list: self.access_list.unwrap_or_default(),
277 authorization_list: self.authorization_list,
278 }
279 .into()),
280 _ => Err(()),
281 }
282 }
283}
284
285#[derive(Debug, Default, Clone, Serialize, Eq, PartialEq, TypeInfo, Encode, Decode)]
287#[serde(rename_all = "camelCase")]
288pub struct TransactionInfo {
289 pub block_hash: H256,
291 pub block_number: U256,
293 pub from: Address,
295 pub hash: H256,
297 pub transaction_index: U256,
299 #[serde(flatten)]
300 pub transaction_signed: TransactionSigned,
301}
302
303impl<'de> Deserialize<'de> for TransactionInfo {
306 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
307 where
308 D: serde::Deserializer<'de>,
309 {
310 use alloc::{collections::BTreeMap, string::String};
311 use serde::de::Error;
312
313 let mut map = <BTreeMap<String, serde_json::Value>>::deserialize(deserializer)?;
315
316 let block_hash =
318 map.remove("blockHash").ok_or_else(|| D::Error::missing_field("blockHash"))?;
319 let block_number = map
320 .remove("blockNumber")
321 .ok_or_else(|| D::Error::missing_field("blockNumber"))?;
322 let from = map.remove("from").ok_or_else(|| D::Error::missing_field("from"))?;
323 let hash = map.remove("hash").ok_or_else(|| D::Error::missing_field("hash"))?;
324 let transaction_index = map
325 .remove("transactionIndex")
326 .ok_or_else(|| D::Error::missing_field("transactionIndex"))?;
327
328 let remaining = serde_json::Value::Object(map.into_iter().collect());
331 let json_str = serde_json::to_string(&remaining).map_err(D::Error::custom)?;
332 let transaction_signed: TransactionSigned =
333 serde_json::from_str(&json_str).map_err(D::Error::custom)?;
334
335 Ok(Self {
336 block_hash: serde_json::from_value(block_hash).map_err(D::Error::custom)?,
337 block_number: serde_json::from_value(block_number).map_err(D::Error::custom)?,
338 from: serde_json::from_value(from).map_err(D::Error::custom)?,
339 hash: serde_json::from_value(hash).map_err(D::Error::custom)?,
340 transaction_index: serde_json::from_value(transaction_index)
341 .map_err(D::Error::custom)?,
342 transaction_signed,
343 })
344 }
345}
346
347#[derive(
348 Debug,
349 Clone,
350 Serialize,
351 Deserialize,
352 From,
353 TryInto,
354 Eq,
355 PartialEq,
356 TypeInfo,
357 Encode,
358 Decode,
359 DecodeWithMemTracking,
360)]
361#[serde(untagged)]
362pub enum TransactionSigned {
363 Transaction7702Signed(Transaction7702Signed),
364 Transaction4844Signed(Transaction4844Signed),
365 Transaction1559Signed(Transaction1559Signed),
366 Transaction2930Signed(Transaction2930Signed),
367 TransactionLegacySigned(TransactionLegacySigned),
368}
369
370impl Default for TransactionSigned {
371 fn default() -> Self {
372 TransactionSigned::TransactionLegacySigned(Default::default())
373 }
374}
375
376#[derive(Debug, Clone, Serialize, Deserialize, From, TryInto, Eq, PartialEq)]
377#[serde(untagged)]
378pub enum TransactionUnsigned {
379 Transaction7702Unsigned(Transaction7702Unsigned),
380 Transaction4844Unsigned(Transaction4844Unsigned),
381 Transaction1559Unsigned(Transaction1559Unsigned),
382 Transaction2930Unsigned(Transaction2930Unsigned),
383 TransactionLegacyUnsigned(TransactionLegacyUnsigned),
384}
385
386impl Default for TransactionUnsigned {
387 fn default() -> Self {
388 TransactionUnsigned::TransactionLegacyUnsigned(Default::default())
389 }
390}
391
392impl From<TransactionSigned> for TransactionUnsigned {
393 fn from(tx: TransactionSigned) -> Self {
394 use TransactionSigned::*;
395 match tx {
396 Transaction7702Signed(tx) => tx.transaction_7702_unsigned.into(),
397 Transaction4844Signed(tx) => tx.transaction_4844_unsigned.into(),
398 Transaction1559Signed(tx) => tx.transaction_1559_unsigned.into(),
399 Transaction2930Signed(tx) => tx.transaction_2930_unsigned.into(),
400 TransactionLegacySigned(tx) => tx.transaction_legacy_unsigned.into(),
401 }
402 }
403}
404
405#[derive(
407 Debug,
408 Default,
409 Clone,
410 Serialize,
411 Deserialize,
412 Eq,
413 PartialEq,
414 TypeInfo,
415 Encode,
416 Decode,
417 DecodeWithMemTracking,
418)]
419#[serde(rename_all = "camelCase")]
420pub struct Transaction1559Unsigned {
421 pub access_list: AccessList,
424 pub chain_id: U256,
427 pub gas: U256,
429 pub gas_price: U256,
434 pub input: Bytes,
436 pub max_fee_per_gas: U256,
440 pub max_priority_fee_per_gas: U256,
443 pub nonce: U256,
445 pub to: Option<Address>,
447 pub r#type: TypeEip1559,
449 pub value: U256,
451}
452#[derive(
454 Debug,
455 Default,
456 Clone,
457 Serialize,
458 Deserialize,
459 Eq,
460 PartialEq,
461 TypeInfo,
462 Encode,
463 Decode,
464 DecodeWithMemTracking,
465)]
466#[serde(rename_all = "camelCase")]
467pub struct Transaction2930Unsigned {
468 pub access_list: AccessList,
471 pub chain_id: U256,
474 pub gas: U256,
476 pub gas_price: U256,
479 pub input: Bytes,
481 pub nonce: U256,
483 pub to: Option<Address>,
485 pub r#type: TypeEip2930,
487 pub value: U256,
489}
490#[derive(
492 Debug,
493 Default,
494 Clone,
495 Serialize,
496 Deserialize,
497 Eq,
498 PartialEq,
499 TypeInfo,
500 Encode,
501 Decode,
502 DecodeWithMemTracking,
503)]
504#[serde(rename_all = "camelCase")]
505pub struct Transaction4844Unsigned {
506 pub access_list: AccessList,
509 pub blob_versioned_hashes: Vec<H256>,
512 pub chain_id: U256,
515 pub gas: U256,
517 pub input: Bytes,
519 pub max_fee_per_blob_gas: U256,
522 pub max_fee_per_gas: U256,
526 pub max_priority_fee_per_gas: U256,
529 pub nonce: U256,
531 pub to: Address,
533 pub r#type: TypeEip4844,
535 pub value: U256,
537}
538#[derive(
540 Debug,
541 Default,
542 Clone,
543 Serialize,
544 Deserialize,
545 Eq,
546 PartialEq,
547 TypeInfo,
548 Encode,
549 Decode,
550 DecodeWithMemTracking,
551)]
552#[serde(rename_all = "camelCase")]
553pub struct TransactionLegacyUnsigned {
554 #[serde(skip_serializing_if = "Option::is_none")]
557 pub chain_id: Option<U256>,
558 pub gas: U256,
560 pub gas_price: U256,
563 pub input: Bytes,
565 pub nonce: U256,
567 pub to: Option<Address>,
569 pub r#type: TypeLegacy,
571 pub value: U256,
573}
574#[derive(
576 Debug,
577 Clone,
578 Serialize,
579 Deserialize,
580 Default,
581 From,
582 Eq,
583 PartialEq,
584 TypeInfo,
585 Encode,
586 Decode,
587 DecodeWithMemTracking,
588)]
589#[serde(rename_all = "camelCase")]
590pub struct Transaction7702Unsigned {
591 pub access_list: AccessList,
594 pub authorization_list: Vec<AuthorizationListEntry>,
597 pub chain_id: U256,
600 pub gas: U256,
602 pub input: Bytes,
604 pub max_fee_per_gas: U256,
608 pub max_priority_fee_per_gas: U256,
611 pub nonce: U256,
613 pub to: Address,
619 pub r#type: TypeEip7702,
621 pub value: U256,
623}
624#[derive(
626 Debug,
627 Clone,
628 Serialize,
629 Deserialize,
630 Eq,
631 PartialEq,
632 TypeInfo,
633 Encode,
634 Decode,
635 DecodeWithMemTracking,
636)]
637#[serde(rename_all = "camelCase")]
638pub struct Transaction7702Signed {
639 #[serde(flatten)]
640 pub transaction_7702_unsigned: Transaction7702Unsigned,
641 pub r: U256,
643 pub s: U256,
645 #[serde(skip_serializing_if = "Option::is_none")]
649 pub v: Option<U256>,
650 pub y_parity: U256,
653}
654#[derive(
656 Debug,
657 Default,
658 Clone,
659 Serialize,
660 Deserialize,
661 Eq,
662 PartialEq,
663 TypeInfo,
664 Encode,
665 Decode,
666 DecodeWithMemTracking,
667)]
668#[serde(rename_all = "camelCase")]
669pub struct Transaction1559Signed {
670 #[serde(flatten)]
671 pub transaction_1559_unsigned: Transaction1559Unsigned,
672 pub r: U256,
674 pub s: U256,
676 #[serde(skip_serializing_if = "Option::is_none")]
680 pub v: Option<U256>,
681 pub y_parity: U256,
684}
685#[derive(
687 Debug,
688 Default,
689 Clone,
690 Serialize,
691 Deserialize,
692 Eq,
693 PartialEq,
694 TypeInfo,
695 Encode,
696 Decode,
697 DecodeWithMemTracking,
698)]
699#[serde(rename_all = "camelCase")]
700pub struct Transaction2930Signed {
701 #[serde(flatten)]
702 pub transaction_2930_unsigned: Transaction2930Unsigned,
703 pub r: U256,
705 pub s: U256,
707 #[serde(skip_serializing_if = "Option::is_none")]
711 pub v: Option<U256>,
712 pub y_parity: U256,
715}
716#[derive(
718 Debug,
719 Default,
720 Clone,
721 Serialize,
722 Deserialize,
723 Eq,
724 PartialEq,
725 TypeInfo,
726 Encode,
727 Decode,
728 DecodeWithMemTracking,
729)]
730#[serde(rename_all = "camelCase")]
731pub struct Transaction4844Signed {
732 #[serde(flatten)]
733 pub transaction_4844_unsigned: Transaction4844Unsigned,
734 pub r: U256,
736 pub s: U256,
738 pub y_parity: U256,
741}
742#[derive(
744 Debug,
745 Default,
746 Clone,
747 Serialize,
748 Deserialize,
749 Eq,
750 PartialEq,
751 TypeInfo,
752 Encode,
753 Decode,
754 DecodeWithMemTracking,
755)]
756#[serde(rename_all = "camelCase")]
757pub struct TransactionLegacySigned {
758 #[serde(flatten)]
759 pub transaction_legacy_unsigned: TransactionLegacyUnsigned,
760 pub r: U256,
762 pub s: U256,
764 pub v: U256,
766}
767
768pub type AccessList = Vec<AccessListEntry>;
770
771#[derive(
773 Debug,
774 Default,
775 Clone,
776 Encode,
777 Decode,
778 TypeInfo,
779 Serialize,
780 Deserialize,
781 Eq,
782 PartialEq,
783 DecodeWithMemTracking,
784)]
785#[serde(rename_all = "camelCase")]
786pub struct AccessListEntry {
787 pub address: Address,
788 pub storage_keys: Vec<H256>,
789}
790
791#[derive(
793 Debug,
794 Default,
795 Clone,
796 Serialize,
797 Deserialize,
798 Eq,
799 PartialEq,
800 TypeInfo,
801 Encode,
802 Decode,
803 DecodeWithMemTracking,
804)]
805#[serde(rename_all = "camelCase")]
806pub struct AuthorizationListEntry {
807 pub chain_id: U256,
809 pub address: Address,
811 pub nonce: U256,
813 pub y_parity: U256,
815 pub r: U256,
817 pub s: U256,
819}
820
821#[derive(
822 Debug, Clone, Serialize, Deserialize, From, TryInto, Eq, PartialEq, TypeInfo, Encode, Decode,
823)]
824#[serde(untagged)]
825pub enum HashesOrTransactionInfos {
826 Hashes(Vec<H256>),
828 TransactionInfos(Vec<TransactionInfo>),
830}
831
832impl Default for HashesOrTransactionInfos {
833 fn default() -> Self {
834 HashesOrTransactionInfos::Hashes(Default::default())
835 }
836}
837
838impl HashesOrTransactionInfos {
839 pub fn push_hash(&mut self, hash: H256) {
840 match self {
841 HashesOrTransactionInfos::Hashes(hashes) => hashes.push(hash),
842 _ => {},
843 }
844 }
845
846 pub fn len(&self) -> usize {
847 match self {
848 HashesOrTransactionInfos::Hashes(v) => v.len(),
849 HashesOrTransactionInfos::TransactionInfos(v) => v.len(),
850 }
851 }
852
853 pub fn is_empty(&self) -> bool {
854 self.len() == 0
855 }
856
857 pub fn contains_tx(&self, hash: H256) -> bool {
858 match self {
859 HashesOrTransactionInfos::Hashes(hashes) => hashes.iter().any(|h256| *h256 == hash),
860 HashesOrTransactionInfos::TransactionInfos(transaction_infos) => {
861 transaction_infos.iter().any(|ti| ti.hash == hash)
862 },
863 }
864 }
865}
866
867#[derive(
869 Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq,
870)]
871pub struct InputOrData {
872 #[serde(skip_serializing_if = "Option::is_none")]
873 input: Option<Bytes>,
874 #[serde(skip_serializing_if = "Option::is_none")]
875 data: Option<Bytes>,
876}
877
878impl From<Bytes> for InputOrData {
879 fn from(value: Bytes) -> Self {
880 InputOrData { input: Some(value), data: None }
881 }
882}
883
884impl From<Vec<u8>> for InputOrData {
885 fn from(value: Vec<u8>) -> Self {
886 InputOrData { input: Some(Bytes(value)), data: None }
887 }
888}
889
890impl InputOrData {
891 pub fn to_bytes(self) -> Bytes {
893 match self {
894 InputOrData { input: Some(input), data: _ } => input,
895 InputOrData { input: None, data: Some(data) } => data,
896 _ => Default::default(),
897 }
898 }
899
900 pub fn to_vec(self) -> Vec<u8> {
902 self.to_bytes().0
903 }
904
905 pub fn as_slice(&self) -> &[u8] {
907 self.input
908 .as_ref()
909 .or(self.data.as_ref())
910 .map(|bytes| bytes.0.as_slice())
911 .unwrap_or_default()
912 }
913
914 pub fn is_empty(&self) -> bool {
916 self.as_slice().is_empty()
917 }
918}
919
920fn deserialize_input_or_data<'d, D: Deserializer<'d>>(d: D) -> Result<InputOrData, D::Error> {
921 let value = InputOrData::deserialize(d)?;
922 match &value {
923 InputOrData { input: Some(input), data: Some(data) } if input != data => {
924 Err(serde::de::Error::custom(
925 "Both \"data\" and \"input\" are set and not equal. Please use \"input\" to pass transaction call data",
926 ))
927 },
928 _ => Ok(value),
929 }
930}
931
932#[cfg(test)]
933mod tests {
934 use crate::evm::*;
935
936 #[test]
937 fn test_transaction_info_deserialize_from_value() {
938 let tx_info_expected = serde_json::json!({
941 "blockHash": "0xfb8c980d1da1a75e68c2ea4d55cb88d62dedbbb5eaf69df8fe337e9f6922b73a",
942 "blockNumber": "0x161bd0f",
943 "from": "0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97",
944 "hash": "0x2c522d01183e9ed70caaf75c940ba9908d573cfc9996b3e7adc90313798279c8",
945 "transactionIndex": "0x7a",
946 "chainId": "0x1",
947 "gas": "0x565f",
948 "gasPrice": "0x23cf3fd4",
949 "input": "0x",
950 "nonce": "0x2c5ce1",
951 "r": "0x4a5703e4d8daf045f021cb32897a25b17d61b9ab629a59f0731ef4cce63f93d6",
952 "s": "0x711812237c1fed6aaf08e9f47fc47e547fdaceba9ab7507e62af29a945354fb6",
953 "to": "0x388c818ca8b9251b393131c08a736a67ccb19297",
954 "type": "0x0",
955 "v": "0x1",
956 "value": "0x12bf92aae0c2e70"
957 });
958
959 let tx_info_from_value: Result<TransactionInfo, serde_json::Error> =
965 serde_json::from_value(tx_info_expected.clone());
966 assert!(
967 tx_info_from_value.is_ok(),
968 "Failed to deserialize from Value: {:?}",
969 tx_info_from_value.err()
970 );
971
972 let json_str = serde_json::to_string(&tx_info_expected).unwrap();
974 let tx_info_from_str: Result<TransactionInfo, serde_json::Error> =
975 serde_json::from_str(&json_str);
976 assert!(
977 tx_info_from_str.is_ok(),
978 "Failed to deserialize from string: {:?}",
979 tx_info_from_str.err()
980 );
981
982 let tx_info_from_value = tx_info_from_value.unwrap();
984 let tx_info_from_str = tx_info_from_str.unwrap();
985 assert_eq!(
986 tx_info_from_value, tx_info_from_str,
987 "Value and string deserialization should match"
988 );
989
990 let tx_info_serialized = serde_json::to_value(&tx_info_from_value);
992 assert!(
993 tx_info_serialized.is_ok(),
994 "Failed to serialize to value: {:?}",
995 tx_info_serialized.err()
996 );
997 let tx_info_serialized = tx_info_serialized.unwrap();
998
999 assert_eq!(tx_info_serialized, tx_info_expected);
1001 }
1002
1003 #[test]
1004 fn test_transaction_hashes_deserialization() {
1005 let json = r#"["0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"]"#;
1006 let result: HashesOrTransactionInfos = serde_json::from_str(json).unwrap();
1007 assert!(matches!(result, HashesOrTransactionInfos::Hashes(_)));
1008
1009 let json = r#"[]"#;
1010 let result: HashesOrTransactionInfos = serde_json::from_str(json).unwrap();
1011 assert!(matches!(result, HashesOrTransactionInfos::Hashes(_)));
1012
1013 let json = r#"[{"invalid": "data"}]"#;
1014 let result: Result<HashesOrTransactionInfos, _> = serde_json::from_str(json);
1015 assert!(result.is_err());
1016 }
1017
1018 #[test]
1019 fn test_transaction_infos_deserialization() {
1020 let json = r#"[{
1021 "accessList": [{
1022 "address": "0x9008d19f58aabd9ed0d60971565aa8510560ab41",
1023 "storageKeys": [
1024 "0x0000000000000000000000000000000000000000000000000000000000000001"
1025 ]
1026 }],
1027 "blockHash": "0xfb8c980d1da1a75e68c2ea4d55cb88d62dedbbb5eaf69df8fe337e9f6922b73a",
1028 "blockNumber": "0x161bd0f",
1029 "chainId": "0x1",
1030 "from": "0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97",
1031 "gas": "0x565f",
1032 "gasPrice": "0x23cf3fd4",
1033 "hash": "0x2c522d01183e9ed70caaf75c940ba9908d573cfc9996b3e7adc90313798279c8",
1034 "input": "0x",
1035 "maxFeePerGas": "0x23cf3fd4",
1036 "maxPriorityFeePerGas": "0x0",
1037 "nonce": "0x2c5ce1",
1038 "r": "0x4a5703e4d8daf045f021cb32897a25b17d61b9ab629a59f0731ef4cce63f93d6",
1039 "s": "0x711812237c1fed6aaf08e9f47fc47e547fdaceba9ab7507e62af29a945354fb6",
1040 "to": "0x388c818ca8b9251b393131c08a736a67ccb19297",
1041 "transactionIndex": "0x7a",
1042 "type": "0x2",
1043 "v": "0x0",
1044 "value": "0x12bf92aae0c2e70",
1045 "yParity": "0x0"
1046 }]
1047 "#;
1048 let result: HashesOrTransactionInfos = serde_json::from_str(json).unwrap();
1049 assert!(matches!(result, HashesOrTransactionInfos::TransactionInfos(_)));
1050 }
1051
1052 #[test]
1053 fn can_deserialize_input_or_data_field_from_generic_transaction() {
1054 let cases = [
1055 ("with input", r#"{"input": "0x01"}"#),
1056 ("with data", r#"{"data": "0x01"}"#),
1057 ("with both", r#"{"data": "0x01", "input": "0x01"}"#),
1058 ];
1059
1060 for (name, json) in cases {
1061 let tx = serde_json::from_str::<GenericTransaction>(json).unwrap();
1062 assert_eq!(tx.input.to_vec(), vec![1u8], "{}", name);
1063 }
1064
1065 let err =
1066 serde_json::from_str::<GenericTransaction>(r#"{"data": "0x02", "input": "0x01"}"#)
1067 .unwrap_err();
1068 assert!(
1069 err.to_string().starts_with(
1070 "Both \"data\" and \"input\" are set and not equal. Please use \"input\" to pass transaction call data"
1071 )
1072 );
1073 }
1074
1075 #[test]
1076 fn from_unsigned_works_for_legacy() {
1077 let base_gas_price = U256::from(10);
1078 let tx = TransactionUnsigned::from(TransactionLegacyUnsigned {
1079 chain_id: Some(U256::from(1)),
1080 input: Bytes::from(vec![1u8]),
1081 nonce: U256::from(1),
1082 value: U256::from(1),
1083 to: Some(H160::zero()),
1084 gas: U256::from(1),
1085 gas_price: U256::from(10),
1086 ..Default::default()
1087 });
1088
1089 let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
1090 assert_eq!(generic.gas_price, Some(U256::from(10)));
1091
1092 let tx2 = generic.try_into_unsigned().unwrap();
1093 assert_eq!(tx, tx2);
1094 }
1095
1096 #[test]
1097 fn from_unsigned_works_for_1559() {
1098 let base_gas_price = U256::from(10);
1099 let tx = TransactionUnsigned::from(Transaction1559Unsigned {
1100 chain_id: U256::from(1),
1101 input: Bytes::from(vec![1u8]),
1102 nonce: U256::from(1),
1103 value: U256::from(1),
1104 to: Some(H160::zero()),
1105 gas: U256::from(1),
1106 gas_price: U256::from(20),
1107 max_fee_per_gas: U256::from(20),
1108 max_priority_fee_per_gas: U256::from(1),
1109 ..Default::default()
1110 });
1111
1112 let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
1113 assert_eq!(generic.gas_price, Some(U256::from(10)));
1114
1115 let tx2 = generic.try_into_unsigned().unwrap();
1116 assert_eq!(tx, tx2);
1117 }
1118
1119 #[test]
1120 fn from_unsigned_works_for_7702() {
1121 let base_gas_price = U256::from(10);
1122 let tx = TransactionUnsigned::from(Transaction7702Unsigned {
1123 chain_id: U256::from(1),
1124 input: Bytes::from(vec![1u8]),
1125 nonce: U256::from(1),
1126 value: U256::from(1),
1127 to: H160::zero(),
1128 gas: U256::from(1),
1129 max_fee_per_gas: U256::from(20),
1130 max_priority_fee_per_gas: U256::from(1),
1131 authorization_list: vec![AuthorizationListEntry {
1132 chain_id: U256::from(1),
1133 address: H160::from_low_u64_be(42),
1134 nonce: U256::from(0),
1135 y_parity: U256::from(1),
1136 r: U256::from(1),
1137 s: U256::from(2),
1138 }],
1139 ..Default::default()
1140 });
1141
1142 let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
1143 assert_eq!(generic.gas_price, Some(U256::from(10)));
1144
1145 let tx2 = generic.try_into_unsigned().unwrap();
1146 assert_eq!(tx, tx2);
1147 }
1148}