1use alloy::primitives::{Address, B256, Bytes, U256};
17use nautilus_model::defi::{Block, DexType, rpc::RpcLog};
18use serde::Deserialize;
19use serde_json::Value;
20
21use crate::events::{
22 burn::BurnEvent, collect::CollectEvent, fee_protocol_collect::FeeProtocolCollectEvent,
23 fee_protocol_update::FeeProtocolUpdateEvent, flash::FlashEvent, mint::MintEvent,
24 swap::SwapEvent,
25};
26
27#[derive(Debug, Clone)]
29pub enum BlockchainMessage {
30 Block(Block),
31 SwapEvent(SwapEvent),
32 MintEvent(MintEvent),
33 BurnEvent(BurnEvent),
34 CollectEvent(CollectEvent),
35 FlashEvent(FlashEvent),
36 FeeProtocolUpdateEvent(FeeProtocolUpdateEvent),
37 FeeProtocolCollectEvent(FeeProtocolCollectEvent),
38}
39
40#[derive(Debug, Clone, Copy, Hash, PartialOrd, Ord, PartialEq, Eq)]
45pub enum RpcEventType {
46 NewBlock,
47 PoolSwap(DexType),
48 PoolMint(DexType),
49 PoolBurn(DexType),
50 PoolCollect(DexType),
51 PoolFlash(DexType),
52 PoolFeeProtocolUpdate(DexType),
53 PoolFeeProtocolCollect(DexType),
54}
55
56#[cfg(feature = "hypersync")]
58#[derive(Debug, Clone, PartialEq, Eq)]
59pub(crate) enum RpcCallResult {
60 Success(Bytes),
61 Reverted,
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
66#[serde(rename_all = "camelCase")]
67pub struct RpcBlock {
68 #[serde(deserialize_with = "deserialize_hex_u64")]
70 pub number: u64,
71 pub hash: B256,
73 pub parent_hash: B256,
75 #[serde(deserialize_with = "deserialize_hex_u64")]
77 pub timestamp: u64,
78 #[serde(default, deserialize_with = "deserialize_hex_u128_opt")]
80 pub base_fee_per_gas: Option<u128>,
81 #[serde(skip)]
83 pub transactions: Vec<RpcTransaction>,
84}
85
86#[derive(Debug, Deserialize)]
87pub(crate) struct RpcBlockResponse {
88 #[serde(flatten)]
89 pub block: RpcBlock,
90 #[serde(default)]
91 pub transactions: Vec<Value>,
92}
93
94#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
96#[serde(rename_all = "camelCase")]
97pub struct RpcTransaction {
98 pub hash: B256,
100 pub from: Address,
102 #[serde(deserialize_with = "deserialize_hex_u64")]
104 pub nonce: u64,
105 #[serde(default, deserialize_with = "deserialize_hex_u64_opt")]
107 pub chain_id: Option<u64>,
108 #[serde(default, rename = "type", deserialize_with = "deserialize_hex_u8_opt")]
110 pub transaction_type: Option<u8>,
111 pub to: Option<Address>,
113 pub input: Bytes,
115 pub value: U256,
117 #[serde(default, deserialize_with = "deserialize_hex_u64_opt")]
119 pub gas: Option<u64>,
120 pub max_fee_per_gas: Option<U256>,
122 pub max_priority_fee_per_gas: Option<U256>,
124}
125
126#[derive(Debug, Clone, Deserialize)]
128#[serde(rename_all = "camelCase")]
129pub struct RpcTransactionReceipt {
130 pub transaction_hash: B256,
132 pub block_hash: B256,
134 #[serde(deserialize_with = "deserialize_hex_u64")]
136 pub block_number: u64,
137 #[serde(deserialize_with = "deserialize_hex_u64")]
139 pub gas_used: u64,
140 pub effective_gas_price: U256,
142 #[serde(deserialize_with = "deserialize_hex_u64")]
144 pub transaction_index: u64,
145 #[serde(deserialize_with = "deserialize_hex_bool")]
147 pub status: bool,
148 #[serde(default)]
150 pub logs: Vec<RpcLog>,
151}
152
153#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
155#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
156pub enum RpcCallType {
157 Call,
158 Callcode,
159 Delegatecall,
160 Staticcall,
161 Create,
162 Create2,
163 Selfdestruct,
164}
165
166#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
168#[serde(rename_all = "camelCase")]
169pub struct RpcCallTrace {
170 #[serde(rename = "type")]
172 pub call_type: RpcCallType,
173 pub from: Address,
175 pub to: Option<Address>,
177 #[serde(default)]
179 pub value: U256,
180 #[serde(deserialize_with = "deserialize_hex_u64")]
182 pub gas: u64,
183 #[serde(deserialize_with = "deserialize_hex_u64")]
185 pub gas_used: u64,
186 #[serde(default)]
188 pub input: Bytes,
189 #[serde(default)]
191 pub output: Bytes,
192 #[serde(default)]
194 pub error: Option<String>,
195 #[serde(default)]
197 pub calls: Vec<Self>,
198}
199
200fn deserialize_hex_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
201where
202 D: serde::Deserializer<'de>,
203{
204 let s = String::deserialize(deserializer)?;
205 let value = parse_hex_quantity(&s).map_err(serde::de::Error::custom)?;
206 u64::try_from(value).map_err(serde::de::Error::custom)
207}
208
209fn deserialize_hex_u64_opt<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
210where
211 D: serde::Deserializer<'de>,
212{
213 let value = Option::<String>::deserialize(deserializer)?
214 .map(|value| parse_hex_quantity(&value))
215 .transpose()
216 .map_err(serde::de::Error::custom)?;
217 value
218 .map(u64::try_from)
219 .transpose()
220 .map_err(serde::de::Error::custom)
221}
222
223fn deserialize_hex_u8_opt<'de, D>(deserializer: D) -> Result<Option<u8>, D::Error>
224where
225 D: serde::Deserializer<'de>,
226{
227 let value = Option::<String>::deserialize(deserializer)?
228 .map(|value| parse_hex_quantity(&value))
229 .transpose()
230 .map_err(serde::de::Error::custom)?;
231 value
232 .map(u8::try_from)
233 .transpose()
234 .map_err(serde::de::Error::custom)
235}
236
237fn deserialize_hex_u128_opt<'de, D>(deserializer: D) -> Result<Option<u128>, D::Error>
238where
239 D: serde::Deserializer<'de>,
240{
241 let s: Option<String> = Option::deserialize(deserializer)?;
242 s.map(|s| parse_hex_quantity(&s).map_err(serde::de::Error::custom))
243 .transpose()
244}
245
246fn deserialize_hex_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
247where
248 D: serde::Deserializer<'de>,
249{
250 let s = String::deserialize(deserializer)?;
251 match s.as_str() {
252 "0x0" => Ok(false),
253 "0x1" => Ok(true),
254 _ => Err(serde::de::Error::custom(
255 "invalid transaction receipt status; expected 0x0 or 0x1",
256 )),
257 }
258}
259
260fn parse_hex_quantity(s: &str) -> anyhow::Result<u128> {
261 let stripped = s.strip_prefix("0x").unwrap_or(s);
262 u128::from_str_radix(stripped, 16)
263 .map_err(|e| anyhow::anyhow!("Failed to parse hex quantity '{s}': {e}"))
264}