moonbeam_runtime_common/
apis.rs

1// Copyright 2019-2025 PureStake Inc.
2// This file is part of Moonbeam.
3
4// Moonbeam is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Moonbeam is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16
17#[macro_export]
18macro_rules! impl_runtime_apis_plus_common {
19    ({$($custom:tt)*} {$($bench_custom:tt)*}) => {
20    	use ethereum::AuthorizationList;
21
22		#[cfg(feature = "evm-tracing")]
23		// Helper function to replay the "on_idle" hook for all pallets, we need this for
24		// evm-tracing because some ethereum-xcm transactions might be executed at on_idle.
25		//
26		// We need to make sure that we replay on_idle exactly the same way as the
27		// original block execution, but unfortunatly frame executive diosn't provide a function
28		// to replay only on_idle, so we need to copy here some code inside frame executive.
29		fn replay_on_idle() {
30			use frame_system::pallet_prelude::BlockNumberFor;
31			use frame_support::traits::OnIdle;
32
33			let weight = <frame_system::Pallet<Runtime>>::block_weight();
34			let max_weight = <
35					<Runtime as frame_system::Config>::BlockWeights as
36					frame_support::traits::Get<_>
37				>::get().max_block;
38			let remaining_weight = max_weight.saturating_sub(weight.total());
39			if remaining_weight.all_gt(Weight::zero()) {
40				let _ = <AllPalletsWithSystem as OnIdle<BlockNumberFor<Runtime>>>::on_idle(
41					<frame_system::Pallet<Runtime>>::block_number(),
42					remaining_weight,
43				);
44			}
45		}
46
47		/// AccountId Converter used for benchmarks.
48		///
49		/// * AccountId32 Junction is being used in pallet_xcm_benchmarks
50		/// * Parent is used as valid destination location for benchmarking.
51		#[cfg(feature = "runtime-benchmarks")]
52		pub struct BenchAccountIdConverter<AccountId>(sp_std::marker::PhantomData<AccountId>);
53
54		#[cfg(feature = "runtime-benchmarks")]
55		impl<AccountId: From<[u8; 20]> + Into<[u8; 20]> + Clone> xcm_executor::traits::ConvertLocation<AccountId>
56			for BenchAccountIdConverter<AccountId>
57		{
58			fn convert_location(location: &xcm::latest::prelude::Location) -> Option<AccountId> {
59				match location.unpack() {
60					(0, [xcm::latest::prelude::AccountId32 { id, network: None }]) => {
61						// take the first 20 bytes of the id and convert to fixed-size array
62						let mut id20: [u8; 20] = [0u8; 20];
63						id20.copy_from_slice(&id[..20]);
64						Some(id20.into())
65					},
66					(1, []) => {
67						Some([1u8; 20].into())
68					},
69					_ => return None,
70				}
71			}
72		}
73
74		impl_runtime_apis! {
75			$($custom)*
76
77			impl sp_api::Core<Block> for Runtime {
78				fn version() -> RuntimeVersion {
79					VERSION
80				}
81
82				fn execute_block(block: Block) {
83					Executive::execute_block(block)
84				}
85
86				fn initialize_block(header: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
87					Executive::initialize_block(header)
88				}
89			}
90
91			impl cumulus_primitives_core::RelayParentOffsetApi<Block> for Runtime {
92				fn relay_parent_offset() -> u32 {
93					crate::RELAY_PARENT_OFFSET
94				}
95			}
96
97			impl cumulus_primitives_core::GetCoreSelectorApi<Block> for Runtime {
98				fn core_selector() -> (cumulus_primitives_core::CoreSelector, cumulus_primitives_core::ClaimQueueOffset) {
99					ParachainSystem::core_selector()
100				}
101			}
102
103			impl sp_api::Metadata<Block> for Runtime {
104				fn metadata() -> OpaqueMetadata {
105					OpaqueMetadata::new(Runtime::metadata().into())
106				}
107
108				fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> {
109					Runtime::metadata_at_version(version)
110				}
111
112				fn metadata_versions() -> Vec<u32> {
113					Runtime::metadata_versions()
114				}
115			}
116
117			impl sp_block_builder::BlockBuilder<Block> for Runtime {
118				fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {
119					Executive::apply_extrinsic(extrinsic)
120				}
121
122				fn finalize_block() -> <Block as BlockT>::Header {
123					Executive::finalize_block()
124				}
125
126				fn inherent_extrinsics(
127					data: sp_inherents::InherentData,
128				) -> Vec<<Block as BlockT>::Extrinsic> {
129					data.create_extrinsics()
130				}
131
132				fn check_inherents(
133					block: Block,
134					data: sp_inherents::InherentData,
135				) -> sp_inherents::CheckInherentsResult {
136					data.check_extrinsics(&block)
137				}
138			}
139
140			impl sp_offchain::OffchainWorkerApi<Block> for Runtime {
141				fn offchain_worker(header: &<Block as BlockT>::Header) {
142					Executive::offchain_worker(header)
143				}
144			}
145
146			impl sp_session::SessionKeys<Block> for Runtime {
147				fn decode_session_keys(
148					encoded: Vec<u8>,
149				) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
150					opaque::SessionKeys::decode_into_raw_public_keys(&encoded)
151				}
152
153				fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
154					opaque::SessionKeys::generate(seed)
155				}
156			}
157
158			impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
159				fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result {
160					frame_support::genesis_builder_helper::build_state::<RuntimeGenesisConfig>(config)
161				}
162
163				#[cfg(not(feature = "disable-genesis-builder"))]
164				fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
165					frame_support::genesis_builder_helper::get_preset::<RuntimeGenesisConfig>(id, genesis_config_preset::get_preset)
166				}
167				#[cfg(feature = "disable-genesis-builder")]
168				fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
169					None
170				}
171
172				#[cfg(not(feature = "disable-genesis-builder"))]
173				fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
174					genesis_config_preset::preset_names()
175				}
176				#[cfg(feature = "disable-genesis-builder")]
177				fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
178					Default::default()
179				}
180			}
181
182			impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {
183				fn account_nonce(account: AccountId) -> Index {
184					System::account_nonce(account)
185				}
186			}
187
188			impl moonbeam_rpc_primitives_debug::DebugRuntimeApi<Block> for Runtime {
189				fn trace_transaction(
190					extrinsics: Vec<<Block as BlockT>::Extrinsic>,
191					traced_transaction: &EthereumTransaction,
192					header: &<Block as BlockT>::Header,
193				) -> Result<
194					(),
195					sp_runtime::DispatchError,
196				> {
197					#[cfg(feature = "evm-tracing")]
198					{
199						use moonbeam_evm_tracer::tracer::{
200							EthereumTracingStatus,
201							EvmTracer,
202							EthereumTracer
203						};
204						use frame_support::storage::unhashed;
205						use frame_system::pallet_prelude::BlockNumberFor;
206
207						// Tell the CallDispatcher we are tracing a specific Transaction.
208						EthereumTracer::transaction(traced_transaction.hash(), || {
209							// Initialize block: calls the "on_initialize" hook on every pallet
210							// in AllPalletsWithSystem.
211							// After pallet message queue was introduced, this must be done only after
212							// enabling XCM tracing by calling ETHEREUM_TRACING_STATUS::using
213							// in the storage
214							Executive::initialize_block(header);
215
216							// Apply a subset of extrinsics: all the substrate-specific or ethereum
217							// transactions that preceded the requested transaction.
218							for ext in extrinsics.into_iter() {
219								let _ = match &ext.0.function {
220									RuntimeCall::Ethereum(transact { transaction }) => {
221										// Reset the previously consumed weight when tracing ethereum transactions.
222										// This is necessary because EVM tracing introduces additional
223										// (ref_time) overhead, which differs from the production runtime behavior.
224										// Without resetting the block weight, the extra tracing overhead could
225										// leading to some transactions to incorrectly fail during tracing.
226										frame_system::BlockWeight::<Runtime>::kill();
227
228										if transaction == traced_transaction {
229											EvmTracer::new().trace(|| Executive::apply_extrinsic(ext));
230											return Ok(());
231										} else {
232											Executive::apply_extrinsic(ext)
233										}
234									}
235									_ => Executive::apply_extrinsic(ext),
236								};
237
238								if let Some(EthereumTracingStatus::TransactionExited) = EthereumTracer::status() {
239									return Ok(());
240								}
241							}
242
243							if let Some(EthereumTracingStatus::Transaction(_)) = EthereumTracer::status() {
244								// If the transaction was not found, it might be
245								// an eth-xcm transaction that was executed at on_idle
246								replay_on_idle();
247							}
248
249							if let Some(EthereumTracingStatus::TransactionExited) = EthereumTracer::status() {
250								// The transaction was found
251								Ok(())
252							} else {
253								// The transaction was not-found
254								Err(sp_runtime::DispatchError::Other(
255									"Failed to find Ethereum transaction among the extrinsics.",
256								))
257							}
258						})
259					}
260					#[cfg(not(feature = "evm-tracing"))]
261					Err(sp_runtime::DispatchError::Other(
262						"Missing `evm-tracing` compile time feature flag.",
263					))
264				}
265
266				fn trace_block(
267					extrinsics: Vec<<Block as BlockT>::Extrinsic>,
268					known_transactions: Vec<H256>,
269					header: &<Block as BlockT>::Header,
270				) -> Result<
271					(),
272					sp_runtime::DispatchError,
273				> {
274					#[cfg(feature = "evm-tracing")]
275					{
276						use moonbeam_evm_tracer::tracer::{
277							EthereumTracingStatus,
278							EvmTracer,
279							EthereumTracer
280						};
281						use frame_system::pallet_prelude::BlockNumberFor;
282
283						// Tell the CallDispatcher we are tracing a full Block.
284						EthereumTracer::block(|| {
285							let mut config = <Runtime as pallet_evm::Config>::config().clone();
286							config.estimate = true;
287
288							// Initialize block: calls the "on_initialize" hook on every pallet
289							// in AllPalletsWithSystem.
290							// After pallet message queue was introduced, this must be done only after
291							// enabling XCM tracing by calling ETHEREUM_TRACING_STATUS::using
292							// in the storage
293							Executive::initialize_block(header);
294
295							// Apply all extrinsics. Ethereum extrinsics are traced.
296							for ext in extrinsics.into_iter() {
297								match &ext.0.function {
298									RuntimeCall::Ethereum(transact { transaction }) => {
299
300										// Reset the previously consumed weight when tracing multiple transactions.
301										// This is necessary because EVM tracing introduces additional
302										// (ref_time) overhead, which differs from the production runtime behavior.
303										// Without resetting the block weight, the extra tracing overhead could
304										// leading to some transactions to incorrectly fail during tracing.
305										frame_system::BlockWeight::<Runtime>::kill();
306
307										let tx_hash = &transaction.hash();
308										if known_transactions.contains(&tx_hash) {
309											// Each known extrinsic is a new call stack.
310											EvmTracer::emit_new();
311											EvmTracer::new().trace(|| {
312												if let Err(err) = Executive::apply_extrinsic(ext) {
313													log::debug!(
314														target: "tracing",
315														"Could not trace eth transaction (hash: {}): {:?}",
316														&tx_hash,
317														err
318													);
319												}
320											});
321										} else {
322											if let Err(err) = Executive::apply_extrinsic(ext) {
323												log::debug!(
324													target: "tracing",
325													"Failed to apply eth extrinsic (hash: {}): {:?}",
326													&tx_hash,
327													err
328												);
329											}
330										}
331									}
332									_ => {
333										if let Err(err) = Executive::apply_extrinsic(ext) {
334											log::debug!(
335												target: "tracing",
336												"Failed to apply non-eth extrinsic: {:?}",
337												err
338											);
339										}
340									}
341								};
342							}
343
344							// Replay on_idle
345							// Some XCM messages with eth-xcm transaction might be executed at on_idle
346							replay_on_idle();
347
348							Ok(())
349						})
350					}
351					#[cfg(not(feature = "evm-tracing"))]
352					Err(sp_runtime::DispatchError::Other(
353						"Missing `evm-tracing` compile time feature flag.",
354					))
355				}
356
357				fn trace_call(
358					header: &<Block as BlockT>::Header,
359					from: H160,
360					to: H160,
361					data: Vec<u8>,
362					value: U256,
363					gas_limit: U256,
364					max_fee_per_gas: Option<U256>,
365					max_priority_fee_per_gas: Option<U256>,
366					nonce: Option<U256>,
367					access_list: Option<Vec<(H160, Vec<H256>)>>,
368					authorization_list: Option<AuthorizationList>,
369				) -> Result<(), sp_runtime::DispatchError> {
370					#[cfg(feature = "evm-tracing")]
371					{
372						use moonbeam_evm_tracer::tracer::EvmTracer;
373
374						// Initialize block: calls the "on_initialize" hook on every pallet
375						// in AllPalletsWithSystem.
376						Executive::initialize_block(header);
377
378						EvmTracer::new().trace(|| {
379							let is_transactional = false;
380							let validate = true;
381
382							let transaction_data = pallet_ethereum::TransactionData::new(
383								pallet_ethereum::TransactionAction::Call(to),
384								data.clone(),
385								nonce.unwrap_or_default(),
386								gas_limit,
387								None,
388								max_fee_per_gas.or(Some(U256::default())),
389								max_priority_fee_per_gas.or(Some(U256::default())),
390								value,
391								Some(<Runtime as pallet_evm::Config>::ChainId::get()),
392								access_list.clone().unwrap_or_default(),
393								authorization_list.clone().unwrap_or_default(),
394							);
395
396							let gas_limit = gas_limit.min(u64::MAX.into()).low_u64();
397
398							let (weight_limit, proof_size_base_cost) = pallet_ethereum::Pallet::<Runtime>::transaction_weight(&transaction_data);
399
400							let _ = <Runtime as pallet_evm::Config>::Runner::call(
401								from,
402								to,
403								data,
404								value,
405								gas_limit,
406								max_fee_per_gas,
407								max_priority_fee_per_gas,
408								nonce,
409								access_list.unwrap_or_default(),
410								authorization_list.unwrap_or_default(),
411								is_transactional,
412								validate,
413								weight_limit,
414								proof_size_base_cost,
415								<Runtime as pallet_evm::Config>::config(),
416							);
417						});
418						Ok(())
419					}
420					#[cfg(not(feature = "evm-tracing"))]
421					Err(sp_runtime::DispatchError::Other(
422						"Missing `evm-tracing` compile time feature flag.",
423					))
424				}
425			}
426
427			impl moonbeam_rpc_primitives_txpool::TxPoolRuntimeApi<Block> for Runtime {
428				fn extrinsic_filter(
429					xts_ready: Vec<<Block as BlockT>::Extrinsic>,
430					xts_future: Vec<<Block as BlockT>::Extrinsic>,
431				) -> TxPoolResponse {
432					TxPoolResponse {
433						ready: xts_ready
434							.into_iter()
435							.filter_map(|xt| match xt.0.function {
436								RuntimeCall::Ethereum(transact { transaction }) => Some(transaction),
437								_ => None,
438							})
439							.collect(),
440						future: xts_future
441							.into_iter()
442							.filter_map(|xt| match xt.0.function {
443								RuntimeCall::Ethereum(transact { transaction }) => Some(transaction),
444								_ => None,
445							})
446							.collect(),
447					}
448				}
449			}
450
451			impl fp_rpc::EthereumRuntimeRPCApi<Block> for Runtime {
452				fn chain_id() -> u64 {
453					<Runtime as pallet_evm::Config>::ChainId::get()
454				}
455
456				fn account_basic(address: H160) -> EVMAccount {
457					let (account, _) = EVM::account_basic(&address);
458					account
459				}
460
461				fn gas_price() -> U256 {
462					let (gas_price, _) = <Runtime as pallet_evm::Config>::FeeCalculator::min_gas_price();
463					gas_price
464				}
465
466				fn account_code_at(address: H160) -> Vec<u8> {
467					pallet_evm::AccountCodes::<Runtime>::get(address)
468				}
469
470				fn author() -> H160 {
471					<pallet_evm::Pallet<Runtime>>::find_author()
472				}
473
474				fn storage_at(address: H160, index: U256) -> H256 {
475					let tmp: [u8; 32] = index.to_big_endian();
476					pallet_evm::AccountStorages::<Runtime>::get(address, H256::from_slice(&tmp[..]))
477				}
478
479				fn call(
480					from: H160,
481					to: H160,
482					data: Vec<u8>,
483					value: U256,
484					gas_limit: U256,
485					max_fee_per_gas: Option<U256>,
486					max_priority_fee_per_gas: Option<U256>,
487					nonce: Option<U256>,
488					estimate: bool,
489					access_list: Option<Vec<(H160, Vec<H256>)>>,
490					authorization_list: Option<AuthorizationList>,
491				) -> Result<pallet_evm::CallInfo, sp_runtime::DispatchError> {
492					let config = if estimate {
493						let mut config = <Runtime as pallet_evm::Config>::config().clone();
494						config.estimate = true;
495						Some(config)
496					} else {
497						None
498					};
499					let is_transactional = false;
500					let validate = true;
501
502					let transaction_data = pallet_ethereum::TransactionData::new(
503						pallet_ethereum::TransactionAction::Call(to),
504						data.clone(),
505						nonce.unwrap_or_default(),
506						gas_limit,
507						None,
508						max_fee_per_gas.or(Some(U256::default())),
509						max_priority_fee_per_gas.or(Some(U256::default())),
510						value,
511						Some(<Runtime as pallet_evm::Config>::ChainId::get()),
512						access_list.clone().unwrap_or_default(),
513						authorization_list.clone().unwrap_or_default(),
514					);
515
516					let gas_limit = gas_limit.min(u64::MAX.into()).low_u64();
517
518					let (weight_limit, proof_size_base_cost) = pallet_ethereum::Pallet::<Runtime>::transaction_weight(&transaction_data);
519
520					<Runtime as pallet_evm::Config>::Runner::call(
521						from,
522						to,
523						data,
524						value,
525						gas_limit,
526						max_fee_per_gas,
527						max_priority_fee_per_gas,
528						nonce,
529						access_list.unwrap_or_default(),
530						authorization_list.unwrap_or_default(),
531						is_transactional,
532						validate,
533						weight_limit,
534						proof_size_base_cost,
535						config.as_ref().unwrap_or(<Runtime as pallet_evm::Config>::config()),
536					).map_err(|err| err.error.into())
537				}
538
539				fn create(
540					from: H160,
541					data: Vec<u8>,
542					value: U256,
543					gas_limit: U256,
544					max_fee_per_gas: Option<U256>,
545					max_priority_fee_per_gas: Option<U256>,
546					nonce: Option<U256>,
547					estimate: bool,
548					access_list: Option<Vec<(H160, Vec<H256>)>>,
549					authorization_list: Option<AuthorizationList>,
550				) -> Result<pallet_evm::CreateInfo, sp_runtime::DispatchError> {
551					let config = if estimate {
552						let mut config = <Runtime as pallet_evm::Config>::config().clone();
553						config.estimate = true;
554						Some(config)
555					} else {
556						None
557					};
558					let is_transactional = false;
559					let validate = true;
560
561					let transaction_data = pallet_ethereum::TransactionData::new(
562						pallet_ethereum::TransactionAction::Create,
563						data.clone(),
564						nonce.unwrap_or_default(),
565						gas_limit,
566						None,
567						max_fee_per_gas.or(Some(U256::default())),
568						max_priority_fee_per_gas.or(Some(U256::default())),
569						value,
570						Some(<Runtime as pallet_evm::Config>::ChainId::get()),
571						access_list.clone().unwrap_or_default(),
572						authorization_list.clone().unwrap_or_default(),
573					);
574
575					let gas_limit = gas_limit.min(u64::MAX.into()).low_u64();
576
577					let (weight_limit, proof_size_base_cost) = pallet_ethereum::Pallet::<Runtime>::transaction_weight(&transaction_data);
578
579					#[allow(clippy::or_fun_call)] // suggestion not helpful here
580					<Runtime as pallet_evm::Config>::Runner::create(
581						from,
582						data,
583						value,
584						gas_limit,
585						max_fee_per_gas,
586						max_priority_fee_per_gas,
587						nonce,
588						access_list.unwrap_or_default(),
589						authorization_list.unwrap_or_default(),
590						is_transactional,
591						validate,
592						weight_limit,
593						proof_size_base_cost,
594						config.as_ref().unwrap_or(<Runtime as pallet_evm::Config>::config()),
595					).map_err(|err| err.error.into())
596				}
597
598				fn current_transaction_statuses() -> Option<Vec<TransactionStatus>> {
599					pallet_ethereum::CurrentTransactionStatuses::<Runtime>::get()
600				}
601
602				fn current_block() -> Option<pallet_ethereum::Block> {
603					pallet_ethereum::CurrentBlock::<Runtime>::get()
604				}
605
606				fn current_receipts() -> Option<Vec<pallet_ethereum::Receipt>> {
607					pallet_ethereum::CurrentReceipts::<Runtime>::get()
608				}
609
610				fn current_all() -> (
611					Option<pallet_ethereum::Block>,
612					Option<Vec<pallet_ethereum::Receipt>>,
613					Option<Vec<TransactionStatus>>,
614				) {
615					(
616						pallet_ethereum::CurrentBlock::<Runtime>::get(),
617						pallet_ethereum::CurrentReceipts::<Runtime>::get(),
618						pallet_ethereum::CurrentTransactionStatuses::<Runtime>::get(),
619					)
620				}
621
622				fn extrinsic_filter(
623					xts: Vec<<Block as BlockT>::Extrinsic>,
624				) -> Vec<EthereumTransaction> {
625					xts.into_iter().filter_map(|xt| match xt.0.function {
626						RuntimeCall::Ethereum(transact { transaction }) => Some(transaction),
627						_ => None
628					}).collect::<Vec<EthereumTransaction>>()
629				}
630
631				fn elasticity() -> Option<Permill> {
632					None
633				}
634
635				fn gas_limit_multiplier_support() {}
636
637				fn pending_block(
638					xts: Vec<<Block as sp_runtime::traits::Block>::Extrinsic>
639				) -> (
640					Option<pallet_ethereum::Block>, Option<sp_std::prelude::Vec<TransactionStatus>>
641				) {
642					for ext in xts.into_iter() {
643						let _ = Executive::apply_extrinsic(ext);
644					}
645
646					Ethereum::on_finalize(System::block_number() + 1);
647
648					(
649						pallet_ethereum::CurrentBlock::<Runtime>::get(),
650						pallet_ethereum::CurrentTransactionStatuses::<Runtime>::get()
651					)
652				 }
653
654				fn initialize_pending_block(header: &<Block as BlockT>::Header) {
655					pallet_randomness::vrf::using_fake_vrf(|| {
656						pallet_author_slot_filter::using_fake_author(|| {
657							let _ = Executive::initialize_block(header);
658						})
659					})
660				}
661			}
662
663			impl fp_rpc::ConvertTransactionRuntimeApi<Block> for Runtime {
664				fn convert_transaction(
665					transaction: pallet_ethereum::Transaction
666				) -> <Block as BlockT>::Extrinsic {
667					UncheckedExtrinsic::new_bare(
668						pallet_ethereum::Call::<Runtime>::transact { transaction }.into(),
669					)
670				}
671			}
672
673			impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance>
674			for Runtime {
675				fn query_info(
676					uxt: <Block as BlockT>::Extrinsic,
677					len: u32,
678				) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> {
679					TransactionPayment::query_info(uxt, len)
680				}
681
682				fn query_fee_details(
683					uxt: <Block as BlockT>::Extrinsic,
684					len: u32,
685				) -> pallet_transaction_payment::FeeDetails<Balance> {
686					TransactionPayment::query_fee_details(uxt, len)
687				}
688
689				fn query_weight_to_fee(weight: Weight) -> Balance {
690					TransactionPayment::weight_to_fee(weight)
691				}
692
693				fn query_length_to_fee(length: u32) -> Balance {
694					TransactionPayment::length_to_fee(length)
695				}
696			}
697
698			impl nimbus_primitives::NimbusApi<Block> for Runtime {
699				fn can_author(
700					author: nimbus_primitives::NimbusId,
701					slot: u32,
702					parent_header: &<Block as BlockT>::Header
703				) -> bool {
704					use pallet_parachain_staking::Config as PalletParachainStakingConfig;
705
706					let block_number = parent_header.number + 1;
707
708					// The Moonbeam runtimes use an entropy source that needs to do some accounting
709					// work during block initialization. Therefore we initialize it here to match
710					// the state it will be in when the next block is being executed.
711					use frame_support::traits::OnInitialize;
712					System::initialize(
713						&block_number,
714						&parent_header.hash(),
715						&parent_header.digest,
716					);
717
718					// Because the staking solution calculates the next staking set at the beginning
719					// of the first block in the new round, the only way to accurately predict the
720					// authors is to compute the selection during prediction.
721					if pallet_parachain_staking::Pallet::<Self>::round()
722						.should_update(block_number) {
723						// get author account id
724						use nimbus_primitives::AccountLookup;
725						let author_account_id = if let Some(account) =
726							pallet_author_mapping::Pallet::<Self>::lookup_account(&author) {
727							account
728						} else {
729							// return false if author mapping not registered like in can_author impl
730							return false
731						};
732						let candidates = pallet_parachain_staking::Pallet::<Self>::compute_top_candidates();
733						if candidates.is_empty() {
734							// If there are zero selected candidates, we use the same eligibility
735							// as the previous round
736							return AuthorInherent::can_author(&author, &slot);
737						}
738
739						// predict eligibility post-selection by computing selection results now
740						let (eligible, _) =
741							pallet_author_slot_filter::compute_pseudo_random_subset::<Self>(
742								candidates,
743								&slot
744							);
745						eligible.contains(&author_account_id)
746					} else {
747						AuthorInherent::can_author(&author, &slot)
748					}
749				}
750			}
751
752			impl cumulus_primitives_core::CollectCollationInfo<Block> for Runtime {
753				fn collect_collation_info(
754					header: &<Block as BlockT>::Header
755				) -> cumulus_primitives_core::CollationInfo {
756					ParachainSystem::collect_collation_info(header)
757				}
758			}
759
760			impl session_keys_primitives::VrfApi<Block> for Runtime {
761				fn get_last_vrf_output() -> Option<<Block as BlockT>::Hash> {
762					// TODO: remove in future runtime upgrade along with storage item
763					if pallet_randomness::Pallet::<Self>::not_first_block().is_none() {
764						return None;
765					}
766					pallet_randomness::Pallet::<Self>::local_vrf_output()
767				}
768				fn vrf_key_lookup(
769					nimbus_id: nimbus_primitives::NimbusId
770				) -> Option<session_keys_primitives::VrfId> {
771					use session_keys_primitives::KeysLookup;
772					AuthorMapping::lookup_keys(&nimbus_id)
773				}
774			}
775
776			impl xcm_runtime_apis::fees::XcmPaymentApi<Block> for Runtime {
777				fn query_acceptable_payment_assets(
778					xcm_version: xcm::Version
779				) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
780					XcmWeightTrader::query_acceptable_payment_assets(xcm_version)
781				}
782
783				fn query_weight_to_asset_fee(
784					weight: Weight, asset: VersionedAssetId
785				) -> Result<u128, XcmPaymentApiError> {
786					XcmWeightTrader::query_weight_to_asset_fee(weight, asset)
787				}
788
789				fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> {
790					PolkadotXcm::query_xcm_weight(message)
791				}
792
793				fn query_delivery_fees(
794					destination: VersionedLocation, message: VersionedXcm<()>
795				) -> Result<VersionedAssets, XcmPaymentApiError> {
796					PolkadotXcm::query_delivery_fees(destination, message)
797				}
798			}
799
800			impl xcm_runtime_apis::dry_run::DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller>
801				for Runtime {
802					fn dry_run_call(
803						origin: OriginCaller,
804						call: RuntimeCall,
805						result_xcms_version: XcmVersion
806					) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
807						PolkadotXcm::dry_run_call::<
808							Runtime,
809							xcm_config::XcmRouter,
810							OriginCaller,
811							RuntimeCall>(origin, call, result_xcms_version)
812					}
813
814					fn dry_run_xcm(
815						origin_location: VersionedLocation,
816						xcm: VersionedXcm<RuntimeCall>
817					) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> {
818						PolkadotXcm::dry_run_xcm::<xcm_config::XcmRouter>(origin_location, xcm)
819					}
820				}
821
822			impl xcm_runtime_apis::conversions::LocationToAccountApi<Block, AccountId> for Runtime {
823				fn convert_location(location: VersionedLocation) -> Result<
824					AccountId,
825					xcm_runtime_apis::conversions::Error
826				> {
827					xcm_runtime_apis::conversions::LocationToAccountHelper::<
828						AccountId,
829						xcm_config::LocationToAccountId,
830					>::convert_location(location)
831				}
832			}
833
834			#[cfg(feature = "runtime-benchmarks")]
835			impl frame_benchmarking::Benchmark<Block> for Runtime {
836
837				fn benchmark_metadata(extra: bool) -> (
838					Vec<frame_benchmarking::BenchmarkList>,
839					Vec<frame_support::traits::StorageInfo>,
840				) {
841					use frame_benchmarking::{list_benchmark, Benchmarking, BenchmarkList};
842					use frame_system_benchmarking::Pallet as SystemBench;
843					use frame_support::traits::StorageInfoTrait;
844
845					use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark;
846					use pallet_transaction_payment::benchmarking::Pallet as TransactionPaymentBenchmark;
847
848					let mut list = Vec::<BenchmarkList>::new();
849					list_benchmarks!(list, extra);
850
851					let storage_info = AllPalletsWithSystem::storage_info();
852
853					return (list, storage_info)
854				}
855
856				#[allow(non_local_definitions)]
857				fn dispatch_benchmark(
858					config: frame_benchmarking::BenchmarkConfig,
859				) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, alloc::string::String> {
860					use frame_benchmarking::{add_benchmark, BenchmarkBatch, Benchmarking};
861					use frame_support::traits::TrackedStorageKey;
862					use cumulus_primitives_core::ParaId;
863
864					use xcm::latest::prelude::{
865						GeneralIndex, Junction, Junctions, Location, Response, NetworkId, AssetId, Here,
866						Assets as XcmAssets, Fungible, Asset, ParentThen, Parachain, Parent, WeightLimit,
867						AccountId32,
868					};
869					use xcm_config::{SelfReserve, MaxAssetsIntoHolding, AssetHubLocation, RelayLocation};
870					use frame_benchmarking::BenchmarkError;
871					use xcm_executor::traits::ConvertLocation;
872
873					use frame_system_benchmarking::Pallet as SystemBench;
874					// Needed to run `set_code` and `apply_authorized_upgrade` frame_system benchmarks
875					// https://github.com/paritytech/cumulus/pull/2766
876					impl frame_system_benchmarking::Config for Runtime {
877						fn setup_set_code_requirements(code: &Vec<u8>) -> Result<(), BenchmarkError> {
878							ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32);
879							Ok(())
880						}
881
882						fn verify_set_code() {
883							System::assert_last_event(cumulus_pallet_parachain_system::Event::<Runtime>::ValidationFunctionStored.into());
884						}
885					}
886
887					// Needed to run `charge_transaction_payment` benchmark which distributes
888					// fees to block author. Moonbeam requires an author to be set for fee distribution.
889					use pallet_transaction_payment::benchmarking::Pallet as TransactionPaymentBenchmark;
890					impl pallet_transaction_payment::benchmarking::Config for Runtime {
891						fn setup_benchmark_environment() {
892							// Set a dummy author for the block so fee distribution doesn't panic
893							let author: AccountId = frame_benchmarking::whitelisted_caller();
894							pallet_author_inherent::Author::<Runtime>::put(author);
895						}
896					}
897
898					use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark;
899					parameter_types! {
900						pub const RandomParaId: ParaId = ParaId::new(43211234);
901					}
902
903					/// Custom delivery helper for Moonbeam that works with H160 accounts.
904					/// This is needed because Moonbeam uses AccountKey20 (H160) accounts
905					/// instead of AccountId32, and the standard ToParentDeliveryHelper
906					/// fails when trying to deposit assets to an origin location.
907					pub struct TestDeliveryHelper;
908					impl xcm_builder::EnsureDelivery for TestDeliveryHelper {
909						fn ensure_successful_delivery(
910							origin_ref: &Location,
911							dest: &Location,
912							_fee_reason: xcm_executor::traits::FeeReason,
913						) -> (Option<xcm_executor::FeesMode>, Option<XcmAssets>) {
914							use xcm_executor::traits::ConvertLocation;
915
916							// Ensure the XCM sender is properly configured for benchmarks
917							// This sets up the HostConfiguration for sending messages
918							<xcm_config::XcmRouter as xcm::latest::SendXcm>::ensure_successful_delivery(Some(dest.clone()));
919
920							// Open HRMP channel for sibling parachain destinations
921							if let Some(Parachain(para_id)) = dest.interior().first() {
922								ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(
923									(*para_id).into()
924								);
925							}
926
927							// Deposit existential deposit to the origin account if we can convert it
928							if let Some(account) = xcm_config::LocationToH160::convert_location(origin_ref) {
929								let balance = ExistentialDeposit::get() * 1000u128;
930								let _ = <Balances as frame_support::traits::Currency<_>>::
931									make_free_balance_be(&account.into(), balance);
932							}
933
934							(None, None)
935						}
936					}
937
938					impl pallet_xcm::benchmarking::Config for Runtime {
939				        type DeliveryHelper = TestDeliveryHelper;
940
941						fn get_asset() -> Asset {
942							Asset {
943								id: AssetId(SelfReserve::get()),
944								fun: Fungible(ExistentialDeposit::get()),
945							}
946						}
947
948						fn reachable_dest() -> Option<Location> {
949							Some(Parent.into())
950						}
951
952						fn teleportable_asset_and_dest() -> Option<(Asset, Location)> {
953							None
954						}
955
956						fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> {
957							use xcm_config::SelfReserve;
958
959							ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(
960								RandomParaId::get().into()
961							);
962
963							Some((
964								Asset {
965									fun: Fungible(ExistentialDeposit::get()),
966									id: AssetId(SelfReserve::get().into())
967								},
968								// Moonbeam can reserve transfer native token to
969								// some random parachain.
970								ParentThen(Parachain(RandomParaId::get().into()).into()).into(),
971							))
972						}
973
974						fn set_up_complex_asset_transfer(
975						) -> Option<(XcmAssets, u32, Location, Box<dyn FnOnce()>)> {
976							use xcm_config::SelfReserve;
977
978							let destination: xcm::v5::Location = Parent.into();
979
980							let fee_amount: u128 = <Runtime as pallet_balances::Config>::ExistentialDeposit::get();
981							let fee_asset: Asset = (SelfReserve::get(), fee_amount).into();
982
983							// Give some multiple of transferred amount
984							let balance = fee_amount * 1000;
985							let who = frame_benchmarking::whitelisted_caller();
986							let _ =
987								<Balances as frame_support::traits::Currency<_>>::make_free_balance_be(&who, balance);
988
989							// verify initial balance
990							assert_eq!(Balances::free_balance(&who), balance);
991
992							// set up foreign asset
993							let asset_amount: u128 = 10u128;
994							let initial_asset_amount: u128 = asset_amount * 10;
995
996							let asset_id = pallet_moonbeam_foreign_assets::default_asset_id::<Runtime>() + 1;
997							let (_, location, _) = pallet_moonbeam_foreign_assets::create_default_minted_foreign_asset::<Runtime>(
998								asset_id,
999								initial_asset_amount,
1000							);
1001							let transfer_asset: Asset = (AssetId(location), asset_amount).into();
1002
1003							let assets: XcmAssets = vec![fee_asset.clone(), transfer_asset].into();
1004							let fee_index: u32 = 0;
1005
1006							let verify: Box<dyn FnOnce()> = Box::new(move || {
1007								// verify balance after transfer, decreased by
1008								// transferred amount (and delivery fees)
1009								assert!(Balances::free_balance(&who) <= balance - fee_amount);
1010							});
1011
1012							Some((assets, fee_index, destination, verify))
1013						}
1014					}
1015
1016					impl pallet_xcm_benchmarks::Config for Runtime {
1017						type XcmConfig = xcm_config::XcmExecutorConfig;
1018						type AccountIdConverter = BenchAccountIdConverter<AccountId>;
1019						type DeliveryHelper = TestDeliveryHelper;
1020						fn valid_destination() -> Result<Location, BenchmarkError> {
1021							Ok(Location::parent())
1022						}
1023						fn worst_case_holding(_depositable_count: u32) -> XcmAssets {
1024							const HOLDING_FUNGIBLES: u32 = MaxAssetsIntoHolding::get();
1025							let fungibles_amount: u128 = 1_000 * ExistentialDeposit::get();
1026							let assets = (1..=HOLDING_FUNGIBLES).map(|i| {
1027								let location: Location = GeneralIndex(i as u128).into();
1028								Asset {
1029									id: AssetId(location),
1030									fun: Fungible(fungibles_amount * i as u128),
1031								}
1032								.into()
1033							})
1034							.chain(
1035								core::iter::once(
1036									Asset {
1037										id: AssetId(Location::parent()),
1038										fun: Fungible(u128::MAX)
1039									}
1040								)
1041							)
1042							.collect::<Vec<_>>();
1043
1044
1045							for (i, asset) in assets.iter().enumerate() {
1046								if let Asset {
1047									id: AssetId(location),
1048									fun: Fungible(_)
1049								} = asset {
1050									EvmForeignAssets::set_asset(
1051										location.clone(),
1052										i as u128
1053									);
1054									XcmWeightTrader::set_asset_price(
1055										location.clone(),
1056										10u128.pow(18)
1057									);
1058								}
1059							}
1060							assets.into()
1061						}
1062					}
1063
1064					parameter_types! {
1065						// Native token location
1066						pub const TokenLocation: Location = Here.into_location();
1067						pub TrustedTeleporter: Option<(Location, Asset)> = None;
1068						pub CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None;
1069						// Reserve location and asset used by the `reserve_asset_deposited` benchmark.
1070						// We use DOT (asset id = `RelayLocation`) whose reserve is Asset Hub.
1071						pub TrustedReserve: Option<(Location, Asset)> = Some((
1072							AssetHubLocation::get(),
1073							Asset {
1074								id: AssetId(RelayLocation::get()),
1075								fun: Fungible(100 * ExistentialDeposit::get()),
1076							}
1077						));
1078					}
1079
1080					impl pallet_xcm_benchmarks::fungible::Config for Runtime {
1081						type TransactAsset = Balances;
1082
1083						type CheckedAccount = CheckedAccount;
1084						type TrustedTeleporter = TrustedTeleporter;
1085						type TrustedReserve = TrustedReserve;
1086
1087						fn get_asset() -> Asset {
1088							// We put more than ED here for being able to keep accounts alive when transferring
1089							// and paying the delivery fees.
1090							let location: Location = GeneralIndex(1).into();
1091							let asset_id = 1u128;
1092							let decimals = 18u8;
1093							let asset = Asset {
1094								id: AssetId(location.clone()),
1095								fun: Fungible(100 * ExistentialDeposit::get()),
1096							};
1097							EvmForeignAssets::set_asset(
1098								location.clone(),
1099								asset_id,
1100							);
1101							XcmWeightTrader::set_asset_price(
1102								location.clone(),
1103								10u128.pow(decimals as u32)
1104							);
1105							EvmForeignAssets::create_asset_contract(
1106								asset_id,
1107								decimals,
1108								"TKN",
1109								"Token",
1110							).unwrap();
1111							asset
1112						}
1113					}
1114
1115					impl pallet_xcm_benchmarks::generic::Config for Runtime {
1116						type RuntimeCall = RuntimeCall;
1117						type TransactAsset = Balances;
1118
1119						fn worst_case_response() -> (u64, Response) {
1120							(0u64, Response::Version(Default::default()))
1121						}
1122
1123						fn worst_case_asset_exchange()
1124							-> Result<(XcmAssets, XcmAssets), BenchmarkError> {
1125							Err(BenchmarkError::Skip)
1126						}
1127
1128						fn universal_alias() -> Result<(Location, Junction), BenchmarkError> {
1129							Err(BenchmarkError::Skip)
1130						}
1131
1132						fn export_message_origin_and_destination()
1133							-> Result<(Location, NetworkId, Junctions), BenchmarkError> {
1134							Err(BenchmarkError::Skip)
1135						}
1136
1137						fn transact_origin_and_runtime_call()
1138							-> Result<(Location, RuntimeCall), BenchmarkError> {
1139							Ok((Location::parent(), frame_system::Call::remark_with_event {
1140								remark: vec![]
1141							}.into()))
1142						}
1143
1144						fn subscribe_origin() -> Result<Location, BenchmarkError> {
1145							Ok(Location::parent())
1146						}
1147
1148						fn claimable_asset()
1149							-> Result<(Location, Location, XcmAssets), BenchmarkError> {
1150							let origin = Location::parent();
1151							let assets: XcmAssets = (AssetId(Location::parent()), 1_000u128)
1152								.into();
1153							let ticket = Location { parents: 0, interior: [].into() /* Here */ };
1154							Ok((origin, ticket, assets))
1155						}
1156
1157						fn worst_case_for_trader() -> Result<(Asset, WeightLimit), BenchmarkError> {
1158							let location: Location = GeneralIndex(1).into();
1159							Ok((
1160								Asset {
1161									id: AssetId(Location::parent()),
1162									fun: Fungible(1_000_000_000_000_000 as u128)
1163								},
1164								WeightLimit::Limited(Weight::from_parts(5000, 5000)),
1165							))
1166						}
1167
1168						fn unlockable_asset()
1169							-> Result<(Location, Location, Asset), BenchmarkError> {
1170							Err(BenchmarkError::Skip)
1171						}
1172
1173						fn alias_origin() -> Result<(Location, Location), BenchmarkError> {
1174							Err(BenchmarkError::Skip)
1175						}
1176					}
1177
1178					$($bench_custom)*
1179
1180					let whitelist: Vec<TrackedStorageKey> = vec![
1181						// Block Number
1182						hex_literal::hex!(  "26aa394eea5630e07c48ae0c9558cef7"
1183											"02a5c1b19ab7a04f536c519aca4983ac")
1184							.to_vec().into(),
1185						// Total Issuance
1186						hex_literal::hex!(  "c2261276cc9d1f8598ea4b6a74b15c2f"
1187											"57c875e4cff74148e4628f264b974c80")
1188							.to_vec().into(),
1189						// Execution Phase
1190						hex_literal::hex!(  "26aa394eea5630e07c48ae0c9558cef7"
1191											"ff553b5a9862a516939d82b3d3d8661a")
1192							.to_vec().into(),
1193						// Event Count
1194						hex_literal::hex!(  "26aa394eea5630e07c48ae0c9558cef7"
1195											"0a98fdbe9ce6c55837576c60c7af3850")
1196							.to_vec().into(),
1197						// System Events
1198						hex_literal::hex!(  "26aa394eea5630e07c48ae0c9558cef7"
1199											"80d41e5e16056765bc8461851072c9d7")
1200							.to_vec().into(),
1201						// System BlockWeight
1202						hex_literal::hex!(  "26aa394eea5630e07c48ae0c9558cef7"
1203											"34abf5cb34d6244378cddbf18e849d96")
1204							.to_vec().into(),
1205						// ParachainStaking Round
1206						hex_literal::hex!(  "a686a3043d0adcf2fa655e57bc595a78"
1207											"13792e785168f725b60e2969c7fc2552")
1208							.to_vec().into(),
1209						// Treasury Account (py/trsry)
1210						hex_literal::hex!(  "26aa394eea5630e07c48ae0c9558cef7"
1211											"b99d880ec681799c0cf30e8886371da9"
1212											"7be2919ac397ba499ea5e57132180ec6"
1213											"6d6f646c70792f747273727900000000"
1214											"00000000"
1215						).to_vec().into(),
1216						// Treasury Account (pc/trsry)
1217						hex_literal::hex!(  "26aa394eea5630e07c48ae0c9558cef7"
1218											"b99d880ec681799c0cf30e8886371da9"
1219											"7be2919ac397ba499ea5e57132180ec6"
1220											"6d6f646c70632f747273727900000000"
1221											"00000000"
1222						).to_vec().into(),
1223						// ParachainInfo ParachainId
1224						hex_literal::hex!(  "0d715f2646c8f85767b5d2764bb27826"
1225											"04a74d81251e398fd8a0a4d55023bb3f")
1226							.to_vec().into(),
1227						// Parameters Parameters
1228						hex_literal::hex!(  "c63bdd4a39095ccf55623a6f2872bf8a" // Pallet: "Parameters"
1229											"c63bdd4a39095ccf55623a6f2872bf8a" // Storage Prefix: "Parameters"
1230											// MoonbaseRuntimeRuntimeParamsRuntimeParametersKey(FeesTreasuryProportion)
1231											"71d0aacb690b61280d0c97c6b6a666640000"
1232										)
1233							.to_vec().into(),
1234
1235					];
1236
1237					let mut batches = Vec::<BenchmarkBatch>::new();
1238					let params = (&config, &whitelist);
1239
1240					add_benchmarks!(params, batches);
1241
1242					Ok(batches)
1243				}
1244			}
1245
1246			#[cfg(feature = "try-runtime")]
1247			impl frame_try_runtime::TryRuntime<Block> for Runtime {
1248				fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) {
1249					log::info!("try-runtime::on_runtime_upgrade()");
1250					// NOTE: intentional expect: we don't want to propagate the error backwards,
1251					// and want to have a backtrace here. If any of the pre/post migration checks
1252					// fail, we shall stop right here and right now.
1253					let weight = Executive::try_runtime_upgrade(checks)
1254						.expect("runtime upgrade logic *must* be infallible");
1255					(weight, RuntimeBlockWeights::get().max_block)
1256				}
1257
1258				fn execute_block(
1259					block: Block,
1260					state_root_check: bool,
1261					signature_check: bool,
1262					select: frame_try_runtime::TryStateSelect
1263				) -> Weight {
1264					log::info!(
1265						"try-runtime: executing block {:?} / root checks: {:?} / try-state-select: {:?}",
1266						block.header.hash(),
1267						state_root_check,
1268						select,
1269					);
1270					// NOTE: intentional unwrap: we don't want to propagate the error backwards,
1271					// and want to have a backtrace here.
1272					Executive::try_execute_block(
1273						block,
1274						state_root_check,
1275						signature_check,
1276						select,
1277					).expect("execute-block failed")
1278				}
1279			}
1280		}
1281	};
1282}