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

            
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
300
			fn dispatch(
30
300
				call: RuntimeCall,
31
300
				origin: RuntimeOrigin,
32
300
			) -> CallResult {
33
300
				if let Ok(raw_origin) = TryInto::<RawOrigin<AccountId>>::try_into(origin.clone().caller) {
34
300
					match call.clone() {
35
218
						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
82
							xcm_transaction, ..
41
					 	}) => {
42
							use crate::EthereumXcm;
43
							use moonbeam_evm_tracer::tracer::EvmTracer;
44
							use xcm_primitives::{
45
								XcmToEthereum,
46
								EthereumXcmTracingStatus,
47
								ETHEREUM_XCM_TRACING_STORAGE_KEY
48
							};
49
							use frame_support::storage::unhashed;
50
							use frame_support::traits::Get;
51

            
52
300
							let dispatch_call = || {
53
300
								RuntimeCall::dispatch(
54
300
									call,
55
300
									match raw_origin {
56
218
										RawOrigin::Signed(account_id) => {
57
218
											pallet_ethereum_xcm::Origin::XcmEthereumTransaction(
58
218
												account_id.into()
59
218
											).into()
60
										},
61
82
										origin => origin.into()
62
									}
63
								)
64
300
							};
65
300

            
66
300
							return match unhashed::get(
67
300
								ETHEREUM_XCM_TRACING_STORAGE_KEY
68
300
							) {
69
								// This runtime instance is used for tracing.
70
								Some(tracing_status) => match tracing_status {
71
									// Tracing a block, all calls are done using environmental.
72
									EthereumXcmTracingStatus::Block => {
73
										// Each known extrinsic is a new call stack.
74
										EvmTracer::emit_new();
75
										let mut res: Option<CallResult> = None;
76
										EvmTracer::new().trace(|| {
77
											res = Some(dispatch_call());
78
										});
79
										res.expect("Invalid dispatch result")
80
									},
81
									// Tracing a transaction, the one matching the trace request
82
									// is done using environmental, the rest dispatched normally.
83
									EthereumXcmTracingStatus::Transaction(traced_transaction_hash) => {
84
										let transaction_hash = xcm_transaction.into_transaction_v2(
85
											EthereumXcm::nonce(),
86
											<Runtime as pallet_evm::Config>::ChainId::get(),
87
											false
88
										)
89
										.expect("Invalid transaction conversion")
90
										.hash();
91
										if transaction_hash == traced_transaction_hash {
92
											let mut res: Option<CallResult> = None;
93
											EvmTracer::new().trace(|| {
94
												res = Some(dispatch_call());
95
											});
96
											// Tracing runtime work is done, just signal instance exit.
97
											unhashed::put::<EthereumXcmTracingStatus>(
98
												xcm_primitives::ETHEREUM_XCM_TRACING_STORAGE_KEY,
99
												&EthereumXcmTracingStatus::TransactionExited,
100
											);
101
											return res.expect("Invalid dispatch result");
102
										}
103
										dispatch_call()
104
									},
105
									// Tracing a transaction that has already been found and
106
									// executed. There's no need to dispatch the rest of the
107
									// calls.
108
									EthereumXcmTracingStatus::TransactionExited => Ok(crate::PostDispatchInfo {
109
										actual_weight: None,
110
										pays_fee: frame_support::pallet_prelude::Pays::No,
111
									}),
112
								},
113
								// This runtime instance is importing a block.
114
300
								None => dispatch_call()
115
							};
116
						},
117
						_ => {}
118
					}
119
				}
120
				RuntimeCall::dispatch(call, origin)
121
			}
122
		}
123
	}
124
}