Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: add network-primitives #1101

Merged
merged 5 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ alloy-eip7547 = { version = "0.2", path = "crates/eip7547", default-features = f
alloy-genesis = { version = "0.2", path = "crates/genesis", default-features = false }
alloy-json-rpc = { version = "0.2", path = "crates/json-rpc", default-features = false }
alloy-network = { version = "0.2", path = "crates/network", default-features = false }
alloy-network-primitives = { version = "0.2", path = "crates/network-primitives", default-features = false }
alloy-node-bindings = { version = "0.2", path = "crates/node-bindings", default-features = false }
alloy-provider = { version = "0.2", path = "crates/provider", default-features = false }
alloy-pubsub = { version = "0.2", path = "crates/pubsub", default-features = false }
Expand Down
1 change: 1 addition & 0 deletions crates/contract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ workspace = true

[dependencies]
alloy-network.workspace = true
alloy-network-primitives.workspace = true
alloy-provider.workspace = true
alloy-rpc-types-eth.workspace = true
alloy-transport.workspace = true
Expand Down
3 changes: 2 additions & 1 deletion crates/contract/src/call.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::{CallDecoder, Error, EthCall, Result};
use alloy_dyn_abi::{DynSolValue, JsonAbiExt};
use alloy_json_abi::Function;
use alloy_network::{Ethereum, Network, ReceiptResponse, TransactionBuilder};
use alloy_network::{Ethereum, Network, TransactionBuilder};
use alloy_network_primitives::ReceiptResponse;
use alloy_primitives::{Address, Bytes, ChainId, TxKind, U256};
use alloy_provider::{PendingTransactionBuilder, Provider};
use alloy_rpc_types_eth::{state::StateOverride, AccessList, BlobTransactionSidecar, BlockId};
Expand Down
28 changes: 28 additions & 0 deletions crates/network-primitives/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "alloy-network-primitives"
description = "Primitive types for Alloy network abstraction"

version.workspace = true
edition.workspace = true
rust-version.workspace = true
authors.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
exclude.workspace = true

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[lints]
workspace = true

[dependencies]
alloy-primitives.workspace = true
alloy-serde.workspace = true

serde.workspace = true

[dev-dependencies]
rand.workspace = true
3 changes: 3 additions & 0 deletions crates/network-primitives/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# alloy-network-primitives

Primitive types for Alloy network abstraction.
258 changes: 258 additions & 0 deletions crates/network-primitives/src/block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
use alloy_primitives::B256;
use serde::{Deserialize, Serialize};

use crate::TransactionResponse;

/// Block Transactions depending on the boolean attribute of `eth_getBlockBy*`,
/// or if used by `eth_getUncle*`
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum BlockTransactions<T> {
/// Full transactions
Full(Vec<T>),
/// Only hashes
Hashes(Vec<B256>),
/// Special case for uncle response.
Uncle,
}

impl<T> Default for BlockTransactions<T> {
fn default() -> Self {
Self::Hashes(Vec::default())
}
}

impl<T> BlockTransactions<T> {
/// Check if the enum variant is used for hashes.
#[inline]
pub const fn is_hashes(&self) -> bool {
matches!(self, Self::Hashes(_))
}

/// Fallibly cast to a slice of hashes.
pub fn as_hashes(&self) -> Option<&[B256]> {
match self {
Self::Hashes(hashes) => Some(hashes),
_ => None,
}
}

/// Returns true if the enum variant is used for full transactions.
#[inline]
pub const fn is_full(&self) -> bool {
matches!(self, Self::Full(_))
}

/// Fallibly cast to a slice of transactions.
///
/// Returns `None` if the enum variant is not `Full`.
pub fn as_transactions(&self) -> Option<&[T]> {
match self {
Self::Full(txs) => Some(txs),
_ => None,
}
}

/// Returns true if the enum variant is used for an uncle response.
#[inline]
pub const fn is_uncle(&self) -> bool {
matches!(self, Self::Uncle)
}

/// Returns an iterator over the transactions (if any). This will be empty
/// if the block is an uncle or if the transaction list contains only
/// hashes.
#[doc(alias = "transactions")]
pub fn txns(&self) -> impl Iterator<Item = &T> {
self.as_transactions().map(|txs| txs.iter()).unwrap_or_else(|| [].iter())
}

/// Returns an iterator over the transactions (if any). This will be empty if the block is not
/// full.
pub fn into_transactions(self) -> std::vec::IntoIter<T> {
match self {
Self::Full(txs) => txs.into_iter(),
_ => std::vec::IntoIter::default(),
}
}

/// Returns an instance of BlockTransactions with the Uncle special case.
#[inline]
pub const fn uncle() -> Self {
Self::Uncle
}

/// Returns the number of transactions.
#[inline]
pub fn len(&self) -> usize {
match self {
Self::Hashes(h) => h.len(),
Self::Full(f) => f.len(),
Self::Uncle => 0,
}
}

/// Whether the block has no transactions.
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}

impl<T: TransactionResponse> BlockTransactions<T> {
/// Converts `self` into `Hashes`.
#[inline]
pub fn convert_to_hashes(&mut self) {
if !self.is_hashes() {
*self = Self::Hashes(self.hashes().collect());
}
}

/// Converts `self` into `Hashes`.
#[inline]
pub fn into_hashes(mut self) -> Self {
self.convert_to_hashes();
self
}

/// Returns an iterator over the transaction hashes.
#[deprecated = "use `hashes` instead"]
#[inline]
pub fn iter(&self) -> BlockTransactionHashes<'_, T> {
self.hashes()
}

/// Returns an iterator over references to the transaction hashes.
#[inline]
pub fn hashes(&self) -> BlockTransactionHashes<'_, T> {
BlockTransactionHashes::new(self)
}
}

impl<T> From<Vec<B256>> for BlockTransactions<T> {
fn from(hashes: Vec<B256>) -> Self {
Self::Hashes(hashes)
}
}

impl<T: TransactionResponse> From<Vec<T>> for BlockTransactions<T> {
fn from(transactions: Vec<T>) -> Self {
Self::Full(transactions)
}
}

/// An iterator over the transaction hashes of a block.
///
/// See [`BlockTransactions::hashes`].
#[derive(Clone, Debug)]
pub struct BlockTransactionHashes<'a, T>(BlockTransactionHashesInner<'a, T>);

#[derive(Clone, Debug)]
enum BlockTransactionHashesInner<'a, T> {
Hashes(std::slice::Iter<'a, B256>),
Full(std::slice::Iter<'a, T>),
Uncle,
}

impl<'a, T> BlockTransactionHashes<'a, T> {
#[inline]
fn new(txs: &'a BlockTransactions<T>) -> Self {
Self(match txs {
BlockTransactions::Hashes(txs) => BlockTransactionHashesInner::Hashes(txs.iter()),
BlockTransactions::Full(txs) => BlockTransactionHashesInner::Full(txs.iter()),
BlockTransactions::Uncle => BlockTransactionHashesInner::Uncle,
})
}
}

impl<'a, T: TransactionResponse> Iterator for BlockTransactionHashes<'a, T> {
type Item = B256;

#[inline]
fn next(&mut self) -> Option<Self::Item> {
match &mut self.0 {
BlockTransactionHashesInner::Hashes(txs) => txs.next().copied(),
BlockTransactionHashesInner::Full(txs) => txs.next().map(|tx| tx.tx_hash()),
BlockTransactionHashesInner::Uncle => None,
}
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
match &self.0 {
BlockTransactionHashesInner::Full(txs) => txs.size_hint(),
BlockTransactionHashesInner::Hashes(txs) => txs.size_hint(),
BlockTransactionHashesInner::Uncle => (0, Some(0)),
}
}
}

impl<T: TransactionResponse> ExactSizeIterator for BlockTransactionHashes<'_, T> {
#[inline]
fn len(&self) -> usize {
match &self.0 {
BlockTransactionHashesInner::Full(txs) => txs.len(),
BlockTransactionHashesInner::Hashes(txs) => txs.len(),
BlockTransactionHashesInner::Uncle => 0,
}
}
}

impl<T: TransactionResponse> DoubleEndedIterator for BlockTransactionHashes<'_, T> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
match &mut self.0 {
BlockTransactionHashesInner::Full(txs) => txs.next_back().map(|tx| tx.tx_hash()),
BlockTransactionHashesInner::Hashes(txs) => txs.next_back().copied(),
BlockTransactionHashesInner::Uncle => None,
}
}
}

impl<'a, T: TransactionResponse> std::iter::FusedIterator for BlockTransactionHashes<'a, T> {}

/// Determines how the `transactions` field of block should be filled.
///
/// This essentially represents the `full:bool` argument in RPC calls that determine whether the
/// response should include full transaction objects or just the hashes.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum BlockTransactionsKind {
/// Only include hashes: [BlockTransactions::Hashes]
#[default]
Hashes,
/// Include full transaction objects: [BlockTransactions::Full]
Full,
}

impl From<bool> for BlockTransactionsKind {
fn from(is_full: bool) -> Self {
if is_full {
Self::Full
} else {
Self::Hashes
}
}
}

impl From<BlockTransactionsKind> for bool {
fn from(kind: BlockTransactionsKind) -> Self {
match kind {
BlockTransactionsKind::Full => true,
BlockTransactionsKind::Hashes => false,
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_full_conversion() {
let full = true;
assert_eq!(BlockTransactionsKind::Full, full.into());

let full = false;
assert_eq!(BlockTransactionsKind::Hashes, full.into());
}
}
13 changes: 13 additions & 0 deletions crates/network-primitives/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/alloy.jpg",
html_favicon_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/favicon.ico"
)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

mod traits;
pub use traits::{ReceiptResponse, TransactionResponse};

mod block;
pub use block::{BlockTransactionHashes, BlockTransactions, BlockTransactionsKind};
Loading
Loading