nautilus_blockchain/hypersync/transform.rs
1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3// https://nautechsystems.io
4//
5// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6// You may not use this file except in compliance with the License.
7// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use alloy::primitives::U256;
17use hypersync_client::format::Hex;
18use nautilus_core::{UnixNanos, datetime::NANOSECONDS_IN_SECOND};
19use nautilus_model::defi::{Block, Blockchain, hex::from_str_hex_to_u64};
20use ustr::Ustr;
21
22/// Converts a HyperSync block format to our internal [`Block`] type.
23///
24/// # Errors
25///
26/// Returns an error if required block fields are missing or if hex parsing fails.
27pub fn transform_hypersync_block(
28 chain: Blockchain,
29 received_block: hypersync_client::simple_types::Block,
30) -> Result<Block, anyhow::Error> {
31 let number = received_block
32 .number
33 .ok_or_else(|| anyhow::anyhow!("Missing block number"))?;
34 let gas_limit = from_str_hex_to_u64(
35 received_block
36 .gas_limit
37 .ok_or_else(|| anyhow::anyhow!("Missing gas limit"))?
38 .encode_hex()
39 .as_str(),
40 )?;
41 let gas_used = from_str_hex_to_u64(
42 received_block
43 .gas_used
44 .ok_or_else(|| anyhow::anyhow!("Missing gas used"))?
45 .encode_hex()
46 .as_str(),
47 )?;
48 let timestamp = from_str_hex_to_u64(
49 received_block
50 .timestamp
51 .ok_or_else(|| anyhow::anyhow!("Missing timestamp"))?
52 .encode_hex()
53 .as_str(),
54 )?;
55
56 let mut block = Block::new(
57 received_block
58 .hash
59 .ok_or_else(|| anyhow::anyhow!("Missing hash"))?
60 .to_string(),
61 received_block
62 .parent_hash
63 .ok_or_else(|| anyhow::anyhow!("Missing parent hash"))?
64 .to_string(),
65 number,
66 Ustr::from(
67 received_block
68 .miner
69 .ok_or_else(|| anyhow::anyhow!("Missing miner"))?
70 .to_string()
71 .as_str(),
72 ),
73 gas_limit,
74 gas_used,
75 UnixNanos::new(timestamp * NANOSECONDS_IN_SECOND),
76 Some(chain),
77 );
78
79 if let Some(base_fee_hex) = received_block.base_fee_per_gas {
80 let s = base_fee_hex.encode_hex();
81 let val = U256::from_str_radix(s.trim_start_matches("0x"), 16)?;
82 block = block.with_base_fee(val);
83 }
84
85 if let (Some(used_hex), Some(excess_hex)) =
86 (received_block.blob_gas_used, received_block.excess_blob_gas)
87 {
88 let used = U256::from_str_radix(used_hex.encode_hex().trim_start_matches("0x"), 16)?;
89 let excess = U256::from_str_radix(excess_hex.encode_hex().trim_start_matches("0x"), 16)?;
90 block = block.with_blob_gas(used, excess);
91 }
92
93 // TODO: HyperSync does not yet publush L1 gas metadata fields
94 // if let (Some(price_hex), Some(l1_used_hex), Some(scalar_hex)) = (
95 // received_block.l1_gas_price,
96 // received_block.l1_gas_used,
97 // received_block.l1_fee_scalar,
98 // ) {
99 // let price = U256::from_str_radix(price_hex.encode_hex().trim_start_matches("0x"), 16)?;
100 // let used = from_str_hex_to_u64(l1_used_hex.encode_hex().as_str())?;
101 // let scalar = from_str_hex_to_u64(scalar_hex.encode_hex().as_str())?;
102 // block = block.with_l1_fee_components(price, used, scalar);
103 // }
104
105 Ok(block)
106}
107
108////////////////////////////////////////////////////////////////////////////////
109// Tests
110////////////////////////////////////////////////////////////////////////////////
111
112#[cfg(test)]
113mod tests {}