moonbeam_runtime_common/
impl_moonbeam_xcm_call_tracing.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_moonbeam_xcm_call_tracing {
19	{} => {
20		use moonbeam_evm_tracer::tracer::EthereumTracingStatus;
21		type CallResult =
22			Result<
23				PostDispatchInfoOf<RuntimeCall>,
24				DispatchErrorWithPostInfo<PostDispatchInfoOf<RuntimeCall>>
25			>;
26
27		pub struct MoonbeamCall;
28		impl CallDispatcher<RuntimeCall> for MoonbeamCall {
29			fn dispatch(
30				call: RuntimeCall,
31				origin: RuntimeOrigin,
32			) -> CallResult {
33				if let Ok(raw_origin) = TryInto::<RawOrigin<AccountId>>::try_into(origin.clone().caller) {
34					match call.clone() {
35						RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::transact { xcm_transaction }) |
36						RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::transact_through_proxy {
37							xcm_transaction, ..
38						}) |
39						RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::force_transact_as {
40							xcm_transaction, ..
41					 	}) => {
42							use crate::EthereumXcm;
43							use moonbeam_evm_tracer::tracer::{
44								EthereumTracer,
45								EvmTracer,
46								EthereumTracingStatus
47							};
48							use xcm_primitives::{
49								XcmToEthereum,
50							};
51							use frame_support::storage::unhashed;
52							use frame_support::traits::Get;
53
54							let dispatch_call = || {
55								RuntimeCall::dispatch(
56									call,
57									match raw_origin {
58										RawOrigin::Signed(account_id) => {
59											pallet_ethereum_xcm::Origin::XcmEthereumTransaction(
60												account_id.into()
61											).into()
62										},
63										origin => origin.into()
64									}
65								)
66							};
67
68							return match EthereumTracer::status() {
69								// This runtime instance is used for tracing.
70								Some(tracing_status) => {
71									match tracing_status {
72										// Tracing a block, all calls are done using environmental.
73										EthereumTracingStatus::Block => {
74											// Each known extrinsic is a new call stack.
75											EvmTracer::emit_new();
76											let mut res: Option<CallResult> = None;
77											EvmTracer::new().trace(|| {
78												res = Some(dispatch_call());
79											});
80											res.expect("Invalid dispatch result")
81										},
82										// Tracing a transaction, the one matching the trace request
83										// is done using environmental, the rest dispatched normally.
84										EthereumTracingStatus::Transaction(traced_transaction_hash) => {
85											let transaction_hash = xcm_transaction.into_transaction(
86												EthereumXcm::nonce(),
87												<Runtime as pallet_evm::Config>::ChainId::get(),
88												false
89											)
90											.expect("Invalid transaction conversion")
91											.hash();
92											if transaction_hash == traced_transaction_hash {
93												let mut res: Option<CallResult> = None;
94												EvmTracer::new().trace(|| {
95													res = Some(dispatch_call());
96												});
97												// Tracing runtime work is done, just signal instance exit.
98												EthereumTracer::transaction_exited();
99												return res.expect("Invalid dispatch result");
100											}
101											dispatch_call()
102										},
103										// Tracing a transaction that has already been found and
104										// executed. There's no need to dispatch the rest of the
105										// calls.
106										EthereumTracingStatus::TransactionExited => Ok(crate::PostDispatchInfo {
107											actual_weight: None,
108											pays_fee: frame_support::pallet_prelude::Pays::No,
109										}),
110									}
111								}
112								// This runtime instance is importing a block.
113								None => dispatch_call()
114							};
115						},
116						_ => {}
117					}
118				}
119				RuntimeCall::dispatch(call, origin)
120			}
121		}
122	}
123}