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 const fn is_nullable(&self) -> bool {
987 match self {
988 TransactionField::From
989 | TransactionField::GasPrice
990 | TransactionField::To
991 | TransactionField::V
992 | TransactionField::R
993 | TransactionField::S
994 | TransactionField::MaxPriorityFeePerGas
995 | TransactionField::MaxFeePerGas
996 | TransactionField::ChainId
997 | TransactionField::ContractAddress
998 | TransactionField::Type
999 | TransactionField::Root
1000 | TransactionField::Status
1001 | TransactionField::Sighash
1002 | TransactionField::YParity
1003 | TransactionField::AccessList
1004 | TransactionField::AuthorizationList
1005 | TransactionField::L1Fee
1006 | TransactionField::L1GasPrice
1007 | TransactionField::L1GasUsed
1008 | TransactionField::L1FeeScalar
1009 | TransactionField::GasUsedForL1
1010 | TransactionField::MaxFeePerBlobGas
1011 | TransactionField::BlobVersionedHashes
1012 | TransactionField::DepositNonce
1013 | TransactionField::BlobGasPrice
1014 | TransactionField::DepositReceiptVersion
1015 | TransactionField::BlobGasUsed
1016 | TransactionField::L1BaseFeeScalar
1017 | TransactionField::L1BlobBaseFee
1018 | TransactionField::L1BlobBaseFeeScalar
1019 | TransactionField::L1BlockNumber
1020 | TransactionField::Mint
1021 | TransactionField::SourceHash => true,
1022 TransactionField::BlockHash
1023 | TransactionField::BlockNumber
1024 | TransactionField::Gas
1025 | TransactionField::Hash
1026 | TransactionField::Input
1027 | TransactionField::Nonce
1028 | TransactionField::TransactionIndex
1029 | TransactionField::Value
1030 | TransactionField::CumulativeGasUsed
1031 | TransactionField::EffectiveGasPrice
1032 | TransactionField::GasUsed
1033 | TransactionField::LogsBloom => false,
1034 }
1035 }
1036
1037 pub fn to_capnp(&self) -> crate::hypersync_net_types_capnp::TransactionField {
1039 match self {
1040 TransactionField::BlockHash => {
1041 crate::hypersync_net_types_capnp::TransactionField::BlockHash
1042 }
1043 TransactionField::BlockNumber => {
1044 crate::hypersync_net_types_capnp::TransactionField::BlockNumber
1045 }
1046 TransactionField::Gas => crate::hypersync_net_types_capnp::TransactionField::Gas,
1047 TransactionField::Hash => crate::hypersync_net_types_capnp::TransactionField::Hash,
1048 TransactionField::Input => crate::hypersync_net_types_capnp::TransactionField::Input,
1049 TransactionField::Nonce => crate::hypersync_net_types_capnp::TransactionField::Nonce,
1050 TransactionField::TransactionIndex => {
1051 crate::hypersync_net_types_capnp::TransactionField::TransactionIndex
1052 }
1053 TransactionField::Value => crate::hypersync_net_types_capnp::TransactionField::Value,
1054 TransactionField::CumulativeGasUsed => {
1055 crate::hypersync_net_types_capnp::TransactionField::CumulativeGasUsed
1056 }
1057 TransactionField::EffectiveGasPrice => {
1058 crate::hypersync_net_types_capnp::TransactionField::EffectiveGasPrice
1059 }
1060 TransactionField::GasUsed => {
1061 crate::hypersync_net_types_capnp::TransactionField::GasUsed
1062 }
1063 TransactionField::LogsBloom => {
1064 crate::hypersync_net_types_capnp::TransactionField::LogsBloom
1065 }
1066 TransactionField::From => crate::hypersync_net_types_capnp::TransactionField::From,
1067 TransactionField::GasPrice => {
1068 crate::hypersync_net_types_capnp::TransactionField::GasPrice
1069 }
1070 TransactionField::To => crate::hypersync_net_types_capnp::TransactionField::To,
1071 TransactionField::V => crate::hypersync_net_types_capnp::TransactionField::V,
1072 TransactionField::R => crate::hypersync_net_types_capnp::TransactionField::R,
1073 TransactionField::S => crate::hypersync_net_types_capnp::TransactionField::S,
1074 TransactionField::MaxPriorityFeePerGas => {
1075 crate::hypersync_net_types_capnp::TransactionField::MaxPriorityFeePerGas
1076 }
1077 TransactionField::MaxFeePerGas => {
1078 crate::hypersync_net_types_capnp::TransactionField::MaxFeePerGas
1079 }
1080 TransactionField::ChainId => {
1081 crate::hypersync_net_types_capnp::TransactionField::ChainId
1082 }
1083 TransactionField::ContractAddress => {
1084 crate::hypersync_net_types_capnp::TransactionField::ContractAddress
1085 }
1086 TransactionField::Type => crate::hypersync_net_types_capnp::TransactionField::Type,
1087 TransactionField::Root => crate::hypersync_net_types_capnp::TransactionField::Root,
1088 TransactionField::Status => crate::hypersync_net_types_capnp::TransactionField::Status,
1089 TransactionField::YParity => {
1090 crate::hypersync_net_types_capnp::TransactionField::YParity
1091 }
1092 TransactionField::AccessList => {
1093 crate::hypersync_net_types_capnp::TransactionField::AccessList
1094 }
1095 TransactionField::AuthorizationList => {
1096 crate::hypersync_net_types_capnp::TransactionField::AuthorizationList
1097 }
1098 TransactionField::L1Fee => crate::hypersync_net_types_capnp::TransactionField::L1Fee,
1099 TransactionField::L1GasPrice => {
1100 crate::hypersync_net_types_capnp::TransactionField::L1GasPrice
1101 }
1102 TransactionField::L1GasUsed => {
1103 crate::hypersync_net_types_capnp::TransactionField::L1GasUsed
1104 }
1105 TransactionField::L1FeeScalar => {
1106 crate::hypersync_net_types_capnp::TransactionField::L1FeeScalar
1107 }
1108 TransactionField::GasUsedForL1 => {
1109 crate::hypersync_net_types_capnp::TransactionField::GasUsedForL1
1110 }
1111 TransactionField::MaxFeePerBlobGas => {
1112 crate::hypersync_net_types_capnp::TransactionField::MaxFeePerBlobGas
1113 }
1114 TransactionField::BlobVersionedHashes => {
1115 crate::hypersync_net_types_capnp::TransactionField::BlobVersionedHashes
1116 }
1117 TransactionField::BlobGasPrice => {
1118 crate::hypersync_net_types_capnp::TransactionField::BlobGasPrice
1119 }
1120 TransactionField::BlobGasUsed => {
1121 crate::hypersync_net_types_capnp::TransactionField::BlobGasUsed
1122 }
1123 TransactionField::DepositNonce => {
1124 crate::hypersync_net_types_capnp::TransactionField::DepositNonce
1125 }
1126 TransactionField::DepositReceiptVersion => {
1127 crate::hypersync_net_types_capnp::TransactionField::DepositReceiptVersion
1128 }
1129 TransactionField::L1BaseFeeScalar => {
1130 crate::hypersync_net_types_capnp::TransactionField::L1BaseFeeScalar
1131 }
1132 TransactionField::L1BlobBaseFee => {
1133 crate::hypersync_net_types_capnp::TransactionField::L1BlobBaseFee
1134 }
1135 TransactionField::L1BlobBaseFeeScalar => {
1136 crate::hypersync_net_types_capnp::TransactionField::L1BlobBaseFeeScalar
1137 }
1138 TransactionField::L1BlockNumber => {
1139 crate::hypersync_net_types_capnp::TransactionField::L1BlockNumber
1140 }
1141 TransactionField::Mint => crate::hypersync_net_types_capnp::TransactionField::Mint,
1142 TransactionField::Sighash => {
1143 crate::hypersync_net_types_capnp::TransactionField::Sighash
1144 }
1145 TransactionField::SourceHash => {
1146 crate::hypersync_net_types_capnp::TransactionField::SourceHash
1147 }
1148 }
1149 }
1150
1151 pub fn from_capnp(field: crate::hypersync_net_types_capnp::TransactionField) -> Self {
1153 match field {
1154 crate::hypersync_net_types_capnp::TransactionField::BlockHash => {
1155 TransactionField::BlockHash
1156 }
1157 crate::hypersync_net_types_capnp::TransactionField::BlockNumber => {
1158 TransactionField::BlockNumber
1159 }
1160 crate::hypersync_net_types_capnp::TransactionField::Gas => TransactionField::Gas,
1161 crate::hypersync_net_types_capnp::TransactionField::Hash => TransactionField::Hash,
1162 crate::hypersync_net_types_capnp::TransactionField::Input => TransactionField::Input,
1163 crate::hypersync_net_types_capnp::TransactionField::Nonce => TransactionField::Nonce,
1164 crate::hypersync_net_types_capnp::TransactionField::TransactionIndex => {
1165 TransactionField::TransactionIndex
1166 }
1167 crate::hypersync_net_types_capnp::TransactionField::Value => TransactionField::Value,
1168 crate::hypersync_net_types_capnp::TransactionField::CumulativeGasUsed => {
1169 TransactionField::CumulativeGasUsed
1170 }
1171 crate::hypersync_net_types_capnp::TransactionField::EffectiveGasPrice => {
1172 TransactionField::EffectiveGasPrice
1173 }
1174 crate::hypersync_net_types_capnp::TransactionField::GasUsed => {
1175 TransactionField::GasUsed
1176 }
1177 crate::hypersync_net_types_capnp::TransactionField::LogsBloom => {
1178 TransactionField::LogsBloom
1179 }
1180 crate::hypersync_net_types_capnp::TransactionField::From => TransactionField::From,
1181 crate::hypersync_net_types_capnp::TransactionField::GasPrice => {
1182 TransactionField::GasPrice
1183 }
1184 crate::hypersync_net_types_capnp::TransactionField::To => TransactionField::To,
1185 crate::hypersync_net_types_capnp::TransactionField::V => TransactionField::V,
1186 crate::hypersync_net_types_capnp::TransactionField::R => TransactionField::R,
1187 crate::hypersync_net_types_capnp::TransactionField::S => TransactionField::S,
1188 crate::hypersync_net_types_capnp::TransactionField::MaxPriorityFeePerGas => {
1189 TransactionField::MaxPriorityFeePerGas
1190 }
1191 crate::hypersync_net_types_capnp::TransactionField::MaxFeePerGas => {
1192 TransactionField::MaxFeePerGas
1193 }
1194 crate::hypersync_net_types_capnp::TransactionField::ChainId => {
1195 TransactionField::ChainId
1196 }
1197 crate::hypersync_net_types_capnp::TransactionField::ContractAddress => {
1198 TransactionField::ContractAddress
1199 }
1200 crate::hypersync_net_types_capnp::TransactionField::Type => TransactionField::Type,
1201 crate::hypersync_net_types_capnp::TransactionField::Root => TransactionField::Root,
1202 crate::hypersync_net_types_capnp::TransactionField::Status => TransactionField::Status,
1203 crate::hypersync_net_types_capnp::TransactionField::YParity => {
1204 TransactionField::YParity
1205 }
1206 crate::hypersync_net_types_capnp::TransactionField::AccessList => {
1207 TransactionField::AccessList
1208 }
1209 crate::hypersync_net_types_capnp::TransactionField::AuthorizationList => {
1210 TransactionField::AuthorizationList
1211 }
1212 crate::hypersync_net_types_capnp::TransactionField::L1Fee => TransactionField::L1Fee,
1213 crate::hypersync_net_types_capnp::TransactionField::L1GasPrice => {
1214 TransactionField::L1GasPrice
1215 }
1216 crate::hypersync_net_types_capnp::TransactionField::L1GasUsed => {
1217 TransactionField::L1GasUsed
1218 }
1219 crate::hypersync_net_types_capnp::TransactionField::L1FeeScalar => {
1220 TransactionField::L1FeeScalar
1221 }
1222 crate::hypersync_net_types_capnp::TransactionField::GasUsedForL1 => {
1223 TransactionField::GasUsedForL1
1224 }
1225 crate::hypersync_net_types_capnp::TransactionField::MaxFeePerBlobGas => {
1226 TransactionField::MaxFeePerBlobGas
1227 }
1228 crate::hypersync_net_types_capnp::TransactionField::BlobVersionedHashes => {
1229 TransactionField::BlobVersionedHashes
1230 }
1231 crate::hypersync_net_types_capnp::TransactionField::BlobGasPrice => {
1232 TransactionField::BlobGasPrice
1233 }
1234 crate::hypersync_net_types_capnp::TransactionField::BlobGasUsed => {
1235 TransactionField::BlobGasUsed
1236 }
1237 crate::hypersync_net_types_capnp::TransactionField::DepositNonce => {
1238 TransactionField::DepositNonce
1239 }
1240 crate::hypersync_net_types_capnp::TransactionField::DepositReceiptVersion => {
1241 TransactionField::DepositReceiptVersion
1242 }
1243 crate::hypersync_net_types_capnp::TransactionField::L1BaseFeeScalar => {
1244 TransactionField::L1BaseFeeScalar
1245 }
1246 crate::hypersync_net_types_capnp::TransactionField::L1BlobBaseFee => {
1247 TransactionField::L1BlobBaseFee
1248 }
1249 crate::hypersync_net_types_capnp::TransactionField::L1BlobBaseFeeScalar => {
1250 TransactionField::L1BlobBaseFeeScalar
1251 }
1252 crate::hypersync_net_types_capnp::TransactionField::L1BlockNumber => {
1253 TransactionField::L1BlockNumber
1254 }
1255 crate::hypersync_net_types_capnp::TransactionField::Mint => TransactionField::Mint,
1256 crate::hypersync_net_types_capnp::TransactionField::Sighash => {
1257 TransactionField::Sighash
1258 }
1259 crate::hypersync_net_types_capnp::TransactionField::SourceHash => {
1260 TransactionField::SourceHash
1261 }
1262 }
1263 }
1264}
1265
1266#[cfg(test)]
1267mod tests {
1268 use hypersync_format::Hex;
1269
1270 use super::*;
1271 use crate::{query::tests::test_query_serde, Query};
1272
1273 #[test]
1274 fn test_all_fields_in_schema() {
1275 let schema = hypersync_schema::transaction();
1276 let schema_fields = schema
1277 .fields
1278 .iter()
1279 .map(|f| f.name().clone())
1280 .collect::<std::collections::BTreeSet<_>>();
1281 let all_fields = TransactionField::all()
1282 .into_iter()
1283 .map(|f| f.as_ref().to_string())
1284 .collect::<std::collections::BTreeSet<_>>();
1285 assert_eq!(schema_fields, all_fields);
1286 }
1287
1288 #[test]
1289 fn test_serde_matches_strum() {
1290 for field in TransactionField::all() {
1291 let serialized = serde_json::to_string(&field).unwrap();
1292 let strum = serde_json::to_string(&field.as_ref()).unwrap();
1293 assert_eq!(serialized, strum, "strum value should be the same as serde");
1294 }
1295 }
1296
1297 #[test]
1298 fn test_transaction_filter_serde_with_defaults() {
1299 let transaction_filter = TransactionSelection::default();
1300 let query = Query::new()
1301 .where_transactions(transaction_filter)
1302 .select_transaction_fields(TransactionField::all());
1303
1304 test_query_serde(query, "transaction selection with defaults");
1305 }
1306 #[test]
1307 fn test_transaction_filter_serde_with_explicit_defaults() {
1308 let transaction_filter = TransactionFilter {
1309 from: Vec::default(),
1310 from_filter: Some(FilterWrapper::new(16, 0)),
1311 to: Vec::default(),
1312 to_filter: Some(FilterWrapper::new(16, 0)),
1313 sighash: Vec::default(),
1314 status: Some(u8::default()),
1315 type_: Vec::default(),
1316 contract_address: Vec::default(),
1317 contract_address_filter: Some(FilterWrapper::new(16, 0)),
1318 hash: Vec::default(),
1319 authorization_list: Vec::default(),
1320 };
1321 let query = Query::new()
1322 .where_transactions(transaction_filter)
1323 .select_transaction_fields(TransactionField::all());
1324
1325 test_query_serde(query, "transaction selection with explicit defaults");
1326 }
1327
1328 #[test]
1329 fn test_transaction_filter_serde_with_full_values() {
1330 let transaction_filter = TransactionFilter {
1331 from: vec![Address::decode_hex("0xdadB0d80178819F2319190D340ce9A924f783711").unwrap()],
1332 from_filter: Some(FilterWrapper::new(16, 1)),
1333 to: vec![Address::decode_hex("0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6").unwrap()],
1334 to_filter: Some(FilterWrapper::new(16, 1)),
1335 sighash: vec![Sighash::from([0x12, 0x34, 0x56, 0x78])],
1336 status: Some(1),
1337 type_: vec![2],
1338 contract_address: vec![Address::decode_hex(
1339 "0x1234567890123456789012345678901234567890",
1340 )
1341 .unwrap()],
1342 contract_address_filter: Some(FilterWrapper::new(16, 1)),
1343 hash: vec![Hash::decode_hex(
1344 "0x40d008f2a1653f09b7b028d30c7fd1ba7c84900fcfb032040b3eb3d16f84d294",
1345 )
1346 .unwrap()],
1347 authorization_list: Vec::default(),
1348 };
1349 let query = Query::new()
1350 .where_transactions(transaction_filter)
1351 .select_transaction_fields(TransactionField::all());
1352
1353 test_query_serde(query, "transaction selection with full values");
1354 }
1355
1356 #[test]
1357 fn test_authorization_selection_serde_with_values() {
1358 let auth_selection = AuthorizationSelection {
1359 chain_id: vec![1, 137, 42161],
1360 address: vec![
1361 Address::decode_hex("0xdadB0d80178819F2319190D340ce9A924f783711").unwrap(),
1362 ],
1363 };
1364 let transaction_filter = TransactionFilter {
1365 from: Vec::default(),
1366 from_filter: Some(FilterWrapper::new(16, 0)),
1367 to: Vec::default(),
1368 to_filter: Some(FilterWrapper::new(16, 0)),
1369 sighash: Vec::default(),
1370 status: Some(u8::default()),
1371 type_: Vec::default(),
1372 contract_address: Vec::default(),
1373 contract_address_filter: Some(FilterWrapper::new(16, 0)),
1374 hash: Vec::default(),
1375 authorization_list: vec![auth_selection],
1376 };
1377 let query = Query::new()
1378 .where_transactions(transaction_filter)
1379 .select_transaction_fields(TransactionField::all());
1380
1381 test_query_serde(query, "authorization selection with rest defaults");
1382 }
1383
1384 #[test]
1385 fn nullable_fields() {
1386 use std::collections::HashMap;
1387
1388 let is_nullable_map: HashMap<_, _> = TransactionField::all()
1389 .iter()
1390 .map(|f| (f.to_string(), f.is_nullable()))
1391 .collect();
1392 for field in hypersync_schema::transaction().fields.iter() {
1393 let should_be_nullable = is_nullable_map.get(field.name().as_str()).unwrap();
1394 assert_eq!(
1395 field.is_nullable(),
1396 *should_be_nullable,
1397 "field {} nullable mismatch",
1398 field.name()
1399 );
1400 }
1401 }
1402}