1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
//! Rust client for the Blokli GraphQL API and transaction endpoints.
//!
//! `blokli-client` provides a typed, async interface to a Blokli service. It wraps Blokli's GraphQL API with
//! ergonomic Rust traits for:
//!
//! - querying accounts, balances, channels, safes, ticket statistics, chain metadata, and transaction state;
//! - subscribing to server-sent event streams for accounts, channels, health, graph changes, safe deployments, ticket
//! redemptions, and tracked transactions;
//! - submitting signed transactions and optionally waiting for tracking or confirmation.
//!
//! The main entry point is [`BlokliClient`]. Most operations are trait methods, so bring the trait for the operation
//! family into scope:
//!
//! - [`BlokliQueryClient`] for one-shot GraphQL queries;
//! - [`BlokliSubscriptionClient`] for streaming subscriptions;
//! - [`BlokliTransactionClient`] for signed transaction submission and tracking.
//!
//! # API concepts
//!
//! A [`BlokliClient`] is configured with the Blokli service base URL, not the GraphQL endpoint itself. For example,
//! `https://blokli.example.org` becomes `https://blokli.example.org/graphql` for GraphQL requests and SSE
//! subscriptions.
//!
//! Queries and subscriptions use typed selectors instead of generic filter maps. Address-like values such as
//! [`ChainAddress`], [`ChannelId`], [`PacketKey`], and [`TxReceipt`] are byte arrays at the public boundary and are
//! encoded as hex strings for GraphQL. Blokli-specific identifiers such as [`KeyId`] and [`TxId`] are kept distinct:
//! a [`TxId`] is a Blokli tracking id, while a [`TxReceipt`] is the on-chain transaction hash returned by submission
//! endpoints.
//!
//! Subscriptions are GraphQL operations delivered over server-sent events. They yield streams of `Result<T, E>` so
//! callers can decide how to handle item-level errors. Transaction helpers operate on already-signed raw transaction
//! bytes; this crate does not sign transactions.
//!
//! # Quick start
//!
//! Create a client with a Blokli base URL. The client derives the GraphQL endpoint by appending `/graphql` to that
//! base URL.
//!
//! ```no_run
//! use blokli_client::{BlokliClient, BlokliClientConfig, BlokliQueryClient};
//!
//! async fn example() -> Result<(), Box<dyn std::error::Error>> {
//! let client = BlokliClient::new("https://blokli.example.org".parse()?, BlokliClientConfig::default());
//!
//! let version = client.query_version().await?;
//! let chain = client.query_chain_info().await?;
//!
//! println!("Blokli {version} indexes chain {}", chain.chain_id);
//! Ok(())
//! }
//! ```
//!
//! # Querying
//!
//! Selectors are strongly typed. For example, channel queries use [`ChannelSelector`] plus an optional
//! [`ChannelFilter`] and status.
//!
//! ```no_run
//! use blokli_client::{BlokliClient, BlokliClientConfig, BlokliQueryClient, ChannelFilter, ChannelSelector};
//!
//! async fn example(source: u32) -> Result<(), Box<dyn std::error::Error>> {
//! let client = BlokliClient::new("https://blokli.example.org".parse()?, BlokliClientConfig::default());
//! let selector = ChannelSelector {
//! filter: Some(ChannelFilter::SourceKeyId(source)),
//! ..Default::default()
//! };
//!
//! let channels = client.query_channels(selector).await?;
//! println!("{} channels found", channels.channels.len());
//! Ok(())
//! }
//! ```
//!
//! # Subscriptions
//!
//! Subscription methods return [`futures::Stream`] values. Streams use Blokli's SSE endpoint and yield `Result` items
//! so callers can decide how to handle transient transport, parsing, or GraphQL errors.
//!
//! ```no_run
//! use blokli_client::{AccountSelector, BlokliClient, BlokliClientConfig, BlokliSubscriptionClient};
//! use futures::TryStreamExt;
//!
//! async fn example() -> Result<(), Box<dyn std::error::Error>> {
//! let client = BlokliClient::new("https://blokli.example.org".parse()?, BlokliClientConfig::default());
//! let mut accounts = Box::pin(client.subscribe_accounts(AccountSelector::Any)?);
//!
//! while let Some(account) = accounts.try_next().await? {
//! println!("account key id: {}", account.keyid);
//! }
//!
//! Ok(())
//! }
//! ```
//!
//! # Transactions
//!
//! Transaction methods expect already-signed raw transactions. `submit_transaction` returns immediately after
//! submission, `submit_and_track_transaction` returns a Blokli tracking id, and `submit_and_confirm_transaction`
//! waits for the requested number of confirmations.
//!
//! ```no_run
//! use std::time::Duration;
//!
//! use blokli_client::{BlokliClient, BlokliClientConfig, BlokliTransactionClient};
//!
//! async fn example(signed_transaction: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
//! let client = BlokliClient::new("https://blokli.example.org".parse()?, BlokliClientConfig::default());
//!
//! let tx_id = client.submit_and_track_transaction(signed_transaction).await?;
//! let transaction = client.track_transaction(tx_id, Duration::from_secs(120)).await?;
//!
//! println!("transaction status: {:?}", transaction.status);
//! Ok(())
//! }
//! ```
//!
//! # Public API layout
//!
//! Common selectors, traits, and address aliases are re-exported at the crate root. The versioned API remains available
//! under [`api::v1`], and GraphQL response models are grouped under [`types`].
//!
//! # Errors
//!
//! Fallible operations return [`BlokliClientError`]. Use [`BlokliClientError::kind`] when matching on stable client
//! error categories such as invalid input, GraphQL errors, timeouts, or transaction tracking failures.
//!
//! # DNS override
//!
//! By default, [`BlokliClient`] uses the system DNS resolver through `reqwest`. Callers that need to keep Blokli
//! communication working while DNS is unreliable can configure [`BlokliClientConfig::dns_override`] to pin the Blokli
//! URL hostname to a fixed IP address.
//!
//! The request hostname is not rewritten. For example, a client configured with `https://blokli.example.org` and a DNS
//! override still sends requests for `blokli.example.org`, preserving TLS SNI and certificate validation while
//! bypassing system DNS for that hostname. When [`BlokliDnsOverride::port`] is set, it becomes the request port;
//! otherwise the original URL port or scheme default is used.
//!
//! ```no_run
//! use std::net::IpAddr;
//!
//! use blokli_client::{BlokliClient, BlokliClientConfig, BlokliDnsOverride};
//!
//! let client = BlokliClient::new(
//! "https://blokli.example.org".parse()?,
//! BlokliClientConfig {
//! dns_override: Some(BlokliDnsOverride {
//! ip: IpAddr::from([203, 0, 113, 10]),
//! port: None,
//! }),
//! ..Default::default()
//! },
//! );
//! # let _ = client;
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! Leave `dns_override` as `None` to use normal system DNS resolution.
/// Current Blokli client API.
/// Errors returned by the Blokli client.
/// Version of the `blokli-client` crate.
pub const CLIENT_VERSION: &str = env!;
pub use ;
pub use ;
pub use ;
pub use ;