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_evm_runner_precompile_or_eth_xcm {
19
	{} => {
20
		use fp_evm::{CallInfo, CallOrCreateInfo, Context, Transfer};
21
		use frame_support::dispatch::CallableCallFor;
22
		use pallet_evm::{Runner, RunnerError};
23
		use precompile_utils::{prelude::*, evm::handle::with_precompile_handle};
24
		use sp_core::U256;
25
		use sp_runtime::DispatchError;
26
		use sp_std::vec::Vec;
27
		use xcm_primitives::{EthereumXcmTransaction, EthereumXcmTransactionV2};
28

            
29
		pub struct EvmRunnerPrecompileOrEthXcm<CallDispatcher, Runtime>(
30
			core::marker::PhantomData<(CallDispatcher, Runtime)>,
31
		);
32

            
33
		impl<CallDispatcher, Runtime> Runner<Runtime>
34
			for EvmRunnerPrecompileOrEthXcm<CallDispatcher, Runtime>
35
		where
36
			CallDispatcher: xcm_executor::traits::CallDispatcher<RuntimeCall>,
37
			Runtime: pallet_evm::Config + pallet_ethereum_xcm::Config,
38
			Runtime::RuntimeOrigin: From<pallet_ethereum_xcm::RawOrigin>,
39
		{
40
			type Error = DispatchError;
41

            
42
59
			fn call(
43
59
				source: H160,
44
59
				target: H160,
45
59
				input: Vec<u8>,
46
59
				value: U256,
47
59
				gas_limit: u64,
48
59
				_max_fee_per_gas: Option<U256>,
49
59
				_max_priority_fee_per_gas: Option<U256>,
50
59
				_nonce: Option<U256>,
51
59
				access_list: Vec<(H160, Vec<H256>)>,
52
59
				_is_transactional: bool,
53
59
				_validate: bool,
54
59
				_weight_limit: Option<Weight>,
55
59
				_transaction_len: Option<u64>,
56
59
				_config: &fp_evm::Config,
57
59
			) -> Result<CallInfo, RunnerError<Self::Error>> {
58
				// The `with_precompile_handle` function will execute the closure (and return the
59
				// result in a Some) if and only if there is an available EVM context. Otherwise,
60
				// it will return None.
61
59
				if let Some((exit_reason, value)) = with_precompile_handle(|precompile_handle| {
62
27
					let transfer = if value.is_zero() {
63
27
						None
64
					} else {
65
						Some(Transfer {
66
							source,
67
							target,
68
							value,
69
						})
70
					};
71

            
72
27
					precompile_handle.call(
73
27
						target,
74
27
						transfer,
75
27
						input.clone(),
76
27
						Some(gas_limit),
77
27
						false,
78
27
						&Context {
79
27
							address: target,
80
27
							caller: source,
81
27
							apparent_value: value,
82
27
						},
83
27
					)
84
59
				}) {
85
27
					Ok(CallInfo {
86
27
						exit_reason,
87
27
						value,
88
27
						used_gas: fp_evm::UsedGas {
89
27
							standard: U256::default(),
90
27
							effective: U256::default(),
91
27
						},
92
27
						logs: Default::default(),
93
27
						weight_info: None,
94
27
					})
95
				} else {
96
32
					let xcm_transaction = EthereumXcmTransaction::V2(EthereumXcmTransactionV2 {
97
32
						gas_limit: gas_limit.into(),
98
32
						action: pallet_ethereum_xcm::TransactionAction::Call(target),
99
32
						value,
100
32
						input: input.try_into().map_err(|_| RunnerError {
101
							error: DispatchError::Exhausted,
102
							weight: Default::default(),
103
32
						})?,
104
32
						access_list: Some(access_list),
105
32
					});
106
32

            
107
32
					let mut execution_info: Option<CallOrCreateInfo> = None;
108
32
					pallet_ethereum::catch_exec_info(&mut execution_info, || {
109
32
						CallDispatcher::dispatch(
110
32
							RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::transact { xcm_transaction }),
111
32
							RawOrigin::Signed(source.into()).into(),
112
32
						)
113
32
						.map_err(|DispatchErrorWithPostInfo { error, .. }| RunnerError {
114
							error,
115
							weight: Default::default(),
116
32
						})
117
32
					})?;
118

            
119
32
					if let Some(CallOrCreateInfo::Call(call_info))= execution_info {
120
32
						Ok(call_info)
121
					} else {
122
						// `execution_info` must have been filled in
123
						Err(RunnerError {
124
							error: DispatchError::Unavailable,
125
							weight: Default::default(),
126
						})
127
					}
128
				}
129
59
			}
130
59

            
131
59
			fn create(
132
				_source: H160,
133
				_init: Vec<u8>,
134
				_value: U256,
135
				_gas_limit: u64,
136
				_max_fee_per_gas: Option<U256>,
137
				_max_priority_fee_per_gas: Option<U256>,
138
				_nonce: Option<U256>,
139
				_access_list: Vec<(H160, Vec<H256>)>,
140
				_is_transactional: bool,
141
				_validate: bool,
142
				_weight_limit: Option<Weight>,
143
				_transaction_len: Option<u64>,
144
				_config: &fp_evm::Config,
145
			) -> Result<fp_evm::CreateInfo, RunnerError<Self::Error>> {
146
				unimplemented!()
147
			}
148

            
149
			fn create2(
150
				_source: H160,
151
				_init: Vec<u8>,
152
				_salt: H256,
153
				_value: U256,
154
				_gas_limit: u64,
155
				_max_fee_per_gas: Option<U256>,
156
				_max_priority_fee_per_gas: Option<U256>,
157
				_nonce: Option<U256>,
158
				_access_list: Vec<(H160, Vec<H256>)>,
159
				_is_transactional: bool,
160
				_validate: bool,
161
				_weight_limit: Option<Weight>,
162
				_transaction_len: Option<u64>,
163
				_config: &fp_evm::Config,
164
			) -> Result<fp_evm::CreateInfo, RunnerError<Self::Error>> {
165
				unimplemented!()
166
			}
167

            
168
12
			fn create_force_address(
169
12
				source: H160,
170
12
				init: Vec<u8>,
171
12
				value: U256,
172
12
				gas_limit: u64,
173
12
				max_fee_per_gas: Option<U256>,
174
12
				max_priority_fee_per_gas: Option<U256>,
175
12
				nonce: Option<U256>,
176
12
				access_list: Vec<(H160, Vec<H256>)>,
177
12
				is_transactional: bool,
178
12
				validate: bool,
179
12
				weight_limit: Option<Weight>,
180
12
				transaction_len: Option<u64>,
181
12
				config: &fp_evm::Config,
182
12
				force_address: H160,
183
12
			) -> Result<fp_evm::CreateInfo, RunnerError<Self::Error>> {
184
12
				let xcm_transaction = EthereumXcmTransaction::V2(EthereumXcmTransactionV2 {
185
12
					gas_limit: gas_limit.into(),
186
12
					action: pallet_ethereum_xcm::TransactionAction::Create,
187
12
					value,
188
12
					input: init.try_into().map_err(|_| RunnerError {
189
						error: DispatchError::Exhausted,
190
						weight: Default::default(),
191
12
					})?,
192
12
					access_list: Some(access_list),
193
12
				});
194
12

            
195
12
				let mut execution_info: Option<CallOrCreateInfo> = None;
196
12
				pallet_ethereum::catch_exec_info(&mut execution_info, || {
197
12
					CallDispatcher::dispatch(
198
12
						RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::force_transact_as {
199
12
							transact_as: source,
200
12
							xcm_transaction,
201
12
							force_create_address: Some(force_address),
202
12
						}),
203
12
						RawOrigin::Root.into(),
204
12
					)
205
12
					.map_err(|DispatchErrorWithPostInfo { error, .. }| RunnerError {
206
						error,
207
						weight: Default::default(),
208
12
					})
209
12
				})?;
210

            
211
12
				if let Some(CallOrCreateInfo::Create(create_info))= execution_info {
212
12
					Ok(create_info)
213
				} else {
214
					// `execution_info` must have been filled in
215
					Err(RunnerError {
216
						error: DispatchError::Unavailable,
217
						weight: Default::default(),
218
					})
219
				}
220
12
			}
221
12

            
222
12
			fn validate(
223
				_source: H160,
224
				_target: Option<H160>,
225
				_input: Vec<u8>,
226
				_value: U256,
227
				_gas_limit: u64,
228
				_max_fee_per_gas: Option<U256>,
229
				_max_priority_fee_per_gas: Option<U256>,
230
				_nonce: Option<U256>,
231
				_access_list: Vec<(H160, Vec<H256>)>,
232
				_is_transactional: bool,
233
				_weight_limit: Option<Weight>,
234
				_transaction_len: Option<u64>,
235
				_evm_config: &fp_evm::Config,
236
			) -> Result<(), RunnerError<Self::Error>> {
237
				unimplemented!()
238
			}
239
		}
240

            
241
	}
242
}