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    /// Retrieves an execution payload from a previously started build process, as specified for the
114    /// Paris fork.
115    ///
116    /// Caution: This should not return the `withdrawals` field
117    ///
118    /// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/paris.md#engine_getpayloadv1>
119    ///
120    /// Note:
121    /// > Provider software MAY stop the corresponding build process after serving this call.
122    async fn get_payload_v1(&self, payload_id: PayloadId) -> TransportResult<ExecutionPayloadV1>;
123
124    /// Retrieves an execution payload from a previously started build process, as specified for the
125    /// Shanghai fork.
126    ///
127    /// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/shanghai.md#engine_getpayloadv2>
128    ///
129    /// Note:
130    /// > Provider software MAY stop the corresponding build process after serving this call.
131    async fn get_payload_v2(
132        &self,
133        payload_id: PayloadId,
134    ) -> TransportResult<ExecutionPayloadEnvelopeV2>;
135
136    /// Retrieves an execution payload from a previously started build process, as specified for the
137    /// Cancun fork.
138    ///
139    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/cancun.md#engine_getpayloadv3>
140    ///
141    /// Note:
142    /// > Provider software MAY stop the corresponding build process after serving this call.
143    async fn get_payload_v3(
144        &self,
145        payload_id: PayloadId,
146    ) -> TransportResult<ExecutionPayloadEnvelopeV3>;
147
148    /// Returns the most recent version of the payload that is available in the corresponding
149    /// payload build process at the time of receiving this call.
150    ///
151    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/prague.md#engine_getpayloadv4>
152    ///
153    /// Note:
154    /// > Provider software MAY stop the corresponding build process after serving this call.
155    async fn get_payload_v4(
156        &self,
157        payload_id: PayloadId,
158    ) -> TransportResult<ExecutionPayloadEnvelopeV4>;
159
160    /// Returns the most recent version of the payload that is available in the corresponding
161    /// payload build process at the time of receiving this call.
162    ///
163    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/osaka.md#engine_getpayloadv5>
164    ///
165    /// Note:
166    /// > Provider software MAY stop the corresponding build process after serving this call.
167    async fn get_payload_v5(
168        &self,
169        payload_id: PayloadId,
170    ) -> TransportResult<ExecutionPayloadEnvelopeV5>;
171
172    /// Returns the most recent version of the payload that is available in the corresponding
173    /// payload build process at the time of receiving this call.
174    ///
175    /// See also <https://github.com/ethereum/execution-apis/blob/7b4d9f62a3fe62b9b8dcb355f1c5a38b5ff084f6/src/engine/amsterdam.md#engine_getpayloadv6>
176    ///
177    /// Note:
178    /// > Provider software MAY stop the corresponding build process after serving this call.
179    async fn get_payload_v6(
180        &self,
181        payload_id: PayloadId,
182    ) -> TransportResult<ExecutionPayloadEnvelopeV6>;
183
184    /// Returns the execution payload bodies by the given hash.
185    ///
186    /// See also <https://github.com/ethereum/execution-apis/blob/6452a6b194d7db269bf1dbd087a267251d3cc7f8/src/engine/shanghai.md#engine_getpayloadbodiesbyhashv1>
187    async fn get_payload_bodies_by_hash_v1(
188        &self,
189        block_hashes: Vec<BlockHash>,
190    ) -> TransportResult<ExecutionPayloadBodiesV1>;
191
192    /// Returns the execution payload bodies by the range starting at `start`, containing `count`
193    /// blocks.
194    ///
195    /// WARNING: This method is associated with the BeaconBlocksByRange message in the consensus
196    /// layer p2p specification, meaning the input should be treated as untrusted or potentially
197    /// adversarial.
198    ///
199    /// Implementers should take care when acting on the input to this method, specifically
200    /// ensuring that the range is limited properly, and that the range boundaries are computed
201    /// correctly and without panics.
202    ///
203    /// See also <https://github.com/ethereum/execution-apis/blob/6452a6b194d7db269bf1dbd087a267251d3cc7f8/src/engine/shanghai.md#engine_getpayloadbodiesbyrangev1>
204    async fn get_payload_bodies_by_range_v1(
205        &self,
206        start: u64,
207        count: u64,
208    ) -> TransportResult<ExecutionPayloadBodiesV1>;
209
210    /// Returns the execution payload bodies by the given hash.
211    ///
212    /// This is a V2 variant that includes the `blockAccessList` field per EIP-7928.
213    ///
214    /// See also <https://eips.ethereum.org/EIPS/eip-7928>
215    async fn get_payload_bodies_by_hash_v2(
216        &self,
217        block_hashes: Vec<BlockHash>,
218    ) -> TransportResult<ExecutionPayloadBodiesV2>;
219
220    /// Returns the execution payload bodies by the range starting at `start`, containing `count`
221    /// blocks.
222    ///
223    /// This is a V2 variant that includes the `blockAccessList` field per EIP-7928.
224    ///
225    /// WARNING: This method is associated with the BeaconBlocksByRange message in the consensus
226    /// layer p2p specification, meaning the input should be treated as untrusted or potentially
227    /// adversarial.
228    ///
229    /// Implementers should take care when acting on the input to this method, specifically
230    /// ensuring that the range is limited properly, and that the range boundaries are computed
231    /// correctly and without panics.
232    ///
233    /// See also <https://eips.ethereum.org/EIPS/eip-7928>
234    async fn get_payload_bodies_by_range_v2(
235        &self,
236        start: u64,
237        count: u64,
238    ) -> TransportResult<ExecutionPayloadBodiesV2>;
239
240    /// Returns the Block Access Lists for the given block hashes.
241    ///
242    /// See also <https://eips.ethereum.org/EIPS/eip-7928>
243    async fn get_bals_by_hash_v1(
244        &self,
245        block_hashes: Vec<BlockHash>,
246    ) -> TransportResult<Vec<Bytes>>;
247
248    /// Returns the Block Access Lists for the given block range.
249    ///
250    /// See also <https://eips.ethereum.org/EIPS/eip-7928>
251    async fn get_bals_by_range_v1(&self, start: u64, count: u64) -> TransportResult<Vec<Bytes>>;
252
253    /// Returns the requested blobs and their associated proofs for the given versioned hashes.
254    ///
255    /// Returns `None` for any blob that is not available.
256    ///
257    /// See also <https://github.com/ethereum/execution-apis/pull/559>
258    async fn get_blobs_v1(
259        &self,
260        versioned_hashes: Vec<B256>,
261    ) -> TransportResult<Vec<Option<BlobAndProofV1>>>;
262
263    /// Returns the requested blobs and their associated cell proofs for the given versioned
264    /// hashes.
265    ///
266    /// Returns `None` for any blob that is not available.
267    ///
268    /// See also <https://github.com/ethereum/execution-apis/pull/630>
269    async fn get_blobs_v2(
270        &self,
271        versioned_hashes: Vec<B256>,
272    ) -> TransportResult<Vec<Option<BlobAndProofV2>>>;
273
274    /// Returns the execution client version information.
275    ///
276    /// Note:
277    /// > The `client_version` parameter identifies the consensus client.
278    ///
279    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/identification.md#engine_getclientversionv1>
280    async fn get_client_version_v1(
281        &self,
282        client_version: ClientVersionV1,
283    ) -> TransportResult<Vec<ClientVersionV1>>;
284
285    /// Returns the list of Engine API methods supported by the execution layer client software.
286    ///
287    /// See also <https://github.com/ethereum/execution-apis/blob/6452a6b194d7db269bf1dbd087a267251d3cc7f8/src/engine/common.md#capabilities>
288    async fn exchange_capabilities(
289        &self,
290        capabilities: Vec<String>,
291    ) -> TransportResult<Vec<String>>;
292}
293
294#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
295#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
296impl<N, P> EngineApi<N> for P
297where
298    N: Network,
299    P: Provider<N>,
300{
301    async fn new_payload_v1(&self, payload: ExecutionPayloadV1) -> TransportResult<PayloadStatus> {
302        self.client().request("engine_newPayloadV1", (payload,)).await
303    }
304
305    async fn new_payload_v2(
306        &self,
307        payload: ExecutionPayloadInputV2,
308    ) -> TransportResult<PayloadStatus> {
309        self.client().request("engine_newPayloadV2", (payload,)).await
310    }
311
312    async fn new_payload_v3(
313        &self,
314        payload: ExecutionPayloadV3,
315        versioned_hashes: Vec<B256>,
316        parent_beacon_block_root: B256,
317    ) -> TransportResult<PayloadStatus> {
318        self.client()
319            .request("engine_newPayloadV3", (payload, versioned_hashes, parent_beacon_block_root))
320            .await
321    }
322
323    async fn new_payload_v4(
324        &self,
325        payload: ExecutionPayloadV3,
326        versioned_hashes: Vec<B256>,
327        parent_beacon_block_root: B256,
328        execution_requests: Vec<Bytes>,
329    ) -> TransportResult<PayloadStatus> {
330        self.client()
331            .request(
332                "engine_newPayloadV4",
333                (payload, versioned_hashes, parent_beacon_block_root, execution_requests),
334            )
335            .await
336    }
337
338    async fn new_payload_v4_requests(
339        &self,
340        payload: ExecutionPayloadV3,
341        versioned_hashes: Vec<B256>,
342        parent_beacon_block_root: B256,
343        execution_requests: RequestsOrHash,
344    ) -> TransportResult<PayloadStatus> {
345        self.client()
346            .request(
347                "engine_newPayloadV4",
348                (payload, versioned_hashes, parent_beacon_block_root, execution_requests),
349            )
350            .await
351    }
352
353    async fn new_payload_v5(
354        &self,
355        payload: ExecutionPayloadV4,
356        versioned_hashes: Vec<B256>,
357        parent_beacon_block_root: B256,
358        execution_requests: RequestsOrHash,
359    ) -> TransportResult<PayloadStatus> {
360        self.client()
361            .request(
362                "engine_newPayloadV5",
363                (payload, versioned_hashes, parent_beacon_block_root, execution_requests),
364            )
365            .await
366    }
367
368    async fn fork_choice_updated_v1(
369        &self,
370        fork_choice_state: ForkchoiceState,
371        payload_attributes: Option<PayloadAttributes>,
372    ) -> TransportResult<ForkchoiceUpdated> {
373        self.client()
374            .request("engine_forkchoiceUpdatedV1", (fork_choice_state, payload_attributes))
375            .await
376    }
377
378    async fn fork_choice_updated_v2(
379        &self,
380        fork_choice_state: ForkchoiceState,
381        payload_attributes: Option<PayloadAttributes>,
382    ) -> TransportResult<ForkchoiceUpdated> {
383        self.client()
384            .request("engine_forkchoiceUpdatedV2", (fork_choice_state, payload_attributes))
385            .await
386    }
387
388    async fn fork_choice_updated_v3(
389        &self,
390        fork_choice_state: ForkchoiceState,
391        payload_attributes: Option<PayloadAttributes>,
392    ) -> TransportResult<ForkchoiceUpdated> {
393        self.client()
394            .request("engine_forkchoiceUpdatedV3", (fork_choice_state, payload_attributes))
395            .await
396    }
397
398    async fn get_payload_v1(&self, payload_id: PayloadId) -> TransportResult<ExecutionPayloadV1> {
399        self.client().request("engine_getPayloadV1", (payload_id,)).await
400    }
401
402    async fn get_payload_v2(
403        &self,
404        payload_id: PayloadId,
405    ) -> TransportResult<ExecutionPayloadEnvelopeV2> {
406        self.client().request("engine_getPayloadV2", (payload_id,)).await
407    }
408
409    async fn get_payload_v3(
410        &self,
411        payload_id: PayloadId,
412    ) -> TransportResult<ExecutionPayloadEnvelopeV3> {
413        self.client().request("engine_getPayloadV3", (payload_id,)).await
414    }
415
416    async fn get_payload_v4(
417        &self,
418        payload_id: PayloadId,
419    ) -> TransportResult<ExecutionPayloadEnvelopeV4> {
420        self.client().request("engine_getPayloadV4", (payload_id,)).await
421    }
422
423    async fn get_payload_v5(
424        &self,
425        payload_id: PayloadId,
426    ) -> TransportResult<ExecutionPayloadEnvelopeV5> {
427        self.client().request("engine_getPayloadV5", (payload_id,)).await
428    }
429
430    async fn get_payload_v6(
431        &self,
432        payload_id: PayloadId,
433    ) -> TransportResult<ExecutionPayloadEnvelopeV6> {
434        self.client().request("engine_getPayloadV6", (payload_id,)).await
435    }
436
437    async fn get_payload_bodies_by_hash_v1(
438        &self,
439        block_hashes: Vec<BlockHash>,
440    ) -> TransportResult<ExecutionPayloadBodiesV1> {
441        self.client().request("engine_getPayloadBodiesByHashV1", (block_hashes,)).await
442    }
443
444    async fn get_payload_bodies_by_range_v1(
445        &self,
446        start: u64,
447        count: u64,
448    ) -> TransportResult<ExecutionPayloadBodiesV1> {
449        self.client()
450            .request("engine_getPayloadBodiesByRangeV1", (U64::from(start), U64::from(count)))
451            .await
452    }
453
454    async fn get_payload_bodies_by_hash_v2(
455        &self,
456        block_hashes: Vec<BlockHash>,
457    ) -> TransportResult<ExecutionPayloadBodiesV2> {
458        self.client().request("engine_getPayloadBodiesByHashV2", (block_hashes,)).await
459    }
460
461    async fn get_payload_bodies_by_range_v2(
462        &self,
463        start: u64,
464        count: u64,
465    ) -> TransportResult<ExecutionPayloadBodiesV2> {
466        self.client()
467            .request("engine_getPayloadBodiesByRangeV2", (U64::from(start), U64::from(count)))
468            .await
469    }
470
471    async fn get_bals_by_hash_v1(
472        &self,
473        block_hashes: Vec<BlockHash>,
474    ) -> TransportResult<Vec<Bytes>> {
475        self.client().request("engine_getBALsByHashV1", (block_hashes,)).await
476    }
477
478    async fn get_bals_by_range_v1(&self, start: u64, count: u64) -> TransportResult<Vec<Bytes>> {
479        self.client().request("engine_getBALsByRangeV1", (U64::from(start), U64::from(count))).await
480    }
481
482    async fn get_blobs_v1(
483        &self,
484        versioned_hashes: Vec<B256>,
485    ) -> TransportResult<Vec<Option<BlobAndProofV1>>> {
486        self.client().request("engine_getBlobsV1", (versioned_hashes,)).await
487    }
488
489    async fn get_blobs_v2(
490        &self,
491        versioned_hashes: Vec<B256>,
492    ) -> TransportResult<Vec<Option<BlobAndProofV2>>> {
493        self.client().request("engine_getBlobsV2", (versioned_hashes,)).await
494    }
495
496    async fn get_client_version_v1(
497        &self,
498        client_version: ClientVersionV1,
499    ) -> TransportResult<Vec<ClientVersionV1>> {
500        self.client().request("engine_getClientVersionV1", (client_version,)).await
501    }
502
503    async fn exchange_capabilities(
504        &self,
505        capabilities: Vec<String>,
506    ) -> TransportResult<Vec<String>> {
507        self.client().request("engine_exchangeCapabilities", (capabilities,)).await
508    }
509}