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]
18
macro_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
641
			fn dispatch(
30
641
				call: RuntimeCall,
31
641
				origin: RuntimeOrigin,
32
641
			) -> CallResult {
33
641
				if let Ok(raw_origin) = TryInto::<RawOrigin<AccountId>>::try_into(origin.clone().caller) {
34
641
					match call.clone() {
35
467
						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
174
							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
641
							let dispatch_call = || {
55
641
								RuntimeCall::dispatch(
56
641
									call,
57
641
									match raw_origin {
58
467
										RawOrigin::Signed(account_id) => {
59
467
											pallet_ethereum_xcm::Origin::XcmEthereumTransaction(
60
467
												account_id.into()
61
467
											).into()
62
										},
63
174
										origin => origin.into()
64
									}
65
								)
66
641
							};
67

            
68
641
							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
641
								None => dispatch_call()
114
							};
115
						},
116
						_ => {}
117
					}
118
				}
119
				RuntimeCall::dispatch(call, origin)
120
641
			}
121
		}
122
	}
123
}