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 crate::mock::{
18
	sent_xcm, AccountId, Balances, ExtBuilder, PCall, ParentAccount, Precompiles, PrecompilesValue,
19
	Runtime, SiblingParachainAccount, System,
20
};
21
use frame_support::{traits::PalletInfo, weights::Weight};
22
use parity_scale_codec::Encode;
23
use precompile_utils::{prelude::*, testing::*};
24
use sp_core::{H160, U256};
25
use xcm::prelude::*;
26

            
27
9
fn precompiles() -> Precompiles<Runtime> {
28
9
	PrecompilesValue::get()
29
9
}
30

            
31
#[test]
32
1
fn test_selector_enum() {
33
1
	assert!(PCall::multilocation_to_address_selectors().contains(&0x343b3e00));
34
1
	assert!(PCall::weight_message_selectors().contains(&0x25d54154));
35
1
	assert!(PCall::get_units_per_second_selectors().contains(&0x3f0f65db));
36
1
}
37

            
38
#[test]
39
1
fn modifiers() {
40
1
	ExtBuilder::default().build().execute_with(|| {
41
1
		let mut tester = PrecompilesModifierTester::new(precompiles(), Alice, Precompile1);
42
1

            
43
1
		tester.test_view_modifier(PCall::multilocation_to_address_selectors());
44
1
		tester.test_view_modifier(PCall::weight_message_selectors());
45
1
		tester.test_view_modifier(PCall::get_units_per_second_selectors());
46
1
	});
47
1
}
48

            
49
#[test]
50
1
fn test_get_account_parent() {
51
1
	ExtBuilder::default().build().execute_with(|| {
52
1
		let input = PCall::multilocation_to_address {
53
1
			location: Location::parent(),
54
1
		};
55
1

            
56
1
		let expected_address: H160 = ParentAccount.into();
57
1

            
58
1
		precompiles()
59
1
			.prepare_test(Alice, Precompile1, input)
60
1
			.expect_cost(2)
61
1
			.expect_no_logs()
62
1
			.execute_returns(Address(expected_address));
63
1
	});
64
1
}
65

            
66
#[test]
67
1
fn test_get_account_sibling() {
68
1
	ExtBuilder::default().build().execute_with(|| {
69
1
		let input = PCall::multilocation_to_address {
70
1
			location: Location {
71
1
				parents: 1,
72
1
				interior: [Junction::Parachain(2000u32)].into(),
73
1
			}
74
1
			.into(),
75
1
		};
76
1

            
77
1
		let expected_address: H160 = SiblingParachainAccount(2000u32).into();
78
1

            
79
1
		precompiles()
80
1
			.prepare_test(Alice, Precompile1, input)
81
1
			.expect_cost(2)
82
1
			.expect_no_logs()
83
1
			.execute_returns(Address(expected_address));
84
1
	});
85
1
}
86

            
87
#[test]
88
1
fn test_weight_message() {
89
1
	ExtBuilder::default().build().execute_with(|| {
90
1
		let message: Vec<u8> = xcm::VersionedXcm::<()>::V5(Xcm(vec![ClearOrigin])).encode();
91
1

            
92
1
		let input = PCall::weight_message {
93
1
			message: message.into(),
94
1
		};
95
1

            
96
1
		precompiles()
97
1
			.prepare_test(Alice, Precompile1, input)
98
1
			.expect_cost(1)
99
1
			.expect_no_logs()
100
1
			.execute_returns(1000u64);
101
1
	});
102
1
}
103

            
104
#[test]
105
1
fn test_get_units_per_second() {
106
1
	ExtBuilder::default().build().execute_with(|| {
107
1
		let input = PCall::get_units_per_second {
108
1
			location: Location::parent(),
109
1
		};
110
1

            
111
1
		precompiles()
112
1
			.prepare_test(Alice, Precompile1, input)
113
1
			.expect_cost(2)
114
1
			.expect_no_logs()
115
1
			.execute_returns(U256::from(1_000_000_000_000u128));
116
1
	});
117
1
}
118

            
119
#[test]
120
1
fn test_executor_clear_origin() {
121
1
	ExtBuilder::default().build().execute_with(|| {
122
1
		let xcm_to_execute = VersionedXcm::<()>::V5(Xcm(vec![ClearOrigin])).encode();
123
1

            
124
1
		let input = PCall::xcm_execute {
125
1
			message: xcm_to_execute.into(),
126
1
			weight: 10000u64,
127
1
		};
128
1

            
129
1
		precompiles()
130
1
			.prepare_test(Alice, Precompile1, input)
131
1
			.expect_cost(100001001)
132
1
			.expect_no_logs()
133
1
			.execute_returns(());
134
1
	})
135
1
}
136

            
137
#[test]
138
1
fn test_executor_send() {
139
1
	ExtBuilder::default().build().execute_with(|| {
140
1
		let withdrawn_asset: Asset = (Location::parent(), 1u128).into();
141
1
		let xcm_to_execute = VersionedXcm::<()>::V5(Xcm(vec![
142
1
			WithdrawAsset(vec![withdrawn_asset].into()),
143
1
			InitiateReserveWithdraw {
144
1
				assets: AssetFilter::Wild(All),
145
1
				reserve: Location::parent(),
146
1
				xcm: Xcm(vec![]),
147
1
			},
148
1
		]))
149
1
		.encode();
150
1

            
151
1
		let input = PCall::xcm_execute {
152
1
			message: xcm_to_execute.into(),
153
1
			weight: 10000u64,
154
1
		};
155
1

            
156
1
		precompiles()
157
1
			.prepare_test(Alice, Precompile1, input)
158
1
			.expect_cost(100002001)
159
1
			.expect_no_logs()
160
1
			.execute_returns(());
161
1

            
162
1
		let sent_messages = sent_xcm();
163
1
		let (_, sent_message) = sent_messages.first().unwrap();
164
1
		// Lets make sure the message is as expected
165
1
		assert!(sent_message.0.contains(&ClearOrigin));
166
1
	});
167
1
}
168

            
169
#[test]
170
1
fn test_executor_transact() {
171
1
	ExtBuilder::default()
172
1
		.with_balances(vec![(CryptoAlith.into(), 1000000000)])
173
1
		.build()
174
1
		.execute_with(|| {
175
1
			let mut encoded: Vec<u8> = Vec::new();
176
1
			let index =
177
1
				<Runtime as frame_system::Config>::PalletInfo::index::<Balances>().unwrap() as u8;
178
1

            
179
1
			encoded.push(index);
180
1

            
181
1
			// Then call bytes
182
1
			let mut call_bytes = pallet_balances::Call::<Runtime>::transfer_allow_death {
183
1
				dest: CryptoBaltathar.into(),
184
1
				value: 100u32.into(),
185
1
			}
186
1
			.encode();
187
1
			encoded.append(&mut call_bytes);
188
1
			let xcm_to_execute = VersionedXcm::<()>::V5(Xcm(vec![Transact {
189
1
				origin_kind: OriginKind::SovereignAccount,
190
1
				fallback_max_weight: Some(Weight::from_parts(1_000_000_000u64, 5206u64)),
191
1
				call: encoded.into(),
192
1
			}]))
193
1
			.encode();
194
1

            
195
1
			let input = PCall::xcm_execute {
196
1
				message: xcm_to_execute.into(),
197
1
				weight: 2_000_000_000u64,
198
1
			};
199
1

            
200
1
			precompiles()
201
1
				.prepare_test(CryptoAlith, Precompile1, input)
202
1
				.expect_cost(276106001)
203
1
				.expect_no_logs()
204
1
				.execute_returns(());
205
1

            
206
1
			// Transact executed
207
1
			let baltathar_account: AccountId = CryptoBaltathar.into();
208
1
			assert_eq!(System::account(baltathar_account).data.free, 100);
209
1
		});
210
1
}
211

            
212
#[test]
213
1
fn test_send_clear_origin() {
214
1
	ExtBuilder::default().build().execute_with(|| {
215
1
		let xcm_to_send = VersionedXcm::<()>::V5(Xcm(vec![ClearOrigin])).encode();
216
1

            
217
1
		let input = PCall::xcm_send {
218
1
			dest: Location::parent(),
219
1
			message: xcm_to_send.into(),
220
1
		};
221
1

            
222
1
		precompiles()
223
1
			.prepare_test(CryptoAlith, Precompile1, input)
224
1
			// Only the cost of TestWeightInfo
225
1
			.expect_cost(100000001)
226
1
			.expect_no_logs()
227
1
			.execute_returns(());
228
1

            
229
1
		let sent_messages = sent_xcm();
230
1
		let (_, sent_message) = sent_messages.first().unwrap();
231
1
		// Lets make sure the message is as expected
232
1
		assert!(sent_message.0.contains(&ClearOrigin));
233
1
	})
234
1
}
235

            
236
#[test]
237
1
fn execute_fails_if_called_by_smart_contract() {
238
1
	ExtBuilder::default()
239
1
		.with_balances(vec![
240
1
			(CryptoAlith.into(), 1000),
241
1
			(CryptoBaltathar.into(), 1000),
242
1
		])
243
1
		.build()
244
1
		.execute_with(|| {
245
1
			// Set code to Alice address as it if was a smart contract.
246
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
247
1
			pallet_evm::AccountCodesMetadata::<Runtime>::insert(
248
1
				H160::from(Alice),
249
1
				pallet_evm::CodeMetadata {
250
1
					size: 10,
251
1
					hash: sp_core::H256::default(),
252
1
				},
253
1
			);
254
1

            
255
1
			let xcm_to_execute = VersionedXcm::<()>::V5(Xcm(vec![ClearOrigin])).encode();
256
1

            
257
1
			let input = PCall::xcm_execute {
258
1
				message: xcm_to_execute.into(),
259
1
				weight: 10000u64,
260
1
			};
261
1

            
262
1
			PrecompilesValue::get()
263
1
				.prepare_test(Alice, Precompile1, input)
264
1
				.execute_reverts(|output| output == b"Function not callable by smart contracts");
265
1
		})
266
1
}
267

            
268
#[test]
269
1
fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() {
270
1
	check_precompile_implements_solidity_interfaces(&["XcmUtils.sol"], PCall::supports_selector)
271
1
}