1use crate::{
2 hypersync_net_types_capnp,
3 types::{AnyOf, Sighash},
4 CapnpBuilder, CapnpReader, Selection,
5};
6use anyhow::Context;
7use hypersync_format::{Address, FilterWrapper, Hash};
8use serde::{Deserialize, Serialize};
9
10#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq)]
11#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
12pub struct AuthorizationSelection {
13 #[serde(default, skip_serializing_if = "Vec::is_empty")]
15 pub chain_id: Vec<u64>,
16 #[serde(default, skip_serializing_if = "Vec::is_empty")]
18 pub address: Vec<Address>,
19}
20
21impl AuthorizationSelection {
22 pub fn all() -> Self {
27 Default::default()
28 }
29
30 pub fn and_chain_id<I>(mut self, chain_ids: I) -> Self
59 where
60 I: IntoIterator<Item = u64>,
61 {
62 self.chain_id = chain_ids.into_iter().collect();
63 self
64 }
65
66 pub fn and_address<I, A>(mut self, addresses: I) -> anyhow::Result<Self>
104 where
105 I: IntoIterator<Item = A>,
106 A: TryInto<Address>,
107 A::Error: std::error::Error + Send + Sync + 'static,
108 {
109 let mut converted_addresses: Vec<Address> = Vec::new();
110 for (idx, address) in addresses.into_iter().enumerate() {
111 converted_addresses.push(
112 address
113 .try_into()
114 .with_context(|| format!("invalid authorization address at position {idx}"))?,
115 );
116 }
117 self.address = converted_addresses;
118 Ok(self)
119 }
120}
121
122pub type TransactionSelection = Selection<TransactionFilter>;
123
124impl From<TransactionFilter> for AnyOf<TransactionFilter> {
125 fn from(filter: TransactionFilter) -> Self {
126 Self::new(filter)
127 }
128}
129
130#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq)]
131#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
132pub struct TransactionFilter {
133 #[serde(default, skip_serializing_if = "Vec::is_empty")]
137 pub from: Vec<Address>,
138 #[serde(default, skip_serializing_if = "Option::is_none")]
139 pub from_filter: Option<FilterWrapper>,
140 #[serde(default, skip_serializing_if = "Vec::is_empty")]
144 pub to: Vec<Address>,
145 #[serde(default, skip_serializing_if = "Option::is_none")]
146 pub to_filter: Option<FilterWrapper>,
147 #[serde(default, skip_serializing_if = "Vec::is_empty")]
149 pub sighash: Vec<Sighash>,
150 #[serde(skip_serializing_if = "Option::is_none")]
152 pub status: Option<u8>,
153 #[serde(rename = "type")]
155 #[serde(default, skip_serializing_if = "Vec::is_empty")]
156 pub type_: Vec<u8>,
157 #[serde(default, skip_serializing_if = "Vec::is_empty")]
159 pub contract_address: Vec<Address>,
160 #[serde(default, skip_serializing_if = "Option::is_none")]
164 pub contract_address_filter: Option<FilterWrapper>,
165 #[serde(default, skip_serializing_if = "Vec::is_empty")]
168 pub hash: Vec<Hash>,
169
170 #[serde(default, skip_serializing_if = "Vec::is_empty")]
172 pub authorization_list: Vec<AuthorizationSelection>,
173}
174
175impl TransactionFilter {
176 pub fn all() -> Self {
181 Default::default()
182 }
183
184 pub fn or(self, other: Self) -> AnyOf<Self> {
210 AnyOf::new(self).or(other)
211 }
212
213 pub fn and_from<I, A>(mut self, addresses: I) -> anyhow::Result<Self>
251 where
252 I: IntoIterator<Item = A>,
253 A: TryInto<Address>,
254 A::Error: std::error::Error + Send + Sync + 'static,
255 {
256 let mut converted_addresses: Vec<Address> = Vec::new();
257 for (idx, address) in addresses.into_iter().enumerate() {
258 converted_addresses.push(
259 address
260 .try_into()
261 .with_context(|| format!("invalid from address at position {idx}"))?,
262 );
263 }
264 self.from = converted_addresses;
265 Ok(self)
266 }
267
268 pub fn and_to<I, A>(mut self, addresses: I) -> anyhow::Result<Self>
303 where
304 I: IntoIterator<Item = A>,
305 A: TryInto<Address>,
306 A::Error: std::error::Error + Send + Sync + 'static,
307 {
308 let mut converted_addresses: Vec<Address> = Vec::new();
309 for (idx, address) in addresses.into_iter().enumerate() {
310 converted_addresses.push(
311 address
312 .try_into()
313 .with_context(|| format!("invalid to address at position {idx}"))?,
314 );
315 }
316 self.to = converted_addresses;
317 Ok(self)
318 }
319
320 pub fn and_sighash<I, S>(mut self, sighashes: I) -> anyhow::Result<Self>
356 where
357 I: IntoIterator<Item = S>,
358 S: TryInto<Sighash>,
359 S::Error: std::error::Error + Send + Sync + 'static,
360 {
361 let mut converted_sighashes: Vec<Sighash> = Vec::new();
362 for (idx, sighash) in sighashes.into_iter().enumerate() {
363 converted_sighashes.push(
364 sighash
365 .try_into()
366 .with_context(|| format!("invalid sighash at position {idx}"))?,
367 );
368 }
369 self.sighash = converted_sighashes;
370 Ok(self)
371 }
372
373 pub fn and_status(mut self, status: u8) -> Self {
398 self.status = Some(status);
399 self
400 }
401
402 pub fn and_type<I>(mut self, types: I) -> Self
425 where
426 I: IntoIterator<Item = u8>,
427 {
428 self.type_ = types.into_iter().collect();
429 self
430 }
431
432 pub fn and_contract_address<I, A>(mut self, addresses: I) -> anyhow::Result<Self>
462 where
463 I: IntoIterator<Item = A>,
464 A: TryInto<Address>,
465 A::Error: std::error::Error + Send + Sync + 'static,
466 {
467 let mut converted_addresses: Vec<Address> = Vec::new();
468 for (idx, address) in addresses.into_iter().enumerate() {
469 converted_addresses.push(
470 address
471 .try_into()
472 .with_context(|| format!("invalid contract address at position {idx}"))?,
473 );
474 }
475 self.contract_address = converted_addresses;
476 Ok(self)
477 }
478
479 pub fn and_hash<I, H>(mut self, hashes: I) -> anyhow::Result<Self>
517 where
518 I: IntoIterator<Item = H>,
519 H: TryInto<Hash>,
520 H::Error: std::error::Error + Send + Sync + 'static,
521 {
522 let mut converted_hashes: Vec<Hash> = Vec::new();
523 for (idx, hash) in hashes.into_iter().enumerate() {
524 converted_hashes.push(
525 hash.try_into()
526 .with_context(|| format!("invalid transaction hash at position {idx}"))?,
527 );
528 }
529 self.hash = converted_hashes;
530 Ok(self)
531 }
532
533 pub fn and_authorization_list<I>(mut self, selections: I) -> anyhow::Result<Self>
577 where
578 I: IntoIterator<Item = AuthorizationSelection>,
579 {
580 self.authorization_list = selections.into_iter().collect();
581 Ok(self)
582 }
583}
584
585impl CapnpBuilder<hypersync_net_types_capnp::authorization_selection::Owned>
586 for AuthorizationSelection
587{
588 fn populate_builder(
589 &self,
590 builder: &mut hypersync_net_types_capnp::authorization_selection::Builder,
591 ) -> Result<(), capnp::Error> {
592 if !self.chain_id.is_empty() {
594 let mut chain_list = builder.reborrow().init_chain_id(self.chain_id.len() as u32);
595 for (i, chain_id) in self.chain_id.iter().enumerate() {
596 chain_list.set(i as u32, *chain_id);
597 }
598 }
599
600 if !self.address.is_empty() {
602 let mut addr_list = builder.reborrow().init_address(self.address.len() as u32);
603 for (i, addr) in self.address.iter().enumerate() {
604 addr_list.set(i as u32, addr.as_slice());
605 }
606 }
607
608 Ok(())
609 }
610}
611
612impl CapnpReader<hypersync_net_types_capnp::authorization_selection::Owned>
613 for AuthorizationSelection
614{
615 fn from_reader(
617 reader: hypersync_net_types_capnp::authorization_selection::Reader,
618 ) -> Result<Self, capnp::Error> {
619 let mut auth_selection = AuthorizationSelection::default();
620
621 if reader.has_chain_id() {
623 let chain_list = reader.get_chain_id()?;
624 for i in 0..chain_list.len() {
625 auth_selection.chain_id.push(chain_list.get(i));
626 }
627 }
628
629 if reader.has_address() {
631 let addr_list = reader.get_address()?;
632 for i in 0..addr_list.len() {
633 let addr_data = addr_list.get(i)?;
634 if addr_data.len() == 20 {
635 let mut addr_bytes = [0u8; 20];
636 addr_bytes.copy_from_slice(addr_data);
637 auth_selection.address.push(Address::from(addr_bytes));
638 }
639 }
640 }
641
642 Ok(auth_selection)
643 }
644}
645
646impl CapnpBuilder<hypersync_net_types_capnp::transaction_filter::Owned> for TransactionFilter {
647 fn populate_builder(
648 &self,
649 builder: &mut hypersync_net_types_capnp::transaction_filter::Builder,
650 ) -> Result<(), capnp::Error> {
651 if !self.from.is_empty() {
653 let mut from_list = builder.reborrow().init_from(self.from.len() as u32);
654 for (i, addr) in self.from.iter().enumerate() {
655 from_list.set(i as u32, addr.as_slice());
656 }
657 }
658
659 if let Some(filter) = &self.from_filter {
661 builder.reborrow().set_from_filter(filter.0.as_bytes());
662 }
663
664 if !self.to.is_empty() {
666 let mut to_list = builder.reborrow().init_to(self.to.len() as u32);
667 for (i, addr) in self.to.iter().enumerate() {
668 to_list.set(i as u32, addr.as_slice());
669 }
670 }
671
672 if let Some(filter) = &self.to_filter {
674 builder.reborrow().set_to_filter(filter.0.as_bytes());
675 }
676
677 if !self.sighash.is_empty() {
679 let mut sighash_list = builder.reborrow().init_sighash(self.sighash.len() as u32);
680 for (i, sighash) in self.sighash.iter().enumerate() {
681 sighash_list.set(i as u32, sighash.as_slice());
682 }
683 }
684
685 if let Some(status) = self.status {
687 let mut status_builder = builder.reborrow().init_status();
688 status_builder.set_value(status);
689 }
690
691 if !self.type_.is_empty() {
693 let mut type_list = builder.reborrow().init_type(self.type_.len() as u32);
694 for (i, type_) in self.type_.iter().enumerate() {
695 type_list.set(i as u32, *type_);
696 }
697 }
698
699 if !self.contract_address.is_empty() {
701 let mut contract_list = builder
702 .reborrow()
703 .init_contract_address(self.contract_address.len() as u32);
704 for (i, addr) in self.contract_address.iter().enumerate() {
705 contract_list.set(i as u32, addr.as_slice());
706 }
707 }
708
709 if let Some(filter) = &self.contract_address_filter {
711 builder
712 .reborrow()
713 .set_contract_address_filter(filter.0.as_bytes());
714 }
715
716 if !self.hash.is_empty() {
718 let mut hash_list = builder.reborrow().init_hash(self.hash.len() as u32);
719 for (i, hash) in self.hash.iter().enumerate() {
720 hash_list.set(i as u32, hash.as_slice());
721 }
722 }
723
724 if !self.authorization_list.is_empty() {
726 let mut auth_list = builder
727 .reborrow()
728 .init_authorization_list(self.authorization_list.len() as u32);
729 for (i, auth_sel) in self.authorization_list.iter().enumerate() {
730 let mut auth_builder = auth_list.reborrow().get(i as u32);
731 AuthorizationSelection::populate_builder(auth_sel, &mut auth_builder)?;
732 }
733 }
734
735 Ok(())
736 }
737}
738
739impl CapnpReader<hypersync_net_types_capnp::transaction_filter::Owned> for TransactionFilter {
740 fn from_reader(
742 reader: hypersync_net_types_capnp::transaction_filter::Reader,
743 ) -> Result<Self, capnp::Error> {
744 let mut from = Vec::new();
745
746 if reader.has_from() {
748 let from_list = reader.get_from()?;
749 for i in 0..from_list.len() {
750 let addr_data = from_list.get(i)?;
751 if addr_data.len() == 20 {
752 let mut addr_bytes = [0u8; 20];
753 addr_bytes.copy_from_slice(addr_data);
754 from.push(Address::from(addr_bytes));
755 }
756 }
757 }
758
759 let mut from_filter = None;
760
761 if reader.has_from_filter() {
763 let filter_data = reader.get_from_filter()?;
764 let Ok(wrapper) = FilterWrapper::from_bytes(filter_data) else {
766 return Err(capnp::Error::failed("Invalid from filter".to_string()));
767 };
768 from_filter = Some(wrapper);
769 }
770
771 let mut to = Vec::new();
772
773 if reader.has_to() {
775 let to_list = reader.get_to()?;
776 for i in 0..to_list.len() {
777 let addr_data = to_list.get(i)?;
778 if addr_data.len() == 20 {
779 let mut addr_bytes = [0u8; 20];
780 addr_bytes.copy_from_slice(addr_data);
781 to.push(Address::from(addr_bytes));
782 }
783 }
784 }
785
786 let mut to_filter = None;
787
788 if reader.has_to_filter() {
790 let filter_data = reader.get_to_filter()?;
791 let Ok(wrapper) = FilterWrapper::from_bytes(filter_data) else {
792 return Err(capnp::Error::failed("Invalid to filter".to_string()));
793 };
794 to_filter = Some(wrapper);
795 }
796
797 let mut sighash = Vec::new();
798
799 if reader.has_sighash() {
801 let sighash_list = reader.get_sighash()?;
802 for i in 0..sighash_list.len() {
803 let sighash_data = sighash_list.get(i)?;
804 if sighash_data.len() == 4 {
805 let mut sighash_bytes = [0u8; 4];
806 sighash_bytes.copy_from_slice(sighash_data);
807 sighash.push(Sighash::from(sighash_bytes));
808 }
809 }
810 }
811
812 let mut status = None;
814 if reader.has_status() {
815 let status_reader = reader.get_status()?;
816 status = Some(status_reader.get_value());
817 }
818
819 let mut type_ = Vec::new();
820
821 if reader.has_type() {
823 let type_list = reader.get_type()?;
824 for i in 0..type_list.len() {
825 type_.push(type_list.get(i));
826 }
827 }
828
829 let mut contract_address = Vec::new();
830 if reader.has_contract_address() {
832 let contract_list = reader.get_contract_address()?;
833 for i in 0..contract_list.len() {
834 let addr_data = contract_list.get(i)?;
835 if addr_data.len() == 20 {
836 let mut addr_bytes = [0u8; 20];
837 addr_bytes.copy_from_slice(addr_data);
838 contract_address.push(Address::from(addr_bytes));
839 }
840 }
841 }
842
843 let mut contract_address_filter = None;
844
845 if reader.has_contract_address_filter() {
847 let filter_data = reader.get_contract_address_filter()?;
848 let Ok(wrapper) = FilterWrapper::from_bytes(filter_data) else {
849 return Err(capnp::Error::failed(
850 "Invalid contract address filter".to_string(),
851 ));
852 };
853 contract_address_filter = Some(wrapper);
854 }
855
856 let mut hash = Vec::new();
857
858 if reader.has_hash() {
860 let hash_list = reader.get_hash()?;
861 for i in 0..hash_list.len() {
862 let hash_data = hash_list.get(i)?;
863 if hash_data.len() == 32 {
864 let mut hash_bytes = [0u8; 32];
865 hash_bytes.copy_from_slice(hash_data);
866 hash.push(Hash::from(hash_bytes));
867 }
868 }
869 }
870
871 let mut authorization_list = Vec::new();
872
873 if reader.has_authorization_list() {
875 let auth_list = reader.get_authorization_list()?;
876 for i in 0..auth_list.len() {
877 let auth_reader = auth_list.get(i);
878 let auth_selection = AuthorizationSelection::from_reader(auth_reader)?;
879 authorization_list.push(auth_selection);
880 }
881 }
882
883 Ok(Self {
884 from,
885 from_filter,
886 to,
887 to_filter,
888 sighash,
889 status,
890 type_,
891 contract_address,
892 contract_address_filter,
893 hash,
894 authorization_list,
895 })
896 }
897}
898
899#[derive(
900 Debug,
901 Clone,
902 Copy,
903 Serialize,
904 Deserialize,
905 PartialEq,
906 Eq,
907 schemars::JsonSchema,
908 strum_macros::EnumIter,
909 strum_macros::AsRefStr,
910 strum_macros::Display,
911 strum_macros::EnumString,
912)]
913#[serde(rename_all = "snake_case")]
914#[strum(serialize_all = "snake_case")]
915#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
916pub enum TransactionField {
917 BlockHash,
919 BlockNumber,
920 Gas,
921 Hash,
922 Input,
923 Nonce,
924 TransactionIndex,
925 Value,
926 CumulativeGasUsed,
927 EffectiveGasPrice,
928 GasUsed,
929 LogsBloom,
930
931 From,
933 GasPrice,
934 To,
935 V,
936 R,
937 S,
938 MaxPriorityFeePerGas,
939 MaxFeePerGas,
940 ChainId,
941 ContractAddress,
942 Type,
943 Root,
944 Status,
945 YParity,
946 AccessList,
947 AuthorizationList,
948 L1Fee,
949 L1GasPrice,
950 L1GasUsed,
951 L1FeeScalar,
952 GasUsedForL1,
953 MaxFeePerBlobGas,
954 BlobVersionedHashes,
955 BlobGasPrice,
956 BlobGasUsed,
957 DepositNonce,
958 DepositReceiptVersion,
959 L1BaseFeeScalar,
960 L1BlobBaseFee,
961 L1BlobBaseFeeScalar,
962 L1BlockNumber,
963 Mint,
964 Sighash,
965 SourceHash,
966}
967
968impl Ord for TransactionField {
969 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
970 self.as_ref().cmp(other.as_ref())
971 }
972}
973
974impl PartialOrd for TransactionField {
975 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
976 Some(self.cmp(other))
977 }
978}
979
980impl TransactionField {
981 pub fn all() -> std::collections::BTreeSet<Self> {
982 use strum::IntoEnumIterator;
983 Self::iter().collect()
984 }
985
986 pub fn to_capnp(&self) -> crate::hypersync_net_types_capnp::TransactionField {
988 match self {
989 TransactionField::BlockHash => {
990 crate::hypersync_net_types_capnp::TransactionField::BlockHash
991 }
992 TransactionField::BlockNumber => {
993 crate::hypersync_net_types_capnp::TransactionField::BlockNumber
994 }
995 TransactionField::Gas => crate::hypersync_net_types_capnp::TransactionField::Gas,
996 TransactionField::Hash => crate::hypersync_net_types_capnp::TransactionField::Hash,
997 TransactionField::Input => crate::hypersync_net_types_capnp::TransactionField::Input,
998 TransactionField::Nonce => crate::hypersync_net_types_capnp::TransactionField::Nonce,
999 TransactionField::TransactionIndex => {
1000 crate::hypersync_net_types_capnp::TransactionField::TransactionIndex
1001 }
1002 TransactionField::Value => crate::hypersync_net_types_capnp::TransactionField::Value,
1003 TransactionField::CumulativeGasUsed => {
1004 crate::hypersync_net_types_capnp::TransactionField::CumulativeGasUsed
1005 }
1006 TransactionField::EffectiveGasPrice => {
1007 crate::hypersync_net_types_capnp::TransactionField::EffectiveGasPrice
1008 }
1009 TransactionField::GasUsed => {
1010 crate::hypersync_net_types_capnp::TransactionField::GasUsed
1011 }
1012 TransactionField::LogsBloom => {
1013 crate::hypersync_net_types_capnp::TransactionField::LogsBloom
1014 }
1015 TransactionField::From => crate::hypersync_net_types_capnp::TransactionField::From,
1016 TransactionField::GasPrice => {
1017 crate::hypersync_net_types_capnp::TransactionField::GasPrice
1018 }
1019 TransactionField::To => crate::hypersync_net_types_capnp::TransactionField::To,
1020 TransactionField::V => crate::hypersync_net_types_capnp::TransactionField::V,
1021 TransactionField::R => crate::hypersync_net_types_capnp::TransactionField::R,
1022 TransactionField::S => crate::hypersync_net_types_capnp::TransactionField::S,
1023 TransactionField::MaxPriorityFeePerGas => {
1024 crate::hypersync_net_types_capnp::TransactionField::MaxPriorityFeePerGas
1025 }
1026 TransactionField::MaxFeePerGas => {
1027 crate::hypersync_net_types_capnp::TransactionField::MaxFeePerGas
1028 }
1029 TransactionField::ChainId => {
1030 crate::hypersync_net_types_capnp::TransactionField::ChainId
1031 }
1032 TransactionField::ContractAddress => {
1033 crate::hypersync_net_types_capnp::TransactionField::ContractAddress
1034 }
1035 TransactionField::Type => crate::hypersync_net_types_capnp::TransactionField::Type,
1036 TransactionField::Root => crate::hypersync_net_types_capnp::TransactionField::Root,
1037 TransactionField::Status => crate::hypersync_net_types_capnp::TransactionField::Status,
1038 TransactionField::YParity => {
1039 crate::hypersync_net_types_capnp::TransactionField::YParity
1040 }
1041 TransactionField::AccessList => {
1042 crate::hypersync_net_types_capnp::TransactionField::AccessList
1043 }
1044 TransactionField::AuthorizationList => {
1045 crate::hypersync_net_types_capnp::TransactionField::AuthorizationList
1046 }
1047 TransactionField::L1Fee => crate::hypersync_net_types_capnp::TransactionField::L1Fee,
1048 TransactionField::L1GasPrice => {
1049 crate::hypersync_net_types_capnp::TransactionField::L1GasPrice
1050 }
1051 TransactionField::L1GasUsed => {
1052 crate::hypersync_net_types_capnp::TransactionField::L1GasUsed
1053 }
1054 TransactionField::L1FeeScalar => {
1055 crate::hypersync_net_types_capnp::TransactionField::L1FeeScalar
1056 }
1057 TransactionField::GasUsedForL1 => {
1058 crate::hypersync_net_types_capnp::TransactionField::GasUsedForL1
1059 }
1060 TransactionField::MaxFeePerBlobGas => {
1061 crate::hypersync_net_types_capnp::TransactionField::MaxFeePerBlobGas
1062 }
1063 TransactionField::BlobVersionedHashes => {
1064 crate::hypersync_net_types_capnp::TransactionField::BlobVersionedHashes
1065 }
1066 TransactionField::BlobGasPrice => {
1067 crate::hypersync_net_types_capnp::TransactionField::BlobGasPrice
1068 }
1069 TransactionField::BlobGasUsed => {
1070 crate::hypersync_net_types_capnp::TransactionField::BlobGasUsed
1071 }
1072 TransactionField::DepositNonce => {
1073 crate::hypersync_net_types_capnp::TransactionField::DepositNonce
1074 }
1075 TransactionField::DepositReceiptVersion => {
1076 crate::hypersync_net_types_capnp::TransactionField::DepositReceiptVersion
1077 }
1078 TransactionField::L1BaseFeeScalar => {
1079 crate::hypersync_net_types_capnp::TransactionField::L1BaseFeeScalar
1080 }
1081 TransactionField::L1BlobBaseFee => {
1082 crate::hypersync_net_types_capnp::TransactionField::L1BlobBaseFee
1083 }
1084 TransactionField::L1BlobBaseFeeScalar => {
1085 crate::hypersync_net_types_capnp::TransactionField::L1BlobBaseFeeScalar
1086 }
1087 TransactionField::L1BlockNumber => {
1088 crate::hypersync_net_types_capnp::TransactionField::L1BlockNumber
1089 }
1090 TransactionField::Mint => crate::hypersync_net_types_capnp::TransactionField::Mint,
1091 TransactionField::Sighash => {
1092 crate::hypersync_net_types_capnp::TransactionField::Sighash
1093 }
1094 TransactionField::SourceHash => {
1095 crate::hypersync_net_types_capnp::TransactionField::SourceHash
1096 }
1097 }
1098 }
1099
1100 pub fn from_capnp(field: crate::hypersync_net_types_capnp::TransactionField) -> Self {
1102 match field {
1103 crate::hypersync_net_types_capnp::TransactionField::BlockHash => {
1104 TransactionField::BlockHash
1105 }
1106 crate::hypersync_net_types_capnp::TransactionField::BlockNumber => {
1107 TransactionField::BlockNumber
1108 }
1109 crate::hypersync_net_types_capnp::TransactionField::Gas => TransactionField::Gas,
1110 crate::hypersync_net_types_capnp::TransactionField::Hash => TransactionField::Hash,
1111 crate::hypersync_net_types_capnp::TransactionField::Input => TransactionField::Input,
1112 crate::hypersync_net_types_capnp::TransactionField::Nonce => TransactionField::Nonce,
1113 crate::hypersync_net_types_capnp::TransactionField::TransactionIndex => {
1114 TransactionField::TransactionIndex
1115 }
1116 crate::hypersync_net_types_capnp::TransactionField::Value => TransactionField::Value,
1117 crate::hypersync_net_types_capnp::TransactionField::CumulativeGasUsed => {
1118 TransactionField::CumulativeGasUsed
1119 }
1120 crate::hypersync_net_types_capnp::TransactionField::EffectiveGasPrice => {
1121 TransactionField::EffectiveGasPrice
1122 }
1123 crate::hypersync_net_types_capnp::TransactionField::GasUsed => {
1124 TransactionField::GasUsed
1125 }
1126 crate::hypersync_net_types_capnp::TransactionField::LogsBloom => {
1127 TransactionField::LogsBloom
1128 }
1129 crate::hypersync_net_types_capnp::TransactionField::From => TransactionField::From,
1130 crate::hypersync_net_types_capnp::TransactionField::GasPrice => {
1131 TransactionField::GasPrice
1132 }
1133 crate::hypersync_net_types_capnp::TransactionField::To => TransactionField::To,
1134 crate::hypersync_net_types_capnp::TransactionField::V => TransactionField::V,
1135 crate::hypersync_net_types_capnp::TransactionField::R => TransactionField::R,
1136 crate::hypersync_net_types_capnp::TransactionField::S => TransactionField::S,
1137 crate::hypersync_net_types_capnp::TransactionField::MaxPriorityFeePerGas => {
1138 TransactionField::MaxPriorityFeePerGas
1139 }
1140 crate::hypersync_net_types_capnp::TransactionField::MaxFeePerGas => {
1141 TransactionField::MaxFeePerGas
1142 }
1143 crate::hypersync_net_types_capnp::TransactionField::ChainId => {
1144 TransactionField::ChainId
1145 }
1146 crate::hypersync_net_types_capnp::TransactionField::ContractAddress => {
1147 TransactionField::ContractAddress
1148 }
1149 crate::hypersync_net_types_capnp::TransactionField::Type => TransactionField::Type,
1150 crate::hypersync_net_types_capnp::TransactionField::Root => TransactionField::Root,
1151 crate::hypersync_net_types_capnp::TransactionField::Status => TransactionField::Status,
1152 crate::hypersync_net_types_capnp::TransactionField::YParity => {
1153 TransactionField::YParity
1154 }
1155 crate::hypersync_net_types_capnp::TransactionField::AccessList => {
1156 TransactionField::AccessList
1157 }
1158 crate::hypersync_net_types_capnp::TransactionField::AuthorizationList => {
1159 TransactionField::AuthorizationList
1160 }
1161 crate::hypersync_net_types_capnp::TransactionField::L1Fee => TransactionField::L1Fee,
1162 crate::hypersync_net_types_capnp::TransactionField::L1GasPrice => {
1163 TransactionField::L1GasPrice
1164 }
1165 crate::hypersync_net_types_capnp::TransactionField::L1GasUsed => {
1166 TransactionField::L1GasUsed
1167 }
1168 crate::hypersync_net_types_capnp::TransactionField::L1FeeScalar => {
1169 TransactionField::L1FeeScalar
1170 }
1171 crate::hypersync_net_types_capnp::TransactionField::GasUsedForL1 => {
1172 TransactionField::GasUsedForL1
1173 }
1174 crate::hypersync_net_types_capnp::TransactionField::MaxFeePerBlobGas => {
1175 TransactionField::MaxFeePerBlobGas
1176 }
1177 crate::hypersync_net_types_capnp::TransactionField::BlobVersionedHashes => {
1178 TransactionField::BlobVersionedHashes
1179 }
1180 crate::hypersync_net_types_capnp::TransactionField::BlobGasPrice => {
1181 TransactionField::BlobGasPrice
1182 }
1183 crate::hypersync_net_types_capnp::TransactionField::BlobGasUsed => {
1184 TransactionField::BlobGasUsed
1185 }
1186 crate::hypersync_net_types_capnp::TransactionField::DepositNonce => {
1187 TransactionField::DepositNonce
1188 }
1189 crate::hypersync_net_types_capnp::TransactionField::DepositReceiptVersion => {
1190 TransactionField::DepositReceiptVersion
1191 }
1192 crate::hypersync_net_types_capnp::TransactionField::L1BaseFeeScalar => {
1193 TransactionField::L1BaseFeeScalar
1194 }
1195 crate::hypersync_net_types_capnp::TransactionField::L1BlobBaseFee => {
1196 TransactionField::L1BlobBaseFee
1197 }
1198 crate::hypersync_net_types_capnp::TransactionField::L1BlobBaseFeeScalar => {
1199 TransactionField::L1BlobBaseFeeScalar
1200 }
1201 crate::hypersync_net_types_capnp::TransactionField::L1BlockNumber => {
1202 TransactionField::L1BlockNumber
1203 }
1204 crate::hypersync_net_types_capnp::TransactionField::Mint => TransactionField::Mint,
1205 crate::hypersync_net_types_capnp::TransactionField::Sighash => {
1206 TransactionField::Sighash
1207 }
1208 crate::hypersync_net_types_capnp::TransactionField::SourceHash => {
1209 TransactionField::SourceHash
1210 }
1211 }
1212 }
1213}
1214
1215#[cfg(test)]
1216mod tests {
1217 use hypersync_format::Hex;
1218
1219 use super::*;
1220 use crate::{query::tests::test_query_serde, Query};
1221
1222 #[test]
1223 fn test_all_fields_in_schema() {
1224 let schema = hypersync_schema::transaction();
1225 let schema_fields = schema
1226 .fields
1227 .iter()
1228 .map(|f| f.name.clone())
1229 .collect::<std::collections::BTreeSet<_>>();
1230 let all_fields = TransactionField::all()
1231 .into_iter()
1232 .map(|f| f.as_ref().to_string())
1233 .collect::<std::collections::BTreeSet<_>>();
1234 assert_eq!(schema_fields, all_fields);
1235 }
1236
1237 #[test]
1238 fn test_serde_matches_strum() {
1239 for field in TransactionField::all() {
1240 let serialized = serde_json::to_string(&field).unwrap();
1241 let strum = serde_json::to_string(&field.as_ref()).unwrap();
1242 assert_eq!(serialized, strum, "strum value should be the same as serde");
1243 }
1244 }
1245
1246 #[test]
1247 fn test_transaction_filter_serde_with_defaults() {
1248 let transaction_filter = TransactionSelection::default();
1249 let query = Query::new()
1250 .where_transactions(transaction_filter)
1251 .select_transaction_fields(TransactionField::all());
1252
1253 test_query_serde(query, "transaction selection with defaults");
1254 }
1255 #[test]
1256 fn test_transaction_filter_serde_with_explicit_defaults() {
1257 let transaction_filter = TransactionFilter {
1258 from: Vec::default(),
1259 from_filter: Some(FilterWrapper::new(16, 0)),
1260 to: Vec::default(),
1261 to_filter: Some(FilterWrapper::new(16, 0)),
1262 sighash: Vec::default(),
1263 status: Some(u8::default()),
1264 type_: Vec::default(),
1265 contract_address: Vec::default(),
1266 contract_address_filter: Some(FilterWrapper::new(16, 0)),
1267 hash: Vec::default(),
1268 authorization_list: Vec::default(),
1269 };
1270 let query = Query::new()
1271 .where_transactions(transaction_filter)
1272 .select_transaction_fields(TransactionField::all());
1273
1274 test_query_serde(query, "transaction selection with explicit defaults");
1275 }
1276
1277 #[test]
1278 fn test_transaction_filter_serde_with_full_values() {
1279 let transaction_filter = TransactionFilter {
1280 from: vec![Address::decode_hex("0xdadB0d80178819F2319190D340ce9A924f783711").unwrap()],
1281 from_filter: Some(FilterWrapper::new(16, 1)),
1282 to: vec![Address::decode_hex("0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6").unwrap()],
1283 to_filter: Some(FilterWrapper::new(16, 1)),
1284 sighash: vec![Sighash::from([0x12, 0x34, 0x56, 0x78])],
1285 status: Some(1),
1286 type_: vec![2],
1287 contract_address: vec![Address::decode_hex(
1288 "0x1234567890123456789012345678901234567890",
1289 )
1290 .unwrap()],
1291 contract_address_filter: Some(FilterWrapper::new(16, 1)),
1292 hash: vec![Hash::decode_hex(
1293 "0x40d008f2a1653f09b7b028d30c7fd1ba7c84900fcfb032040b3eb3d16f84d294",
1294 )
1295 .unwrap()],
1296 authorization_list: Vec::default(),
1297 };
1298 let query = Query::new()
1299 .where_transactions(transaction_filter)
1300 .select_transaction_fields(TransactionField::all());
1301
1302 test_query_serde(query, "transaction selection with full values");
1303 }
1304
1305 #[test]
1306 fn test_authorization_selection_serde_with_values() {
1307 let auth_selection = AuthorizationSelection {
1308 chain_id: vec![1, 137, 42161],
1309 address: vec![
1310 Address::decode_hex("0xdadB0d80178819F2319190D340ce9A924f783711").unwrap(),
1311 ],
1312 };
1313 let transaction_filter = TransactionFilter {
1314 from: Vec::default(),
1315 from_filter: Some(FilterWrapper::new(16, 0)),
1316 to: Vec::default(),
1317 to_filter: Some(FilterWrapper::new(16, 0)),
1318 sighash: Vec::default(),
1319 status: Some(u8::default()),
1320 type_: Vec::default(),
1321 contract_address: Vec::default(),
1322 contract_address_filter: Some(FilterWrapper::new(16, 0)),
1323 hash: Vec::default(),
1324 authorization_list: vec![auth_selection],
1325 };
1326 let query = Query::new()
1327 .where_transactions(transaction_filter)
1328 .select_transaction_fields(TransactionField::all());
1329
1330 test_query_serde(query, "authorization selection with rest defaults");
1331 }
1332}