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