fc_rpc/eth/
execute.rs

1// This file is part of Frontier.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19use std::{cell::RefCell, collections::BTreeMap, sync::Arc};
20
21use ethereum_types::{H160, H256, U256};
22use evm::{ExitError, ExitReason};
23use jsonrpsee::{core::RpcResult, types::error::CALL_EXECUTION_FAILED_CODE};
24use scale_codec::{Decode, Encode};
25// Substrate
26use sc_client_api::backend::{Backend, StorageProvider};
27use sc_transaction_pool_api::TransactionPool;
28use sp_api::{ApiExt, CallApiAt, CallApiAtParams, CallContext, ProvideRuntimeApi};
29use sp_block_builder::BlockBuilder as BlockBuilderApi;
30use sp_blockchain::HeaderBackend;
31use sp_externalities::Extensions;
32use sp_inherents::CreateInherentDataProviders;
33use sp_io::hashing::{blake2_128, twox_128};
34use sp_runtime::{
35	traits::{Block as BlockT, HashingFor},
36	DispatchError, SaturatedConversion,
37};
38use sp_state_machine::OverlayedChanges;
39// Frontier
40use fc_rpc_core::types::*;
41use fp_evm::{ExecutionInfo, ExecutionInfoV2};
42use fp_rpc::{EthereumRuntimeRPCApi, RuntimeStorageOverride};
43use fp_storage::constants::{EVM_ACCOUNT_CODES, EVM_ACCOUNT_STORAGES, PALLET_EVM};
44
45use crate::{
46	eth::{Eth, EthConfig},
47	frontier_backend_client, internal_err,
48};
49
50/// Allow to adapt a request for `estimate_gas`.
51/// Can be used to estimate gas of some contracts using a different function
52/// in the case the normal gas estimation doesn't work.
53///
54/// Example: a precompile that tries to do a subcall but succeeds regardless of the
55/// success of the subcall. The gas estimation will thus optimize the gas limit down
56/// to the minimum, while we want to estimate a gas limit that will allow the subcall to
57/// have enough gas to succeed.
58pub trait EstimateGasAdapter {
59	fn adapt_request(request: TransactionRequest) -> TransactionRequest;
60}
61
62impl EstimateGasAdapter for () {
63	fn adapt_request(request: TransactionRequest) -> TransactionRequest {
64		request
65	}
66}
67
68impl<B, C, P, CT, BE, CIDP, EC> Eth<B, C, P, CT, BE, CIDP, EC>
69where
70	B: BlockT,
71	C: CallApiAt<B> + ProvideRuntimeApi<B>,
72	C::Api: BlockBuilderApi<B> + EthereumRuntimeRPCApi<B>,
73	C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
74	BE: Backend<B> + 'static,
75	CIDP: CreateInherentDataProviders<B, ()> + Send + 'static,
76	EC: EthConfig<B, C>,
77	P: TransactionPool<Block = B, Hash = B::Hash> + 'static,
78{
79	pub async fn call(
80		&self,
81		request: TransactionRequest,
82		number_or_hash: Option<BlockNumberOrHash>,
83		state_overrides: Option<BTreeMap<H160, CallStateOverride>>,
84	) -> RpcResult<Bytes> {
85		let TransactionRequest {
86			from,
87			to,
88			gas_price,
89			max_fee_per_gas,
90			max_priority_fee_per_gas,
91			gas,
92			value,
93			data,
94			nonce,
95			access_list,
96			authorization_list,
97			..
98		} = request;
99
100		let (gas_price, max_fee_per_gas, max_priority_fee_per_gas) = {
101			let details = fee_details(gas_price, max_fee_per_gas, max_priority_fee_per_gas)?;
102			(
103				details.gas_price,
104				details.max_fee_per_gas,
105				details.max_priority_fee_per_gas,
106			)
107		};
108
109		let (substrate_hash, mut api) = match frontier_backend_client::native_block_id::<B, C>(
110			self.client.as_ref(),
111			self.backend.as_ref(),
112			number_or_hash,
113		)
114		.await?
115		{
116			Some(id) => {
117				let hash = self.client.expect_block_hash_from_id(&id).map_err(|_| {
118					crate::err(CALL_EXECUTION_FAILED_CODE, "header not found", None)
119				})?;
120				(hash, self.client.runtime_api())
121			}
122			None => {
123				// Not mapped in the db, assume pending.
124				let (hash, api) = self.pending_runtime_api().await.map_err(|err| {
125					internal_err(format!("Create pending runtime api error: {err}"))
126				})?;
127				(hash, api)
128			}
129		};
130
131		// Enable proof size recording
132		api.record_proof();
133		let recorder: sp_trie::recorder::Recorder<HashingFor<B>> = Default::default();
134		let ext = sp_trie::proof_size_extension::ProofSizeExt::new(recorder.clone());
135		api.register_extension(ext);
136
137		let api_version = if let Ok(Some(api_version)) =
138			api.api_version::<dyn EthereumRuntimeRPCApi<B>>(substrate_hash)
139		{
140			api_version
141		} else {
142			return Err(internal_err("failed to retrieve Runtime Api version"));
143		};
144
145		let block = if api_version > 1 {
146			api.current_block(substrate_hash)
147				.map_err(|err| internal_err(format!("runtime error: {err}")))?
148		} else {
149			#[allow(deprecated)]
150			let legacy_block = api
151				.current_block_before_version_2(substrate_hash)
152				.map_err(|err| internal_err(format!("runtime error: {err}")))?;
153			legacy_block.map(|block| block.into())
154		};
155
156		let block_gas_limit = block
157			.ok_or_else(|| internal_err("block unavailable, cannot query gas limit"))?
158			.header
159			.gas_limit;
160		let max_gas_limit = block_gas_limit * self.execute_gas_limit_multiplier;
161
162		// use given gas limit or query current block's limit
163		let gas_limit = match gas {
164			Some(amount) => {
165				if amount > max_gas_limit {
166					return Err(internal_err(format!(
167						"provided gas limit is too high (can be up to {}x the block gas limit)",
168						self.execute_gas_limit_multiplier
169					)));
170				}
171				amount
172			}
173			// If gas limit is not specified in the request we either use the multiplier if supported
174			// or fallback to the block gas limit.
175			None => match api.gas_limit_multiplier_support(substrate_hash) {
176				Ok(_) => max_gas_limit,
177				_ => block_gas_limit,
178			},
179		};
180
181		let data = data.into_bytes().map(|d| d.into_vec()).unwrap_or_default();
182		match to {
183			Some(to) => {
184				if api_version == 1 {
185					// Legacy pre-london
186					#[allow(deprecated)]
187					let info = api.call_before_version_2(
188						substrate_hash,
189						from.unwrap_or_default(),
190						to,
191						data,
192						value.unwrap_or_default(),
193						gas_limit,
194						gas_price,
195						nonce,
196						false,
197					)
198					.map_err(|err| internal_err(format!("runtime error: {err}")))?
199					.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
200
201					error_on_execution_failure(&info.exit_reason, &info.value)?;
202					Ok(Bytes(info.value))
203				} else if api_version >= 2 && api_version < 4 {
204					// Post-london
205					#[allow(deprecated)]
206					let info = api.call_before_version_4(
207						substrate_hash,
208						from.unwrap_or_default(),
209						to,
210						data,
211						value.unwrap_or_default(),
212						gas_limit,
213						max_fee_per_gas,
214						max_priority_fee_per_gas,
215						nonce,
216						false,
217					)
218					.map_err(|err| internal_err(format!("runtime error: {err}")))?
219					.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
220
221					error_on_execution_failure(&info.exit_reason, &info.value)?;
222					Ok(Bytes(info.value))
223				} else if api_version == 4 || api_version == 5 {
224					// Post-london + access list support
225					let encoded_params = Encode::encode(&(
226						&from.unwrap_or_default(),
227						&to,
228						&data,
229						&value.unwrap_or_default(),
230						&gas_limit,
231						&max_fee_per_gas,
232						&max_priority_fee_per_gas,
233						&nonce,
234						&false,
235						&Some(
236							access_list
237								.unwrap_or_default()
238								.into_iter()
239								.map(|item| (item.address, item.storage_keys))
240								.collect::<Vec<(sp_core::H160, Vec<H256>)>>(),
241						),
242					));
243					let overlayed_changes = self.create_overrides_overlay(
244						substrate_hash,
245						api_version,
246						state_overrides,
247					)?;
248
249					// Enable proof size recording
250					let recorder: sp_trie::recorder::Recorder<HashingFor<B>> = Default::default();
251					let ext = sp_trie::proof_size_extension::ProofSizeExt::new(recorder.clone());
252					let mut exts = Extensions::new();
253					exts.register(ext);
254
255					let params = CallApiAtParams {
256						at: substrate_hash,
257						function: "EthereumRuntimeRPCApi_call",
258						arguments: encoded_params,
259						overlayed_changes: &RefCell::new(overlayed_changes),
260						call_context: CallContext::Offchain,
261						recorder: &Some(recorder),
262						extensions: &RefCell::new(exts),
263					};
264
265					let value = if api_version == 4 {
266						let info = self
267							.client
268							.call_api_at(params)
269							.and_then(|r| {
270								Result::map_err(
271									<Result<ExecutionInfo::<Vec<u8>>, DispatchError> as Decode>::decode(&mut &r[..]),
272									|error| sp_api::ApiError::FailedToDecodeReturnValue {
273										function: "EthereumRuntimeRPCApi_call",
274										error,
275										raw: r
276									},
277								)
278							})
279							.map_err(|err| internal_err(format!("runtime error: {err}")))?
280							.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
281
282						error_on_execution_failure(&info.exit_reason, &info.value)?;
283						info.value
284					} else if api_version == 5 {
285						let info = self
286							.client
287							.call_api_at(params)
288							.and_then(|r| {
289								Result::map_err(
290									<Result<ExecutionInfoV2::<Vec<u8>>, DispatchError> as Decode>::decode(&mut &r[..]),
291									|error| sp_api::ApiError::FailedToDecodeReturnValue {
292										function: "EthereumRuntimeRPCApi_call",
293										error,
294										raw: r
295									},
296								)
297							})
298							.map_err(|err| internal_err(format!("runtime error: {err}")))?
299							.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
300
301						error_on_execution_failure(&info.exit_reason, &info.value)?;
302						info.value
303					} else {
304						return Err(internal_err(format!(
305							"Unsupported EthereumRuntimeRPCApi version: {api_version}"
306						)));
307					};
308
309					Ok(Bytes(value))
310				} else if api_version == 6 {
311					// Pectra - authorization list support (EIP-7702)
312					let access_list = access_list
313						.unwrap_or_default()
314						.into_iter()
315						.map(|item| (item.address, item.storage_keys))
316						.collect::<Vec<(sp_core::H160, Vec<H256>)>>();
317
318					let encoded_params = Encode::encode(&(
319						&from.unwrap_or_default(),
320						&to,
321						&data,
322						&value.unwrap_or_default(),
323						&gas_limit,
324						&max_fee_per_gas,
325						&max_priority_fee_per_gas,
326						&nonce,
327						&false,
328						&Some(access_list),
329						&authorization_list,
330					));
331					let overlayed_changes = self.create_overrides_overlay(
332						substrate_hash,
333						api_version,
334						state_overrides,
335					)?;
336
337					// Enable proof size recording
338					let recorder: sp_trie::recorder::Recorder<HashingFor<B>> = Default::default();
339					let ext = sp_trie::proof_size_extension::ProofSizeExt::new(recorder.clone());
340					let mut exts = Extensions::new();
341					exts.register(ext);
342
343					let params = CallApiAtParams {
344						at: substrate_hash,
345						function: "EthereumRuntimeRPCApi_call",
346						arguments: encoded_params,
347						overlayed_changes: &RefCell::new(overlayed_changes),
348						call_context: CallContext::Offchain,
349						recorder: &Some(recorder),
350						extensions: &RefCell::new(exts),
351					};
352
353					let info =
354						self.client
355							.call_api_at(params)
356							.and_then(|r| {
357								Result::map_err(
358									<Result<ExecutionInfoV2::<Vec<u8>>, DispatchError> as Decode>::decode(&mut &r[..]),
359									|error| sp_api::ApiError::FailedToDecodeReturnValue {
360										function: "EthereumRuntimeRPCApi_call",
361										error,
362										raw: r
363									},
364								)
365							})
366							.map_err(|err| internal_err(format!("runtime error: {err}")))?
367							.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
368
369					error_on_execution_failure(&info.exit_reason, &info.value)?;
370
371					Ok(Bytes(info.value))
372				} else {
373					Err(internal_err("failed to retrieve Runtime Api version"))
374				}
375			}
376			None => {
377				if api_version == 1 {
378					// Legacy pre-london
379					#[allow(deprecated)]
380					let info = api.create_before_version_2(
381						substrate_hash,
382						from.unwrap_or_default(),
383						data,
384						value.unwrap_or_default(),
385						gas_limit,
386						gas_price,
387						nonce,
388						false,
389					)
390					.map_err(|err| internal_err(format!("runtime error: {err}")))?
391					.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
392
393					error_on_execution_failure(&info.exit_reason, &[])?;
394
395					let code = api
396						.account_code_at(substrate_hash, info.value)
397						.map_err(|err| internal_err(format!("runtime error: {err}")))?;
398					Ok(Bytes(code))
399				} else if api_version >= 2 && api_version < 4 {
400					// Post-london
401					#[allow(deprecated)]
402					let info = api.create_before_version_4(
403						substrate_hash,
404						from.unwrap_or_default(),
405						data,
406						value.unwrap_or_default(),
407						gas_limit,
408						max_fee_per_gas,
409						max_priority_fee_per_gas,
410						nonce,
411						false,
412					)
413					.map_err(|err| internal_err(format!("runtime error: {err}")))?
414					.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
415
416					error_on_execution_failure(&info.exit_reason, &[])?;
417
418					let code = api
419						.account_code_at(substrate_hash, info.value)
420						.map_err(|err| internal_err(format!("runtime error: {err}")))?;
421					Ok(Bytes(code))
422				} else if api_version == 4 {
423					// Post-london + access list support
424					let access_list = access_list.unwrap_or_default();
425					#[allow(deprecated)]
426					let info = api.create_before_version_5(
427						substrate_hash,
428						from.unwrap_or_default(),
429						data,
430						value.unwrap_or_default(),
431						gas_limit,
432						max_fee_per_gas,
433						max_priority_fee_per_gas,
434						nonce,
435						false,
436						Some(
437							access_list
438								.into_iter()
439								.map(|item| (item.address, item.storage_keys))
440								.collect(),
441						),
442					)
443					.map_err(|err| internal_err(format!("runtime error: {err}")))?
444					.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
445
446					error_on_execution_failure(&info.exit_reason, &[])?;
447
448					let code = api
449						.account_code_at(substrate_hash, info.value)
450						.map_err(|err| internal_err(format!("runtime error: {err}")))?;
451					Ok(Bytes(code))
452				} else if api_version == 5 {
453					// Post-london + access list support
454					let access_list = access_list.unwrap_or_default();
455					#[allow(deprecated)]
456					let info = api.create_before_version_6(
457						substrate_hash,
458						from.unwrap_or_default(),
459						data,
460						value.unwrap_or_default(),
461						gas_limit,
462						max_fee_per_gas,
463						max_priority_fee_per_gas,
464						nonce,
465						false,
466						Some(
467							access_list
468								.into_iter()
469								.map(|item| (item.address, item.storage_keys))
470								.collect(),
471						),
472					)
473					.map_err(|err| internal_err(format!("runtime error: {err}")))?
474					.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
475
476					error_on_execution_failure(&info.exit_reason, &[])?;
477
478					let code = api
479						.account_code_at(substrate_hash, info.value)
480						.map_err(|err| internal_err(format!("runtime error: {err}")))?;
481					Ok(Bytes(code))
482				} else if api_version == 6 {
483					// Pectra EIP-7702 support
484					let access_list = access_list.unwrap_or_default();
485					let authorization_list = authorization_list.unwrap_or_default();
486					let info = api
487						.create(
488							substrate_hash,
489							from.unwrap_or_default(),
490							data,
491							value.unwrap_or_default(),
492							gas_limit,
493							max_fee_per_gas,
494							max_priority_fee_per_gas,
495							nonce,
496							false,
497							Some(
498								access_list
499									.into_iter()
500									.map(|item| (item.address, item.storage_keys))
501									.collect(),
502							),
503							Some(authorization_list),
504						)
505						.map_err(|err| internal_err(format!("runtime error: {err}")))?
506						.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
507
508					error_on_execution_failure(&info.exit_reason, &[])?;
509
510					let code = api
511						.account_code_at(substrate_hash, info.value)
512						.map_err(|err| internal_err(format!("runtime error: {err}")))?;
513					Ok(Bytes(code))
514				} else {
515					Err(internal_err("failed to retrieve Runtime Api version"))
516				}
517			}
518		}
519	}
520
521	pub async fn estimate_gas(
522		&self,
523		request: TransactionRequest,
524		number_or_hash: Option<BlockNumberOrHash>,
525	) -> RpcResult<U256> {
526		let client = Arc::clone(&self.client);
527		let block_data_cache = Arc::clone(&self.block_data_cache);
528
529		// Define the lower bound of estimate
530		const MIN_GAS_PER_TX: U256 = U256([21_000, 0, 0, 0]);
531
532		// Get substrate hash and runtime api
533		let (substrate_hash, api) = match frontier_backend_client::native_block_id::<B, C>(
534			self.client.as_ref(),
535			self.backend.as_ref(),
536			number_or_hash,
537		)
538		.await?
539		{
540			Some(id) => {
541				let hash = client.expect_block_hash_from_id(&id).map_err(|_| {
542					crate::err(CALL_EXECUTION_FAILED_CODE, "header not found", None)
543				})?;
544				(hash, client.runtime_api())
545			}
546			None => {
547				// Not mapped in the db, assume pending.
548				let (hash, api) = self.pending_runtime_api().await.map_err(|err| {
549					internal_err(format!("Create pending runtime api error: {err}"))
550				})?;
551				(hash, api)
552			}
553		};
554
555		// Adapt request for gas estimation.
556		let request = EC::EstimateGasAdapter::adapt_request(request);
557
558		// For simple transfer to simple account, return MIN_GAS_PER_TX directly
559		let is_simple_transfer = match &request.data() {
560			None => true,
561			Some(vec) => vec.0.is_empty(),
562		};
563		if is_simple_transfer {
564			if let Some(to) = request.to {
565				let to_code = api
566					.account_code_at(substrate_hash, to)
567					.map_err(|err| internal_err(format!("runtime error: {err}")))?;
568				if to_code.is_empty() {
569					return Ok(MIN_GAS_PER_TX);
570				}
571			}
572		}
573
574		let block_gas_limit = {
575			let block = block_data_cache.current_block(substrate_hash).await;
576			block
577				.ok_or_else(|| internal_err("block unavailable, cannot query gas limit"))?
578				.header
579				.gas_limit
580		};
581
582		let max_gas_limit = block_gas_limit * self.execute_gas_limit_multiplier;
583
584		// Determine the highest possible gas limits
585		let mut highest = match request.gas {
586			Some(amount) => {
587				if amount > max_gas_limit {
588					return Err(internal_err(format!(
589						"provided gas limit is too high (can be up to {}x the block gas limit)",
590						self.execute_gas_limit_multiplier
591					)));
592				}
593				amount
594			}
595			// If gas limit is not specified in the request we either use the multiplier if supported
596			// or fallback to the block gas limit.
597			None => match api.gas_limit_multiplier_support(substrate_hash) {
598				Ok(_) => max_gas_limit,
599				_ => block_gas_limit,
600			},
601		};
602
603		let (gas_price, max_fee_per_gas, max_priority_fee_per_gas, fee_cap) = {
604			let details = fee_details(
605				request.gas_price,
606				request.max_fee_per_gas,
607				request.max_priority_fee_per_gas,
608			)?;
609			(
610				details.gas_price,
611				details.max_fee_per_gas,
612				details.max_priority_fee_per_gas,
613				details.fee_cap,
614			)
615		};
616
617		// Recap the highest gas allowance with account's balance.
618		if let Some(from) = request.from {
619			if fee_cap > U256::zero() {
620				let balance = api
621					.account_basic(substrate_hash, from)
622					.map_err(|err| internal_err(format!("runtime error: {err}")))?
623					.balance;
624				let mut available = balance;
625				if let Some(value) = request.value {
626					if value > available {
627						return Err(internal_err("insufficient funds for transfer"));
628					}
629					available -= value;
630				}
631				let allowance = available / fee_cap;
632				if highest > allowance {
633					log::warn!(
634							"Gas estimation capped by limited funds original {} balance {} sent {} feecap {} fundable {}",
635							highest,
636							balance,
637							request.value.unwrap_or_default(),
638							fee_cap,
639							allowance
640						);
641					highest = allowance;
642				}
643			}
644		}
645
646		struct ExecutableResult {
647			data: Vec<u8>,
648			exit_reason: ExitReason,
649			used_gas: U256,
650		}
651
652		// Create a helper to check if a gas allowance results in an executable transaction.
653		//
654		// A new ApiRef instance needs to be used per execution to avoid the overlayed state to affect
655		// the estimation result of subsequent calls.
656		//
657		// Note that this would have a performance penalty if we introduce gas estimation for past
658		// blocks - and thus, past runtime versions. Substrate has a default `runtime_cache_size` of
659		// 2 slots LRU-style, meaning if users were to access multiple runtime versions in a short period
660		// of time, the RPC response time would degrade a lot, as the VersionedRuntime needs to be compiled.
661		//
662		// To solve that, and if we introduce historical gas estimation, we'd need to increase that default.
663		#[rustfmt::skip]
664			let executable = move |
665				request, gas_limit, api_version, api: sp_api::ApiRef<'_, C::Api>, estimate_mode
666			| -> RpcResult<ExecutableResult> {
667				let TransactionRequest {
668					from,
669					to,
670					gas,
671					value,
672					data,
673					access_list,
674					authorization_list,
675					..
676				} = request;
677
678				// Use request gas limit only if it less than gas_limit parameter
679				let gas_limit = core::cmp::min(gas.unwrap_or(gas_limit), gas_limit);
680
681				let data = data.into_bytes().map(|d| d.0).unwrap_or_default();
682
683				let (exit_reason, data, used_gas) = match to {
684					Some(to) => {
685						if api_version == 1 {
686							// Legacy pre-london
687							#[allow(deprecated)]
688							let info = api.call_before_version_2(
689								substrate_hash,
690								from.unwrap_or_default(),
691								to,
692								data,
693								value.unwrap_or_default(),
694								gas_limit,
695								gas_price,
696								None,
697								estimate_mode,
698							)
699							.map_err(|err| internal_err(format!("runtime error: {err}")))?
700							.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
701
702							(info.exit_reason, info.value, info.used_gas)
703						} else if api_version < 4 {
704							// Post-london
705							#[allow(deprecated)]
706							let info = api.call_before_version_4(
707								substrate_hash,
708								from.unwrap_or_default(),
709								to,
710								data,
711								value.unwrap_or_default(),
712								gas_limit,
713								max_fee_per_gas,
714								max_priority_fee_per_gas,
715								None,
716								estimate_mode,
717							)
718							.map_err(|err| internal_err(format!("runtime error: {err}")))?
719							.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
720
721							(info.exit_reason, info.value, info.used_gas)
722						} else if api_version == 4 {
723							// Post-london + access list support
724							let access_list = access_list.unwrap_or_default();
725							#[allow(deprecated)]
726							let info = api.call_before_version_5(
727								substrate_hash,
728								from.unwrap_or_default(),
729								to,
730								data,
731								value.unwrap_or_default(),
732								gas_limit,
733								max_fee_per_gas,
734								max_priority_fee_per_gas,
735								None,
736								estimate_mode,
737								Some(
738									access_list
739										.into_iter()
740										.map(|item| (item.address, item.storage_keys))
741										.collect(),
742								),
743							)
744							.map_err(|err| internal_err(format!("runtime error: {err}")))?
745							.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
746
747							(info.exit_reason, info.value, info.used_gas)
748						} else if api_version == 5 {
749							// Post-london + access list support (version 5)
750							let encoded_params = Encode::encode(&(
751								&from.unwrap_or_default(),
752								&to,
753								&data,
754								&value.unwrap_or_default(),
755								&gas_limit,
756								&max_fee_per_gas,
757								&max_priority_fee_per_gas,
758								&None::<Option<U256>>,
759								&estimate_mode,
760								&Some(
761									access_list
762										.unwrap_or_default()
763										.into_iter()
764										.map(|item| (item.address, item.storage_keys))
765										.collect::<Vec<(sp_core::H160, Vec<H256>)>>(),
766								),
767							));
768
769							// Proof size recording
770							let recorder: sp_trie::recorder::Recorder<HashingFor<B>> = Default::default();
771							let ext = sp_trie::proof_size_extension::ProofSizeExt::new(recorder.clone());
772							let mut exts = Extensions::new();
773							exts.register(ext);
774
775							let params = CallApiAtParams {
776								at: substrate_hash,
777								function: "EthereumRuntimeRPCApi_call",
778								arguments: encoded_params,
779								overlayed_changes: &RefCell::new(Default::default()),
780								call_context: CallContext::Offchain,
781								recorder: &Some(recorder),
782								extensions: &RefCell::new(exts),
783							};
784
785							let info = self
786								.client
787								.call_api_at(params)
788								.and_then(|r| {
789									Result::map_err(
790										<Result<ExecutionInfoV2::<Vec<u8>>, DispatchError> as Decode>::decode(&mut &r[..]),
791										|error| sp_api::ApiError::FailedToDecodeReturnValue {
792											function: "EthereumRuntimeRPCApi_call",
793											error,
794											raw: r
795										},
796									)
797								})
798								.map_err(|err| internal_err(format!("runtime error: {err}")))?
799								.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
800
801							(info.exit_reason, info.value, info.used_gas.effective)
802						} else if api_version == 6 {
803							// Pectra - authorization list support (EIP-7702)
804							let access_list = access_list
805								.unwrap_or_default()
806								.into_iter()
807								.map(|item| (item.address, item.storage_keys))
808								.collect::<Vec<(sp_core::H160, Vec<H256>)>>();
809
810							let encoded_params = Encode::encode(&(
811								&from.unwrap_or_default(),
812								&to,
813								&data,
814								&value.unwrap_or_default(),
815								&gas_limit,
816								&max_fee_per_gas,
817								&max_priority_fee_per_gas,
818								&None::<Option<U256>>,
819								&estimate_mode,
820								&Some(
821									access_list
822								),
823								&authorization_list,
824							));
825
826							// Proof size recording
827							let recorder: sp_trie::recorder::Recorder<HashingFor<B>> = Default::default();
828							let ext = sp_trie::proof_size_extension::ProofSizeExt::new(recorder.clone());
829							let mut exts = Extensions::new();
830							exts.register(ext);
831
832							let params = CallApiAtParams {
833								at: substrate_hash,
834								function: "EthereumRuntimeRPCApi_call",
835								arguments: encoded_params,
836								overlayed_changes: &RefCell::new(Default::default()),
837								call_context: CallContext::Offchain,
838								recorder: &Some(recorder),
839								extensions: &RefCell::new(exts),
840							};
841
842							let info = self
843								.client
844								.call_api_at(params)
845								.and_then(|r| {
846									Result::map_err(
847										<Result<ExecutionInfoV2::<Vec<u8>>, DispatchError> as Decode>::decode(&mut &r[..]),
848										|error| sp_api::ApiError::FailedToDecodeReturnValue {
849											function: "EthereumRuntimeRPCApi_call",
850											error,
851											raw: r
852										},
853									)
854								})
855								.map_err(|err| internal_err(format!("runtime error: {err}")))?
856								.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
857
858							(info.exit_reason, info.value, info.used_gas.effective)
859						} else {
860							return Err(internal_err(format!("Unsupported EthereumRuntimeRPCApi version: {api_version}")));
861						}
862					}
863					None => {
864						if api_version == 1 {
865							// Legacy pre-london
866							#[allow(deprecated)]
867							let info = api.create_before_version_2(
868								substrate_hash,
869								from.unwrap_or_default(),
870								data,
871								value.unwrap_or_default(),
872								gas_limit,
873								gas_price,
874								None,
875								estimate_mode,
876							)
877							.map_err(|err| internal_err(format!("runtime error: {err}")))?
878							.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
879
880							(info.exit_reason, Vec::new(), info.used_gas)
881						} else if api_version < 4 {
882							// Post-london
883							#[allow(deprecated)]
884							let info = api.create_before_version_4(
885								substrate_hash,
886								from.unwrap_or_default(),
887								data,
888								value.unwrap_or_default(),
889								gas_limit,
890								max_fee_per_gas,
891								max_priority_fee_per_gas,
892								None,
893								estimate_mode,
894							)
895							.map_err(|err| internal_err(format!("runtime error: {err}")))?
896							.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
897
898							(info.exit_reason, Vec::new(), info.used_gas)
899						} else if api_version == 4 {
900							// Post-london + access list support
901							let access_list = access_list.unwrap_or_default();
902							#[allow(deprecated)]
903							let info = api.create_before_version_5(
904								substrate_hash,
905								from.unwrap_or_default(),
906								data,
907								value.unwrap_or_default(),
908								gas_limit,
909								max_fee_per_gas,
910								max_priority_fee_per_gas,
911								None,
912								estimate_mode,
913								Some(
914									access_list
915										.into_iter()
916										.map(|item| (item.address, item.storage_keys))
917										.collect(),
918								),
919							)
920							.map_err(|err| internal_err(format!("runtime error: {err}")))?
921							.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
922
923							(info.exit_reason, Vec::new(), info.used_gas)
924						} else if api_version == 5 {
925							// Post-london + access list support (version 5)
926							let encoded_params = Encode::encode(&(
927								&from.unwrap_or_default(),
928								&data,
929								&value.unwrap_or_default(),
930								&gas_limit,
931								&max_fee_per_gas,
932								&max_priority_fee_per_gas,
933								&None::<Option<U256>>,
934								&estimate_mode,
935								&Some(
936									access_list
937										.unwrap_or_default()
938										.into_iter()
939										.map(|item| (item.address, item.storage_keys))
940										.collect::<Vec<(sp_core::H160, Vec<H256>)>>(),
941								),
942							));
943
944							// Enable proof size recording
945							let recorder: sp_trie::recorder::Recorder<HashingFor<B>> = Default::default();
946							let ext = sp_trie::proof_size_extension::ProofSizeExt::new(recorder.clone());
947							let mut exts = Extensions::new();
948							exts.register(ext);
949
950							let params = CallApiAtParams {
951								at: substrate_hash,
952								function: "EthereumRuntimeRPCApi_create",
953								arguments: encoded_params,
954								overlayed_changes: &RefCell::new(Default::default()),
955								call_context: CallContext::Offchain,
956								recorder: &Some(recorder),
957								extensions: &RefCell::new(exts),
958							};
959
960							let info = self
961							.client
962							.call_api_at(params)
963							.and_then(|r| {
964								Result::map_err(
965									<Result<ExecutionInfoV2::<H160>, DispatchError> as Decode>::decode(&mut &r[..]),
966									|error| sp_api::ApiError::FailedToDecodeReturnValue {
967										function: "EthereumRuntimeRPCApi_create",
968										error,
969										raw: r
970									},
971								)
972							})
973							.map_err(|err| internal_err(format!("runtime error: {err}")))?
974							.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
975
976							(info.exit_reason, Vec::new(), info.used_gas.effective)
977						} else if api_version == 6 {
978							// Pectra - authorization list support (EIP-7702)
979							let access_list = access_list
980								.unwrap_or_default()
981								.into_iter()
982								.map(|item| (item.address, item.storage_keys))
983								.collect::<Vec<(sp_core::H160, Vec<H256>)>>();
984
985							let encoded_params = Encode::encode(&(
986								&from.unwrap_or_default(),
987								&data,
988								&value.unwrap_or_default(),
989								&gas_limit,
990								&max_fee_per_gas,
991								&max_priority_fee_per_gas,
992								&None::<Option<U256>>,
993								&estimate_mode,
994								&Some(
995									access_list
996								),
997								&authorization_list,
998							));
999
1000							// Enable proof size recording
1001							let recorder: sp_trie::recorder::Recorder<HashingFor<B>> = Default::default();
1002							let ext = sp_trie::proof_size_extension::ProofSizeExt::new(recorder.clone());
1003							let mut exts = Extensions::new();
1004							exts.register(ext);
1005
1006							let params = CallApiAtParams {
1007								at: substrate_hash,
1008								function: "EthereumRuntimeRPCApi_create",
1009								arguments: encoded_params,
1010								overlayed_changes: &RefCell::new(Default::default()),
1011								call_context: CallContext::Offchain,
1012								recorder: &Some(recorder),
1013								extensions: &RefCell::new(exts),
1014							};
1015
1016							let info = self
1017							.client
1018							.call_api_at(params)
1019							.and_then(|r| {
1020								Result::map_err(
1021									<Result<ExecutionInfoV2::<H160>, DispatchError> as Decode>::decode(&mut &r[..]),
1022									|error| sp_api::ApiError::FailedToDecodeReturnValue {
1023										function: "EthereumRuntimeRPCApi_create",
1024										error,
1025										raw: r
1026									},
1027								)
1028							})
1029							.map_err(|err| internal_err(format!("runtime error: {err}")))?
1030							.map_err(|err| internal_err(format!("execution fatal: {err:?}")))?;
1031
1032							(info.exit_reason, Vec::new(), info.used_gas.effective)
1033						} else {
1034							return Err(internal_err(format!("Unsupported EthereumRuntimeRPCApi version: {api_version}")));
1035						}
1036					}
1037				};
1038				Ok(ExecutableResult {
1039					exit_reason,
1040					data,
1041					used_gas,
1042				})
1043			};
1044		let api_version = if let Ok(Some(api_version)) =
1045			client
1046				.runtime_api()
1047				.api_version::<dyn EthereumRuntimeRPCApi<B>>(substrate_hash)
1048		{
1049			api_version
1050		} else {
1051			return Err(internal_err("failed to retrieve Runtime Api version"));
1052		};
1053
1054		// Verify that the transaction succeed with the highest capacity
1055		let cap = highest;
1056		let estimate_mode = !cfg!(feature = "rpc-binary-search-estimate");
1057		let ExecutableResult {
1058			data,
1059			exit_reason,
1060			used_gas,
1061		} = executable(
1062			request.clone(),
1063			highest,
1064			api_version,
1065			client.runtime_api(),
1066			estimate_mode,
1067		)?;
1068		match exit_reason {
1069			ExitReason::Succeed(_) => (),
1070			ExitReason::Error(ExitError::OutOfGas) => {
1071				return Err(internal_err(format!(
1072					"gas required exceeds allowance {cap}"
1073				)))
1074			}
1075			// If the transaction reverts, there are two possible cases,
1076			// it can revert because the called contract feels that it does not have enough
1077			// gas left to continue, or it can revert for another reason unrelated to gas.
1078			ExitReason::Revert(revert) => {
1079				if request.gas.is_some() || request.gas_price.is_some() {
1080					// If the user has provided a gas limit or a gas price, then we have executed
1081					// with less block gas limit, so we must reexecute with block gas limit to
1082					// know if the revert is due to a lack of gas or not.
1083					let ExecutableResult {
1084						data,
1085						exit_reason,
1086						used_gas: _,
1087					} = executable(
1088						request.clone(),
1089						max_gas_limit,
1090						api_version,
1091						client.runtime_api(),
1092						estimate_mode,
1093					)?;
1094					match exit_reason {
1095						ExitReason::Succeed(_) => {
1096							return Err(internal_err(format!(
1097								"gas required exceeds allowance {cap}",
1098							)))
1099						}
1100						// The execution has been done with block gas limit, so it is not a lack of gas from the user.
1101						other => error_on_execution_failure(&other, &data)?,
1102					}
1103				} else {
1104					// The execution has already been done with block gas limit, so it is not a lack of gas from the user.
1105					error_on_execution_failure(&ExitReason::Revert(revert), &data)?
1106				}
1107			}
1108			other => error_on_execution_failure(&other, &data)?,
1109		};
1110
1111		#[cfg(not(feature = "rpc-binary-search-estimate"))]
1112		{
1113			Ok(used_gas)
1114		}
1115		#[cfg(feature = "rpc-binary-search-estimate")]
1116		{
1117			// On binary search, evm estimate mode is disabled
1118			let estimate_mode = false;
1119			// Define the lower bound of the binary search
1120			let mut lowest = MIN_GAS_PER_TX;
1121
1122			// Start close to the used gas for faster binary search
1123			let mut mid = std::cmp::min(used_gas * 3, (highest + lowest) / 2);
1124
1125			// Execute the binary search and hone in on an executable gas limit.
1126			let mut previous_highest = highest;
1127			while (highest - lowest) > U256::one() {
1128				let ExecutableResult {
1129					data,
1130					exit_reason,
1131					used_gas: _,
1132				} = executable(
1133					request.clone(),
1134					mid,
1135					api_version,
1136					client.runtime_api(),
1137					estimate_mode,
1138				)?;
1139				match exit_reason {
1140					ExitReason::Succeed(_) => {
1141						highest = mid;
1142						// If the variation in the estimate is less than 10%,
1143						// then the estimate is considered sufficiently accurate.
1144						if (previous_highest - highest) * 10 / previous_highest < U256::one() {
1145							return Ok(highest);
1146						}
1147						previous_highest = highest;
1148					}
1149					ExitReason::Revert(_)
1150					| ExitReason::Error(ExitError::OutOfGas)
1151					| ExitReason::Error(ExitError::InvalidCode(_)) => {
1152						lowest = mid;
1153					}
1154					other => error_on_execution_failure(&other, &data)?,
1155				}
1156				mid = (highest + lowest) / 2;
1157			}
1158
1159			Ok(highest)
1160		}
1161	}
1162
1163	/// Given an address mapped `CallStateOverride`, creates `OverlayedChanges` to be used for
1164	/// `CallApiAt` eth_call.
1165	fn create_overrides_overlay(
1166		&self,
1167		block_hash: B::Hash,
1168		api_version: u32,
1169		state_overrides: Option<BTreeMap<H160, CallStateOverride>>,
1170	) -> RpcResult<OverlayedChanges<HashingFor<B>>> {
1171		let mut overlayed_changes = OverlayedChanges::default();
1172		if let Some(state_overrides) = state_overrides {
1173			for (address, state_override) in state_overrides {
1174				if EC::RuntimeStorageOverride::is_enabled() {
1175					EC::RuntimeStorageOverride::set_overlayed_changes(
1176						self.client.as_ref(),
1177						&mut overlayed_changes,
1178						block_hash,
1179						api_version,
1180						address,
1181						state_override.balance,
1182						state_override.nonce,
1183					);
1184				} else if state_override.balance.is_some() || state_override.nonce.is_some() {
1185					return Err(internal_err(
1186						"state override unsupported for balance and nonce",
1187					));
1188				}
1189
1190				if let Some(code) = &state_override.code {
1191					let mut key = [twox_128(PALLET_EVM), twox_128(EVM_ACCOUNT_CODES)]
1192						.concat()
1193						.to_vec();
1194					key.extend(blake2_128(address.as_bytes()));
1195					key.extend(address.as_bytes());
1196					let encoded_code = code.clone().into_vec().encode();
1197					overlayed_changes.set_storage(key.clone(), Some(encoded_code));
1198				}
1199
1200				let mut account_storage_key =
1201					[twox_128(PALLET_EVM), twox_128(EVM_ACCOUNT_STORAGES)]
1202						.concat()
1203						.to_vec();
1204				account_storage_key.extend(blake2_128(address.as_bytes()));
1205				account_storage_key.extend(address.as_bytes());
1206
1207				// Use `state` first. If `stateDiff` is also present, it resolves consistently
1208				if let Some(state) = &state_override.state {
1209					// clear all storage
1210					if let Ok(all_keys) = self.client.storage_keys(
1211						block_hash,
1212						Some(&sp_storage::StorageKey(account_storage_key.clone())),
1213						None,
1214					) {
1215						for key in all_keys {
1216							overlayed_changes.set_storage(key.0, None);
1217						}
1218					}
1219					// set provided storage
1220					for (k, v) in state {
1221						let mut slot_key = account_storage_key.clone();
1222						slot_key.extend(blake2_128(k.as_bytes()));
1223						slot_key.extend(k.as_bytes());
1224
1225						overlayed_changes.set_storage(slot_key, Some(v.as_bytes().to_owned()));
1226					}
1227				}
1228
1229				if let Some(state_diff) = &state_override.state_diff {
1230					for (k, v) in state_diff {
1231						let mut slot_key = account_storage_key.clone();
1232						slot_key.extend(blake2_128(k.as_bytes()));
1233						slot_key.extend(k.as_bytes());
1234
1235						overlayed_changes.set_storage(slot_key, Some(v.as_bytes().to_owned()));
1236					}
1237				}
1238			}
1239		}
1240
1241		Ok(overlayed_changes)
1242	}
1243}
1244
1245pub fn error_on_execution_failure(reason: &ExitReason, data: &[u8]) -> RpcResult<()> {
1246	match reason {
1247		ExitReason::Succeed(_) => Ok(()),
1248		ExitReason::Error(err) => {
1249			if *err == ExitError::OutOfGas {
1250				// `ServerError(0)` will be useful in estimate gas
1251				return Err(internal_err("out of gas"));
1252			}
1253			Err(crate::internal_err_with_data(
1254				format!("evm error: {err:?}"),
1255				&[],
1256			))
1257		}
1258		ExitReason::Revert(_) => {
1259			const LEN_START: usize = 36;
1260			const MESSAGE_START: usize = 68;
1261
1262			let mut message = "VM Exception while processing transaction: revert".to_string();
1263			// A minimum size of error function selector (4) + offset (32) + string length (32)
1264			// should contain a utf-8 encoded revert reason.
1265			if data.len() > MESSAGE_START {
1266				let message_len = U256::from_big_endian(&data[LEN_START..MESSAGE_START])
1267					.saturated_into::<usize>();
1268				let message_end = MESSAGE_START.saturating_add(message_len);
1269
1270				if data.len() >= message_end {
1271					let body: &[u8] = &data[MESSAGE_START..message_end];
1272					if let Ok(reason) = std::str::from_utf8(body) {
1273						message = format!("{message} {reason}");
1274					}
1275				}
1276			}
1277			Err(crate::internal_err_with_data(message, data))
1278		}
1279		ExitReason::Fatal(err) => Err(crate::internal_err_with_data(
1280			format!("evm fatal: {err:?}"),
1281			&[],
1282		)),
1283	}
1284}
1285
1286struct FeeDetails {
1287	gas_price: Option<U256>,
1288	max_fee_per_gas: Option<U256>,
1289	max_priority_fee_per_gas: Option<U256>,
1290	fee_cap: U256,
1291}
1292
1293fn fee_details(
1294	request_gas_price: Option<U256>,
1295	request_max_fee_per_gas: Option<U256>,
1296	request_priority_fee_per_gas: Option<U256>,
1297) -> RpcResult<FeeDetails> {
1298	match (
1299		request_gas_price,
1300		request_max_fee_per_gas,
1301		request_priority_fee_per_gas,
1302	) {
1303		(Some(_), Some(_), Some(_)) => Err(internal_err(
1304			"both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified",
1305		)),
1306		// Legacy or EIP-2930 transaction.
1307		(gas_price, None, None) if gas_price.is_some() => Ok(FeeDetails {
1308			gas_price,
1309			max_fee_per_gas: None,
1310			max_priority_fee_per_gas: None,
1311			fee_cap: gas_price.unwrap_or_default(),
1312		}),
1313		// EIP-1559 transaction
1314		(None, Some(max_fee), Some(max_priority)) => {
1315			if max_priority > max_fee {
1316				return Err(internal_err(
1317					"Invalid input: `max_priority_fee_per_gas` greater than `max_fee_per_gas`",
1318				));
1319			}
1320			Ok(FeeDetails {
1321				gas_price: None,
1322				max_fee_per_gas: Some(max_fee),
1323				max_priority_fee_per_gas: Some(max_priority),
1324				fee_cap: max_fee,
1325			})
1326		}
1327		// Default to EIP-1559 transaction
1328		_ => Ok(FeeDetails {
1329			gas_price: None,
1330			// Old runtimes require max_fee_per_gas to be None for non transactional calls.
1331			max_fee_per_gas: None,
1332			max_priority_fee_per_gas: Some(U256::zero()),
1333			fee_cap: U256::zero(),
1334		}),
1335	}
1336}