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