alloy_rpc_types_anvil/
lib.rs

1#![doc = include_str!("../README.md")]
2#![doc(
3    html_logo_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/alloy.jpg",
4    html_favicon_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/favicon.ico"
5)]
6#![cfg_attr(not(test), warn(unused_crate_dependencies))]
7#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
8
9use alloy_primitives::{BlockHash, Bytes, ChainId, TxHash, B256, U256};
10use alloy_rpc_types_eth::TransactionRequest;
11use serde::{Deserialize, Deserializer, Serialize};
12use std::collections::BTreeMap;
13
14/// Represents the params to set forking which can take various forms:
15///  - untagged
16///  - tagged forking
17#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
18#[serde(rename_all = "camelCase")]
19pub struct Forking {
20    /// The URL of the JSON-RPC endpoint to fork from.
21    pub json_rpc_url: Option<String>,
22    /// The block number to fork from.
23    pub block_number: Option<u64>,
24}
25
26impl<'de> serde::Deserialize<'de> for Forking {
27    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
28    where
29        D: Deserializer<'de>,
30    {
31        #[derive(serde::Deserialize)]
32        #[serde(rename_all = "camelCase")]
33        struct ForkOpts {
34            json_rpc_url: Option<String>,
35            #[serde(default, with = "alloy_serde::quantity::opt")]
36            block_number: Option<u64>,
37        }
38
39        #[derive(serde::Deserialize)]
40        struct Tagged {
41            forking: ForkOpts,
42        }
43        #[derive(serde::Deserialize)]
44        #[serde(untagged)]
45        enum ForkingVariants {
46            Tagged(Tagged),
47            Fork(ForkOpts),
48        }
49        let f = match ForkingVariants::deserialize(deserializer)? {
50            ForkingVariants::Fork(ForkOpts { json_rpc_url, block_number }) => {
51                Self { json_rpc_url, block_number }
52            }
53            ForkingVariants::Tagged(f) => {
54                Self { json_rpc_url: f.forking.json_rpc_url, block_number: f.forking.block_number }
55            }
56        };
57        Ok(f)
58    }
59}
60
61/// Anvil equivalent of `node_info`.
62#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
63#[serde(rename_all = "camelCase")]
64pub struct NodeInfo {
65    /// The current block number
66    #[serde(with = "alloy_serde::quantity")]
67    pub current_block_number: u64,
68    /// The current block timestamp
69    pub current_block_timestamp: u64,
70    /// The current block hash
71    pub current_block_hash: BlockHash,
72    /// The enabled hardfork
73    pub hard_fork: String,
74    /// How transactions are ordered for mining
75    #[doc(alias = "tx_order")]
76    pub transaction_order: String,
77    /// Info about the node's block environment
78    pub environment: NodeEnvironment,
79    /// Info about the node's fork configuration
80    pub fork_config: NodeForkConfig,
81}
82
83/// The current block environment of the node.
84#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
85#[serde(rename_all = "camelCase")]
86pub struct NodeEnvironment {
87    /// Base fee of the current block
88    #[serde(with = "alloy_serde::quantity")]
89    pub base_fee: u128,
90    /// Chain id of the node.
91    pub chain_id: ChainId,
92    /// Configured block gas limit
93    #[serde(with = "alloy_serde::quantity")]
94    pub gas_limit: u64,
95    /// Configured gas price
96    #[serde(with = "alloy_serde::quantity")]
97    pub gas_price: u128,
98}
99
100/// The node's fork configuration.
101#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
102#[serde(rename_all = "camelCase")]
103pub struct NodeForkConfig {
104    /// URL of the forked network
105    pub fork_url: Option<String>,
106    /// Block number of the forked network
107    pub fork_block_number: Option<u64>,
108    /// Retry backoff for requests
109    pub fork_retry_backoff: Option<u128>,
110}
111
112/// Anvil equivalent of `hardhat_metadata`.
113/// Metadata about the current Anvil instance.
114/// See <https://hardhat.org/hardhat-network/docs/reference#hardhat_metadata>
115#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
116#[serde(rename_all = "camelCase")]
117pub struct Metadata {
118    /// client version
119    pub client_version: String,
120    /// Chain id of the node.
121    pub chain_id: ChainId,
122    /// Unique instance id
123    pub instance_id: B256,
124    /// Latest block number
125    pub latest_block_number: u64,
126    /// Latest block hash
127    pub latest_block_hash: BlockHash,
128    /// Forked network info
129    pub forked_network: Option<ForkedNetwork>,
130    /// Snapshots of the chain
131    pub snapshots: BTreeMap<U256, (u64, B256)>,
132}
133
134/// Information about the forked network.
135/// See <https://hardhat.org/hardhat-network/docs/reference#hardhat_metadata>
136#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
137#[serde(rename_all = "camelCase")]
138pub struct ForkedNetwork {
139    /// Chain id of the node.
140    pub chain_id: ChainId,
141    /// Block number of the forked chain
142    pub fork_block_number: u64,
143    /// Block hash of the forked chain
144    pub fork_block_hash: TxHash,
145}
146
147/// Additional `evm_mine` options
148#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
149#[serde(untagged)]
150pub enum MineOptions {
151    /// The options for mining
152    Options {
153        /// The timestamp the block should be mined with
154        #[serde(with = "alloy_serde::quantity::opt")]
155        timestamp: Option<u64>,
156        /// If `blocks` is given, it will mine exactly blocks number of blocks, regardless of any
157        /// other blocks mined or reverted during it's operation
158        blocks: Option<u64>,
159    },
160    /// The timestamp the block should be mined with
161    #[serde(with = "alloy_serde::quantity::opt")]
162    Timestamp(Option<u64>),
163}
164
165impl Default for MineOptions {
166    fn default() -> Self {
167        Self::Options { timestamp: None, blocks: None }
168    }
169}
170
171/// Represents the options used in `anvil_reorg`
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct ReorgOptions {
174    /// The depth of the reorg
175    pub depth: u64,
176    /// List of transaction requests and blocks pairs to be mined into the new chain
177    pub tx_block_pairs: Vec<(TransactionData, u64)>,
178}
179
180/// Type representing txs in `ReorgOptions`
181#[derive(Debug, Clone, Serialize, Deserialize)]
182#[serde(untagged)]
183pub enum TransactionData {
184    /// Transaction request
185    JSON(TransactionRequest),
186    /// Raw transaction bytes
187    Raw(Bytes),
188}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193    use similar_asserts::assert_eq;
194
195    #[test]
196    fn test_serde_forking_deserialization() {
197        // Test full forking object
198        let json_data = r#"{"forking": {"jsonRpcUrl": "https://ethereumpublicnode.com","blockNumber": "18441649"}}"#;
199        let forking: Forking = serde_json::from_str(json_data).unwrap();
200        assert_eq!(
201            forking,
202            Forking {
203                json_rpc_url: Some("https://ethereumpublicnode.com".into()),
204                block_number: Some(18441649)
205            }
206        );
207
208        // Test forking object with only jsonRpcUrl
209        let json_data = r#"{"forking": {"jsonRpcUrl": "https://ethereumpublicnode.com"}}"#;
210        let forking: Forking = serde_json::from_str(json_data).unwrap();
211        assert_eq!(
212            forking,
213            Forking {
214                json_rpc_url: Some("https://ethereumpublicnode.com".into()),
215                block_number: None
216            }
217        );
218
219        // Test forking object with only blockNumber
220        let json_data = r#"{"forking": {"blockNumber": "18441649"}}"#;
221        let forking: Forking =
222            serde_json::from_str(json_data).expect("Failed to deserialize forking object");
223        assert_eq!(forking, Forking { json_rpc_url: None, block_number: Some(18441649) });
224    }
225
226    #[test]
227    fn test_serde_deserialize_options_with_values() {
228        let data = r#"{"timestamp": 1620000000, "blocks": 10}"#;
229        let deserialized: MineOptions = serde_json::from_str(data).expect("Deserialization failed");
230        assert_eq!(
231            deserialized,
232            MineOptions::Options { timestamp: Some(1620000000), blocks: Some(10) }
233        );
234
235        let data = r#"{"timestamp": "0x608f3d00", "blocks": 10}"#;
236        let deserialized: MineOptions = serde_json::from_str(data).expect("Deserialization failed");
237        assert_eq!(
238            deserialized,
239            MineOptions::Options { timestamp: Some(1620000000), blocks: Some(10) }
240        );
241    }
242
243    #[test]
244    fn test_serde_deserialize_options_with_timestamp() {
245        let data = r#"{"timestamp":"1620000000"}"#;
246        let deserialized: MineOptions = serde_json::from_str(data).expect("Deserialization failed");
247        assert_eq!(
248            deserialized,
249            MineOptions::Options { timestamp: Some(1620000000), blocks: None }
250        );
251
252        let data = r#"{"timestamp":"0x608f3d00"}"#;
253        let deserialized: MineOptions = serde_json::from_str(data).expect("Deserialization failed");
254        assert_eq!(
255            deserialized,
256            MineOptions::Options { timestamp: Some(1620000000), blocks: None }
257        );
258    }
259
260    #[test]
261    fn test_serde_deserialize_timestamp() {
262        let data = r#""1620000000""#;
263        let deserialized: MineOptions = serde_json::from_str(data).expect("Deserialization failed");
264        assert_eq!(deserialized, MineOptions::Timestamp(Some(1620000000)));
265
266        let data = r#""0x608f3d00""#;
267        let deserialized: MineOptions = serde_json::from_str(data).expect("Deserialization failed");
268        assert_eq!(deserialized, MineOptions::Timestamp(Some(1620000000)));
269    }
270}