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
use frame_support::storage::{with_transaction, TransactionOutcome};
18
use sp_runtime::DispatchError;
19
use xcm::latest::prelude::*;
20
use xcm_executor::traits::ProcessTransaction;
21

            
22
63
environmental::environmental!(IS_EVM_REVERT: bool);
23

            
24
/// Transactional processor implementation used by the XCM executor
25
/// to execute each XCM instruction in a transactional way.
26
///
27
/// Behave like FrameTransactionalProcessor except if the XCM instruction call the EVM AND the EVM Revert has occurred.
28
/// In this case, the storage changes should be committed to include the eth-xcm transaction in the "ethereum block storage".
29
pub struct XcmEthTransactionalProcessor;
30

            
31
impl XcmEthTransactionalProcessor {
32
4
	pub fn signal_evm_revert() {
33
4
		IS_EVM_REVERT::with(|is_evm_revert| *is_evm_revert = true);
34
4
	}
35
}
36

            
37
impl ProcessTransaction for XcmEthTransactionalProcessor {
38
	const IS_TRANSACTIONAL: bool = true;
39

            
40
76
	fn process<F>(f: F) -> Result<(), XcmError>
41
76
	where
42
76
		F: FnOnce() -> Result<(), XcmError>,
43
76
	{
44
76
		IS_EVM_REVERT::using(&mut false, || {
45
76
			with_transaction(|| -> TransactionOutcome<Result<_, DispatchError>> {
46
76
				let output = f();
47
76
				match &output {
48
76
					Ok(()) => TransactionOutcome::Commit(Ok(output)),
49
					Err(xcm_error) => {
50
						// If the XCM instruction failed from an EVM revert,
51
						// we should not rollback storage change
52
						if let Some(true) = IS_EVM_REVERT::with(|is_evm_revert| *is_evm_revert) {
53
							TransactionOutcome::Commit(Ok(output))
54
						} else {
55
							// Otherwise, we should rollback storage changes
56
							// to be consistent with FrameTransactionalProcessor
57
							TransactionOutcome::Rollback(Ok(Err(*xcm_error)))
58
						}
59
					}
60
				}
61
76
			})
62
76
			.map_err(|_| XcmError::ExceedsStackLimit)?
63
76
		})
64
76
	}
65
}