blueprint_evm_extra/extract/
block.rs

1//! Block extractors for EVM
2//!
3//! Simple extractors for block-related data from EVM.
4
5use alloy_primitives::B256;
6use blueprint_core::{
7    __composite_rejection as composite_rejection, __define_rejection as define_rejection,
8    __impl_deref as impl_deref, FromJobCallParts, job::call::Parts as JobCallParts,
9};
10
11/// Block number extractor
12#[derive(Debug, Clone, Copy)]
13pub struct BlockNumber(pub u64);
14
15impl BlockNumber {
16    /// Metadata key for block number
17    pub const METADATA_KEY: &'static str = "X-EVM-BLOCK-NUMBER";
18}
19
20impl_deref!(BlockNumber: u64);
21
22define_rejection! {
23    #[body = "No block found in metadata"]
24    /// This rejection is used to indicate that a block was not found in the metadata.
25    pub struct MissingBlock;
26}
27
28define_rejection! {
29    #[body = "Block number must be a valid number"]
30    /// This rejection is used to indicate that the block number is not a valid number.
31    pub struct InvalidBlockNumber;
32}
33
34define_rejection! {
35    #[body = "Block number must be a valid block hash"]
36    /// This rejection is used to indicate that the block hash is not a valid hash.
37    pub struct InvalidBlockHash;
38}
39
40composite_rejection! {
41    /// Rejection for block-related extractors
42    pub enum BlockNumberRejection {
43        MissingBlock,
44        InvalidBlockNumber,
45    }
46}
47
48impl TryFrom<&mut JobCallParts> for BlockNumber {
49    type Error = BlockNumberRejection;
50
51    fn try_from(parts: &mut JobCallParts) -> Result<Self, Self::Error> {
52        let block_number = parts
53            .metadata
54            .get(Self::METADATA_KEY)
55            .ok_or(MissingBlock)?
56            .try_into()
57            .map_err(|_| InvalidBlockNumber)?;
58        Ok(BlockNumber(block_number))
59    }
60}
61
62impl<Ctx> FromJobCallParts<Ctx> for BlockNumber
63where
64    Ctx: Send + Sync,
65{
66    type Rejection = BlockNumberRejection;
67
68    async fn from_job_call_parts(
69        parts: &mut JobCallParts,
70        _: &Ctx,
71    ) -> Result<Self, Self::Rejection> {
72        Self::try_from(parts)
73    }
74}
75
76composite_rejection! {
77    /// Rejection for block-related extractors
78    pub enum BlockHashRejection {
79        MissingBlock,
80        InvalidBlockHash,
81    }
82}
83
84/// Block hash extractor
85#[derive(Debug, Clone, Copy)]
86pub struct BlockHash(pub B256);
87
88impl BlockHash {
89    /// Metadata key for block hash
90    pub const METADATA_KEY: &'static str = "X-EVM-BLOCK-HASH";
91}
92
93impl_deref!(BlockHash: B256);
94
95impl TryFrom<&mut JobCallParts> for BlockHash {
96    type Error = BlockHashRejection;
97
98    fn try_from(parts: &mut JobCallParts) -> Result<Self, Self::Error> {
99        let block_hash = parts
100            .metadata
101            .get(Self::METADATA_KEY)
102            .ok_or(MissingBlock)?
103            .as_bytes();
104
105        let hash = B256::try_from(block_hash).map_err(|_| InvalidBlockHash)?;
106        Ok(BlockHash(hash))
107    }
108}
109
110impl<Ctx> FromJobCallParts<Ctx> for BlockHash
111where
112    Ctx: Send + Sync,
113{
114    type Rejection = BlockHashRejection;
115
116    async fn from_job_call_parts(
117        parts: &mut JobCallParts,
118        _: &Ctx,
119    ) -> Result<Self, Self::Rejection> {
120        Self::try_from(parts)
121    }
122}
123
124/// Block timestamp extractor
125#[derive(Debug, Clone, Copy)]
126pub struct BlockTimestamp(pub u64);
127
128impl BlockTimestamp {
129    /// Metadata key for block timestamp
130    pub const METADATA_KEY: &'static str = "X-EVM-BLOCK-TIMESTAMP";
131}
132
133impl_deref!(BlockTimestamp: u64);
134
135define_rejection! {
136    #[body = "Block timestamp must be a valid block timestamp"]
137    /// This rejection is used to indicate that the block timestamp is not a valid timestamp.
138    pub struct InvalidBlockTimestamp;
139}
140
141composite_rejection! {
142    /// Rejection for block-related extractors
143    pub enum BlockTimestampRejection {
144        MissingBlock,
145        InvalidBlockTimestamp,
146    }
147}
148
149impl TryFrom<&mut JobCallParts> for BlockTimestamp {
150    type Error = BlockTimestampRejection;
151
152    fn try_from(parts: &mut JobCallParts) -> Result<Self, Self::Error> {
153        let timestamp = parts
154            .metadata
155            .get(Self::METADATA_KEY)
156            .ok_or(MissingBlock)?
157            .try_into()
158            .map_err(|_| InvalidBlockTimestamp)?;
159        Ok(BlockTimestamp(timestamp))
160    }
161}
162
163impl<Ctx> FromJobCallParts<Ctx> for BlockTimestamp
164where
165    Ctx: Send + Sync,
166{
167    type Rejection = BlockTimestampRejection;
168
169    async fn from_job_call_parts(
170        parts: &mut JobCallParts,
171        _: &Ctx,
172    ) -> Result<Self, Self::Rejection> {
173        Self::try_from(parts)
174    }
175}