Skip to content

Commit 4ba5323

Browse files
authored
refactor: add network-primitives (#1101)
* refactor: add network-primitives * fix tests * fix docs * add re-exports * Block<T>
1 parent 2de4e23 commit 4ba5323

File tree

20 files changed

+461
-487
lines changed

20 files changed

+461
-487
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ alloy-eip7547 = { version = "0.2", path = "crates/eip7547", default-features = f
4242
alloy-genesis = { version = "0.2", path = "crates/genesis", default-features = false }
4343
alloy-json-rpc = { version = "0.2", path = "crates/json-rpc", default-features = false }
4444
alloy-network = { version = "0.2", path = "crates/network", default-features = false }
45+
alloy-network-primitives = { version = "0.2", path = "crates/network-primitives", default-features = false }
4546
alloy-node-bindings = { version = "0.2", path = "crates/node-bindings", default-features = false }
4647
alloy-provider = { version = "0.2", path = "crates/provider", default-features = false }
4748
alloy-pubsub = { version = "0.2", path = "crates/pubsub", default-features = false }

crates/contract/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ workspace = true
2020

2121
[dependencies]
2222
alloy-network.workspace = true
23+
alloy-network-primitives.workspace = true
2324
alloy-provider.workspace = true
2425
alloy-rpc-types-eth.workspace = true
2526
alloy-transport.workspace = true

crates/contract/src/call.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use crate::{CallDecoder, Error, EthCall, Result};
22
use alloy_dyn_abi::{DynSolValue, JsonAbiExt};
33
use alloy_json_abi::Function;
4-
use alloy_network::{Ethereum, Network, ReceiptResponse, TransactionBuilder};
4+
use alloy_network::{Ethereum, Network, TransactionBuilder};
5+
use alloy_network_primitives::ReceiptResponse;
56
use alloy_primitives::{Address, Bytes, ChainId, TxKind, U256};
67
use alloy_provider::{PendingTransactionBuilder, Provider};
78
use alloy_rpc_types_eth::{state::StateOverride, AccessList, BlobTransactionSidecar, BlockId};

crates/network-primitives/Cargo.toml

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[package]
2+
name = "alloy-network-primitives"
3+
description = "Primitive types for Alloy network abstraction"
4+
5+
version.workspace = true
6+
edition.workspace = true
7+
rust-version.workspace = true
8+
authors.workspace = true
9+
license.workspace = true
10+
homepage.workspace = true
11+
repository.workspace = true
12+
exclude.workspace = true
13+
14+
[package.metadata.docs.rs]
15+
all-features = true
16+
rustdoc-args = ["--cfg", "docsrs"]
17+
18+
[lints]
19+
workspace = true
20+
21+
[dependencies]
22+
alloy-primitives.workspace = true
23+
alloy-serde.workspace = true
24+
25+
serde.workspace = true
26+
27+
[dev-dependencies]
28+
rand.workspace = true

crates/network-primitives/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# alloy-network-primitives
2+
3+
Primitive types for Alloy network abstraction.
+258
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
use alloy_primitives::B256;
2+
use serde::{Deserialize, Serialize};
3+
4+
use crate::TransactionResponse;
5+
6+
/// Block Transactions depending on the boolean attribute of `eth_getBlockBy*`,
7+
/// or if used by `eth_getUncle*`
8+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
9+
#[serde(untagged)]
10+
pub enum BlockTransactions<T> {
11+
/// Full transactions
12+
Full(Vec<T>),
13+
/// Only hashes
14+
Hashes(Vec<B256>),
15+
/// Special case for uncle response.
16+
Uncle,
17+
}
18+
19+
impl<T> Default for BlockTransactions<T> {
20+
fn default() -> Self {
21+
Self::Hashes(Vec::default())
22+
}
23+
}
24+
25+
impl<T> BlockTransactions<T> {
26+
/// Check if the enum variant is used for hashes.
27+
#[inline]
28+
pub const fn is_hashes(&self) -> bool {
29+
matches!(self, Self::Hashes(_))
30+
}
31+
32+
/// Fallibly cast to a slice of hashes.
33+
pub fn as_hashes(&self) -> Option<&[B256]> {
34+
match self {
35+
Self::Hashes(hashes) => Some(hashes),
36+
_ => None,
37+
}
38+
}
39+
40+
/// Returns true if the enum variant is used for full transactions.
41+
#[inline]
42+
pub const fn is_full(&self) -> bool {
43+
matches!(self, Self::Full(_))
44+
}
45+
46+
/// Fallibly cast to a slice of transactions.
47+
///
48+
/// Returns `None` if the enum variant is not `Full`.
49+
pub fn as_transactions(&self) -> Option<&[T]> {
50+
match self {
51+
Self::Full(txs) => Some(txs),
52+
_ => None,
53+
}
54+
}
55+
56+
/// Returns true if the enum variant is used for an uncle response.
57+
#[inline]
58+
pub const fn is_uncle(&self) -> bool {
59+
matches!(self, Self::Uncle)
60+
}
61+
62+
/// Returns an iterator over the transactions (if any). This will be empty
63+
/// if the block is an uncle or if the transaction list contains only
64+
/// hashes.
65+
#[doc(alias = "transactions")]
66+
pub fn txns(&self) -> impl Iterator<Item = &T> {
67+
self.as_transactions().map(|txs| txs.iter()).unwrap_or_else(|| [].iter())
68+
}
69+
70+
/// Returns an iterator over the transactions (if any). This will be empty if the block is not
71+
/// full.
72+
pub fn into_transactions(self) -> std::vec::IntoIter<T> {
73+
match self {
74+
Self::Full(txs) => txs.into_iter(),
75+
_ => std::vec::IntoIter::default(),
76+
}
77+
}
78+
79+
/// Returns an instance of BlockTransactions with the Uncle special case.
80+
#[inline]
81+
pub const fn uncle() -> Self {
82+
Self::Uncle
83+
}
84+
85+
/// Returns the number of transactions.
86+
#[inline]
87+
pub fn len(&self) -> usize {
88+
match self {
89+
Self::Hashes(h) => h.len(),
90+
Self::Full(f) => f.len(),
91+
Self::Uncle => 0,
92+
}
93+
}
94+
95+
/// Whether the block has no transactions.
96+
#[inline]
97+
pub fn is_empty(&self) -> bool {
98+
self.len() == 0
99+
}
100+
}
101+
102+
impl<T: TransactionResponse> BlockTransactions<T> {
103+
/// Converts `self` into `Hashes`.
104+
#[inline]
105+
pub fn convert_to_hashes(&mut self) {
106+
if !self.is_hashes() {
107+
*self = Self::Hashes(self.hashes().collect());
108+
}
109+
}
110+
111+
/// Converts `self` into `Hashes`.
112+
#[inline]
113+
pub fn into_hashes(mut self) -> Self {
114+
self.convert_to_hashes();
115+
self
116+
}
117+
118+
/// Returns an iterator over the transaction hashes.
119+
#[deprecated = "use `hashes` instead"]
120+
#[inline]
121+
pub fn iter(&self) -> BlockTransactionHashes<'_, T> {
122+
self.hashes()
123+
}
124+
125+
/// Returns an iterator over references to the transaction hashes.
126+
#[inline]
127+
pub fn hashes(&self) -> BlockTransactionHashes<'_, T> {
128+
BlockTransactionHashes::new(self)
129+
}
130+
}
131+
132+
impl<T> From<Vec<B256>> for BlockTransactions<T> {
133+
fn from(hashes: Vec<B256>) -> Self {
134+
Self::Hashes(hashes)
135+
}
136+
}
137+
138+
impl<T: TransactionResponse> From<Vec<T>> for BlockTransactions<T> {
139+
fn from(transactions: Vec<T>) -> Self {
140+
Self::Full(transactions)
141+
}
142+
}
143+
144+
/// An iterator over the transaction hashes of a block.
145+
///
146+
/// See [`BlockTransactions::hashes`].
147+
#[derive(Clone, Debug)]
148+
pub struct BlockTransactionHashes<'a, T>(BlockTransactionHashesInner<'a, T>);
149+
150+
#[derive(Clone, Debug)]
151+
enum BlockTransactionHashesInner<'a, T> {
152+
Hashes(std::slice::Iter<'a, B256>),
153+
Full(std::slice::Iter<'a, T>),
154+
Uncle,
155+
}
156+
157+
impl<'a, T> BlockTransactionHashes<'a, T> {
158+
#[inline]
159+
fn new(txs: &'a BlockTransactions<T>) -> Self {
160+
Self(match txs {
161+
BlockTransactions::Hashes(txs) => BlockTransactionHashesInner::Hashes(txs.iter()),
162+
BlockTransactions::Full(txs) => BlockTransactionHashesInner::Full(txs.iter()),
163+
BlockTransactions::Uncle => BlockTransactionHashesInner::Uncle,
164+
})
165+
}
166+
}
167+
168+
impl<'a, T: TransactionResponse> Iterator for BlockTransactionHashes<'a, T> {
169+
type Item = B256;
170+
171+
#[inline]
172+
fn next(&mut self) -> Option<Self::Item> {
173+
match &mut self.0 {
174+
BlockTransactionHashesInner::Hashes(txs) => txs.next().copied(),
175+
BlockTransactionHashesInner::Full(txs) => txs.next().map(|tx| tx.tx_hash()),
176+
BlockTransactionHashesInner::Uncle => None,
177+
}
178+
}
179+
180+
#[inline]
181+
fn size_hint(&self) -> (usize, Option<usize>) {
182+
match &self.0 {
183+
BlockTransactionHashesInner::Full(txs) => txs.size_hint(),
184+
BlockTransactionHashesInner::Hashes(txs) => txs.size_hint(),
185+
BlockTransactionHashesInner::Uncle => (0, Some(0)),
186+
}
187+
}
188+
}
189+
190+
impl<T: TransactionResponse> ExactSizeIterator for BlockTransactionHashes<'_, T> {
191+
#[inline]
192+
fn len(&self) -> usize {
193+
match &self.0 {
194+
BlockTransactionHashesInner::Full(txs) => txs.len(),
195+
BlockTransactionHashesInner::Hashes(txs) => txs.len(),
196+
BlockTransactionHashesInner::Uncle => 0,
197+
}
198+
}
199+
}
200+
201+
impl<T: TransactionResponse> DoubleEndedIterator for BlockTransactionHashes<'_, T> {
202+
#[inline]
203+
fn next_back(&mut self) -> Option<Self::Item> {
204+
match &mut self.0 {
205+
BlockTransactionHashesInner::Full(txs) => txs.next_back().map(|tx| tx.tx_hash()),
206+
BlockTransactionHashesInner::Hashes(txs) => txs.next_back().copied(),
207+
BlockTransactionHashesInner::Uncle => None,
208+
}
209+
}
210+
}
211+
212+
impl<'a, T: TransactionResponse> std::iter::FusedIterator for BlockTransactionHashes<'a, T> {}
213+
214+
/// Determines how the `transactions` field of block should be filled.
215+
///
216+
/// This essentially represents the `full:bool` argument in RPC calls that determine whether the
217+
/// response should include full transaction objects or just the hashes.
218+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
219+
pub enum BlockTransactionsKind {
220+
/// Only include hashes: [BlockTransactions::Hashes]
221+
#[default]
222+
Hashes,
223+
/// Include full transaction objects: [BlockTransactions::Full]
224+
Full,
225+
}
226+
227+
impl From<bool> for BlockTransactionsKind {
228+
fn from(is_full: bool) -> Self {
229+
if is_full {
230+
Self::Full
231+
} else {
232+
Self::Hashes
233+
}
234+
}
235+
}
236+
237+
impl From<BlockTransactionsKind> for bool {
238+
fn from(kind: BlockTransactionsKind) -> Self {
239+
match kind {
240+
BlockTransactionsKind::Full => true,
241+
BlockTransactionsKind::Hashes => false,
242+
}
243+
}
244+
}
245+
246+
#[cfg(test)]
247+
mod tests {
248+
use super::*;
249+
250+
#[test]
251+
fn test_full_conversion() {
252+
let full = true;
253+
assert_eq!(BlockTransactionsKind::Full, full.into());
254+
255+
let full = false;
256+
assert_eq!(BlockTransactionsKind::Hashes, full.into());
257+
}
258+
}

crates/network-primitives/src/lib.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![doc = include_str!("../README.md")]
2+
#![doc(
3+
html_logo_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/alloy.jpg",
4+
html_favicon_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/favicon.ico"
5+
)]
6+
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
7+
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
8+
9+
mod traits;
10+
pub use traits::{ReceiptResponse, TransactionResponse};
11+
12+
mod block;
13+
pub use block::{BlockTransactionHashes, BlockTransactions, BlockTransactionsKind};

0 commit comments

Comments
 (0)