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#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
18#[serde(rename_all = "camelCase")]
19pub struct Forking {
20 pub json_rpc_url: Option<String>,
22 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#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
63#[serde(rename_all = "camelCase")]
64pub struct NodeInfo {
65 #[serde(with = "alloy_serde::quantity")]
67 pub current_block_number: u64,
68 pub current_block_timestamp: u64,
70 pub current_block_hash: BlockHash,
72 pub hard_fork: String,
74 #[doc(alias = "tx_order")]
76 pub transaction_order: String,
77 pub environment: NodeEnvironment,
79 pub fork_config: NodeForkConfig,
81}
82
83#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
85#[serde(rename_all = "camelCase")]
86pub struct NodeEnvironment {
87 #[serde(with = "alloy_serde::quantity")]
89 pub base_fee: u128,
90 pub chain_id: ChainId,
92 #[serde(with = "alloy_serde::quantity")]
94 pub gas_limit: u64,
95 #[serde(with = "alloy_serde::quantity")]
97 pub gas_price: u128,
98}
99
100#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
102#[serde(rename_all = "camelCase")]
103pub struct NodeForkConfig {
104 pub fork_url: Option<String>,
106 pub fork_block_number: Option<u64>,
108 pub fork_retry_backoff: Option<u128>,
110}
111
112#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
116#[serde(rename_all = "camelCase")]
117pub struct Metadata {
118 pub client_version: String,
120 pub chain_id: ChainId,
122 pub instance_id: B256,
124 pub latest_block_number: u64,
126 pub latest_block_hash: BlockHash,
128 pub forked_network: Option<ForkedNetwork>,
130 pub snapshots: BTreeMap<U256, (u64, B256)>,
132}
133
134#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
137#[serde(rename_all = "camelCase")]
138pub struct ForkedNetwork {
139 pub chain_id: ChainId,
141 pub fork_block_number: u64,
143 pub fork_block_hash: TxHash,
145}
146
147#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
149#[serde(untagged)]
150pub enum MineOptions {
151 Options {
153 #[serde(with = "alloy_serde::quantity::opt")]
155 timestamp: Option<u64>,
156 blocks: Option<u64>,
159 },
160 #[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#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct ReorgOptions {
174 pub depth: u64,
176 pub tx_block_pairs: Vec<(TransactionData, u64)>,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
182#[serde(untagged)]
183pub enum TransactionData {
184 JSON(TransactionRequest),
186 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 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 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 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}