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

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

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

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

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

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

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

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

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

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

            
202
19
				let mut execution_info: Option<CallOrCreateInfo> = None;
203
19
				pallet_ethereum::catch_exec_info(&mut execution_info, || {
204
19
					CallDispatcher::dispatch(
205
19
						RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::force_transact_as {
206
19
							transact_as: source,
207
19
							xcm_transaction,
208
19
							force_create_address: Some(force_address),
209
19
						}),
210
19
						RawOrigin::Root.into(),
211
19
					)
212
19
					.map_err(|DispatchErrorWithPostInfo { error, .. }| RunnerError {
213
						error,
214
						weight: Default::default(),
215
19
					})
216
19
				})?;
217

            
218
19
				if let Some(CallOrCreateInfo::Create(create_info))= execution_info {
219
19
					Ok(create_info)
220
				} else {
221
					// `execution_info` must have been filled in
222
					Err(RunnerError {
223
						error: DispatchError::Unavailable,
224
						weight: Default::default(),
225
					})
226
				}
227
19
			}
228
19

            
229
19
			fn validate(
230
				_source: H160,
231
				_target: Option<H160>,
232
				_input: Vec<u8>,
233
				_value: U256,
234
				_gas_limit: u64,
235
				_max_fee_per_gas: Option<U256>,
236
				_max_priority_fee_per_gas: Option<U256>,
237
				_nonce: Option<U256>,
238
				_access_list: Vec<(H160, Vec<H256>)>,
239
				_authorization_list: Vec<(U256, H160, U256, Option<H160>)>,
240
				_is_transactional: bool,
241
				_weight_limit: Option<Weight>,
242
				_transaction_len: Option<u64>,
243
				_evm_config: &fp_evm::Config,
244
			) -> Result<(), RunnerError<Self::Error>> {
245
				unimplemented!()
246
			}
247
		}
248

            
249
	}
250
}