Skip to content

Commit e5d1a95

Browse files
committed
Add basic query builder api for testing
1 parent 3401a92 commit e5d1a95

File tree

4 files changed

+218
-35
lines changed

4 files changed

+218
-35
lines changed

examples/all_erc20/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ serde_json = "1"
1111
polars-arrow = { version = "0.42", features = [
1212
"compute_aggregate",
1313
] }
14-
env_logger = "0.4"
14+
env_logger = "0.4"
15+
anyhow = "1.0.100"

examples/all_erc20/src/main.rs

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use std::{sync::Arc, time::Instant};
55

66
use hypersync_client::{
7+
net_types::{log::LogField, FieldSelection, LogFilter, LogSelection, Query},
78
Client, ClientConfig, ColumnMapping, DataType, SerializationFormat, StreamConfig,
89
};
910
use polars_arrow::{
@@ -13,7 +14,7 @@ use polars_arrow::{
1314
};
1415

1516
#[tokio::main]
16-
async fn main() {
17+
async fn main() -> anyhow::Result<()> {
1718
env_logger::init().unwrap();
1819

1920
// create default client, uses eth mainnet
@@ -25,30 +26,21 @@ async fn main() {
2526
})
2627
.unwrap();
2728

28-
let query = serde_json::from_value(serde_json::json!( {
29+
let query = Query::new()
2930
// start from block 10123123 and go to the end of the chain (we don't specify a toBlock).
30-
"from_block": 10123123,
31+
.from_block(10123123)
3132
// The logs we want. We will also automatically get transactions and blocks relating to these logs (the query implicitly joins them).
32-
"logs": [
33-
{
34-
// We want All ERC20 transfers so no address filter and only a filter for the first topic
35-
"topics": [
36-
["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],
37-
]
38-
}
39-
],
33+
.match_log_filter_any([LogFilter::new()
34+
// We want All ERC20 transfers so no address filter and only a filter for the first topic
35+
.topic0_any(["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"])?])
4036
// Select the fields we are interested in, notice topics are selected as topic0,1,2,3
41-
"field_selection": {
42-
"log": [
43-
"data",
44-
"topic0",
45-
"topic1",
46-
"topic2",
47-
"topic3",
48-
]
49-
}
50-
}))
51-
.unwrap();
37+
.select_fields(FieldSelection::new().log([
38+
LogField::Data,
39+
LogField::Topic0,
40+
LogField::Topic1,
41+
LogField::Topic2,
42+
LogField::Topic3,
43+
]));
5244

5345
println!("Starting the stream");
5446

@@ -115,4 +107,5 @@ async fn main() {
115107
total_amount / num_transfers as f64
116108
);
117109
}
110+
Ok(())
118111
}

hypersync-net-types/src/log.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,6 @@ use serde::{Deserialize, Serialize};
66

77
pub type LogSelection = Selection<LogFilter>;
88

9-
impl LogSelection {
10-
pub fn new() -> Self {
11-
Default::default()
12-
}
13-
14-
pub fn add_address(mut self, address: Address) -> Self {
15-
self.include.address.push(address);
16-
self
17-
}
18-
}
19-
209
#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq)]
2110
pub struct LogFilter {
2211
/// Address of the contract, any logs that has any of these addresses will be returned.

hypersync-net-types/src/query.rs

Lines changed: 201 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::block::{BlockField, BlockSelection};
22
use crate::log::{LogField, LogSelection};
33
use crate::trace::{TraceField, TraceSelection};
44
use crate::transaction::{TransactionField, TransactionSelection};
5-
use crate::{hypersync_net_types_capnp, CapnpBuilder, CapnpReader};
5+
use crate::{hypersync_net_types_capnp, CapnpBuilder, CapnpReader, LogFilter};
66
use anyhow::Context;
77
use capnp::message::Builder;
88
use hypersync_format::FixedSizeData;
@@ -333,6 +333,39 @@ pub enum JoinMode {
333333
}
334334

335335
impl Query {
336+
pub fn new() -> Self {
337+
Default::default()
338+
}
339+
340+
pub fn from_block(mut self, from_block: u64) -> Self {
341+
self.from_block = from_block;
342+
self
343+
}
344+
345+
pub fn to_block(mut self, to_block: u64) -> Self {
346+
self.to_block = Some(to_block);
347+
self
348+
}
349+
350+
pub fn match_log_filter_any<I>(mut self, logs: I) -> Self
351+
where
352+
I: IntoIterator<Item = LogFilter>,
353+
{
354+
let log_selection: Vec<LogSelection> = logs.into_iter().map(From::from).collect();
355+
self.logs = log_selection;
356+
self
357+
}
358+
359+
pub fn add_log_selection(mut self, log_selection: LogSelection) -> Self {
360+
self.logs.push(log_selection);
361+
self
362+
}
363+
364+
pub fn select_fields(mut self, field_selection: FieldSelection) -> Self {
365+
self.field_selection = field_selection;
366+
self
367+
}
368+
336369
pub fn from_capnp_query_body_reader(
337370
query_body_reader: &hypersync_net_types_capnp::query_body::Reader,
338371
from_block: u64,
@@ -448,6 +481,173 @@ pub struct FieldSelection {
448481
pub trace: BTreeSet<TraceField>,
449482
}
450483

484+
impl FieldSelection {
485+
/// Create a new empty field selection.
486+
///
487+
/// This creates a field selection with no fields selected. You can then use the builder
488+
/// methods to select specific fields for each data type (block, transaction, log, trace).
489+
///
490+
/// # Examples
491+
///
492+
/// ```
493+
/// use hypersync_net_types::FieldSelection;
494+
///
495+
/// // Create empty field selection
496+
/// let field_selection = FieldSelection::new();
497+
///
498+
/// // All field sets are empty by default
499+
/// assert!(field_selection.block.is_empty());
500+
/// assert!(field_selection.transaction.is_empty());
501+
/// assert!(field_selection.log.is_empty());
502+
/// assert!(field_selection.trace.is_empty());
503+
/// ```
504+
pub fn new() -> Self {
505+
Self::default()
506+
}
507+
/// Select specific block fields to include in query results.
508+
///
509+
/// This method allows you to specify which block fields should be returned in the query response.
510+
/// Only the selected fields will be included, which can improve performance and reduce payload size.
511+
///
512+
/// # Arguments
513+
/// * `fields` - An iterable of `BlockField` values to select
514+
///
515+
/// # Examples
516+
///
517+
/// ```
518+
/// use hypersync_net_types::{FieldSelection, block::BlockField, transaction::TransactionField};
519+
///
520+
/// // Select specific block fields
521+
/// let field_selection = FieldSelection::new()
522+
/// .block([BlockField::Number, BlockField::Hash, BlockField::Timestamp]);
523+
///
524+
/// // Can also use a vector
525+
/// let fields = vec![BlockField::Number, BlockField::ParentHash];
526+
/// let field_selection = FieldSelection::new()
527+
/// .block(fields);
528+
///
529+
/// // Chain with other field selections
530+
/// let field_selection = FieldSelection::new()
531+
/// .block([BlockField::Number])
532+
/// .transaction([TransactionField::Hash]);
533+
/// ```
534+
pub fn block<T: IntoIterator<Item = BlockField>>(mut self, fields: T) -> Self {
535+
self.block.extend(fields);
536+
self
537+
}
538+
/// Select specific transaction fields to include in query results.
539+
///
540+
/// This method allows you to specify which transaction fields should be returned in the query response.
541+
/// Only the selected fields will be included, which can improve performance and reduce payload size.
542+
///
543+
/// # Arguments
544+
/// * `fields` - An iterable of `TransactionField` values to select
545+
///
546+
/// # Examples
547+
///
548+
/// ```
549+
/// use hypersync_net_types::{FieldSelection, transaction::TransactionField, block::BlockField};
550+
///
551+
/// // Select specific transaction fields
552+
/// let field_selection = FieldSelection::new()
553+
/// .transaction([TransactionField::Hash, TransactionField::From, TransactionField::To]);
554+
///
555+
/// // Select fields related to gas and value
556+
/// let field_selection = FieldSelection::new()
557+
/// .transaction([
558+
/// TransactionField::GasPrice,
559+
/// TransactionField::GasUsed,
560+
/// TransactionField::Value,
561+
/// ]);
562+
///
563+
/// // Chain with other field selections
564+
/// let field_selection = FieldSelection::new()
565+
/// .block([BlockField::Number])
566+
/// .transaction([TransactionField::Hash]);
567+
/// ```
568+
pub fn transaction<T: IntoIterator<Item = TransactionField>>(mut self, fields: T) -> Self {
569+
self.transaction.extend(fields);
570+
self
571+
}
572+
/// Select specific log fields to include in query results.
573+
///
574+
/// This method allows you to specify which log fields should be returned in the query response.
575+
/// Only the selected fields will be included, which can improve performance and reduce payload size.
576+
///
577+
/// # Arguments
578+
/// * `fields` - An iterable of `LogField` values to select
579+
///
580+
/// # Examples
581+
///
582+
/// ```
583+
/// use hypersync_net_types::{FieldSelection, log::LogField, transaction::TransactionField};
584+
///
585+
/// // Select essential log fields
586+
/// let field_selection = FieldSelection::new()
587+
/// .log([LogField::Address, LogField::Data, LogField::Topic0]);
588+
///
589+
/// // Select all topic fields for event analysis
590+
/// let field_selection = FieldSelection::new()
591+
/// .log([
592+
/// LogField::Topic0,
593+
/// LogField::Topic1,
594+
/// LogField::Topic2,
595+
/// LogField::Topic3,
596+
/// ]);
597+
///
598+
/// // Chain with transaction fields for full context
599+
/// let field_selection = FieldSelection::new()
600+
/// .transaction([TransactionField::Hash])
601+
/// .log([LogField::Address, LogField::Data]);
602+
/// ```
603+
pub fn log<T: IntoIterator<Item = LogField>>(mut self, fields: T) -> Self {
604+
self.log.extend(fields);
605+
self
606+
}
607+
/// Select specific trace fields to include in query results.
608+
///
609+
/// This method allows you to specify which trace fields should be returned in the query response.
610+
/// Only the selected fields will be included, which can improve performance and reduce payload size.
611+
///
612+
/// # Arguments
613+
/// * `fields` - An iterable of `TraceField` values to select
614+
///
615+
/// # Examples
616+
///
617+
/// ```
618+
/// use hypersync_net_types::{
619+
/// FieldSelection,
620+
/// trace::TraceField,
621+
/// transaction::TransactionField,
622+
/// log::LogField
623+
/// };
624+
///
625+
/// // Select basic trace information
626+
/// let field_selection = FieldSelection::new()
627+
/// .trace([TraceField::From, TraceField::To, TraceField::Value]);
628+
///
629+
/// // Select trace execution details
630+
/// let field_selection = FieldSelection::new()
631+
/// .trace([
632+
/// TraceField::CallType,
633+
/// TraceField::Input,
634+
/// TraceField::Output,
635+
/// TraceField::Gas,
636+
/// TraceField::GasUsed,
637+
/// ]);
638+
///
639+
/// // Combine with other data types for comprehensive analysis
640+
/// let field_selection = FieldSelection::new()
641+
/// .transaction([TransactionField::Hash])
642+
/// .trace([TraceField::From, TraceField::To])
643+
/// .log([LogField::Address]);
644+
/// ```
645+
pub fn trace<T: IntoIterator<Item = TraceField>>(mut self, fields: T) -> Self {
646+
self.trace.extend(fields);
647+
self
648+
}
649+
}
650+
451651
impl CapnpBuilder<hypersync_net_types_capnp::field_selection::Owned> for FieldSelection {
452652
fn populate_builder(
453653
&self,

0 commit comments

Comments
 (0)