diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f41da2a36f..6c9a75730df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Bug Fixes +- Make Parity TraceResults output optional ([#1102](https://github.com/alloy-rs/alloy/issues/1102)) +- Correctly trim eip7251 bytecode ([#1105](https://github.com/alloy-rs/alloy/issues/1105)) +- [eips] Make SignedAuthorizationList arbitrary less fallible ([#1084](https://github.com/alloy-rs/alloy/issues/1084)) +- [node-bindings] Backport fix from ethers-rs ([#1081](https://github.com/alloy-rs/alloy/issues/1081)) - Trim conflicting key `max_fee_per_blob_gas` from Eip1559 tx type ([#1064](https://github.com/alloy-rs/alloy/issues/1064)) - [provider] Prevent panic from having 0 keys when calling `on_anvil_with_wallet_and_config` ([#1055](https://github.com/alloy-rs/alloy/issues/1055)) - Require storageKeys value broken bincode serialization from [#955](https://github.com/alloy-rs/alloy/issues/955) ([#1058](https://github.com/alloy-rs/alloy/issues/1058)) @@ -19,10 +23,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Dependencies +- Bump jsonrpsee 0.24 ([#1067](https://github.com/alloy-rs/alloy/issues/1067)) - [deps] Bump Trezor client to `=0.1.4` to fix signing bug ([#1045](https://github.com/alloy-rs/alloy/issues/1045)) +### Documentation + +- Readme fix ([#1114](https://github.com/alloy-rs/alloy/issues/1114)) +- Update links to use docs.rs ([#1066](https://github.com/alloy-rs/alloy/issues/1066)) + ### Features +- [engine-types] `PayloadError::PrePragueBlockWithEip7702Transactions` ([#1116](https://github.com/alloy-rs/alloy/issues/1116)) +- Use EncodableSignature for tx encoding ([#1100](https://github.com/alloy-rs/alloy/issues/1100)) +- Eth_simulateV1 Request / Response types ([#1042](https://github.com/alloy-rs/alloy/issues/1042)) +- Add helper for decoding custom errors ([#1098](https://github.com/alloy-rs/alloy/issues/1098)) +- Enable more features transitively in meta crate ([#1097](https://github.com/alloy-rs/alloy/issues/1097)) +- [rpc/trace] Filter matches with trace ([#1090](https://github.com/alloy-rs/alloy/issues/1090)) +- Feat(rpc-type-eth) convert vec TxReq to bundle ([#1091](https://github.com/alloy-rs/alloy/issues/1091)) +- [eip] Make 7702 auth recovery fallible ([#1082](https://github.com/alloy-rs/alloy/issues/1082)) +- [json-rpc] Implement `From for Id` and `From for Id` ([#1088](https://github.com/alloy-rs/alloy/issues/1088)) +- [consensus] Add `From` for `Request` ([#1083](https://github.com/alloy-rs/alloy/issues/1083)) +- Feat(provider) : introduction to eth_sendRawTransactionConditional RPC endpoint type ([#1009](https://github.com/alloy-rs/alloy/issues/1009)) - Expose encoded_len_with_signature() ([#1063](https://github.com/alloy-rs/alloy/issues/1063)) - Add 7702 tx type ([#1046](https://github.com/alloy-rs/alloy/issues/1046)) - [rpc-types-eth] Serde flatten `BlobTransactionSidecar` in tx req ([#1054](https://github.com/alloy-rs/alloy/issues/1054)) @@ -39,15 +60,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 +- [rpc] Make `Deserialize` impl for `FilterChanges` generic over transaction ([#1118](https://github.com/alloy-rs/alloy/issues/1118)) +- Correctly cfg unused type ([#1117](https://github.com/alloy-rs/alloy/issues/1117)) +- Re-export and document network-primitives ([#1107](https://github.com/alloy-rs/alloy/issues/1107)) +- Allow override all group ([#1104](https://github.com/alloy-rs/alloy/issues/1104)) +- Chore : fix typos ([#1087](https://github.com/alloy-rs/alloy/issues/1087)) +- Export rpc account type ([#1075](https://github.com/alloy-rs/alloy/issues/1075)) - Release 0.2.0 - Make auth mandatory in recovered auth ([#1047](https://github.com/alloy-rs/alloy/issues/1047)) - Trace output utils ([#1027](https://github.com/alloy-rs/alloy/issues/1027)) - Fix unnameable types ([#1029](https://github.com/alloy-rs/alloy/issues/1029)) - Add payloadbodies v2 to capabilities set ([#1025](https://github.com/alloy-rs/alloy/issues/1025)) +### Other + +- Add `AccessListResult` type (EIP-2930) ([#1110](https://github.com/alloy-rs/alloy/issues/1110)) +- Derive arbitrary for `TransactionRequest` ([#1113](https://github.com/alloy-rs/alloy/issues/1113)) +- Fix typo in genesis ([#1096](https://github.com/alloy-rs/alloy/issues/1096)) +- Removing async get account ([#1080](https://github.com/alloy-rs/alloy/issues/1080)) +- Added stages to the sync info rpc type ([#1079](https://github.com/alloy-rs/alloy/issues/1079)) +- `alloy-consensus` should use `alloy_primitives::Sealable` ([#1072](https://github.com/alloy-rs/alloy/issues/1072)) + ### Refactor +- Add network-primitives ([#1101](https://github.com/alloy-rs/alloy/issues/1101)) - Replace `U64` with `u64` ([#1057](https://github.com/alloy-rs/alloy/issues/1057)) ### Styling diff --git a/Cargo.toml b/Cargo.toml index 5eb39ab9660..301c474fd48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["crates/*"] resolver = "2" [workspace.package] -version = "0.2.0" +version = "0.2.1" edition = "2021" rust-version = "1.76" authors = ["Alloy Contributors"] @@ -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 } diff --git a/README.md b/README.md index 7f618a9823e..1f710d763ba 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Alloy connects applications to blockchains. Alloy is a rewrite of [`ethers-rs`] from the ground up, with exciting new -features, high performance, and excellent [docs](https://alloy-rs.github.io/alloy/). +features, high performance, and excellent [docs](https://docs.rs/alloy). We also have a [book](https://alloy.rs/) on all things Alloy and many [examples](https://github.com/alloy-rs/examples) to help you get started. @@ -26,7 +26,7 @@ cargo add alloy --features full Alternatively, you can add the following to your `Cargo.toml` file: ```toml -alloy = { version = "0.1", features = ["full"] } +alloy = { version = "0.2", features = ["full"] } ``` For a more fine-grained control over the features you wish to include, you can add the individual crates to your `Cargo.toml` file, or use the `alloy` crate with the features you need. @@ -44,6 +44,7 @@ This repository contains the following crates: - [`alloy-genesis`] - Ethereum genesis file definitions - [`alloy-json-rpc`] - Core data types for JSON-RPC 2.0 clients - [`alloy-network`] - Network abstraction for RPC types + - [`alloy-network-primitives`] - Primitive types for the network abstraction - [`alloy-node-bindings`] - Ethereum execution-layer client bindings - [`alloy-provider`] - Interface with an Ethereum blockchain - [`alloy-pubsub`] - Ethereum JSON-RPC [publish-subscribe] tower service and type definitions @@ -77,6 +78,7 @@ This repository contains the following crates: [`alloy-genesis`]: https://github.com/alloy-rs/alloy/tree/main/crates/genesis [`alloy-json-rpc`]: https://github.com/alloy-rs/alloy/tree/main/crates/json-rpc [`alloy-network`]: https://github.com/alloy-rs/alloy/tree/main/crates/network +[`alloy-network-primitives`]: https://github.com/alloy-rs/alloy/tree/main/crates/network-primitives [`alloy-node-bindings`]: https://github.com/alloy-rs/alloy/tree/main/crates/node-bindings [`alloy-provider`]: https://github.com/alloy-rs/alloy/tree/main/crates/provider [`alloy-pubsub`]: https://github.com/alloy-rs/alloy/tree/main/crates/pubsub diff --git a/crates/alloy/CHANGELOG.md b/crates/alloy/CHANGELOG.md index 87fe5d40423..2b4a653a6e8 100644 --- a/crates/alloy/CHANGELOG.md +++ b/crates/alloy/CHANGELOG.md @@ -5,15 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Features +- Enable more features transitively in meta crate ([#1097](https://github.com/alloy-rs/alloy/issues/1097)) - Add `rpc-types-mev` feature to meta crate ([#1040](https://github.com/alloy-rs/alloy/issues/1040)) ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/alloy/Cargo.toml b/crates/alloy/Cargo.toml index a1733b0c4ef..fefbbaac9f4 100644 --- a/crates/alloy/Cargo.toml +++ b/crates/alloy/Cargo.toml @@ -131,7 +131,7 @@ network = ["dep:alloy-network"] node-bindings = ["dep:alloy-node-bindings", "alloy-provider?/anvil-node"] # providers -providers = ["dep:alloy-provider", "rpc-client", "eips"] +providers = ["dep:alloy-provider", "rpc-client", "transports", "eips"] provider-http = ["providers", "transport-http"] provider-ws = ["providers", "alloy-provider?/ws", "transport-ws"] provider-ipc = ["providers", "alloy-provider?/ipc", "transport-ipc"] @@ -184,9 +184,9 @@ pubsub = [ # rpc rpc = [] json-rpc = ["rpc", "dep:alloy-json-rpc"] -rpc-client = ["rpc", "dep:alloy-rpc-client"] -rpc-client-ws = ["rpc-client", "alloy-rpc-client?/ws"] -rpc-client-ipc = ["rpc-client", "alloy-rpc-client?/ipc"] +rpc-client = ["rpc", "transports", "transport-http", "dep:alloy-rpc-client"] +rpc-client-ws = ["rpc-client", "transport-ws", "alloy-rpc-client?/ws"] +rpc-client-ipc = ["rpc-client", "transport-ipc", "alloy-rpc-client?/ipc"] rpc-types = ["rpc", "dep:alloy-rpc-types", "alloy-rpc-types?/eth"] rpc-types-admin = [ "rpc-types", diff --git a/crates/consensus/CHANGELOG.md b/crates/consensus/CHANGELOG.md index f866a1a7d3c..ac3840c789d 100644 --- a/crates/consensus/CHANGELOG.md +++ b/crates/consensus/CHANGELOG.md @@ -5,18 +5,24 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Features +- Use EncodableSignature for tx encoding ([#1100](https://github.com/alloy-rs/alloy/issues/1100)) +- [consensus] Add `From` for `Request` ([#1083](https://github.com/alloy-rs/alloy/issues/1083)) - Expose encoded_len_with_signature() ([#1063](https://github.com/alloy-rs/alloy/issues/1063)) - Add 7702 tx type ([#1046](https://github.com/alloy-rs/alloy/issues/1046)) - Impl `arbitrary` for tx structs ([#1050](https://github.com/alloy-rs/alloy/issues/1050)) ### Miscellaneous Tasks +- Chore : fix typos ([#1087](https://github.com/alloy-rs/alloy/issues/1087)) - Release 0.2.0 -- Release 0.2.0 + +### Other + +- `alloy-consensus` should use `alloy_primitives::Sealable` ([#1072](https://github.com/alloy-rs/alloy/issues/1072)) ### Styling diff --git a/crates/consensus/src/account.rs b/crates/consensus/src/account.rs index 66d189f349a..66ff20dc741 100644 --- a/crates/consensus/src/account.rs +++ b/crates/consensus/src/account.rs @@ -19,7 +19,7 @@ pub struct Account { } impl Account { - /// Compute hash as committed to in the MPT trie without memoizing. + /// Compute hash as committed to in the MPT trie without memorizing. pub fn trie_hash_slow(&self) -> B256 { keccak256(alloy_rlp::encode(self)) } diff --git a/crates/consensus/src/encodable_signature.rs b/crates/consensus/src/encodable_signature.rs new file mode 100644 index 00000000000..02d8a434421 --- /dev/null +++ b/crates/consensus/src/encodable_signature.rs @@ -0,0 +1,108 @@ +use alloy_primitives::{Parity, SignatureError, U256}; + +/// Helper trait used to streamline signatures encoding. +pub trait EncodableSignature: Sized { + /// Instantiate from v, r, s. + fn from_rs_and_parity, E: Into>( + r: U256, + s: U256, + parity: P, + ) -> Result; + + /// Returns the `r` component of this signature. + fn r(&self) -> U256; + + /// Returns the `s` component of this signature. + fn s(&self) -> U256; + + /// Returns the recovery ID as a `u8`. + fn v(&self) -> Parity; + + /// Sets the recovery ID by normalizing a `v` value. + fn with_parity>(self, parity: T) -> Self; + + /// Modifies the recovery ID by applying [EIP-155] to a `v` value. + /// + /// [EIP-155]: https://eips.ethereum.org/EIPS/eip-155 + #[inline] + fn with_chain_id(self, chain_id: u64) -> Self + where + Self: Copy, + { + self.with_parity(self.v().with_chain_id(chain_id)) + } + + /// Modifies the recovery ID by dropping any [EIP-155] v value, converting + /// to a simple parity bool. + fn with_parity_bool(self) -> Self + where + Self: Copy, + { + self.with_parity(self.v().to_parity_bool()) + } + + /// Decode an RLP-encoded VRS signature. + fn decode_rlp_vrs(buf: &mut &[u8]) -> Result { + use alloy_rlp::Decodable; + + let parity: Parity = Decodable::decode(buf)?; + let r = Decodable::decode(buf)?; + let s = Decodable::decode(buf)?; + + Self::from_rs_and_parity(r, s, parity) + .map_err(|_| alloy_rlp::Error::Custom("attempted to decode invalid field element")) + } + + /// Length of RLP RS field encoding + fn rlp_rs_len(&self) -> usize { + alloy_rlp::Encodable::length(&self.r()) + alloy_rlp::Encodable::length(&self.s()) + } + + /// Length of RLP V field encoding + fn rlp_vrs_len(&self) -> usize { + self.rlp_rs_len() + alloy_rlp::Encodable::length(&self.v()) + } + + /// Write R and S to an RLP buffer in progress. + fn write_rlp_rs(&self, out: &mut dyn alloy_rlp::BufMut) { + alloy_rlp::Encodable::encode(&self.r(), out); + alloy_rlp::Encodable::encode(&self.s(), out); + } + + /// Write the V to an RLP buffer without using EIP-155. + fn write_rlp_v(&self, out: &mut dyn alloy_rlp::BufMut) { + alloy_rlp::Encodable::encode(&self.v(), out); + } + + /// Write the VRS to the output. The V will always be 27 or 28. + fn write_rlp_vrs(&self, out: &mut dyn alloy_rlp::BufMut) { + self.write_rlp_v(out); + self.write_rlp_rs(out); + } +} + +impl EncodableSignature for alloy_primitives::Signature { + fn from_rs_and_parity, E: Into>( + r: U256, + s: U256, + parity: P, + ) -> Result { + Self::from_rs_and_parity(r, s, parity) + } + + fn r(&self) -> U256 { + self.r() + } + + fn s(&self) -> U256 { + self.s() + } + + fn v(&self) -> Parity { + self.v() + } + + fn with_parity>(self, parity: T) -> Self { + self.with_parity(parity) + } +} diff --git a/crates/consensus/src/header.rs b/crates/consensus/src/header.rs index 91f17b797af..f593b873929 100644 --- a/crates/consensus/src/header.rs +++ b/crates/consensus/src/header.rs @@ -1,9 +1,10 @@ -use crate::Sealable; use alloy_eips::{ eip1559::{calc_next_block_base_fee, BaseFeeParams}, eip4844::{calc_blob_gasprice, calc_excess_blob_gas}, }; -use alloy_primitives::{b256, keccak256, Address, BlockNumber, Bloom, Bytes, B256, B64, U256}; +use alloy_primitives::{ + b256, keccak256, Address, BlockNumber, Bloom, Bytes, Sealable, B256, B64, U256, +}; use alloy_rlp::{ length_of_length, Buf, BufMut, Decodable, Encodable, EMPTY_LIST_CODE, EMPTY_STRING_CODE, }; @@ -161,7 +162,7 @@ impl Default for Header { } impl Sealable for Header { - fn hash(&self) -> B256 { + fn hash_slow(&self) -> B256 { self.hash_slow() } } diff --git a/crates/consensus/src/lib.rs b/crates/consensus/src/lib.rs index da2e1cb1dcc..b1748bbde4c 100644 --- a/crates/consensus/src/lib.rs +++ b/crates/consensus/src/lib.rs @@ -15,6 +15,9 @@ pub use account::Account; pub mod constants; +mod encodable_signature; +pub use encodable_signature::EncodableSignature; + mod header; pub use header::{Header, EMPTY_OMMER_ROOT_HASH, EMPTY_ROOT_HASH}; @@ -42,8 +45,7 @@ pub use alloy_eips::eip4844::{ #[cfg(feature = "kzg")] pub use alloy_eips::eip4844::env_settings::EnvKzgSettings; -mod sealed; -pub use sealed::{Sealable, Sealed}; +pub use alloy_primitives::{Sealable, Sealed}; mod signed; pub use signed::Signed; diff --git a/crates/consensus/src/receipt/receipts.rs b/crates/consensus/src/receipt/receipts.rs index f57e648c3e4..e9476a81f72 100644 --- a/crates/consensus/src/receipt/receipts.rs +++ b/crates/consensus/src/receipt/receipts.rs @@ -107,7 +107,7 @@ impl From> for Receipt { /// This convenience type allows us to lazily calculate the bloom filter for a /// receipt, similar to [`Sealed`]. /// -/// [`Sealed`]: crate::sealed::Sealed +/// [`Sealed`]: crate::Sealed #[derive(Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] diff --git a/crates/consensus/src/request.rs b/crates/consensus/src/request.rs index 6b6cb3211a9..6087d856f19 100644 --- a/crates/consensus/src/request.rs +++ b/crates/consensus/src/request.rs @@ -41,6 +41,12 @@ impl From for Request { } } +impl From for Request { + fn from(v: ConsolidationRequest) -> Self { + Self::ConsolidationRequest(v) + } +} + impl Request { /// Whether this is a [`DepositRequest`]. pub const fn is_deposit_request(&self) -> bool { diff --git a/crates/consensus/src/sealed.rs b/crates/consensus/src/sealed.rs deleted file mode 100644 index 9db492d7523..00000000000 --- a/crates/consensus/src/sealed.rs +++ /dev/null @@ -1,67 +0,0 @@ -use alloy_primitives::B256; - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -/// A consensus hashable item, with its memoized hash. -/// -/// We do not implement -pub struct Sealed { - /// The inner item - inner: T, - /// Its hash. - seal: B256, -} - -impl core::ops::Deref for Sealed { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.inner() - } -} - -impl Sealed { - /// Instantiate without performing the hash. This should be used carefully. - pub const fn new_unchecked(inner: T, seal: B256) -> Self { - Self { inner, seal } - } - - /// Decompose into parts. - pub fn into_parts(self) -> (T, B256) { - (self.inner, self.seal) - } - - /// Get the inner item. - #[inline(always)] - pub const fn inner(&self) -> &T { - &self.inner - } - - /// Get the hash. - #[inline(always)] - pub const fn seal(&self) -> B256 { - self.seal - } - - /// Geth the hash (alias for [`Self::seal`]). - #[inline(always)] - pub const fn hash(&self) -> B256 { - self.seal() - } -} - -/// Sealable objects. -pub trait Sealable: Sized { - /// Calculate the seal hash, this may be slow. - fn hash(&self) -> B256; - - /// Seal the object by calculating the hash. This may be slow. - fn seal_slow(self) -> Sealed { - let seal = self.hash(); - Sealed::new_unchecked(self, seal) - } - - /// Instantiate an unchecked seal. This should be used with caution. - fn seal_unchecked(self, seal: B256) -> Sealed { - Sealed::new_unchecked(self, seal) - } -} diff --git a/crates/consensus/src/transaction/eip1559.rs b/crates/consensus/src/transaction/eip1559.rs index 3d09bc93a5b..219cf98c6fd 100644 --- a/crates/consensus/src/transaction/eip1559.rs +++ b/crates/consensus/src/transaction/eip1559.rs @@ -1,4 +1,4 @@ -use crate::{SignableTransaction, Signed, Transaction, TxType}; +use crate::{EncodableSignature, SignableTransaction, Signed, Transaction, TxType}; use alloy_eips::eip2930::AccessList; use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, U256}; use alloy_rlp::{BufMut, Decodable, Encodable, Header}; @@ -153,7 +153,10 @@ impl TxEip1559 { /// /// If `with_header` is `true`, the payload length will include the RLP header length. /// If `with_header` is `false`, the payload length will not include the RLP header length. - pub fn encoded_len_with_signature(&self, signature: &Signature, with_header: bool) -> usize { + pub fn encoded_len_with_signature(&self, signature: &S, with_header: bool) -> usize + where + S: EncodableSignature, + { // this counts the tx fields and signature fields let payload_length = self.fields_len() + signature.rlp_vrs_len(); @@ -228,7 +231,10 @@ impl TxEip1559 { /// tx type byte or string header. /// /// This __does__ encode a list header and include a signature. - pub(crate) fn encode_with_signature_fields(&self, signature: &Signature, out: &mut dyn BufMut) { + pub fn encode_with_signature_fields(&self, signature: &S, out: &mut dyn BufMut) + where + S: EncodableSignature, + { let payload_length = self.fields_len() + signature.rlp_vrs_len(); let header = Header { list: true, payload_length }; header.encode(out); diff --git a/crates/consensus/src/transaction/eip2930.rs b/crates/consensus/src/transaction/eip2930.rs index 621dd15edad..ee0f0896323 100644 --- a/crates/consensus/src/transaction/eip2930.rs +++ b/crates/consensus/src/transaction/eip2930.rs @@ -1,4 +1,4 @@ -use crate::{SignableTransaction, Signed, Transaction, TxType}; +use crate::{EncodableSignature, SignableTransaction, Signed, Transaction, TxType}; use alloy_eips::eip2930::AccessList; use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, U256}; use alloy_rlp::{length_of_length, BufMut, Decodable, Encodable, Header}; @@ -131,7 +131,10 @@ impl TxEip2930 { /// /// If `with_header` is `true`, the payload length will include the RLP header length. /// If `with_header` is `false`, the payload length will not include the RLP header length. - pub fn encoded_len_with_signature(&self, signature: &Signature, with_header: bool) -> usize { + pub fn encoded_len_with_signature(&self, signature: &S, with_header: bool) -> usize + where + S: EncodableSignature, + { // this counts the tx fields and signature fields let payload_length = self.fields_len() + signature.rlp_vrs_len(); @@ -176,7 +179,10 @@ impl TxEip2930 { /// tx type byte or string header. /// /// This __does__ encode a list header and include a signature. - pub(crate) fn encode_with_signature_fields(&self, signature: &Signature, out: &mut dyn BufMut) { + pub fn encode_with_signature_fields(&self, signature: &S, out: &mut dyn BufMut) + where + S: EncodableSignature, + { let payload_length = self.fields_len() + signature.rlp_vrs_len(); let header = Header { list: true, payload_length }; header.encode(out); diff --git a/crates/consensus/src/transaction/eip4844.rs b/crates/consensus/src/transaction/eip4844.rs index 4c00cbfc825..0b886fcfe19 100644 --- a/crates/consensus/src/transaction/eip4844.rs +++ b/crates/consensus/src/transaction/eip4844.rs @@ -1,4 +1,4 @@ -use crate::{SignableTransaction, Signed, Transaction, TxType}; +use crate::{EncodableSignature, SignableTransaction, Signed, Transaction, TxType}; use alloy_eips::{eip2930::AccessList, eip4844::DATA_GAS_PER_BLOB}; use alloy_primitives::{keccak256, Address, Bytes, ChainId, Signature, TxKind, B256, U256}; @@ -493,7 +493,10 @@ impl TxEip4844 { /// /// If `with_header` is `true`, the payload length will include the RLP header length. /// If `with_header` is `false`, the payload length will not include the RLP header length. - pub fn encoded_len_with_signature(&self, signature: &Signature, with_header: bool) -> usize { + pub fn encoded_len_with_signature(&self, signature: &S, with_header: bool) -> usize + where + S: EncodableSignature, + { // this counts the tx fields and signature fields let payload_length = self.fields_len() + signature.rlp_vrs_len(); @@ -538,7 +541,10 @@ impl TxEip4844 { /// tx type byte or string header. /// /// This __does__ encode a list header and include a signature. - pub(crate) fn encode_with_signature_fields(&self, signature: &Signature, out: &mut dyn BufMut) { + pub fn encode_with_signature_fields(&self, signature: &S, out: &mut dyn BufMut) + where + S: EncodableSignature, + { let payload_length = self.fields_len() + signature.rlp_vrs_len(); let header = Header { list: true, payload_length }; header.encode(out); diff --git a/crates/consensus/src/transaction/eip7702.rs b/crates/consensus/src/transaction/eip7702.rs index e852cee0ab0..6db47db3905 100644 --- a/crates/consensus/src/transaction/eip7702.rs +++ b/crates/consensus/src/transaction/eip7702.rs @@ -1,4 +1,4 @@ -use crate::{SignableTransaction, Signed, Transaction, TxType}; +use crate::{EncodableSignature, SignableTransaction, Signed, Transaction, TxType}; use alloy_eips::eip2930::AccessList; use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, U256}; use alloy_rlp::{BufMut, Decodable, Encodable, Header}; @@ -161,7 +161,10 @@ impl TxEip7702 { /// /// If `with_header` is `true`, the payload length will include the RLP header length. /// If `with_header` is `false`, the payload length will not include the RLP header length. - pub fn encoded_len_with_signature(&self, signature: &Signature, with_header: bool) -> usize { + pub fn encoded_len_with_signature(&self, signature: &S, with_header: bool) -> usize + where + S: EncodableSignature, + { // this counts the tx fields and signature fields let payload_length = self.fields_len() + signature.rlp_vrs_len(); @@ -236,7 +239,10 @@ impl TxEip7702 { /// tx type byte or string header. /// /// This __does__ encode a list header and include a signature. - pub(crate) fn encode_with_signature_fields(&self, signature: &Signature, out: &mut dyn BufMut) { + pub fn encode_with_signature_fields(&self, signature: &S, out: &mut dyn BufMut) + where + S: EncodableSignature, + { let payload_length = self.fields_len() + signature.rlp_vrs_len(); let header = Header { list: true, payload_length }; header.encode(out); diff --git a/crates/consensus/src/transaction/legacy.rs b/crates/consensus/src/transaction/legacy.rs index 6b2fb66ff22..04733e405cc 100644 --- a/crates/consensus/src/transaction/legacy.rs +++ b/crates/consensus/src/transaction/legacy.rs @@ -1,4 +1,4 @@ -use crate::{SignableTransaction, Signed, Transaction}; +use crate::{EncodableSignature, SignableTransaction, Signed, Transaction}; use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, U256}; use alloy_rlp::{length_of_length, BufMut, Decodable, Encodable, Header, Result}; use core::mem; @@ -104,11 +104,10 @@ impl TxLegacy { /// tx type byte or string header. /// /// This __does__ encode a list header and include a signature. - pub fn encode_with_signature_fields( - &self, - signature: &Signature, - out: &mut dyn alloy_rlp::BufMut, - ) { + pub fn encode_with_signature_fields(&self, signature: &S, out: &mut dyn alloy_rlp::BufMut) + where + S: EncodableSignature, + { let payload_length = self.fields_len() + signature.rlp_vrs_len(); let header = Header { list: true, payload_length }; header.encode(out); @@ -118,7 +117,10 @@ impl TxLegacy { /// Returns what the encoded length should be, if the transaction were RLP encoded with the /// given signature. - pub fn encoded_len_with_signature(&self, signature: &Signature) -> usize { + pub fn encoded_len_with_signature(&self, signature: &S) -> usize + where + S: EncodableSignature, + { let payload_length = self.fields_len() + signature.rlp_vrs_len(); Header { list: true, payload_length }.length() + payload_length } diff --git a/crates/contract/CHANGELOG.md b/crates/contract/CHANGELOG.md index 43945c28fe7..9dc353d0561 100644 --- a/crates/contract/CHANGELOG.md +++ b/crates/contract/CHANGELOG.md @@ -5,14 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 - Fix unnameable types ([#1029](https://github.com/alloy-rs/alloy/issues/1029)) +### Refactor + +- Add network-primitives ([#1101](https://github.com/alloy-rs/alloy/issues/1101)) + ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 ### Miscellaneous Tasks diff --git a/crates/contract/Cargo.toml b/crates/contract/Cargo.toml index 67610f3688a..b1862a2b99e 100644 --- a/crates/contract/Cargo.toml +++ b/crates/contract/Cargo.toml @@ -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 diff --git a/crates/contract/src/call.rs b/crates/contract/src/call.rs index 77ba97815a2..d9aaf5279ce 100644 --- a/crates/contract/src/call.rs +++ b/crates/contract/src/call.rs @@ -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}; diff --git a/crates/eip7547/CHANGELOG.md b/crates/eip7547/CHANGELOG.md index 516d9874b5f..9e0795538dd 100644 --- a/crates/eip7547/CHANGELOG.md +++ b/crates/eip7547/CHANGELOG.md @@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/eips/CHANGELOG.md b/crates/eips/CHANGELOG.md index 382eff41105..a5424f88e7a 100644 --- a/crates/eips/CHANGELOG.md +++ b/crates/eips/CHANGELOG.md @@ -5,16 +5,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Bug Fixes +- Correctly trim eip7251 bytecode ([#1105](https://github.com/alloy-rs/alloy/issues/1105)) +- [eips] Make SignedAuthorizationList arbitrary less fallible ([#1084](https://github.com/alloy-rs/alloy/issues/1084)) - Require storageKeys value broken bincode serialization from [#955](https://github.com/alloy-rs/alloy/issues/955) ([#1058](https://github.com/alloy-rs/alloy/issues/1058)) - Cargo fmt ([#1044](https://github.com/alloy-rs/alloy/issues/1044)) - [eip7702] Add correct rlp decode/encode ([#1034](https://github.com/alloy-rs/alloy/issues/1034)) ### Features +- [eip] Make 7702 auth recovery fallible ([#1082](https://github.com/alloy-rs/alloy/issues/1082)) - Add authorization list to rpc transaction and tx receipt types ([#1051](https://github.com/alloy-rs/alloy/issues/1051)) - Generate valid signed auth signatures ([#1041](https://github.com/alloy-rs/alloy/issues/1041)) - Add arbitrary to auth ([#1036](https://github.com/alloy-rs/alloy/issues/1036)) @@ -22,8 +25,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Miscellaneous Tasks +- Release 0.2.0 - Make auth mandatory in recovered auth ([#1047](https://github.com/alloy-rs/alloy/issues/1047)) +### Other + +- Add `AccessListResult` type (EIP-2930) ([#1110](https://github.com/alloy-rs/alloy/issues/1110)) + ### Styling - Remove proptest in all crates and Arbitrary derives ([#966](https://github.com/alloy-rs/alloy/issues/966)) diff --git a/crates/eips/Cargo.toml b/crates/eips/Cargo.toml index 4c2a1866ca5..aaff60fa33d 100644 --- a/crates/eips/Cargo.toml +++ b/crates/eips/Cargo.toml @@ -41,6 +41,7 @@ arbitrary = { workspace = true, features = ["derive"], optional = true } # for signed authorization list arbitrary k256 = { workspace = true, optional = true } +rand = { workspace = true, optional = true } [dev-dependencies] alloy-primitives = { workspace = true, features = [ @@ -80,6 +81,7 @@ arbitrary = [ "std", "kzg-sidecar", "dep:arbitrary", + "dep:rand", "alloy-primitives/arbitrary", "alloy-serde?/arbitrary", ] diff --git a/crates/eips/src/eip2930.rs b/crates/eips/src/eip2930.rs index 2686d7bf8ec..e215eec9e04 100644 --- a/crates/eips/src/eip2930.rs +++ b/crates/eips/src/eip2930.rs @@ -3,7 +3,7 @@ //! [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930 #[cfg(not(feature = "std"))] -use alloc::vec::Vec; +use alloc::{string::String, vec::Vec}; use alloy_primitives::{Address, B256, U256}; use alloy_rlp::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper}; @@ -141,6 +141,39 @@ pub struct AccessListWithGasUsed { pub gas_used: U256, } +/// `AccessListResult` for handling errors from `eth_createAccessList` +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] +pub struct AccessListResult { + /// List with accounts accessed during transaction. + pub access_list: AccessList, + /// Estimated gas used with access list. + pub gas_used: U256, + /// Optional error message if the transaction failed. + #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))] + pub error: Option, +} + +impl AccessListResult { + /// Ensures the result is OK, returning [`AccessListWithGasUsed`] if so, or an error message if + /// not. + pub fn ensure_ok(self) -> Result { + match self.error { + Some(err) => Err(err), + None => { + Ok(AccessListWithGasUsed { access_list: self.access_list, gas_used: self.gas_used }) + } + } + } + + /// Checks if there is an error in the result. + #[inline] + pub const fn is_err(&self) -> bool { + self.error.is_some() + } +} + #[cfg(all(test, feature = "serde"))] mod tests { use super::*; @@ -158,15 +191,16 @@ mod tests { #[test] fn access_list_with_gas_used() { - let list = AccessListWithGasUsed { + let list = AccessListResult { access_list: AccessList(vec![ AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] }, AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] }, ]), gas_used: U256::from(100), + error: None, }; let json = serde_json::to_string(&list).unwrap(); - let list2 = serde_json::from_str::(&json).unwrap(); + let list2 = serde_json::from_str(&json).unwrap(); assert_eq!(list, list2); } } diff --git a/crates/eips/src/eip7251.rs b/crates/eips/src/eip7251.rs index 23406b59efb..a5ffc0b19f9 100644 --- a/crates/eips/src/eip7251.rs +++ b/crates/eips/src/eip7251.rs @@ -11,7 +11,7 @@ pub const CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS: Address = address!("00b42dbF2194e931E80326D950320f7d9Dbeac02"); /// The code for the EIP-7251 consolidation requests contract. -pub static CONSOLIDATION_REQUEST_PREDEPLOY_CODE: Bytes = bytes!("0f5f395ff33373fffffffffffffffffffffffffffffffffffffffe146098573615156028575f545f5260205ff35b36606014156101445760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061014457600154600101600155600354806004026004013381556001015f35815560010160203581556001016040359055600101600355005b6003546002548082038060011160ac575060015b5f5b81811460f15780607402838201600402600401805490600101805490600101805490600101549260601b84529083601401528260340152906054015260010160ae565b9101809214610103579060025561010e565b90505f6002555f6003555b5f548061049d141561011d57505f5b6001546001828201116101325750505f610138565b01600190035b5f555f6001556074025ff35b5f5ffd"); +pub static CONSOLIDATION_REQUEST_PREDEPLOY_CODE: Bytes = bytes!("3373fffffffffffffffffffffffffffffffffffffffe146098573615156028575f545f5260205ff35b36606014156101445760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061014457600154600101600155600354806004026004013381556001015f35815560010160203581556001016040359055600101600355005b6003546002548082038060011160ac575060015b5f5b81811460f15780607402838201600402600401805490600101805490600101805490600101549260601b84529083601401528260340152906054015260010160ae565b9101809214610103579060025561010e565b90505f6002555f6003555b5f548061049d141561011d57505f5b6001546001828201116101325750505f610138565b01600190035b5f555f6001556074025ff35b5f5ffd"); /// The [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685) request type for consolidation requests. pub const CONSOLIDATION_REQUEST_TYPE: u8 = 0x02; diff --git a/crates/eips/src/eip7702/auth_list.rs b/crates/eips/src/eip7702/auth_list.rs index d7ef4142639..bfb0db9203c 100644 --- a/crates/eips/src/eip7702/auth_list.rs +++ b/crates/eips/src/eip7702/auth_list.rs @@ -9,6 +9,37 @@ use alloy_rlp::{ }; use core::hash::{Hash, Hasher}; +/// Represents the outcome of an attempt to recover the authority from an authorization. +/// It can either be valid (containing an [`Address`]) or invalid (indicating recovery failure). +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum RecoveredAuthority { + /// Indicates a successfully recovered authority address. + Valid(Address), + /// Indicates a failed recovery attempt where no valid address could be recovered. + Invalid, +} + +impl RecoveredAuthority { + /// Returns an optional address if valid. + pub const fn address(&self) -> Option
{ + match *self { + Self::Valid(address) => Some(address), + Self::Invalid => None, + } + } + + /// Returns true if the authority is valid. + pub const fn is_valid(&self) -> bool { + matches!(self, Self::Valid(_)) + } + + /// Returns true if the authority is invalid. + pub const fn is_invalid(&self) -> bool { + matches!(self, Self::Invalid) + } +} + /// An unsigned EIP-7702 authorization. #[derive(Debug, Clone, Hash, RlpEncodable, RlpDecodable, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -158,11 +189,12 @@ impl SignedAuthorization { /// Recover the authority and transform the signed authorization into a /// [`RecoveredAuthorization`]. - pub fn try_into_recovered( - self, - ) -> Result { - let authority = self.recover_authority()?; - Ok(RecoveredAuthorization { inner: self.inner, authority }) + pub fn into_recovered(self) -> RecoveredAuthorization { + let authority_result = self.recover_authority(); + let authority = + authority_result.map_or(RecoveredAuthority::Invalid, RecoveredAuthority::Valid); + + RecoveredAuthorization { inner: self.inner, authority } } } @@ -177,10 +209,15 @@ impl Deref for SignedAuthorization { #[cfg(all(any(test, feature = "arbitrary"), feature = "k256"))] impl<'a> arbitrary::Arbitrary<'a> for SignedAuthorization { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - use k256::ecdsa::{signature::hazmat::PrehashSigner, SigningKey}; - let key_bytes = u.arbitrary::<[u8; 32]>()?; - let signing_key = SigningKey::from_bytes(&key_bytes.into()) - .map_err(|_| arbitrary::Error::IncorrectFormat)?; + use k256::{ + ecdsa::{signature::hazmat::PrehashSigner, SigningKey}, + NonZeroScalar, + }; + use rand::{rngs::StdRng, SeedableRng}; + + let rng_seed = u.arbitrary::<[u8; 32]>()?; + let mut rand_gen = StdRng::from_seed(rng_seed); + let signing_key: SigningKey = NonZeroScalar::random(&mut rand_gen).into(); let inner = u.arbitrary::()?; let signature_hash = inner.signature_hash(); @@ -200,35 +237,41 @@ impl<'a> arbitrary::Arbitrary<'a> for SignedAuthorization { pub struct RecoveredAuthorization { #[cfg_attr(feature = "serde", serde(flatten))] inner: Authorization, - authority: Address, + /// The result of the authority recovery process, which can either be a valid address or + /// indicate a failure. + authority: RecoveredAuthority, } impl RecoveredAuthorization { /// Instantiate without performing recovery. This should be used carefully. - pub const fn new_unchecked(inner: Authorization, authority: Address) -> Self { + pub const fn new_unchecked(inner: Authorization, authority: RecoveredAuthority) -> Self { Self { inner, authority } } - /// Get the `authority` for the authorization. - pub const fn authority(&self) -> Address { - self.authority + /// Returns an optional address based on the current state of the authority. + pub const fn authority(&self) -> Option
{ + self.authority.address() } /// Splits the authorization into parts. - pub const fn into_parts(self) -> (Authorization, Address) { + pub const fn into_parts(self) -> (Authorization, RecoveredAuthority) { (self.inner, self.authority) } } #[cfg(feature = "k256")] -impl TryFrom for RecoveredAuthorization { - type Error = alloy_primitives::SignatureError; - - fn try_from(value: SignedAuthorization) -> Result { - value.try_into_recovered() +impl From for RecoveredAuthority { + fn from(value: SignedAuthorization) -> Self { + value.into_recovered().authority } } +#[cfg(feature = "k256")] +impl From for RecoveredAuthorization { + fn from(value: SignedAuthorization) -> Self { + value.into_recovered() + } +} impl Deref for RecoveredAuthorization { type Target = Authorization; @@ -307,7 +350,6 @@ impl Deref for OptionalNonce { mod tests { use super::*; use alloy_primitives::{hex, Signature}; - use arbitrary::Arbitrary; use core::str::FromStr; fn test_encode_decode_roundtrip(auth: Authorization) { @@ -367,10 +409,15 @@ mod tests { assert_eq!(decoded, auth); } - #[cfg(feature = "k256")] + #[cfg(all(feature = "arbitrary", feature = "k256"))] #[test] fn test_arbitrary_auth() { + use arbitrary::Arbitrary; let mut unstructured = arbitrary::Unstructured::new(b"unstructured auth"); + // try this multiple times + let _auth = SignedAuthorization::arbitrary(&mut unstructured).unwrap(); + let _auth = SignedAuthorization::arbitrary(&mut unstructured).unwrap(); + let _auth = SignedAuthorization::arbitrary(&mut unstructured).unwrap(); let _auth = SignedAuthorization::arbitrary(&mut unstructured).unwrap(); } } diff --git a/crates/eips/src/lib.rs b/crates/eips/src/lib.rs index 393ba4f343e..b1111f8eeb5 100644 --- a/crates/eips/src/lib.rs +++ b/crates/eips/src/lib.rs @@ -11,6 +11,11 @@ #[macro_use] extern crate alloc; +// To ensure no unused imports, since signed auth list requires arbitrary _and_ k256 features, but +// is only enabled using the `arbitrary` feature. +#[cfg(all(not(feature = "k256"), feature = "arbitrary"))] +use rand as _; + pub mod eip1559; pub use eip1559::calc_next_block_base_fee; diff --git a/crates/genesis/CHANGELOG.md b/crates/genesis/CHANGELOG.md index a44f3b900df..2a74f64d576 100644 --- a/crates/genesis/CHANGELOG.md +++ b/crates/genesis/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Features @@ -13,8 +13,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Miscellaneous Tasks +- Release 0.2.1 - Release 0.2.0 -- Release 0.2.0 + +### Other + +- Fix typo in genesis ([#1096](https://github.com/alloy-rs/alloy/issues/1096)) ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/genesis/src/lib.rs b/crates/genesis/src/lib.rs index 20156ad2818..76407a08a2e 100644 --- a/crates/genesis/src/lib.rs +++ b/crates/genesis/src/lib.rs @@ -64,8 +64,8 @@ pub struct Genesis { } impl Genesis { - /// Creates a chain config for Clique using the given chain id. - /// and funds the given address with max coins. + /// Creates a chain config for Clique using the given chain id and funds the given address with + /// max coins. /// /// Enables all hard forks up to London at genesis. pub fn clique_genesis(chain_id: u64, signer_addr: Address) -> Self { diff --git a/crates/json-rpc/CHANGELOG.md b/crates/json-rpc/CHANGELOG.md index ac25e512b06..cb5d37d600d 100644 --- a/crates/json-rpc/CHANGELOG.md +++ b/crates/json-rpc/CHANGELOG.md @@ -5,11 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 + +### Features + +- Add helper for decoding custom errors ([#1098](https://github.com/alloy-rs/alloy/issues/1098)) +- [json-rpc] Implement `From for Id` and `From for Id` ([#1088](https://github.com/alloy-rs/alloy/issues/1088)) ### Miscellaneous Tasks -- Release 0.2.0 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/json-rpc/Cargo.toml b/crates/json-rpc/Cargo.toml index 9a2deef43c8..81a2d6b9b28 100644 --- a/crates/json-rpc/Cargo.toml +++ b/crates/json-rpc/Cargo.toml @@ -24,3 +24,4 @@ serde.workspace = true serde_json = { workspace = true, features = ["std", "raw_value"] } thiserror.workspace = true tracing.workspace = true +alloy-sol-types.workspace = true diff --git a/crates/json-rpc/src/common.rs b/crates/json-rpc/src/common.rs index 5c23e0fae16..c47228446fd 100644 --- a/crates/json-rpc/src/common.rs +++ b/crates/json-rpc/src/common.rs @@ -32,6 +32,18 @@ pub enum Id { None, } +impl From for Id { + fn from(value: u64) -> Self { + Self::Number(value) + } +} + +impl From for Id { + fn from(value: String) -> Self { + Self::String(value) + } +} + impl Display for Id { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -70,14 +82,14 @@ impl<'de> Deserialize<'de> for Id { where E: serde::de::Error, { - Ok(Id::Number(v)) + Ok(v.into()) } fn visit_str(self, v: &str) -> Result where E: serde::de::Error, { - Ok(Id::String(v.to_owned())) + Ok(v.to_owned().into()) } fn visit_none(self) -> Result diff --git a/crates/json-rpc/src/response/error.rs b/crates/json-rpc/src/response/error.rs index c6de4dba544..edf94315d26 100644 --- a/crates/json-rpc/src/response/error.rs +++ b/crates/json-rpc/src/response/error.rs @@ -1,8 +1,10 @@ +use alloy_primitives::Bytes; +use alloy_sol_types::SolInterface; use serde::{ de::{DeserializeOwned, MapAccess, Visitor}, Deserialize, Deserializer, Serialize, }; -use serde_json::value::RawValue; +use serde_json::{value::RawValue, Value}; use std::{borrow::Borrow, fmt, marker::PhantomData}; /// A JSONRPC-2.0 error object. @@ -67,6 +69,18 @@ impl ErrorPayload { } } +/// Recursively traverses the value, looking for hex data that it can extract. +/// +/// Inspired by ethers-js logic: +/// +fn spelunk_revert(value: &Value) -> Option { + match value { + Value::String(s) => s.parse().ok(), + Value::Object(o) => o.values().find_map(spelunk_revert), + _ => None, + } +} + impl fmt::Display for ErrorPayload { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "error code {}: {}", self.code, self.message) @@ -224,10 +238,38 @@ where _ => Err(self), } } + + /// Attempt to extract revert data from the JsonRpcError be recursively + /// traversing the error's data field + /// + /// This returns the first hex it finds in the data object, and its + /// behavior may change with `serde_json` internal changes. + /// + /// If no hex object is found, it will return an empty bytes IFF the error + /// is a revert + /// + /// Inspired by ethers-js logic: + /// + pub fn as_revert_data(&self) -> Option { + if self.message.contains("revert") { + let value = Value::deserialize(self.data.as_ref()?.borrow()).ok()?; + spelunk_revert(&value) + } else { + None + } + } + + /// Extracts revert data and tries decoding it into given custom errors set. + pub fn as_decoded_error(&self, validate: bool) -> Option { + self.as_revert_data().and_then(|data| E::abi_decode(&data, validate).ok()) + } } #[cfg(test)] mod test { + use alloy_primitives::U256; + use alloy_sol_types::sol; + use super::BorrowedErrorPayload; use crate::ErrorPayload; @@ -265,4 +307,21 @@ mod test { assert_eq!(payload.message, "20/second request limit reached - reduce calls per second or upgrade your account at quicknode.com"); assert!(payload.data.is_none()); } + + #[test] + fn custom_error_decoding() { + sol!( + library Errors { + error SomeCustomError(uint256 a); + } + ); + + let json = r#"{"code":3,"message":"execution reverted: ","data":"0x810f00230000000000000000000000000000000000000000000000000000000000000001"}"#; + let payload: ErrorPayload = serde_json::from_str(json).unwrap(); + + let Errors::ErrorsErrors::SomeCustomError(value) = + payload.as_decoded_error::(false).unwrap(); + + assert_eq!(value.a, U256::from(1)); + } } diff --git a/crates/network-primitives/CHANGELOG.md b/crates/network-primitives/CHANGELOG.md new file mode 100644 index 00000000000..11ab247977c --- /dev/null +++ b/crates/network-primitives/CHANGELOG.md @@ -0,0 +1,79 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 + +### Miscellaneous Tasks + +- Release 0.2.1 + +### Refactor + +- Add network-primitives ([#1101](https://github.com/alloy-rs/alloy/issues/1101)) + +[`alloy`]: https://crates.io/crates/alloy +[alloy]: https://crates.io/crates/alloy +[`alloy-core`]: https://crates.io/crates/alloy-core +[alloy-core]: https://crates.io/crates/alloy-core +[`alloy-consensus`]: https://crates.io/crates/alloy-consensus +[alloy-consensus]: https://crates.io/crates/alloy-consensus +[`alloy-contract`]: https://crates.io/crates/alloy-contract +[alloy-contract]: https://crates.io/crates/alloy-contract +[`alloy-eips`]: https://crates.io/crates/alloy-eips +[alloy-eips]: https://crates.io/crates/alloy-eips +[`alloy-genesis`]: https://crates.io/crates/alloy-genesis +[alloy-genesis]: https://crates.io/crates/alloy-genesis +[`alloy-json-rpc`]: https://crates.io/crates/alloy-json-rpc +[alloy-json-rpc]: https://crates.io/crates/alloy-json-rpc +[`alloy-network`]: https://crates.io/crates/alloy-network +[alloy-network]: https://crates.io/crates/alloy-network +[`alloy-node-bindings`]: https://crates.io/crates/alloy-node-bindings +[alloy-node-bindings]: https://crates.io/crates/alloy-node-bindings +[`alloy-provider`]: https://crates.io/crates/alloy-provider +[alloy-provider]: https://crates.io/crates/alloy-provider +[`alloy-pubsub`]: https://crates.io/crates/alloy-pubsub +[alloy-pubsub]: https://crates.io/crates/alloy-pubsub +[`alloy-rpc-client`]: https://crates.io/crates/alloy-rpc-client +[alloy-rpc-client]: https://crates.io/crates/alloy-rpc-client +[`alloy-rpc-types`]: https://crates.io/crates/alloy-rpc-types +[alloy-rpc-types]: https://crates.io/crates/alloy-rpc-types +[`alloy-rpc-types-anvil`]: https://crates.io/crates/alloy-rpc-types-anvil +[alloy-rpc-types-anvil]: https://crates.io/crates/alloy-rpc-types-anvil +[`alloy-rpc-types-beacon`]: https://crates.io/crates/alloy-rpc-types-beacon +[alloy-rpc-types-beacon]: https://crates.io/crates/alloy-rpc-types-beacon +[`alloy-rpc-types-engine`]: https://crates.io/crates/alloy-rpc-types-engine +[alloy-rpc-types-engine]: https://crates.io/crates/alloy-rpc-types-engine +[`alloy-rpc-types-eth`]: https://crates.io/crates/alloy-rpc-types-eth +[alloy-rpc-types-eth]: https://crates.io/crates/alloy-rpc-types-eth +[`alloy-rpc-types-trace`]: https://crates.io/crates/alloy-rpc-types-trace +[alloy-rpc-types-trace]: https://crates.io/crates/alloy-rpc-types-trace +[`alloy-serde`]: https://crates.io/crates/alloy-serde +[alloy-serde]: https://crates.io/crates/alloy-serde +[`alloy-signer`]: https://crates.io/crates/alloy-signer +[alloy-signer]: https://crates.io/crates/alloy-signer +[`alloy-signer-aws`]: https://crates.io/crates/alloy-signer-aws +[alloy-signer-aws]: https://crates.io/crates/alloy-signer-aws +[`alloy-signer-gcp`]: https://crates.io/crates/alloy-signer-gcp +[alloy-signer-gcp]: https://crates.io/crates/alloy-signer-gcp +[`alloy-signer-ledger`]: https://crates.io/crates/alloy-signer-ledger +[alloy-signer-ledger]: https://crates.io/crates/alloy-signer-ledger +[`alloy-signer-local`]: https://crates.io/crates/alloy-signer-local +[alloy-signer-local]: https://crates.io/crates/alloy-signer-local +[`alloy-signer-trezor`]: https://crates.io/crates/alloy-signer-trezor +[alloy-signer-trezor]: https://crates.io/crates/alloy-signer-trezor +[`alloy-signer-wallet`]: https://crates.io/crates/alloy-signer-wallet +[alloy-signer-wallet]: https://crates.io/crates/alloy-signer-wallet +[`alloy-transport`]: https://crates.io/crates/alloy-transport +[alloy-transport]: https://crates.io/crates/alloy-transport +[`alloy-transport-http`]: https://crates.io/crates/alloy-transport-http +[alloy-transport-http]: https://crates.io/crates/alloy-transport-http +[`alloy-transport-ipc`]: https://crates.io/crates/alloy-transport-ipc +[alloy-transport-ipc]: https://crates.io/crates/alloy-transport-ipc +[`alloy-transport-ws`]: https://crates.io/crates/alloy-transport-ws +[alloy-transport-ws]: https://crates.io/crates/alloy-transport-ws + + diff --git a/crates/network-primitives/Cargo.toml b/crates/network-primitives/Cargo.toml new file mode 100644 index 00000000000..a56fb3e6239 --- /dev/null +++ b/crates/network-primitives/Cargo.toml @@ -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 diff --git a/crates/network-primitives/README.md b/crates/network-primitives/README.md new file mode 100644 index 00000000000..d5ef0b8d631 --- /dev/null +++ b/crates/network-primitives/README.md @@ -0,0 +1,3 @@ +# alloy-network-primitives + +Primitive types for Alloy network abstraction. \ No newline at end of file diff --git a/crates/network-primitives/src/block.rs b/crates/network-primitives/src/block.rs new file mode 100644 index 00000000000..1ec52f338ba --- /dev/null +++ b/crates/network-primitives/src/block.rs @@ -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 { + /// Full transactions + Full(Vec), + /// Only hashes + Hashes(Vec), + /// Special case for uncle response. + Uncle, +} + +impl Default for BlockTransactions { + fn default() -> Self { + Self::Hashes(Vec::default()) + } +} + +impl BlockTransactions { + /// 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 { + 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 { + 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 BlockTransactions { + /// 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 From> for BlockTransactions { + fn from(hashes: Vec) -> Self { + Self::Hashes(hashes) + } +} + +impl From> for BlockTransactions { + fn from(transactions: Vec) -> 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) -> 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 { + 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) { + match &self.0 { + BlockTransactionHashesInner::Full(txs) => txs.size_hint(), + BlockTransactionHashesInner::Hashes(txs) => txs.size_hint(), + BlockTransactionHashesInner::Uncle => (0, Some(0)), + } + } +} + +impl 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 DoubleEndedIterator for BlockTransactionHashes<'_, T> { + #[inline] + fn next_back(&mut self) -> Option { + 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 for BlockTransactionsKind { + fn from(is_full: bool) -> Self { + if is_full { + Self::Full + } else { + Self::Hashes + } + } +} + +impl From 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()); + } +} diff --git a/crates/network-primitives/src/lib.rs b/crates/network-primitives/src/lib.rs new file mode 100644 index 00000000000..42f43f134ef --- /dev/null +++ b/crates/network-primitives/src/lib.rs @@ -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}; diff --git a/crates/network-primitives/src/traits.rs b/crates/network-primitives/src/traits.rs new file mode 100644 index 00000000000..9580f19b584 --- /dev/null +++ b/crates/network-primitives/src/traits.rs @@ -0,0 +1,91 @@ +use alloy_primitives::{Address, BlockHash, Bytes, TxHash, U256}; +use alloy_serde::WithOtherFields; + +/// Receipt JSON-RPC response. +pub trait ReceiptResponse { + /// Address of the created contract, or `None` if the transaction was not a deployment. + fn contract_address(&self) -> Option
; + + /// Status of the transaction. + /// + /// ## Note + /// + /// Caution must be taken when using this method for deep-historical + /// receipts, as it may not accurately reflect the status of the + /// transaction. The transaction status is not knowable from the receipt + /// for transactions before [EIP-658]. + fn status(&self) -> bool; + + /// Hash of the block this transaction was included within. + fn block_hash(&self) -> Option; + + /// Number of the block this transaction was included within. + fn block_number(&self) -> Option; +} + +/// Transaction JSON-RPC response. +pub trait TransactionResponse { + /// Hash of the transaction + #[doc(alias = "transaction_hash")] + fn tx_hash(&self) -> TxHash; + + /// Sender of the transaction + fn from(&self) -> Address; + + /// Recipient of the transaction + fn to(&self) -> Option
; + + /// Transferred value + fn value(&self) -> U256; + + /// Gas limit + fn gas(&self) -> u128; + + /// Input data + #[doc(alias = "calldata")] + fn input(&self) -> &Bytes; +} + +impl TransactionResponse for WithOtherFields { + fn tx_hash(&self) -> TxHash { + self.inner.tx_hash() + } + + fn from(&self) -> Address { + self.inner.from() + } + + fn to(&self) -> Option
{ + self.inner.to() + } + + fn value(&self) -> U256 { + self.inner.value() + } + + fn gas(&self) -> u128 { + self.inner.gas() + } + + fn input(&self) -> &Bytes { + self.inner.input() + } +} + +impl ReceiptResponse for WithOtherFields { + fn contract_address(&self) -> Option
{ + self.inner.contract_address() + } + + fn status(&self) -> bool { + self.inner.status() + } + + fn block_hash(&self) -> Option { + self.inner.block_hash() + } + + fn block_number(&self) -> Option { + self.inner.block_number() + } +} diff --git a/crates/network/CHANGELOG.md b/crates/network/CHANGELOG.md index 9b2aa65b1bf..ef0ed84f3b7 100644 --- a/crates/network/CHANGELOG.md +++ b/crates/network/CHANGELOG.md @@ -5,14 +5,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 +- Re-export and document network-primitives ([#1107](https://github.com/alloy-rs/alloy/issues/1107)) - Release 0.2.0 - Fix unnameable types ([#1029](https://github.com/alloy-rs/alloy/issues/1029)) +### Refactor + +- Add network-primitives ([#1101](https://github.com/alloy-rs/alloy/issues/1101)) + ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 ### Features diff --git a/crates/network/Cargo.toml b/crates/network/Cargo.toml index c84e8f6a706..9eda78c2426 100644 --- a/crates/network/Cargo.toml +++ b/crates/network/Cargo.toml @@ -22,6 +22,7 @@ workspace = true alloy-consensus = { workspace = true, features = ["std"] } alloy-eips = { workspace = true, features = ["serde"] } alloy-json-rpc.workspace = true +alloy-network-primitives.workspace = true alloy-primitives.workspace = true alloy-rpc-types-eth.workspace = true alloy-signer.workspace = true diff --git a/crates/network/src/any/mod.rs b/crates/network/src/any/mod.rs index 2acb0a1a5a1..8103b562ca2 100644 --- a/crates/network/src/any/mod.rs +++ b/crates/network/src/any/mod.rs @@ -1,7 +1,6 @@ -use crate::{Network, ReceiptResponse, TransactionResponse}; +use crate::Network; use alloy_consensus::TxType; use alloy_eips::eip2718::Eip2718Error; -use alloy_primitives::Bytes; use alloy_rpc_types_eth::{AnyTransactionReceipt, Header, Transaction, TransactionRequest}; use alloy_serde::WithOtherFields; use core::fmt; @@ -76,48 +75,3 @@ impl Network for AnyNetwork { type HeaderResponse = WithOtherFields
; } - -impl ReceiptResponse for AnyTransactionReceipt { - fn contract_address(&self) -> Option { - self.contract_address - } - - fn status(&self) -> bool { - self.inner.inner.status() - } - - fn block_hash(&self) -> Option { - self.inner.block_hash - } - - fn block_number(&self) -> Option { - self.inner.block_number - } -} - -impl TransactionResponse for WithOtherFields { - #[doc(alias = "transaction_hash")] - fn tx_hash(&self) -> alloy_primitives::B256 { - self.hash - } - - fn from(&self) -> alloy_primitives::Address { - self.from - } - - fn to(&self) -> Option { - self.to - } - - fn value(&self) -> alloy_primitives::U256 { - self.value - } - - fn gas(&self) -> u128 { - self.gas - } - - fn input(&self) -> &Bytes { - &self.input - } -} diff --git a/crates/network/src/ethereum/mod.rs b/crates/network/src/ethereum/mod.rs index 68729e3f0f9..0d2266874e4 100644 --- a/crates/network/src/ethereum/mod.rs +++ b/crates/network/src/ethereum/mod.rs @@ -1,5 +1,4 @@ -use crate::{Network, ReceiptResponse, TransactionResponse}; -use alloy_primitives::Bytes; +use crate::Network; mod builder; @@ -31,48 +30,3 @@ impl Network for Ethereum { type HeaderResponse = alloy_rpc_types_eth::Header; } - -impl ReceiptResponse for alloy_rpc_types_eth::TransactionReceipt { - fn contract_address(&self) -> Option { - self.contract_address - } - - fn status(&self) -> bool { - self.inner.status() - } - - fn block_hash(&self) -> Option { - self.block_hash - } - - fn block_number(&self) -> Option { - self.block_number - } -} - -impl TransactionResponse for alloy_rpc_types_eth::Transaction { - #[doc(alias = "transaction_hash")] - fn tx_hash(&self) -> alloy_primitives::B256 { - self.hash - } - - fn from(&self) -> alloy_primitives::Address { - self.from - } - - fn to(&self) -> Option { - self.to - } - - fn value(&self) -> alloy_primitives::U256 { - self.value - } - - fn gas(&self) -> u128 { - self.gas - } - - fn input(&self) -> &Bytes { - &self.input - } -} diff --git a/crates/network/src/lib.rs b/crates/network/src/lib.rs index 54431385f32..55a931ed40f 100644 --- a/crates/network/src/lib.rs +++ b/crates/network/src/lib.rs @@ -9,7 +9,6 @@ use alloy_consensus::TxReceipt; use alloy_eips::eip2718::{Eip2718Envelope, Eip2718Error}; use alloy_json_rpc::RpcObject; -use alloy_primitives::{Address, BlockHash, Bytes, TxHash, U256}; use core::fmt::{Debug, Display}; mod transaction; @@ -25,64 +24,7 @@ mod any; pub use any::{AnyNetwork, AnyTxType}; pub use alloy_eips::eip2718; - -/// A receipt response. -/// -/// This is distinct from [`TxReceipt`], since this is for JSON-RPC receipts. -/// -/// [`TxReceipt`]: alloy_consensus::TxReceipt -pub trait ReceiptResponse { - /// Address of the created contract, or `None` if the transaction was not a deployment. - fn contract_address(&self) -> Option
; - - /// Status of the transaction. - /// - /// ## Note - /// - /// Caution must be taken when using this method for deep-historical - /// receipts, as it may not accurately reflect the status of the - /// transaction. The transaction status is not knowable from the receipt - /// for transactions before [EIP-658]. - /// - /// This can be handled using [`TxReceipt::status_or_post_state`]. - /// - /// [EIP-658]: https://eips.ethereum.org/EIPS/eip-658 - /// [`TxReceipt::status_or_post_state`]: alloy_consensus::TxReceipt::status_or_post_state - fn status(&self) -> bool; - - /// Hash of the block this transaction was included within. - fn block_hash(&self) -> Option; - - /// Number of the block this transaction was included within. - fn block_number(&self) -> Option; -} - -/// Transaction Response -/// -/// This is distinct from [`Transaction`], since this is a JSON-RPC response. -/// -/// [`Transaction`]: alloy_consensus::Transaction -pub trait TransactionResponse { - /// Hash of the transaction - #[doc(alias = "transaction_hash")] - fn tx_hash(&self) -> TxHash; - - /// Sender of the transaction - fn from(&self) -> Address; - - /// Recipient of the transaction - fn to(&self) -> Option
; - - /// Transferred value - fn value(&self) -> U256; - - /// Gas limit - fn gas(&self) -> u128; - - /// Input data - #[doc(alias = "calldata")] - fn input(&self) -> &Bytes; -} +pub use alloy_network_primitives::{self as primitives, ReceiptResponse, TransactionResponse}; /// Captures type info for network-specific RPC requests/responses. /// diff --git a/crates/node-bindings/CHANGELOG.md b/crates/node-bindings/CHANGELOG.md index e65787af257..c62c5f6322b 100644 --- a/crates/node-bindings/CHANGELOG.md +++ b/crates/node-bindings/CHANGELOG.md @@ -5,16 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Bug Fixes +- [node-bindings] Backport fix from ethers-rs ([#1081](https://github.com/alloy-rs/alloy/issues/1081)) - [provider] Prevent panic from having 0 keys when calling `on_anvil_with_wallet_and_config` ([#1055](https://github.com/alloy-rs/alloy/issues/1055)) - [admin] Id in NodeInfo is string instead of B256 ([#1038](https://github.com/alloy-rs/alloy/issues/1038)) ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/node-bindings/src/anvil.rs b/crates/node-bindings/src/anvil.rs index 62903e9c2e7..a20cdaaf12a 100644 --- a/crates/node-bindings/src/anvil.rs +++ b/crates/node-bindings/src/anvil.rs @@ -318,7 +318,7 @@ impl Anvil { let mut child = cmd.spawn().map_err(AnvilError::SpawnError)?; - let stdout = child.stdout.as_mut().ok_or(AnvilError::NoStderr)?; + let stdout = child.stdout.take().ok_or(AnvilError::NoStderr)?; let start = Instant::now(); let mut reader = BufReader::new(stdout); diff --git a/crates/provider/CHANGELOG.md b/crates/provider/CHANGELOG.md index 14bfae1615d..7ee14d13b7c 100644 --- a/crates/provider/CHANGELOG.md +++ b/crates/provider/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Bug Fixes @@ -19,10 +19,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 +- Correctly cfg unused type ([#1117](https://github.com/alloy-rs/alloy/issues/1117)) - Release 0.2.0 - Fix unnameable types ([#1029](https://github.com/alloy-rs/alloy/issues/1029)) +### Other + +- Add `AccessListResult` type (EIP-2930) ([#1110](https://github.com/alloy-rs/alloy/issues/1110)) +- Removing async get account ([#1080](https://github.com/alloy-rs/alloy/issues/1080)) + +### Refactor + +- Add network-primitives ([#1101](https://github.com/alloy-rs/alloy/issues/1101)) + ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 ### Bug Fixes diff --git a/crates/provider/Cargo.toml b/crates/provider/Cargo.toml index 3fda009b291..d7b692c83e2 100644 --- a/crates/provider/Cargo.toml +++ b/crates/provider/Cargo.toml @@ -23,6 +23,7 @@ alloy-eips.workspace = true alloy-consensus.workspace = true alloy-json-rpc.workspace = true alloy-network.workspace = true +alloy-network-primitives.workspace = true alloy-node-bindings = { workspace = true, optional = true } alloy-signer-local = { workspace = true, optional = true } alloy-rpc-client.workspace = true diff --git a/crates/provider/src/builder.rs b/crates/provider/src/builder.rs index 3871ac86865..802daae0c84 100644 --- a/crates/provider/src/builder.rs +++ b/crates/provider/src/builder.rs @@ -346,6 +346,7 @@ impl ProviderBuilder { } } +#[cfg(any(test, feature = "anvil-node"))] type JoinedEthereumWalletFiller = JoinFill>; #[cfg(any(test, feature = "anvil-node"))] diff --git a/crates/provider/src/heart.rs b/crates/provider/src/heart.rs index db7d4b7af0c..58de345c6a2 100644 --- a/crates/provider/src/heart.rs +++ b/crates/provider/src/heart.rs @@ -555,13 +555,13 @@ impl Heartbeat { self.past_blocks.retain(|(h, _)| h < block_height); } } - self.past_blocks.push_back((*block_height, block.transactions.hashes().copied().collect())); + self.past_blocks.push_back((*block_height, block.transactions.hashes().collect())); // Check if we are watching for any of the transactions in this block. let to_check: Vec<_> = block .transactions .hashes() - .filter_map(|tx_hash| self.unconfirmed.remove(tx_hash)) + .filter_map(|tx_hash| self.unconfirmed.remove(&tx_hash)) .collect(); for mut watcher in to_check { // If `confirmations` is not more than 1 we can notify the watcher immediately. diff --git a/crates/provider/src/provider/trait.rs b/crates/provider/src/provider/trait.rs index 9ccd9c93f27..4d2664718af 100644 --- a/crates/provider/src/provider/trait.rs +++ b/crates/provider/src/provider/trait.rs @@ -7,15 +7,16 @@ use crate::{ }; use alloy_eips::eip2718::Encodable2718; use alloy_json_rpc::{RpcError, RpcParam, RpcReturn}; -use alloy_network::{Ethereum, Network, ReceiptResponse as _}; +use alloy_network::{Ethereum, Network}; +use alloy_network_primitives::{BlockTransactionsKind, ReceiptResponse}; use alloy_primitives::{ hex, Address, BlockHash, BlockNumber, Bytes, StorageKey, StorageValue, TxHash, B256, U128, U256, U64, }; use alloy_rpc_client::{ClientRef, PollerBuilder, RpcCall, WeakClient}; use alloy_rpc_types_eth::{ - AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag, BlockTransactionsKind, - EIP1186AccountProofResponse, FeeHistory, Filter, FilterChanges, Log, SyncStatus, + AccessListResult, Block, BlockId, BlockNumberOrTag, EIP1186AccountProofResponse, FeeHistory, + Filter, FilterChanges, Log, SyncStatus, }; use alloy_transport::{BoxTransport, Transport, TransportErrorKind, TransportResult}; use serde_json::value::RawValue; @@ -147,7 +148,10 @@ pub trait Provider: /// Not all client implementations support state overrides. #[doc(alias = "eth_call")] #[doc(alias = "call_with_overrides")] - fn call<'req>(&self, tx: &'req N::TransactionRequest) -> EthCall<'req, 'static, T, N, Bytes> { + fn call<'req, 'state>( + &self, + tx: &'req N::TransactionRequest, + ) -> EthCall<'req, 'state, T, N, Bytes> { EthCall::new(self.weak_client(), tx) } @@ -162,7 +166,7 @@ pub trait Provider: fn create_access_list<'a>( &self, request: &'a N::TransactionRequest, - ) -> RpcWithBlock { + ) -> RpcWithBlock { RpcWithBlock::new(self.weak_client(), "eth_createAccessList", request) } @@ -241,10 +245,7 @@ pub trait Provider: /// Retrieves account information ([Account](alloy_consensus::Account)) for the given [Address] /// at the particular [BlockId]. - async fn get_account( - &self, - address: Address, - ) -> RpcWithBlock { + fn get_account(&self, address: Address) -> RpcWithBlock { RpcWithBlock::new(self.weak_client(), "eth_getAccount", address) } diff --git a/crates/pubsub/CHANGELOG.md b/crates/pubsub/CHANGELOG.md index 7403bfabfd2..2449052513e 100644 --- a/crates/pubsub/CHANGELOG.md +++ b/crates/pubsub/CHANGELOG.md @@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 - Fix unnameable types ([#1029](https://github.com/alloy-rs/alloy/issues/1029)) diff --git a/crates/rpc-client/CHANGELOG.md b/crates/rpc-client/CHANGELOG.md index e4fc5179bd2..f645b508cf3 100644 --- a/crates/rpc-client/CHANGELOG.md +++ b/crates/rpc-client/CHANGELOG.md @@ -5,11 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 + +### Features + +- [json-rpc] Implement `From for Id` and `From for Id` ([#1088](https://github.com/alloy-rs/alloy/issues/1088)) ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 - Fix unnameable types ([#1029](https://github.com/alloy-rs/alloy/issues/1029)) diff --git a/crates/rpc-client/src/client.rs b/crates/rpc-client/src/client.rs index 75ed70c99cb..e52ca93278a 100644 --- a/crates/rpc-client/src/client.rs +++ b/crates/rpc-client/src/client.rs @@ -255,7 +255,7 @@ impl RpcClientInner { /// Reserve a request ID u64. #[inline] pub fn next_id(&self) -> Id { - Id::Number(self.increment_id()) + self.increment_id().into() } } diff --git a/crates/rpc-types-admin/CHANGELOG.md b/crates/rpc-types-admin/CHANGELOG.md index 370e48150e6..860dc7b9388 100644 --- a/crates/rpc-types-admin/CHANGELOG.md +++ b/crates/rpc-types-admin/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Bug Fixes @@ -13,7 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 +- Chore : fix typos ([#1087](https://github.com/alloy-rs/alloy/issues/1087)) - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/rpc-types-admin/src/admin.rs b/crates/rpc-types-admin/src/admin.rs index d227d117670..65dc2af2f5d 100644 --- a/crates/rpc-types-admin/src/admin.rs +++ b/crates/rpc-types-admin/src/admin.rs @@ -213,7 +213,7 @@ pub struct PeerEvent { pub kind: PeerEventType, /// The peer's enode ID. pub peer: String, - /// An error ocurred on the peer. + /// An error occurred on the peer. #[serde(default, skip_serializing_if = "Option::is_none")] pub error: Option, /// The protocol of the peer. diff --git a/crates/rpc-types-anvil/CHANGELOG.md b/crates/rpc-types-anvil/CHANGELOG.md index 2d120db226c..85acc1f683f 100644 --- a/crates/rpc-types-anvil/CHANGELOG.md +++ b/crates/rpc-types-anvil/CHANGELOG.md @@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/rpc-types-beacon/CHANGELOG.md b/crates/rpc-types-beacon/CHANGELOG.md index 2f16fd707a6..578219cf9d4 100644 --- a/crates/rpc-types-beacon/CHANGELOG.md +++ b/crates/rpc-types-beacon/CHANGELOG.md @@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/rpc-types-engine/CHANGELOG.md b/crates/rpc-types-engine/CHANGELOG.md index 349a15fd96f..d7b2b80c814 100644 --- a/crates/rpc-types-engine/CHANGELOG.md +++ b/crates/rpc-types-engine/CHANGELOG.md @@ -5,11 +5,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 + +### Dependencies + +- Bump jsonrpsee 0.24 ([#1067](https://github.com/alloy-rs/alloy/issues/1067)) + +### Features + +- [engine-types] `PayloadError::PrePragueBlockWithEip7702Transactions` ([#1116](https://github.com/alloy-rs/alloy/issues/1116)) ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 - Add payloadbodies v2 to capabilities set ([#1025](https://github.com/alloy-rs/alloy/issues/1025)) diff --git a/crates/rpc-types-engine/Cargo.toml b/crates/rpc-types-engine/Cargo.toml index fe489961498..dbc30871e26 100644 --- a/crates/rpc-types-engine/Cargo.toml +++ b/crates/rpc-types-engine/Cargo.toml @@ -35,7 +35,7 @@ serde = { workspace = true, features = ["derive"] } thiserror.workspace = true # jsonrpsee -jsonrpsee-types = { version = "0.23", optional = true } +jsonrpsee-types = { version = "0.24", optional = true } # jwt jsonwebtoken = "9.3.0" diff --git a/crates/rpc-types-engine/src/payload.rs b/crates/rpc-types-engine/src/payload.rs index 82269f3a378..d79701657be 100644 --- a/crates/rpc-types-engine/src/payload.rs +++ b/crates/rpc-types-engine/src/payload.rs @@ -813,6 +813,9 @@ pub enum PayloadError { /// cancun fields missing in post-cancun payload. #[error("cancun fields missing in post-cancun payload")] PostCancunWithoutCancunFields, + /// blob transactions present in pre-prague payload. + #[error("eip 7702 transactions present in pre-prague payload")] + PrePragueBlockWithEip7702Transactions, /// Invalid payload block hash. #[error("block hash mismatch: want {consensus}, got {execution}")] diff --git a/crates/rpc-types-eth/CHANGELOG.md b/crates/rpc-types-eth/CHANGELOG.md index 85720423448..132541f26d3 100644 --- a/crates/rpc-types-eth/CHANGELOG.md +++ b/crates/rpc-types-eth/CHANGELOG.md @@ -5,25 +5,41 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Bug Fixes - Trim conflicting key `max_fee_per_blob_gas` from Eip1559 tx type ([#1064](https://github.com/alloy-rs/alloy/issues/1064)) +### Dependencies + +- Bump jsonrpsee 0.24 ([#1067](https://github.com/alloy-rs/alloy/issues/1067)) + ### Features +- Eth_simulateV1 Request / Response types ([#1042](https://github.com/alloy-rs/alloy/issues/1042)) +- Feat(rpc-type-eth) convert vec TxReq to bundle ([#1091](https://github.com/alloy-rs/alloy/issues/1091)) +- Feat(provider) : introduction to eth_sendRawTransactionConditional RPC endpoint type ([#1009](https://github.com/alloy-rs/alloy/issues/1009)) - [rpc-types-eth] Serde flatten `BlobTransactionSidecar` in tx req ([#1054](https://github.com/alloy-rs/alloy/issues/1054)) - Add authorization list to rpc transaction and tx receipt types ([#1051](https://github.com/alloy-rs/alloy/issues/1051)) ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 +- [rpc] Make `Deserialize` impl for `FilterChanges` generic over transaction ([#1118](https://github.com/alloy-rs/alloy/issues/1118)) +- Export rpc account type ([#1075](https://github.com/alloy-rs/alloy/issues/1075)) - Release 0.2.0 - Fix unnameable types ([#1029](https://github.com/alloy-rs/alloy/issues/1029)) +### Other + +- Add `AccessListResult` type (EIP-2930) ([#1110](https://github.com/alloy-rs/alloy/issues/1110)) +- Derive arbitrary for `TransactionRequest` ([#1113](https://github.com/alloy-rs/alloy/issues/1113)) +- Added stages to the sync info rpc type ([#1079](https://github.com/alloy-rs/alloy/issues/1079)) + ### Refactor +- Add network-primitives ([#1101](https://github.com/alloy-rs/alloy/issues/1101)) - Replace `U64` with `u64` ([#1057](https://github.com/alloy-rs/alloy/issues/1057)) ### Styling diff --git a/crates/rpc-types-eth/Cargo.toml b/crates/rpc-types-eth/Cargo.toml index a3b9c7e8e58..1a8048b8ce8 100644 --- a/crates/rpc-types-eth/Cargo.toml +++ b/crates/rpc-types-eth/Cargo.toml @@ -26,6 +26,8 @@ alloy-serde.workspace = true alloy-consensus = { workspace = true, features = ["std", "serde"] } alloy-eips = { workspace = true, features = ["std", "serde"] } +alloy-network-primitives.workspace = true + itertools.workspace = true serde = { workspace = true, features = ["derive"] } serde_json.workspace = true @@ -35,7 +37,7 @@ thiserror.workspace = true arbitrary = { version = "1.3", features = ["derive"], optional = true } # jsonrpsee -jsonrpsee-types = { version = "0.23", optional = true } +jsonrpsee-types = { version = "0.24", optional = true } alloy-sol-types.workspace = true [dev-dependencies] diff --git a/crates/rpc-types-eth/src/account.rs b/crates/rpc-types-eth/src/account.rs index 10d6e9e3b16..439e24dd5a3 100644 --- a/crates/rpc-types-eth/src/account.rs +++ b/crates/rpc-types-eth/src/account.rs @@ -1,6 +1,10 @@ use alloy_primitives::{Address, Bytes, B256, B512, U256}; use alloy_serde::storage::JsonStorageKey; use serde::{Deserialize, Serialize}; + +// re-export account type for `eth_getAccount` +pub use alloy_consensus::Account; + /// Account information. #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct AccountInfo { diff --git a/crates/rpc-types-eth/src/block.rs b/crates/rpc-types-eth/src/block.rs index 3a549f55c82..1622b3c74f0 100644 --- a/crates/rpc-types-eth/src/block.rs +++ b/crates/rpc-types-eth/src/block.rs @@ -1,6 +1,7 @@ //! Block RPC types. use crate::{ConversionError, Transaction, Withdrawal}; +use alloy_network_primitives::BlockTransactions; use alloy_primitives::{Address, BlockHash, Bloom, Bytes, B256, B64, U256}; use alloy_serde::OtherFields; use serde::{ser::Error, Deserialize, Serialize, Serializer}; @@ -204,324 +205,6 @@ impl TryFrom
for alloy_consensus::Header { } } -/// 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 { - /// Full transactions - Full(Vec), - /// Only hashes - Hashes(Vec), - /// Special case for uncle response. - Uncle, -} - -impl Default for BlockTransactions { - fn default() -> Self { - Self::Hashes(Vec::default()) - } -} - -impl BlockTransactions { - /// 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 { - 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 { - 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 BlockTransactions { - /// Converts `self` into `Hashes`. - #[inline] - pub fn convert_to_hashes(&mut self) { - if !self.is_hashes() { - *self = Self::Hashes(self.hashes().copied().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<'_, Transaction> { - self.hashes() - } - - /// Returns an iterator over references to the transaction hashes. - #[inline] - pub fn hashes(&self) -> BlockTransactionHashes<'_, Transaction> { - BlockTransactionHashes::new(self) - } - - /// Returns an iterator over mutable references to the transaction hashes. - #[inline] - pub fn hashes_mut(&mut self) -> BlockTransactionHashesMut<'_, Transaction> { - BlockTransactionHashesMut::new(self) - } -} - -impl From> for BlockTransactions { - fn from(hashes: Vec) -> Self { - Self::Hashes(hashes) - } -} - -impl From> for BlockTransactions { - fn from(transactions: Vec) -> 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 = Transaction> { - 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) -> Self { - Self(match txs { - BlockTransactions::Hashes(txs) => BlockTransactionHashesInner::Hashes(txs.iter()), - BlockTransactions::Full(txs) => BlockTransactionHashesInner::Full(txs.iter()), - BlockTransactions::Uncle => BlockTransactionHashesInner::Uncle, - }) - } -} - -impl<'a> Iterator for BlockTransactionHashes<'a, Transaction> { - type Item = &'a B256; - - #[inline] - fn next(&mut self) -> Option { - match &mut self.0 { - BlockTransactionHashesInner::Hashes(txs) => txs.next(), - BlockTransactionHashesInner::Full(txs) => txs.next().map(|tx| &tx.hash), - BlockTransactionHashesInner::Uncle => None, - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - match &self.0 { - BlockTransactionHashesInner::Full(txs) => txs.size_hint(), - BlockTransactionHashesInner::Hashes(txs) => txs.size_hint(), - BlockTransactionHashesInner::Uncle => (0, Some(0)), - } - } -} - -impl ExactSizeIterator for BlockTransactionHashes<'_, Transaction> { - #[inline] - fn len(&self) -> usize { - match &self.0 { - BlockTransactionHashesInner::Full(txs) => txs.len(), - BlockTransactionHashesInner::Hashes(txs) => txs.len(), - BlockTransactionHashesInner::Uncle => 0, - } - } -} - -impl DoubleEndedIterator for BlockTransactionHashes<'_, Transaction> { - #[inline] - fn next_back(&mut self) -> Option { - match &mut self.0 { - BlockTransactionHashesInner::Full(txs) => txs.next_back().map(|tx| &tx.hash), - BlockTransactionHashesInner::Hashes(txs) => txs.next_back(), - BlockTransactionHashesInner::Uncle => None, - } - } -} - -impl<'a> std::iter::FusedIterator for BlockTransactionHashes<'a, Transaction> {} - -/// An Iterator over the transaction hashes of a block. -/// -/// See [`BlockTransactions::hashes_mut`]. -#[derive(Debug)] -pub struct BlockTransactionHashesMut<'a, T = Transaction>(BlockTransactionHashesInnerMut<'a, T>); - -#[derive(Debug)] -enum BlockTransactionHashesInnerMut<'a, T = Transaction> { - Hashes(std::slice::IterMut<'a, B256>), - Full(std::slice::IterMut<'a, T>), - Uncle, -} - -impl<'a, T> BlockTransactionHashesMut<'a, T> { - #[inline] - fn new(txs: &'a mut BlockTransactions) -> Self { - Self(match txs { - BlockTransactions::Hashes(txs) => { - BlockTransactionHashesInnerMut::Hashes(txs.iter_mut()) - } - BlockTransactions::Full(txs) => BlockTransactionHashesInnerMut::Full(txs.iter_mut()), - BlockTransactions::Uncle => BlockTransactionHashesInnerMut::Uncle, - }) - } -} - -impl<'a> Iterator for BlockTransactionHashesMut<'a, Transaction> { - type Item = &'a mut B256; - - #[inline] - fn next(&mut self) -> Option { - match &mut self.0 { - BlockTransactionHashesInnerMut::Full(txs) => txs.next().map(|tx| &mut tx.hash), - BlockTransactionHashesInnerMut::Hashes(txs) => txs.next(), - BlockTransactionHashesInnerMut::Uncle => None, - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - match &self.0 { - BlockTransactionHashesInnerMut::Full(txs) => txs.size_hint(), - BlockTransactionHashesInnerMut::Hashes(txs) => txs.size_hint(), - BlockTransactionHashesInnerMut::Uncle => (0, Some(0)), - } - } -} - -impl ExactSizeIterator for BlockTransactionHashesMut<'_, Transaction> { - #[inline] - fn len(&self) -> usize { - match &self.0 { - BlockTransactionHashesInnerMut::Full(txs) => txs.len(), - BlockTransactionHashesInnerMut::Hashes(txs) => txs.len(), - BlockTransactionHashesInnerMut::Uncle => 0, - } - } -} - -impl DoubleEndedIterator for BlockTransactionHashesMut<'_, Transaction> { - #[inline] - fn next_back(&mut self) -> Option { - match &mut self.0 { - BlockTransactionHashesInnerMut::Full(txs) => txs.next_back().map(|tx| &mut tx.hash), - BlockTransactionHashesInnerMut::Hashes(txs) => txs.next_back(), - BlockTransactionHashesInnerMut::Uncle => None, - } - } -} - -impl<'a> std::iter::FusedIterator for BlockTransactionHashesMut<'a, Transaction> {} - -/// 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 for BlockTransactionsKind { - fn from(is_full: bool) -> Self { - if is_full { - Self::Full - } else { - Self::Hashes - } - } -} - -impl From for bool { - fn from(kind: BlockTransactionsKind) -> Self { - match kind { - BlockTransactionsKind::Full => true, - BlockTransactionsKind::Hashes => false, - } - } -} - /// Error that can occur when converting other types to blocks #[derive(Clone, Copy, Debug, thiserror::Error)] pub enum BlockError { @@ -648,15 +331,6 @@ mod tests { let _: Header = Header::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap(); } - #[test] - fn test_full_conversion() { - let full = true; - assert_eq!(BlockTransactionsKind::Full, full.into()); - - let full = false; - assert_eq!(BlockTransactionsKind::Hashes, full.into()); - } - #[test] #[cfg(feature = "jsonrpsee-types")] fn serde_json_header() { diff --git a/crates/rpc-types-eth/src/call.rs b/crates/rpc-types-eth/src/call.rs index 09f47660e5b..4f4d65c3370 100644 --- a/crates/rpc-types-eth/src/call.rs +++ b/crates/rpc-types-eth/src/call.rs @@ -12,6 +12,13 @@ pub struct Bundle { pub block_override: Option, } +impl From> for Bundle { + /// Converts a `TransactionRequest` into a `Bundle`. + fn from(tx_request: Vec) -> Self { + Self { transactions: tx_request, block_override: None } + } +} + /// State context for callMany #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(default, rename_all = "camelCase")] diff --git a/crates/rpc-types-eth/src/eip4337.rs b/crates/rpc-types-eth/src/eip4337.rs new file mode 100644 index 00000000000..0a43da2f44f --- /dev/null +++ b/crates/rpc-types-eth/src/eip4337.rs @@ -0,0 +1,41 @@ +use alloy_primitives::{Address, BlockNumber, B256, U256}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// Options for conditional raw transaction submissions. +// reference for the implementation +// See also +#[derive(Debug, Serialize, Deserialize, Clone, Default)] +#[serde(rename_all = "camelCase")] +pub struct ConditionalOptions { + /// A map of account addresses to their expected storage states. + /// Each account can have a specified storage root or explicit slot-value pairs. + #[serde(default)] + pub known_accounts: HashMap, + /// The minimal block number at which the transaction can be included. + /// `None` indicates no minimum block number constraint. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub block_number_min: Option, + /// The maximal block number at which the transaction can be included. + /// `None` indicates no maximum block number constraint. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub block_number_max: Option, + /// The minimal timestamp at which the transaction can be included. + /// `None` indicates no minimum timestamp constraint. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timestamp_min: Option, + /// The maximal timestamp at which the transaction can be included. + /// `None` indicates no maximum timestamp constraint. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timestamp_max: Option, +} + +/// Represents the expected state of an account for a transaction to be conditionally accepted. +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(untagged)] +pub enum AccountStorage { + /// Expected storage root hash of the account. + RootHash(B256), + /// Explicit storage slots and their expected values. + Slots(HashMap), +} diff --git a/crates/rpc-types-eth/src/filter.rs b/crates/rpc-types-eth/src/filter.rs index 19f92f91411..2adf2022d6e 100644 --- a/crates/rpc-types-eth/src/filter.rs +++ b/crates/rpc-types-eth/src/filter.rs @@ -1021,7 +1021,10 @@ mod empty_array { } } -impl<'de> Deserialize<'de> for FilterChanges { +impl<'de, T> Deserialize<'de> for FilterChanges +where + T: Deserialize<'de>, +{ fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, diff --git a/crates/rpc-types-eth/src/lib.rs b/crates/rpc-types-eth/src/lib.rs index 006ddc36f67..a005d2c42aa 100644 --- a/crates/rpc-types-eth/src/lib.rs +++ b/crates/rpc-types-eth/src/lib.rs @@ -14,6 +14,10 @@ pub use account::*; mod block; pub use block::*; +pub use alloy_network_primitives::{ + BlockTransactionHashes, BlockTransactions, BlockTransactionsKind, +}; + mod call; pub use call::{Bundle, EthCallResponse, StateContext, TransactionIndex}; @@ -49,3 +53,8 @@ pub use work::Work; mod sidecars; pub use sidecars::*; + +/// This module provides implementations for EIP-4337. +pub mod eip4337; + +pub mod simulate; diff --git a/crates/rpc-types-eth/src/simulate.rs b/crates/rpc-types-eth/src/simulate.rs new file mode 100644 index 00000000000..715c4a99dfd --- /dev/null +++ b/crates/rpc-types-eth/src/simulate.rs @@ -0,0 +1,184 @@ +//! 'eth_simulateV1' Request / Response types: + +use alloy_primitives::{Address, Bytes, Log, B256}; +use serde::{Deserialize, Serialize}; + +use crate::{state::StateOverride, BlockOverrides, TransactionRequest}; + +/// The maximum number of blocks that can be simulated in a single request, +pub const MAX_SIMULATE_BLOCKS: u64 = 256; + +/// Represents a batch of calls to be simulated sequentially within a block. +/// This struct includes block and state overrides as well as the transaction requests to be +/// executed. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SimBlock { + /// Modifications to the default block characteristics. + pub block_overrides: BlockOverrides, + /// State modifications to apply before executing the transactions. + pub state_overrides: StateOverride, + /// A vector of transactions to be simulated. + pub calls: Vec, +} +/// Represents the result of simulating a block. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SimulatedBlock { + /// The number of the block. + #[serde(with = "alloy_serde::quantity")] + pub number: u64, + /// The hash of the block. + pub hash: B256, + /// The timestamp of the block. + #[serde(with = "alloy_serde::quantity")] + pub timestamp: u64, + /// The gas limit of the block. + #[serde(with = "alloy_serde::quantity")] + pub gas_limit: u64, + /// The amount of gas used in the block. + #[serde(with = "alloy_serde::quantity")] + pub gas_used: u64, + /// The recipient of the block's fees. + pub fee_recipient: Address, + /// The base fee per gas unit for the block. + #[serde(with = "alloy_serde::quantity")] + pub base_fee_per_gas: u64, + /// The previous RANDAO value of the block. + pub prev_randao: B256, + /// A vector of results for each call in the block. + pub calls: Vec, +} +/// The response type for the eth_simulateV1 method. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SimulateV1Response { + /// Simulated blocks vector. + pub simulated_blocks: Vec, +} +/// Captures the outcome of a transaction simulation. +/// It includes the return value, logs produced, gas used, and the status of the transaction. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SimCallResult { + /// The raw bytes returned by the transaction. + pub return_value: Bytes, + /// Logs generated during the execution of the transaction. + #[serde(default)] + pub logs: Vec, + /// The amount of gas used by the transaction. + #[serde(with = "alloy_serde::quantity")] + pub gas_used: u64, + /// The final status of the transaction, typically indicating success or failure. + #[serde(with = "alloy_serde::quantity")] + pub status: u64, + /// Error in case the call failed + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, +} + +/// Simulation options for executing multiple blocks and transactions. +/// This struct configures how simulations are executed, including whether to trace token transfers, +/// validate transaction sequences, and whether to return full transaction objects. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SimulatePayload { + /// Array of block state calls to be executed at specific, optional block/state. + pub block_state_calls: Vec, + /// Flag to determine whether to trace ERC20/ERC721 token transfers within transactions. + #[serde(default)] + pub trace_transfers: bool, + /// Flag to enable or disable validation of the transaction sequence in the blocks. + #[serde(default)] + pub validation: bool, + /// Flag to decide if full transactions should be returned instead of just their hashes. + pub return_full_transactions: bool, +} + +/// The error response returned by the `eth_simulateV1` method. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SimulateError { + /// Code error + /// -3200: Execution reverted + /// -32015: VM execution error + pub code: i32, + /// Message error + pub message: String, +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_primitives::{Address, TxKind}; + use serde_json::json; + + #[test] + fn test_eth_simulate_v1_account_not_precompile() { + let request_json = json!({ + "jsonrpc": "2.0", + "id": 1, + "method": "eth_simulateV1", + "params": [{ + "blockStateCalls": [ + { + "blockOverrides": {}, + "stateOverrides": { + "0xc000000000000000000000000000000000000000": { + "nonce": "0x5" + } + }, + "calls": [] + }, + { + "blockOverrides": {}, + "stateOverrides": { + "0xc000000000000000000000000000000000000000": { + "code": "0x600035600055" + } + }, + "calls": [ + { + "from": "0xc000000000000000000000000000000000000000", + "to": "0xc000000000000000000000000000000000000000", + "nonce": "0x0" + }, + { + "from": "0xc100000000000000000000000000000000000000", + "to": "0xc100000000000000000000000000000000000000", + "nonce": "0x5" + } + ] + } + ], + "traceTransfers": false, + "validation": true, + "returnFullTransactions": false + }, "latest"] + }); + + let sim_opts: SimulatePayload = + serde_json::from_value(request_json["params"][0].clone()).unwrap(); + + let address_1: Address = "0xc000000000000000000000000000000000000000".parse().unwrap(); + let address_2: Address = "0xc100000000000000000000000000000000000000".parse().unwrap(); + + assert!(sim_opts.validation); + assert_eq!(sim_opts.block_state_calls.len(), 2); + + let block_state_call_1 = &sim_opts.block_state_calls[0]; + assert!(block_state_call_1.state_overrides.contains_key(&address_1)); + assert_eq!(block_state_call_1.state_overrides.get(&address_1).unwrap().nonce.unwrap(), 5); + + let block_state_call_2 = &sim_opts.block_state_calls[1]; + assert!(block_state_call_2.state_overrides.contains_key(&address_1)); + + assert_eq!(block_state_call_2.calls.len(), 2); + assert_eq!(block_state_call_2.calls[0].from.unwrap(), address_1); + assert_eq!(block_state_call_2.calls[0].to.unwrap(), TxKind::Call(address_1)); + assert_eq!(block_state_call_2.calls[0].nonce.unwrap(), 0); + assert_eq!(block_state_call_2.calls[1].from.unwrap(), address_2); + assert_eq!(block_state_call_2.calls[1].to.unwrap(), TxKind::Call(address_2)); + assert_eq!(block_state_call_2.calls[1].nonce.unwrap(), 5); + } +} diff --git a/crates/rpc-types-eth/src/syncing.rs b/crates/rpc-types-eth/src/syncing.rs index 0be858e61cd..68bfd1d0f68 100644 --- a/crates/rpc-types-eth/src/syncing.rs +++ b/crates/rpc-types-eth/src/syncing.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::collections::BTreeMap; /// Syncing info -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SyncInfo { /// Starting block @@ -16,6 +16,22 @@ pub struct SyncInfo { pub warp_chunks_amount: Option, /// Warp sync snapshot chunks processed. pub warp_chunks_processed: Option, + /// The details of the sync stages as an hashmap + /// where the key is the name of the stage and the value is the block number. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stages: Option>, +} + +/// The detail of the sync stages. +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Stage { + /// The name of the sync stage. + #[serde(alias = "stage_name")] + pub name: String, + /// Indicates the progress of the sync stage. + #[serde(alias = "block_number", with = "alloy_serde::quantity")] + pub block: u64, } /// Peers info @@ -99,10 +115,10 @@ pub struct PipProtocolInfo { } /// Sync status -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum SyncStatus { /// Info when syncing - Info(SyncInfo), + Info(Box), /// Not syncing None, } @@ -117,7 +133,7 @@ impl<'de> Deserialize<'de> for SyncStatus { enum Syncing { /// When client is synced to the highest block, eth_syncing with return "false" None(bool), - IsSyncing(SyncInfo), + IsSyncing(Box), } match Syncing::deserialize(deserializer)? { diff --git a/crates/rpc-types-eth/src/transaction/mod.rs b/crates/rpc-types-eth/src/transaction/mod.rs index 4d567e790d2..c81ecf414e7 100644 --- a/crates/rpc-types-eth/src/transaction/mod.rs +++ b/crates/rpc-types-eth/src/transaction/mod.rs @@ -5,13 +5,14 @@ use alloy_consensus::{ TxLegacy, TxType, }; use alloy_eips::eip7702::SignedAuthorization; +use alloy_network_primitives::TransactionResponse; use alloy_primitives::{Address, BlockHash, Bytes, ChainId, TxHash, TxKind, B256, U256}; use alloy_serde::OtherFields; use serde::{Deserialize, Serialize}; pub use alloy_consensus::BlobTransactionSidecar; pub use alloy_eips::{ - eip2930::{AccessList, AccessListItem, AccessListWithGasUsed}, + eip2930::{AccessList, AccessListItem, AccessListResult}, eip7702::Authorization, }; @@ -276,6 +277,31 @@ impl TryFrom for TxEnvelope { } } +impl TransactionResponse for Transaction { + fn tx_hash(&self) -> B256 { + self.hash + } + + fn from(&self) -> Address { + self.from + } + + fn to(&self) -> Option
{ + self.to + } + + fn value(&self) -> U256 { + self.value + } + + fn gas(&self) -> u128 { + self.gas + } + + fn input(&self) -> &Bytes { + &self.input + } +} #[cfg(test)] mod tests { use super::*; diff --git a/crates/rpc-types-eth/src/transaction/receipt.rs b/crates/rpc-types-eth/src/transaction/receipt.rs index 8d49e1488dc..a95e5dce66b 100644 --- a/crates/rpc-types-eth/src/transaction/receipt.rs +++ b/crates/rpc-types-eth/src/transaction/receipt.rs @@ -1,6 +1,7 @@ use crate::Log; -use alloy_consensus::{AnyReceiptEnvelope, ReceiptEnvelope, TxType}; +use alloy_consensus::{AnyReceiptEnvelope, ReceiptEnvelope, TxReceipt, TxType}; use alloy_eips::eip7702::SignedAuthorization; +use alloy_network_primitives::ReceiptResponse; use alloy_primitives::{Address, BlockHash, TxHash, B256}; use alloy_serde::WithOtherFields; use serde::{Deserialize, Serialize}; @@ -128,6 +129,24 @@ impl TransactionReceipt { #[doc(alias = "AnyTxReceipt")] pub type AnyTransactionReceipt = WithOtherFields>>; +impl> ReceiptResponse for TransactionReceipt { + fn contract_address(&self) -> Option { + self.contract_address + } + + fn status(&self) -> bool { + self.inner.status() + } + + fn block_hash(&self) -> Option { + self.block_hash + } + + fn block_number(&self) -> Option { + self.block_number + } +} + #[cfg(test)] mod test { use super::*; diff --git a/crates/rpc-types-eth/src/transaction/request.rs b/crates/rpc-types-eth/src/transaction/request.rs index 6fae40d2b89..b61d2875cfb 100644 --- a/crates/rpc-types-eth/src/transaction/request.rs +++ b/crates/rpc-types-eth/src/transaction/request.rs @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize}; use std::hash::Hash; /// Represents _all_ transaction requests to/from RPC. +#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] #[doc(alias = "TxRequest")] @@ -485,6 +486,7 @@ impl TransactionRequest { /// /// If both fields are set, it is expected that they contain the same value, otherwise an error is /// returned. +#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)] #[doc(alias = "TxInput")] pub struct TransactionInput { diff --git a/crates/rpc-types-mev/CHANGELOG.md b/crates/rpc-types-mev/CHANGELOG.md index f1c0bc3a696..46c273218aa 100644 --- a/crates/rpc-types-mev/CHANGELOG.md +++ b/crates/rpc-types-mev/CHANGELOG.md @@ -5,11 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 +- Chore : fix typos ([#1087](https://github.com/alloy-rs/alloy/issues/1087)) - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/rpc-types-mev/src/mev_calls.rs b/crates/rpc-types-mev/src/mev_calls.rs index f85ecfa294a..ea4b3950751 100644 --- a/crates/rpc-types-mev/src/mev_calls.rs +++ b/crates/rpc-types-mev/src/mev_calls.rs @@ -312,7 +312,7 @@ mod tests { } #[test] - fn can_dererialize_sim_response() { + fn can_deserialize_sim_response() { let expected = r#" { "success": true, diff --git a/crates/rpc-types-trace/CHANGELOG.md b/crates/rpc-types-trace/CHANGELOG.md index f7b1b92a24e..bb2dea7d1cb 100644 --- a/crates/rpc-types-trace/CHANGELOG.md +++ b/crates/rpc-types-trace/CHANGELOG.md @@ -5,15 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 + +### Bug Fixes + +- Make Parity TraceResults output optional ([#1102](https://github.com/alloy-rs/alloy/issues/1102)) ### Features +- [rpc/trace] Filter matches with trace ([#1090](https://github.com/alloy-rs/alloy/issues/1090)) - [otterscan] Add ots slim block and serialze OperationType to int ([#1043](https://github.com/alloy-rs/alloy/issues/1043)) ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 +- Chore : fix typos ([#1087](https://github.com/alloy-rs/alloy/issues/1087)) - Release 0.2.0 - Trace output utils ([#1027](https://github.com/alloy-rs/alloy/issues/1027)) diff --git a/crates/rpc-types-trace/src/filter.rs b/crates/rpc-types-trace/src/filter.rs index d0fae8e2412..3e17294ad69 100644 --- a/crates/rpc-types-trace/src/filter.rs +++ b/crates/rpc-types-trace/src/filter.rs @@ -1,4 +1,8 @@ //! `trace_filter` types and support +use crate::parity::{ + Action, CallAction, CreateAction, CreateOutput, RewardAction, SelfdestructAction, TraceOutput, + TransactionTrace, +}; use alloy_primitives::Address; use serde::{Deserialize, Serialize}; use std::collections::HashSet; @@ -93,31 +97,96 @@ pub enum TraceFilterMode { Intersection, } -/// Helper type for matching `from` and `to` addresses. Empty sets match all addresses. +/// Address filter. +/// This is a set of addresses to match against. +/// An empty set matches all addresses. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct AddressFilter(pub HashSet
); + +impl FromIterator
for AddressFilter { + fn from_iter>(iter: I) -> Self { + Self(iter.into_iter().collect()) + } +} + +impl From> for AddressFilter { + fn from(addrs: Vec
) -> Self { + Self::from_iter(addrs) + } +} + +impl AddressFilter { + /// Returns `true` if the given address is in the filter or the filter address set is empty. + pub fn matches(&self, addr: &Address) -> bool { + self.matches_all() || self.0.contains(addr) + } + + /// Returns `true` if the address set is empty. + pub fn matches_all(&self) -> bool { + self.0.is_empty() + } +} + +/// `TraceFilterMatcher` is a filter used for matching `TransactionTrace` based on +/// it's action and result(if available). It allows filtering traces by their mode, from address +/// set, and to address set, and empty address set means match all addresses. #[derive(Clone, Debug, PartialEq, Eq)] pub struct TraceFilterMatcher { mode: TraceFilterMode, - from_addresses: HashSet
, - to_addresses: HashSet
, + from_addresses: AddressFilter, + to_addresses: AddressFilter, } impl TraceFilterMatcher { - /// Returns `true` if the given `from` and `to` addresses match this filter. - pub fn matches(&self, from: Address, to: Option
) -> bool { - match (self.from_addresses.is_empty(), self.to_addresses.is_empty()) { - (true, true) => true, - (false, true) => self.from_addresses.contains(&from), - (true, false) => to.map_or(false, |to_addr| self.to_addresses.contains(&to_addr)), - (false, false) => match self.mode { - TraceFilterMode::Union => { - self.from_addresses.contains(&from) - || to.map_or(false, |to_addr| self.to_addresses.contains(&to_addr)) - } - TraceFilterMode::Intersection => { - self.from_addresses.contains(&from) - && to.map_or(false, |to_addr| self.to_addresses.contains(&to_addr)) - } - }, + /// Returns `true` if the given `TransactionTrace` matches this filter. + /// + /// # Arguments + /// + /// - `trace`: A reference to a `TransactionTrace` to be evaluated against the filter. + /// + /// # Returns + /// + /// - `true` if the transaction trace matches the filter criteria; otherwise, `false`. + /// + /// # Behavior + /// + /// The function evaluates whether the `trace` matches based on its action type: + /// - `Call`: Matches if either the `from` or `to` addresses in the call action match the + /// filter's address criteria. + /// - `Create`: Matches if the `from` address in action matches, and the result's address (if + /// available) matches the filter's address criteria. + /// - `Selfdestruct`: Matches if the `address` and `refund_address` matches the filter's address + /// criteria. + /// - `Reward`: Matches if the `author` address matches the filter's `to_addresses` criteria. + /// + /// The overall result depends on the filter mode: + /// - `Union` mode: The trace matches if either the `from` or `to` address matches. + /// - `Intersection` mode: The trace matches only if both the `from` and `to` addresses match. + pub fn matches(&self, trace: &TransactionTrace) -> bool { + let (from_matches, to_matches) = match trace.action { + Action::Call(CallAction { from, to, .. }) => { + (self.from_addresses.matches(&from), self.to_addresses.matches(&to)) + } + Action::Create(CreateAction { from, .. }) => ( + self.from_addresses.matches(&from), + match trace.result { + Some(TraceOutput::Create(CreateOutput { address: to, .. })) => { + self.to_addresses.matches(&to) + } + _ => self.to_addresses.matches_all(), + }, + ), + Action::Selfdestruct(SelfdestructAction { address, refund_address, .. }) => { + (self.from_addresses.matches(&address), self.to_addresses.matches(&refund_address)) + } + Action::Reward(RewardAction { author, .. }) => { + (self.from_addresses.matches_all(), self.to_addresses.matches(&author)) + } + }; + + match self.mode { + TraceFilterMode::Union => from_matches || to_matches, + TraceFilterMode::Intersection => from_matches && to_matches, } } } @@ -125,6 +194,7 @@ impl TraceFilterMatcher { #[cfg(test)] mod tests { use super::*; + use alloy_primitives::{Bytes, U256}; use serde_json::json; #[test] @@ -137,88 +207,191 @@ mod tests { #[test] fn test_filter_matcher_addresses_unspecified() { - let test_addr_d8 = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse().unwrap(); - let test_addr_16 = "0x160f5f00288e9e1cc8655b327e081566e580a71d".parse().unwrap(); - let filter_json = json!({ - "fromBlock": "0x3", - "toBlock": "0x5", - }); - let filter: TraceFilter = - serde_json::from_value(filter_json).expect("Failed to parse filter"); - let matcher = filter.matcher(); - assert!(matcher.matches(test_addr_d8, None)); - assert!(matcher.matches(test_addr_16, None)); - assert!(matcher.matches(test_addr_d8, Some(test_addr_16))); - assert!(matcher.matches(test_addr_16, Some(test_addr_d8))); - } + let filter_json = json!({ "fromBlock": "0x3", "toBlock": "0x5" }); + let matcher = serde_json::from_value::(filter_json).unwrap().matcher(); + let s = r#"{ + "action": { + "from": "0x66e29f0b6b1b07071f2fde4345d512386cb66f5f", + "callType": "call", + "gas": "0x10bfc", + "input": "0x", + "to": "0x160f5f00288e9e1cc8655b327e081566e580a71d", + "value": "0x244b" + }, + "error": "Reverted", + "result": { + "gasUsed": "0x9daf", + "output": "0x" + }, + "subtraces": 3, + "traceAddress": [], + "type": "call" + }"#; + let trace = serde_json::from_str::(s).unwrap(); - #[test] - fn test_filter_matcher_from_address() { - let test_addr_d8 = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse().unwrap(); - let test_addr_16 = "0x160f5f00288e9e1cc8655b327e081566e580a71d".parse().unwrap(); - let filter_json = json!({ - "fromBlock": "0x3", - "toBlock": "0x5", - "fromAddress": [test_addr_d8] - }); - let filter: TraceFilter = serde_json::from_value(filter_json).unwrap(); - let matcher = filter.matcher(); - assert!(matcher.matches(test_addr_d8, None)); - assert!(!matcher.matches(test_addr_16, None)); - assert!(matcher.matches(test_addr_d8, Some(test_addr_16))); - assert!(!matcher.matches(test_addr_16, Some(test_addr_d8))); + assert!(matcher.matches(&trace)); } #[test] - fn test_filter_matcher_to_address() { - let test_addr_d8 = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse().unwrap(); - let test_addr_16 = "0x160f5f00288e9e1cc8655b327e081566e580a71d".parse().unwrap(); - let filter_json = json!({ - "fromBlock": "0x3", - "toBlock": "0x5", - "toAddress": [test_addr_d8], - }); - let filter: TraceFilter = serde_json::from_value(filter_json).unwrap(); - let matcher = filter.matcher(); - assert!(matcher.matches(test_addr_16, Some(test_addr_d8))); - assert!(!matcher.matches(test_addr_16, None)); - assert!(!matcher.matches(test_addr_d8, Some(test_addr_16))); - } + fn test_filter_matcher() { + let addr0 = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse().unwrap(); + let addr1 = "0x160f5f00288e9e1cc8655b327e081566e580a71d".parse().unwrap(); + let addr2 = "0x160f5f00288e9e1cc8655b327e081566e580a71f".parse().unwrap(); - #[test] - fn test_filter_matcher_both_addresses_union() { - let test_addr_d8 = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse().unwrap(); - let test_addr_16 = "0x160f5f00288e9e1cc8655b327e081566e580a71d".parse().unwrap(); - let filter_json = json!({ - "fromBlock": "0x3", - "toBlock": "0x5", - "fromAddress": [test_addr_16], - "toAddress": [test_addr_d8], - }); - let filter: TraceFilter = serde_json::from_value(filter_json).unwrap(); - let matcher = filter.matcher(); - assert!(matcher.matches(test_addr_16, Some(test_addr_d8))); - assert!(matcher.matches(test_addr_16, None)); - assert!(matcher.matches(test_addr_d8, Some(test_addr_d8))); - assert!(!matcher.matches(test_addr_d8, Some(test_addr_16))); - } + let m0 = TraceFilterMatcher { + mode: TraceFilterMode::Union, + from_addresses: Default::default(), + to_addresses: Default::default(), + }; - #[test] - fn test_filter_matcher_both_addresses_intersection() { - let test_addr_d8 = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse().unwrap(); - let test_addr_16 = "0x160f5f00288e9e1cc8655b327e081566e580a71d".parse().unwrap(); - let filter_json = json!({ - "fromBlock": "0x3", - "toBlock": "0x5", - "fromAddress": [test_addr_16], - "toAddress": [test_addr_d8], - "mode": "intersection", - }); - let filter: TraceFilter = serde_json::from_value(filter_json).unwrap(); - let matcher = filter.matcher(); - assert!(matcher.matches(test_addr_16, Some(test_addr_d8))); - assert!(!matcher.matches(test_addr_16, None)); - assert!(!matcher.matches(test_addr_d8, Some(test_addr_d8))); - assert!(!matcher.matches(test_addr_d8, Some(test_addr_16))); + let m1 = TraceFilterMatcher { + mode: TraceFilterMode::Union, + from_addresses: AddressFilter::from(vec![addr0]), + to_addresses: Default::default(), + }; + + let m2 = TraceFilterMatcher { + mode: TraceFilterMode::Union, + from_addresses: AddressFilter::from(vec![]), + to_addresses: AddressFilter::from(vec![addr1]), + }; + + let m3 = TraceFilterMatcher { + mode: TraceFilterMode::Union, + from_addresses: AddressFilter::from(vec![addr0]), + to_addresses: AddressFilter::from(vec![addr1]), + }; + + let m4 = TraceFilterMatcher { + mode: TraceFilterMode::Intersection, + from_addresses: Default::default(), + to_addresses: Default::default(), + }; + + let m5 = TraceFilterMatcher { + mode: TraceFilterMode::Intersection, + from_addresses: AddressFilter::from(vec![addr0]), + to_addresses: Default::default(), + }; + + let m6 = TraceFilterMatcher { + mode: TraceFilterMode::Intersection, + from_addresses: Default::default(), + to_addresses: AddressFilter::from(vec![addr1]), + }; + + let m7 = TraceFilterMatcher { + mode: TraceFilterMode::Intersection, + from_addresses: AddressFilter::from(vec![addr0]), + to_addresses: AddressFilter::from(vec![addr1]), + }; + + // normal call 0 + let trace = TransactionTrace { + action: Action::Call(CallAction { from: addr0, to: addr1, ..Default::default() }), + ..Default::default() + }; + assert!(m0.matches(&trace)); + assert!(m1.matches(&trace)); + assert!(m2.matches(&trace)); + assert!(m3.matches(&trace)); + assert!(m4.matches(&trace)); + assert!(m5.matches(&trace)); + assert!(m6.matches(&trace)); + assert!(m7.matches(&trace)); + + // normal call 1 + let trace = TransactionTrace { + action: Action::Call(CallAction { from: addr0, to: addr2, ..Default::default() }), + ..Default::default() + }; + assert!(m0.matches(&trace)); + assert!(m1.matches(&trace)); + assert!(m2.matches(&trace)); + assert!(m3.matches(&trace)); + assert!(m4.matches(&trace)); + assert!(m5.matches(&trace)); + assert!(!m6.matches(&trace)); + assert!(!m7.matches(&trace)); + + // create success + let trace = TransactionTrace { + action: Action::Create(CreateAction { + from: addr0, + gas: 10240, + init: Bytes::new(), + value: U256::from(0), + }), + result: Some(TraceOutput::Create(CreateOutput { + address: addr1, + code: Bytes::new(), + gas_used: 1025, + })), + ..Default::default() + }; + assert!(m0.matches(&trace)); + assert!(m1.matches(&trace)); + assert!(m2.matches(&trace)); + assert!(m3.matches(&trace)); + assert!(m4.matches(&trace)); + assert!(m5.matches(&trace)); + assert!(m6.matches(&trace)); + assert!(m7.matches(&trace)); + + // create failure + let trace = TransactionTrace { + action: Action::Create(CreateAction { + from: addr0, + gas: 100, + init: Bytes::new(), + value: U256::from(0), + }), + error: Some("out of gas".into()), + ..Default::default() + }; + assert!(m0.matches(&trace)); + assert!(m1.matches(&trace)); + assert!(m2.matches(&trace)); + assert!(m3.matches(&trace)); + assert!(m4.matches(&trace)); + assert!(m5.matches(&trace)); + assert!(!m6.matches(&trace)); + assert!(!m7.matches(&trace)); + + // selfdestruct + let trace = TransactionTrace { + action: Action::Selfdestruct(SelfdestructAction { + address: addr0, + refund_address: addr1, + balance: U256::from(0), + }), + ..Default::default() + }; + assert!(m0.matches(&trace)); + assert!(m1.matches(&trace)); + assert!(m2.matches(&trace)); + assert!(m3.matches(&trace)); + assert!(m4.matches(&trace)); + assert!(m5.matches(&trace)); + assert!(m6.matches(&trace)); + assert!(m7.matches(&trace)); + + // reward + let trace = TransactionTrace { + action: Action::Reward(RewardAction { + author: addr0, + reward_type: crate::parity::RewardType::Block, + value: U256::from(0), + }), + ..Default::default() + }; + assert!(m0.matches(&trace)); + assert!(m1.matches(&trace)); + assert!(m2.matches(&trace)); + assert!(!m3.matches(&trace)); + assert!(m4.matches(&trace)); + assert!(!m5.matches(&trace)); + assert!(!m6.matches(&trace)); + assert!(!m7.matches(&trace)); } } diff --git a/crates/rpc-types-trace/src/otterscan.rs b/crates/rpc-types-trace/src/otterscan.rs index f5a71778cb2..a56ed47366e 100644 --- a/crates/rpc-types-trace/src/otterscan.rs +++ b/crates/rpc-types-trace/src/otterscan.rs @@ -280,7 +280,7 @@ mod tests { } #[test] - fn test_otterscan_interal_operation() { + fn test_otterscan_internal_operation() { let s = r#"{ "type": 0, "from": "0xea593b730d745fb5fe01b6d20e6603915252c6bf", diff --git a/crates/rpc-types-trace/src/parity.rs b/crates/rpc-types-trace/src/parity.rs index 21abea91bb2..b6794694eb7 100644 --- a/crates/rpc-types-trace/src/parity.rs +++ b/crates/rpc-types-trace/src/parity.rs @@ -29,6 +29,7 @@ pub enum TraceType { #[serde(rename_all = "camelCase")] pub struct TraceResults { /// Output of the trace + #[serde(deserialize_with = "alloy_serde::null_as_default")] pub output: Bytes, /// Enabled if [TraceType::StateDiff] is provided pub state_diff: Option, @@ -821,4 +822,31 @@ mod tests { let serialized = serde_json::to_string_pretty(&trace).unwrap(); similar_asserts::assert_eq!(serialized, reference_data); } + #[test] + fn test_nethermind_trace_result_null_output_value() { + let reference_data = r#"{ + "output": null, + "stateDiff": { + "0x5e1d1eb61e1164d5a50b28c575da73a29595dff7": { + "balance": "=", + "code": "=", + "nonce": "=", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000005": { + "*": { + "from": "0x0000000000000000000000000000000000000000000000000000000000042f66", + "to": "0x0000000000000000000000000000000000000000000000000000000000042f67" + } + } + } + } + }, + "trace": [], + "vmTrace": null, + "transactionHash": "0xe56a5e7455c45b1842b35dbcab9d024b21870ee59820525091e183b573b4f9eb" +}"#; + let trace = + serde_json::from_str::(reference_data).unwrap(); + assert_eq!(trace.full_trace.output, Bytes::default()); + } } diff --git a/crates/rpc-types-txpool/CHANGELOG.md b/crates/rpc-types-txpool/CHANGELOG.md index 34c1bd210a1..323beaef1a5 100644 --- a/crates/rpc-types-txpool/CHANGELOG.md +++ b/crates/rpc-types-txpool/CHANGELOG.md @@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/rpc-types/CHANGELOG.md b/crates/rpc-types/CHANGELOG.md index aff7a29039a..5e26c7bbc87 100644 --- a/crates/rpc-types/CHANGELOG.md +++ b/crates/rpc-types/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Features @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/serde/CHANGELOG.md b/crates/serde/CHANGELOG.md index 9897d637c17..13c026688e5 100644 --- a/crates/serde/CHANGELOG.md +++ b/crates/serde/CHANGELOG.md @@ -5,10 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks +- Release 0.2.0 - Fix unnameable types ([#1029](https://github.com/alloy-rs/alloy/issues/1029)) ### Styling diff --git a/crates/signer-aws/CHANGELOG.md b/crates/signer-aws/CHANGELOG.md index 8d0a305c90b..caba9a2484a 100644 --- a/crates/signer-aws/CHANGELOG.md +++ b/crates/signer-aws/CHANGELOG.md @@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/signer-gcp/CHANGELOG.md b/crates/signer-gcp/CHANGELOG.md index fe18142eb7d..ecbbe7af20e 100644 --- a/crates/signer-gcp/CHANGELOG.md +++ b/crates/signer-gcp/CHANGELOG.md @@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/signer-ledger/CHANGELOG.md b/crates/signer-ledger/CHANGELOG.md index aeb71f23047..9ade16f4cfa 100644 --- a/crates/signer-ledger/CHANGELOG.md +++ b/crates/signer-ledger/CHANGELOG.md @@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/signer-local/CHANGELOG.md b/crates/signer-local/CHANGELOG.md index defe425da03..1c50597d862 100644 --- a/crates/signer-local/CHANGELOG.md +++ b/crates/signer-local/CHANGELOG.md @@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 - Fix unnameable types ([#1029](https://github.com/alloy-rs/alloy/issues/1029)) diff --git a/crates/signer-trezor/CHANGELOG.md b/crates/signer-trezor/CHANGELOG.md index e8df4185f60..ee5bd9f5816 100644 --- a/crates/signer-trezor/CHANGELOG.md +++ b/crates/signer-trezor/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Dependencies @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/signer/CHANGELOG.md b/crates/signer/CHANGELOG.md index ca994bf36f9..0b51a2e155a 100644 --- a/crates/signer/CHANGELOG.md +++ b/crates/signer/CHANGELOG.md @@ -5,11 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 + +### Features + +- Use EncodableSignature for tx encoding ([#1100](https://github.com/alloy-rs/alloy/issues/1100)) ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/signer/src/lib.rs b/crates/signer/src/lib.rs index 43ecb3017d9..3f5e45ee864 100644 --- a/crates/signer/src/lib.rs +++ b/crates/signer/src/lib.rs @@ -27,6 +27,8 @@ macro_rules! sign_transaction_with_chain_id { // sign: lazy Signature, // ) ($signer:expr, $tx:expr, $sign:expr) => {{ + use alloy_consensus::EncodableSignature; + if let Some(chain_id) = $signer.chain_id() { if !$tx.set_chain_id_checked(chain_id) { return Err(alloy_signer::Error::TransactionChainIdMismatch { diff --git a/crates/transport-http/CHANGELOG.md b/crates/transport-http/CHANGELOG.md index 146c016b946..fa45af65000 100644 --- a/crates/transport-http/CHANGELOG.md +++ b/crates/transport-http/CHANGELOG.md @@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/transport-ipc/CHANGELOG.md b/crates/transport-ipc/CHANGELOG.md index edbae0d18dc..b28867b1dea 100644 --- a/crates/transport-ipc/CHANGELOG.md +++ b/crates/transport-ipc/CHANGELOG.md @@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/transport-ws/CHANGELOG.md b/crates/transport-ws/CHANGELOG.md index 59b028483d8..2b673021b7d 100644 --- a/crates/transport-ws/CHANGELOG.md +++ b/crates/transport-ws/CHANGELOG.md @@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 ## [0.1.4](https://github.com/alloy-rs/alloy/releases/tag/v0.1.4) - 2024-07-08 diff --git a/crates/transport/CHANGELOG.md b/crates/transport/CHANGELOG.md index e1b248fc7b2..f9107b3a8e2 100644 --- a/crates/transport/CHANGELOG.md +++ b/crates/transport/CHANGELOG.md @@ -5,11 +5,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.0](https://github.com/alloy-rs/alloy/releases/tag/v0.2.0) - 2024-07-16 +## [0.2.1](https://github.com/alloy-rs/alloy/releases/tag/v0.2.1) - 2024-08-02 + +### Documentation + +- Update links to use docs.rs ([#1066](https://github.com/alloy-rs/alloy/issues/1066)) + +### Features + +- Enable more features transitively in meta crate ([#1097](https://github.com/alloy-rs/alloy/issues/1097)) ### Miscellaneous Tasks -- Release 0.2.0 +- Release 0.2.1 - Release 0.2.0 - Fix unnameable types ([#1029](https://github.com/alloy-rs/alloy/issues/1029)) diff --git a/crates/transport/Cargo.toml b/crates/transport/Cargo.toml index 89c3cca99d3..d6e013f308d 100644 --- a/crates/transport/Cargo.toml +++ b/crates/transport/Cargo.toml @@ -36,5 +36,4 @@ tokio = { workspace = true, features = ["rt", "time"] } wasm-bindgen-futures = { version = "0.4", optional = true } [features] - wasm-bindgen = ["dep:wasm-bindgen-futures"] diff --git a/crates/transport/README.md b/crates/transport/README.md index e4fa5a8a163..5dec20b73b8 100644 --- a/crates/transport/README.md +++ b/crates/transport/README.md @@ -15,7 +15,7 @@ with JSON-RPC servers that provide the standard Ethereum RPC endpoints, or the [alloy-rpc-client] crate, which provides a low-level JSON-RPC API without the specific Ethereum endpoints. -[alloy-provider]: https://alloy-rs.github.io/alloy/alloy_provider/index.html +[alloy-provider]: https://docs.rs/alloy_provider/ [tower `Service`]: https://docs.rs/tower/latest/tower/trait.Service.html ### Transports @@ -27,7 +27,7 @@ Alloy maintains the following transports: [alloy-pubsub]. - [alloy-transport-ipc]: JSON-RPC via IPC, supports pubsub via [alloy-pubsub]. -[alloy-transport-http]: https://alloy-rs.github.io/alloy/alloy_transport_http/index.html -[alloy-transport-ws]: https://alloy-rs.github.io/alloy/alloy_transport_ws/index.html -[alloy-transport-ipc]: https://alloy-rs.github.io/alloy/alloy_transport_ipc/index.html -[alloy-pubsub]: https://alloy-rs.github.io/alloy/alloy_pubsub/index.html \ No newline at end of file +[alloy-transport-http]: https://docs.rs/alloy_transport_http/ +[alloy-transport-ws]: https://docs.rs/alloy_transport_ws/ +[alloy-transport-ipc]: https://docs.rs/alloy_transport_ipc/ +[alloy-pubsub]: https://docs.rs/alloy_pubsub/ diff --git a/deny.toml b/deny.toml index 901fbbe7947..2278d2ddde4 100644 --- a/deny.toml +++ b/deny.toml @@ -16,6 +16,7 @@ allow = [ "Apache-2.0", "Apache-2.0 WITH LLVM-exception", "BSD-3-Clause", + "BSD-2-Clause", "0BSD", "ISC", "Unicode-3.0", @@ -32,6 +33,7 @@ exceptions = [ # https://tldrlegal.com/license/creative-commons-cc0-1.0-universal { allow = ["CC0-1.0"], name = "tiny-keccak" }, { allow = ["CC0-1.0"], name = "trezor-client" }, + { allow = ["BSD-2-Clause"], name = "zerocopy" }, ] [[licenses.clarify]]