Skip to main content

alloy_provider/ext/
engine.rs

1use crate::Provider;
2use alloy_eips::eip7685::RequestsOrHash;
3use alloy_network::Network;
4use alloy_primitives::{BlockHash, Bytes, B256, U64};
5use alloy_rpc_types_engine::{
6    BlobAndProofV1, BlobAndProofV2, ClientVersionV1, ExecutionPayloadBodiesV1,
7    ExecutionPayloadBodiesV2, ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3,
8    ExecutionPayloadEnvelopeV4, ExecutionPayloadEnvelopeV5, ExecutionPayloadEnvelopeV6,
9    ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV3, ExecutionPayloadV4,
10    ForkchoiceState, ForkchoiceUpdated, PayloadAttributes, PayloadId, PayloadStatus,
11};
12use alloy_transport::TransportResult;
13
14/// Extension trait that gives access to engine API RPC methods.
15#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
16#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
17pub trait EngineApi<N>: Send + Sync {
18    /// Sends the given payload to the execution layer client, as specified for the Paris fork.
19    ///
20    /// Caution: This should not accept the `withdrawals` field
21    ///
22    /// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/paris.md#engine_newpayloadv1>
23    async fn new_payload_v1(&self, payload: ExecutionPayloadV1) -> TransportResult<PayloadStatus>;
24
25    /// Sends the given payload to the execution layer client, as specified for the Shanghai fork.
26    ///
27    /// See also <https://github.com/ethereum/execution-apis/blob/584905270d8ad665718058060267061ecfd79ca5/src/engine/shanghai.md#engine_newpayloadv2>
28    async fn new_payload_v2(
29        &self,
30        payload: ExecutionPayloadInputV2,
31    ) -> TransportResult<PayloadStatus>;
32
33    /// Sends the given payload to the execution layer client, as specified for the Cancun fork.
34    ///
35    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/cancun.md#engine_newpayloadv3>
36    async fn new_payload_v3(
37        &self,
38        payload: ExecutionPayloadV3,
39        versioned_hashes: Vec<B256>,
40        parent_beacon_block_root: B256,
41    ) -> TransportResult<PayloadStatus>;
42
43    /// Sends the given payload to the execution layer client, as specified for the Prague fork.
44    ///
45    /// See also <https://github.com/ethereum/execution-apis/blob/03911ffc053b8b806123f1fc237184b0092a485a/src/engine/prague.md#engine_newpayloadv4>
46    async fn new_payload_v4(
47        &self,
48        payload: ExecutionPayloadV3,
49        versioned_hashes: Vec<B256>,
50        parent_beacon_block_root: B256,
51        execution_requests: Vec<Bytes>,
52    ) -> TransportResult<PayloadStatus>;
53
54    /// Sends the given payload to the execution layer client, as specified for the Prague fork.
55    ///
56    /// This is a variant of [`Self::new_payload_v4`] that accepts [`RequestsOrHash`] for the
57    /// execution requests, allowing either the full requests or just a precomputed hash.
58    ///
59    /// See also <https://github.com/ethereum/execution-apis/blob/03911ffc053b8b806123f1fc237184b0092a485a/src/engine/prague.md#engine_newpayloadv4>
60    async fn new_payload_v4_requests(
61        &self,
62        payload: ExecutionPayloadV3,
63        versioned_hashes: Vec<B256>,
64        parent_beacon_block_root: B256,
65        execution_requests: RequestsOrHash,
66    ) -> TransportResult<PayloadStatus>;
67
68    /// Sends the given payload to the execution layer client, as specified for the Amsterdam fork.
69    ///
70    /// See also <https://github.com/ethereum/execution-apis/blob/7b4d9f62a3fe62b9b8dcb355f1c5a38b5ff084f6/src/engine/amsterdam.md#engine_newpayloadv5>
71    async fn new_payload_v5(
72        &self,
73        payload: ExecutionPayloadV4,
74        versioned_hashes: Vec<B256>,
75        parent_beacon_block_root: B256,
76        execution_requests: RequestsOrHash,
77    ) -> TransportResult<PayloadStatus>;
78
79    /// Updates the execution layer client with the given fork choice, as specified for the Paris
80    /// fork.
81    ///
82    /// Caution: This should not accept the `withdrawals` field in the payload attributes.
83    ///
84    /// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/paris.md#engine_forkchoiceupdatedv1>
85    async fn fork_choice_updated_v1(
86        &self,
87        fork_choice_state: ForkchoiceState,
88        payload_attributes: Option<PayloadAttributes>,
89    ) -> TransportResult<ForkchoiceUpdated>;
90
91    /// Updates the execution layer client with the given fork choice, as specified for the Shanghai
92    /// fork.
93    ///
94    /// Caution: This should not accept the `parentBeaconBlockRoot` field in the payload attributes.
95    ///
96    /// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/shanghai.md#engine_forkchoiceupdatedv2>
97    async fn fork_choice_updated_v2(
98        &self,
99        fork_choice_state: ForkchoiceState,
100        payload_attributes: Option<PayloadAttributes>,
101    ) -> TransportResult<ForkchoiceUpdated>;
102
103    /// Updates the execution layer client with the given fork choice, as specified for the Cancun
104    /// fork.
105    ///
106    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/cancun.md#engine_forkchoiceupdatedv3>
107    async fn fork_choice_updated_v3(
108        &self,
109        fork_choice_state: ForkchoiceState,
110        payload_attributes: Option<PayloadAttributes>,
111    ) -> TransportResult<ForkchoiceUpdated>;
112
113    /// Updates the execution layer client with the given fork choice, as specified for the
114    /// Amsterdam fork.
115    ///
116    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/amsterdam.md#engine_forkchoiceupdatedv4>
117    async fn fork_choice_updated_v4(
118        &self,
119        fork_choice_state: ForkchoiceState,
120        payload_attributes: Option<PayloadAttributes>,
121    ) -> TransportResult<ForkchoiceUpdated>;
122
123    /// Retrieves an execution payload from a previously started build process, as specified for the
124    /// Paris fork.
125    ///
126    /// Caution: This should not return the `withdrawals` field
127    ///
128    /// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/paris.md#engine_getpayloadv1>
129    ///
130    /// Note:
131    /// > Provider software MAY stop the corresponding build process after serving this call.
132    async fn get_payload_v1(&self, payload_id: PayloadId) -> TransportResult<ExecutionPayloadV1>;
133
134    /// Retrieves an execution payload from a previously started build process, as specified for the
135    /// Shanghai fork.
136    ///
137    /// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/shanghai.md#engine_getpayloadv2>
138    ///
139    /// Note:
140    /// > Provider software MAY stop the corresponding build process after serving this call.
141    async fn get_payload_v2(
142        &self,
143        payload_id: PayloadId,
144    ) -> TransportResult<ExecutionPayloadEnvelopeV2>;
145
146    /// Retrieves an execution payload from a previously started build process, as specified for the
147    /// Cancun fork.
148    ///
149    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/cancun.md#engine_getpayloadv3>
150    ///
151    /// Note:
152    /// > Provider software MAY stop the corresponding build process after serving this call.
153    async fn get_payload_v3(
154        &self,
155        payload_id: PayloadId,
156    ) -> TransportResult<ExecutionPayloadEnvelopeV3>;
157
158    /// Returns the most recent version of the payload that is available in the corresponding
159    /// payload build process at the time of receiving this call.
160    ///
161    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/prague.md#engine_getpayloadv4>
162    ///
163    /// Note:
164    /// > Provider software MAY stop the corresponding build process after serving this call.
165    async fn get_payload_v4(
166        &self,
167        payload_id: PayloadId,
168    ) -> TransportResult<ExecutionPayloadEnvelopeV4>;
169
170    /// Returns the most recent version of the payload that is available in the corresponding
171    /// payload build process at the time of receiving this call.
172    ///
173    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/osaka.md#engine_getpayloadv5>
174    ///
175    /// Note:
176    /// > Provider software MAY stop the corresponding build process after serving this call.
177    async fn get_payload_v5(
178        &self,
179        payload_id: PayloadId,
180    ) -> TransportResult<ExecutionPayloadEnvelopeV5>;
181
182    /// Returns the most recent version of the payload that is available in the corresponding
183    /// payload build process at the time of receiving this call.
184    ///
185    /// See also <https://github.com/ethereum/execution-apis/blob/7b4d9f62a3fe62b9b8dcb355f1c5a38b5ff084f6/src/engine/amsterdam.md#engine_getpayloadv6>
186    ///
187    /// Note:
188    /// > Provider software MAY stop the corresponding build process after serving this call.
189    async fn get_payload_v6(
190        &self,
191        payload_id: PayloadId,
192    ) -> TransportResult<ExecutionPayloadEnvelopeV6>;
193
194    /// Returns the execution payload bodies by the given hash.
195    ///
196    /// See also <https://github.com/ethereum/execution-apis/blob/6452a6b194d7db269bf1dbd087a267251d3cc7f8/src/engine/shanghai.md#engine_getpayloadbodiesbyhashv1>
197    async fn get_payload_bodies_by_hash_v1(
198        &self,
199        block_hashes: Vec<BlockHash>,
200    ) -> TransportResult<ExecutionPayloadBodiesV1>;
201
202    /// Returns the execution payload bodies by the range starting at `start`, containing `count`
203    /// blocks.
204    ///
205    /// WARNING: This method is associated with the BeaconBlocksByRange message in the consensus
206    /// layer p2p specification, meaning the input should be treated as untrusted or potentially
207    /// adversarial.
208    ///
209    /// Implementers should take care when acting on the input to this method, specifically
210    /// ensuring that the range is limited properly, and that the range boundaries are computed
211    /// correctly and without panics.
212    ///
213    /// See also <https://github.com/ethereum/execution-apis/blob/6452a6b194d7db269bf1dbd087a267251d3cc7f8/src/engine/shanghai.md#engine_getpayloadbodiesbyrangev1>
214    async fn get_payload_bodies_by_range_v1(
215        &self,
216        start: u64,
217        count: u64,
218    ) -> TransportResult<ExecutionPayloadBodiesV1>;
219
220    /// Returns the execution payload bodies by the given hash.
221    ///
222    /// This is a V2 variant that includes the `blockAccessList` field per EIP-7928.
223    ///
224    /// See also <https://eips.ethereum.org/EIPS/eip-7928>
225    async fn get_payload_bodies_by_hash_v2(
226        &self,
227        block_hashes: Vec<BlockHash>,
228    ) -> TransportResult<ExecutionPayloadBodiesV2>;
229
230    /// Returns the execution payload bodies by the range starting at `start`, containing `count`
231    /// blocks.
232    ///
233    /// This is a V2 variant that includes the `blockAccessList` field per EIP-7928.
234    ///
235    /// WARNING: This method is associated with the BeaconBlocksByRange message in the consensus
236    /// layer p2p specification, meaning the input should be treated as untrusted or potentially
237    /// adversarial.
238    ///
239    /// Implementers should take care when acting on the input to this method, specifically
240    /// ensuring that the range is limited properly, and that the range boundaries are computed
241    /// correctly and without panics.
242    ///
243    /// See also <https://eips.ethereum.org/EIPS/eip-7928>
244    async fn get_payload_bodies_by_range_v2(
245        &self,
246        start: u64,
247        count: u64,
248    ) -> TransportResult<ExecutionPayloadBodiesV2>;
249
250    /// Returns the requested blobs and their associated proofs for the given versioned hashes.
251    ///
252    /// Returns `None` for any blob that is not available.
253    ///
254    /// See also <https://github.com/ethereum/execution-apis/pull/559>
255    async fn get_blobs_v1(
256        &self,
257        versioned_hashes: Vec<B256>,
258    ) -> TransportResult<Vec<Option<BlobAndProofV1>>>;
259
260    /// Returns the requested blobs and their associated cell proofs for the given versioned
261    /// hashes.
262    ///
263    /// Returns `None` for any blob that is not available.
264    ///
265    /// See also <https://github.com/ethereum/execution-apis/pull/630>
266    async fn get_blobs_v2(
267        &self,
268        versioned_hashes: Vec<B256>,
269    ) -> TransportResult<Vec<Option<BlobAndProofV2>>>;
270
271    /// Returns the execution client version information.
272    ///
273    /// Note:
274    /// > The `client_version` parameter identifies the consensus client.
275    ///
276    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/identification.md#engine_getclientversionv1>
277    async fn get_client_version_v1(
278        &self,
279        client_version: ClientVersionV1,
280    ) -> TransportResult<Vec<ClientVersionV1>>;
281
282    /// Returns the list of Engine API methods supported by the execution layer client software.
283    ///
284    /// See also <https://github.com/ethereum/execution-apis/blob/6452a6b194d7db269bf1dbd087a267251d3cc7f8/src/engine/common.md#capabilities>
285    async fn exchange_capabilities(
286        &self,
287        capabilities: Vec<String>,
288    ) -> TransportResult<Vec<String>>;
289}
290
291#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
292#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
293impl<N, P> EngineApi<N> for P
294where
295    N: Network,
296    P: Provider<N>,
297{
298    async fn new_payload_v1(&self, payload: ExecutionPayloadV1) -> TransportResult<PayloadStatus> {
299        self.client().request("engine_newPayloadV1", (payload,)).await
300    }
301
302    async fn new_payload_v2(
303        &self,
304        payload: ExecutionPayloadInputV2,
305    ) -> TransportResult<PayloadStatus> {
306        self.client().request("engine_newPayloadV2", (payload,)).await
307    }
308
309    async fn new_payload_v3(
310        &self,
311        payload: ExecutionPayloadV3,
312        versioned_hashes: Vec<B256>,
313        parent_beacon_block_root: B256,
314    ) -> TransportResult<PayloadStatus> {
315        self.client()
316            .request("engine_newPayloadV3", (payload, versioned_hashes, parent_beacon_block_root))
317            .await
318    }
319
320    async fn new_payload_v4(
321        &self,
322        payload: ExecutionPayloadV3,
323        versioned_hashes: Vec<B256>,
324        parent_beacon_block_root: B256,
325        execution_requests: Vec<Bytes>,
326    ) -> TransportResult<PayloadStatus> {
327        self.client()
328            .request(
329                "engine_newPayloadV4",
330                (payload, versioned_hashes, parent_beacon_block_root, execution_requests),
331            )
332            .await
333    }
334
335    async fn new_payload_v4_requests(
336        &self,
337        payload: ExecutionPayloadV3,
338        versioned_hashes: Vec<B256>,
339        parent_beacon_block_root: B256,
340        execution_requests: RequestsOrHash,
341    ) -> TransportResult<PayloadStatus> {
342        self.client()
343            .request(
344                "engine_newPayloadV4",
345                (payload, versioned_hashes, parent_beacon_block_root, execution_requests),
346            )
347            .await
348    }
349
350    async fn new_payload_v5(
351        &self,
352        payload: ExecutionPayloadV4,
353        versioned_hashes: Vec<B256>,
354        parent_beacon_block_root: B256,
355        execution_requests: RequestsOrHash,
356    ) -> TransportResult<PayloadStatus> {
357        self.client()
358            .request(
359                "engine_newPayloadV5",
360                (payload, versioned_hashes, parent_beacon_block_root, execution_requests),
361            )
362            .await
363    }
364
365    async fn fork_choice_updated_v1(
366        &self,
367        fork_choice_state: ForkchoiceState,
368        payload_attributes: Option<PayloadAttributes>,
369    ) -> TransportResult<ForkchoiceUpdated> {
370        self.client()
371            .request("engine_forkchoiceUpdatedV1", (fork_choice_state, payload_attributes))
372            .await
373    }
374
375    async fn fork_choice_updated_v2(
376        &self,
377        fork_choice_state: ForkchoiceState,
378        payload_attributes: Option<PayloadAttributes>,
379    ) -> TransportResult<ForkchoiceUpdated> {
380        self.client()
381            .request("engine_forkchoiceUpdatedV2", (fork_choice_state, payload_attributes))
382            .await
383    }
384
385    async fn fork_choice_updated_v3(
386        &self,
387        fork_choice_state: ForkchoiceState,
388        payload_attributes: Option<PayloadAttributes>,
389    ) -> TransportResult<ForkchoiceUpdated> {
390        self.client()
391            .request("engine_forkchoiceUpdatedV3", (fork_choice_state, payload_attributes))
392            .await
393    }
394
395    async fn fork_choice_updated_v4(
396        &self,
397        fork_choice_state: ForkchoiceState,
398        payload_attributes: Option<PayloadAttributes>,
399    ) -> TransportResult<ForkchoiceUpdated> {
400        self.client()
401            .request("engine_forkchoiceUpdatedV4", (fork_choice_state, payload_attributes))
402            .await
403    }
404
405    async fn get_payload_v1(&self, payload_id: PayloadId) -> TransportResult<ExecutionPayloadV1> {
406        self.client().request("engine_getPayloadV1", (payload_id,)).await
407    }
408
409    async fn get_payload_v2(
410        &self,
411        payload_id: PayloadId,
412    ) -> TransportResult<ExecutionPayloadEnvelopeV2> {
413        self.client().request("engine_getPayloadV2", (payload_id,)).await
414    }
415
416    async fn get_payload_v3(
417        &self,
418        payload_id: PayloadId,
419    ) -> TransportResult<ExecutionPayloadEnvelopeV3> {
420        self.client().request("engine_getPayloadV3", (payload_id,)).await
421    }
422
423    async fn get_payload_v4(
424        &self,
425        payload_id: PayloadId,
426    ) -> TransportResult<ExecutionPayloadEnvelopeV4> {
427        self.client().request("engine_getPayloadV4", (payload_id,)).await
428    }
429
430    async fn get_payload_v5(
431        &self,
432        payload_id: PayloadId,
433    ) -> TransportResult<ExecutionPayloadEnvelopeV5> {
434        self.client().request("engine_getPayloadV5", (payload_id,)).await
435    }
436
437    async fn get_payload_v6(
438        &self,
439        payload_id: PayloadId,
440    ) -> TransportResult<ExecutionPayloadEnvelopeV6> {
441        self.client().request("engine_getPayloadV6", (payload_id,)).await
442    }
443
444    async fn get_payload_bodies_by_hash_v1(
445        &self,
446        block_hashes: Vec<BlockHash>,
447    ) -> TransportResult<ExecutionPayloadBodiesV1> {
448        self.client().request("engine_getPayloadBodiesByHashV1", (block_hashes,)).await
449    }
450
451    async fn get_payload_bodies_by_range_v1(
452        &self,
453        start: u64,
454        count: u64,
455    ) -> TransportResult<ExecutionPayloadBodiesV1> {
456        self.client()
457            .request("engine_getPayloadBodiesByRangeV1", (U64::from(start), U64::from(count)))
458            .await
459    }
460
461    async fn get_payload_bodies_by_hash_v2(
462        &self,
463        block_hashes: Vec<BlockHash>,
464    ) -> TransportResult<ExecutionPayloadBodiesV2> {
465        self.client().request("engine_getPayloadBodiesByHashV2", (block_hashes,)).await
466    }
467
468    async fn get_payload_bodies_by_range_v2(
469        &self,
470        start: u64,
471        count: u64,
472    ) -> TransportResult<ExecutionPayloadBodiesV2> {
473        self.client()
474            .request("engine_getPayloadBodiesByRangeV2", (U64::from(start), U64::from(count)))
475            .await
476    }
477
478    async fn get_blobs_v1(
479        &self,
480        versioned_hashes: Vec<B256>,
481    ) -> TransportResult<Vec<Option<BlobAndProofV1>>> {
482        self.client().request("engine_getBlobsV1", (versioned_hashes,)).await
483    }
484
485    async fn get_blobs_v2(
486        &self,
487        versioned_hashes: Vec<B256>,
488    ) -> TransportResult<Vec<Option<BlobAndProofV2>>> {
489        self.client().request("engine_getBlobsV2", (versioned_hashes,)).await
490    }
491
492    async fn get_client_version_v1(
493        &self,
494        client_version: ClientVersionV1,
495    ) -> TransportResult<Vec<ClientVersionV1>> {
496        self.client().request("engine_getClientVersionV1", (client_version,)).await
497    }
498
499    async fn exchange_capabilities(
500        &self,
501        capabilities: Vec<String>,
502    ) -> TransportResult<Vec<String>> {
503        self.client().request("engine_exchangeCapabilities", (capabilities,)).await
504    }
505}