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::{
18
	assert_event_emitted, assert_event_not_emitted,
19
	mock::{
20
		AccountId, ExtBuilder, PCall, PrecompilesValue, ProxyType, Runtime, RuntimeCall,
21
		RuntimeEvent, RuntimeOrigin,
22
	},
23
};
24
use frame_support::assert_ok;
25
use pallet_evm::Call as EvmCall;
26
use pallet_proxy::{
27
	Call as ProxyCall, Event as ProxyEvent, Pallet as ProxyPallet, ProxyDefinition,
28
};
29
use precompile_utils::{precompile_set::AddressU64, prelude::*, testing::*};
30
use sp_core::{Get, H160, H256, U256};
31
use sp_runtime::traits::Dispatchable;
32
use std::cell::Cell;
33
use std::rc::Rc;
34
use std::str::from_utf8;
35

            
36
#[test]
37
1
fn test_selector_less_than_four_bytes_reverts() {
38
1
	ExtBuilder::default().build().execute_with(|| {
39
1
		PrecompilesValue::get()
40
1
			.prepare_test(Alice, Precompile1, vec![1u8, 2, 3])
41
1
			.execute_reverts(|output| output == b"Tried to read selector out of bounds");
42
1
	});
43
1
}
44

            
45
#[test]
46
1
fn test_unimplemented_selector_reverts() {
47
1
	ExtBuilder::default().build().execute_with(|| {
48
1
		PrecompilesValue::get()
49
1
			.prepare_test(Alice, Precompile1, vec![1u8, 2, 3, 4])
50
1
			.execute_reverts(|output| output == b"Unknown selector");
51
1
	});
52
1
}
53

            
54
#[test]
55
1
fn selectors() {
56
1
	assert!(PCall::add_proxy_selectors().contains(&0x74a34dd3));
57
1
	assert!(PCall::remove_proxy_selectors().contains(&0xfef3f708));
58
1
	assert!(PCall::remove_proxies_selectors().contains(&0x14a5b5fa));
59
1
	assert!(PCall::proxy_selectors().contains(&0x0d3cff86));
60
1
	assert!(PCall::proxy_force_type_selectors().contains(&0x4a36b2cd));
61
1
	assert!(PCall::is_proxy_selectors().contains(&0xe26d38ed));
62
1
}
63

            
64
#[test]
65
1
fn modifiers() {
66
1
	ExtBuilder::default().build().execute_with(|| {
67
1
		let mut tester =
68
1
			PrecompilesModifierTester::new(PrecompilesValue::get(), Alice, Precompile1);
69
1

            
70
1
		tester.test_default_modifier(PCall::add_proxy_selectors());
71
1
		tester.test_default_modifier(PCall::remove_proxy_selectors());
72
1
		tester.test_default_modifier(PCall::remove_proxies_selectors());
73
1
		tester.test_payable_modifier(PCall::proxy_selectors());
74
1
		tester.test_payable_modifier(PCall::proxy_force_type_selectors());
75
1
		tester.test_view_modifier(PCall::is_proxy_selectors());
76
1
	});
77
1
}
78

            
79
#[test]
80
1
fn test_add_proxy_fails_if_invalid_value_for_proxy_type() {
81
1
	ExtBuilder::default()
82
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
83
1
		.build()
84
1
		.execute_with(|| {
85
1
			PrecompilesValue::get()
86
1
				.prepare_test(
87
1
					Alice,
88
1
					Precompile1,
89
1
					PCall::add_proxy {
90
1
						delegate: Address(Bob.into()),
91
1
						proxy_type: 10,
92
1
						delay: 0,
93
1
					},
94
1
				)
95
1
				.execute_reverts(|o| o == b"proxyType: Failed decoding value to ProxyType");
96
1
		})
97
1
}
98

            
99
#[test]
100
1
fn test_add_proxy_fails_if_duplicate_proxy() {
101
1
	ExtBuilder::default()
102
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
103
1
		.build()
104
1
		.execute_with(|| {
105
1
			assert_ok!(RuntimeCall::Proxy(ProxyCall::add_proxy {
106
1
				delegate: Bob.into(),
107
1
				proxy_type: ProxyType::Something,
108
1
				delay: 0,
109
1
			})
110
1
			.dispatch(RuntimeOrigin::signed(Alice.into())));
111

            
112
1
			PrecompilesValue::get()
113
1
				.prepare_test(
114
1
					Alice,
115
1
					Precompile1,
116
1
					PCall::add_proxy {
117
1
						delegate: Address(Bob.into()),
118
1
						proxy_type: ProxyType::Something as u8,
119
1
						delay: 0,
120
1
					},
121
1
				)
122
1
				.execute_reverts(|o| o == b"Cannot add more than one proxy");
123
1
		})
124
1
}
125

            
126
#[test]
127
1
fn test_add_proxy_fails_if_less_permissive_proxy() {
128
1
	ExtBuilder::default()
129
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
130
1
		.build()
131
1
		.execute_with(|| {
132
1
			assert_ok!(RuntimeCall::Proxy(ProxyCall::add_proxy {
133
1
				delegate: Bob.into(),
134
1
				proxy_type: ProxyType::Something,
135
1
				delay: 0,
136
1
			})
137
1
			.dispatch(RuntimeOrigin::signed(Alice.into())));
138

            
139
1
			PrecompilesValue::get()
140
1
				.prepare_test(
141
1
					Alice,
142
1
					Precompile1,
143
1
					PCall::add_proxy {
144
1
						delegate: Address(Bob.into()),
145
1
						proxy_type: ProxyType::Nothing as u8,
146
1
						delay: 0,
147
1
					},
148
1
				)
149
1
				.execute_reverts(|o| o == b"Cannot add more than one proxy");
150
1
		})
151
1
}
152

            
153
#[test]
154
1
fn test_add_proxy_fails_if_more_permissive_proxy() {
155
1
	ExtBuilder::default()
156
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
157
1
		.build()
158
1
		.execute_with(|| {
159
1
			assert_ok!(RuntimeCall::Proxy(ProxyCall::add_proxy {
160
1
				delegate: Bob.into(),
161
1
				proxy_type: ProxyType::Something,
162
1
				delay: 0,
163
1
			})
164
1
			.dispatch(RuntimeOrigin::signed(Alice.into())));
165

            
166
1
			PrecompilesValue::get()
167
1
				.prepare_test(
168
1
					Alice,
169
1
					Precompile1,
170
1
					PCall::add_proxy {
171
1
						delegate: Address(Bob.into()),
172
1
						proxy_type: ProxyType::Any as u8,
173
1
						delay: 0,
174
1
					},
175
1
				)
176
1
				.execute_reverts(|o| o == b"Cannot add more than one proxy");
177
1
		})
178
1
}
179

            
180
#[test]
181
1
fn test_add_proxy_succeeds() {
182
1
	ExtBuilder::default()
183
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
184
1
		.build()
185
1
		.execute_with(|| {
186
1
			PrecompilesValue::get()
187
1
				.prepare_test(
188
1
					Alice,
189
1
					Precompile1,
190
1
					PCall::add_proxy {
191
1
						delegate: Address(Bob.into()),
192
1
						proxy_type: ProxyType::Something as u8,
193
1
						delay: 1,
194
1
					},
195
1
				)
196
1
				.execute_returns(());
197
1
			assert_event_emitted!(RuntimeEvent::Proxy(ProxyEvent::ProxyAdded {
198
1
				delegator: Alice.into(),
199
1
				delegatee: Bob.into(),
200
1
				proxy_type: ProxyType::Something,
201
1
				delay: 1,
202
1
			}));
203

            
204
1
			let proxies = <ProxyPallet<Runtime>>::proxies(AccountId::from(Alice)).0;
205
1
			assert_eq!(
206
1
				proxies,
207
1
				vec![ProxyDefinition {
208
1
					delegate: Bob.into(),
209
1
					proxy_type: ProxyType::Something,
210
1
					delay: 1,
211
1
				}],
212
1
			)
213
1
		})
214
1
}
215

            
216
#[test]
217
1
fn test_remove_proxy_fails_if_invalid_value_for_proxy_type() {
218
1
	ExtBuilder::default()
219
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
220
1
		.build()
221
1
		.execute_with(|| {
222
1
			assert_ok!(RuntimeCall::Proxy(ProxyCall::add_proxy {
223
1
				delegate: Bob.into(),
224
1
				proxy_type: ProxyType::Something,
225
1
				delay: 0,
226
1
			})
227
1
			.dispatch(RuntimeOrigin::signed(Alice.into())));
228

            
229
1
			PrecompilesValue::get()
230
1
				.prepare_test(
231
1
					Alice,
232
1
					Precompile1,
233
1
					PCall::remove_proxy {
234
1
						delegate: Address(Bob.into()),
235
1
						proxy_type: 10,
236
1
						delay: 0,
237
1
					},
238
1
				)
239
1
				.execute_reverts(|o| o == b"proxyType: Failed decoding value to ProxyType");
240
1
		})
241
1
}
242

            
243
#[test]
244
1
fn test_remove_proxy_fails_if_proxy_not_exist() {
245
1
	ExtBuilder::default()
246
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
247
1
		.build()
248
1
		.execute_with(|| {
249
1
			PrecompilesValue::get()
250
1
				.prepare_test(
251
1
					Alice,
252
1
					Precompile1,
253
1
					PCall::remove_proxy {
254
1
						delegate: Address(Bob.into()),
255
1
						proxy_type: ProxyType::Something as u8,
256
1
						delay: 0,
257
1
					},
258
1
				)
259
1
				.execute_reverts(|output| from_utf8(&output).unwrap().contains("NotFound"));
260
1
		})
261
1
}
262

            
263
#[test]
264
1
fn test_remove_proxy_succeeds() {
265
1
	ExtBuilder::default()
266
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
267
1
		.build()
268
1
		.execute_with(|| {
269
1
			assert_ok!(RuntimeCall::Proxy(ProxyCall::add_proxy {
270
1
				delegate: Bob.into(),
271
1
				proxy_type: ProxyType::Something,
272
1
				delay: 0,
273
1
			})
274
1
			.dispatch(RuntimeOrigin::signed(Alice.into())));
275

            
276
1
			PrecompilesValue::get()
277
1
				.prepare_test(
278
1
					Alice,
279
1
					Precompile1,
280
1
					PCall::remove_proxy {
281
1
						delegate: Address(Bob.into()),
282
1
						proxy_type: ProxyType::Something as u8,
283
1
						delay: 0,
284
1
					},
285
1
				)
286
1
				.execute_returns(());
287
1
			assert_event_emitted!(RuntimeEvent::Proxy(ProxyEvent::ProxyRemoved {
288
1
				delegator: Alice.into(),
289
1
				delegatee: Bob.into(),
290
1
				proxy_type: ProxyType::Something,
291
1
				delay: 0,
292
1
			}));
293

            
294
1
			let proxies = <ProxyPallet<Runtime>>::proxies(AccountId::from(Alice)).0;
295
1
			assert_eq!(proxies, vec![])
296
1
		})
297
1
}
298

            
299
#[test]
300
1
fn test_remove_proxies_succeeds() {
301
1
	ExtBuilder::default()
302
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
303
1
		.build()
304
1
		.execute_with(|| {
305
1
			assert_ok!(RuntimeCall::Proxy(ProxyCall::add_proxy {
306
1
				delegate: Bob.into(),
307
1
				proxy_type: ProxyType::Something,
308
1
				delay: 0,
309
1
			})
310
1
			.dispatch(RuntimeOrigin::signed(Alice.into())));
311
1
			assert_ok!(RuntimeCall::Proxy(ProxyCall::add_proxy {
312
1
				delegate: Charlie.into(),
313
1
				proxy_type: ProxyType::Any,
314
1
				delay: 0,
315
1
			})
316
1
			.dispatch(RuntimeOrigin::signed(Alice.into())));
317

            
318
1
			PrecompilesValue::get()
319
1
				.prepare_test(Alice, Precompile1, PCall::remove_proxies {})
320
1
				.execute_returns(());
321
1

            
322
1
			let proxies = <ProxyPallet<Runtime>>::proxies(AccountId::from(Alice)).0;
323
1
			assert_eq!(proxies, vec![])
324
1
		})
325
1
}
326

            
327
#[test]
328
1
fn test_remove_proxies_succeeds_when_no_proxy_exists() {
329
1
	ExtBuilder::default()
330
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
331
1
		.build()
332
1
		.execute_with(|| {
333
1
			PrecompilesValue::get()
334
1
				.prepare_test(Alice, Precompile1, PCall::remove_proxies {})
335
1
				.execute_returns(());
336
1

            
337
1
			let proxies = <ProxyPallet<Runtime>>::proxies(AccountId::from(Alice)).0;
338
1
			assert_eq!(proxies, vec![])
339
1
		})
340
1
}
341

            
342
#[test]
343
1
fn test_proxy_force_type_fails_if_invalid_value_for_force_proxy_type() {
344
1
	ExtBuilder::default()
345
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
346
1
		.build()
347
1
		.execute_with(|| {
348
1
			PrecompilesValue::get()
349
1
				.prepare_test(
350
1
					Alice,
351
1
					Precompile1,
352
1
					PCall::proxy_force_type {
353
1
						real: Address(Bob.into()),
354
1
						force_proxy_type: 10,
355
1
						call_to: Address(Alice.into()),
356
1
						call_data: BoundedBytes::from([]),
357
1
					},
358
1
				)
359
1
				.execute_reverts(|o| o == b"forceProxyType: Failed decoding value to ProxyType");
360
1
		})
361
1
}
362

            
363
#[test]
364
1
fn test_proxy_fails_if_not_proxy() {
365
1
	ExtBuilder::default()
366
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
367
1
		.build()
368
1
		.execute_with(|| {
369
1
			PrecompilesValue::get()
370
1
				.prepare_test(
371
1
					Alice,
372
1
					Precompile1,
373
1
					PCall::proxy {
374
1
						real: Address(Bob.into()),
375
1
						call_to: Address(Alice.into()),
376
1
						call_data: BoundedBytes::from([]),
377
1
					},
378
1
				)
379
1
				.execute_reverts(|o| o == b"Not proxy");
380
1
		})
381
1
}
382

            
383
#[test]
384
1
fn test_proxy_fails_if_call_filtered() {
385
1
	ExtBuilder::default()
386
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
387
1
		.build()
388
1
		.execute_with(|| {
389
1
			// add delayed proxy
390
1
			PrecompilesValue::get()
391
1
				.prepare_test(
392
1
					Alice,
393
1
					Precompile1,
394
1
					PCall::add_proxy {
395
1
						delegate: Address(Bob.into()),
396
1
						proxy_type: 2,
397
1
						delay: 0,
398
1
					},
399
1
				)
400
1
				.execute_returns(());
401
1

            
402
1
			// Trying to use delayed proxy without any announcement
403
1
			PrecompilesValue::get()
404
1
				.prepare_test(
405
1
					Bob,
406
1
					Precompile1,
407
1
					PCall::proxy {
408
1
						real: Address(Alice.into()),
409
1
						call_to: Address(Bob.into()),
410
1
						call_data: BoundedBytes::from([]),
411
1
					},
412
1
				)
413
1
				.execute_reverts(|o| o == b"CallFiltered");
414
1
		})
415
1
}
416

            
417
#[test]
418
1
fn test_is_proxy_returns_false_if_not_proxy() {
419
1
	ExtBuilder::default()
420
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
421
1
		.build()
422
1
		.execute_with(|| {
423
1
			PrecompilesValue::get()
424
1
				.prepare_test(
425
1
					Alice,
426
1
					Precompile1,
427
1
					PCall::is_proxy {
428
1
						real: Address(Alice.into()),
429
1
						delegate: Address(Bob.into()),
430
1
						proxy_type: ProxyType::Something as u8,
431
1
						delay: 0,
432
1
					},
433
1
				)
434
1
				.execute_returns(false);
435
1
		})
436
1
}
437

            
438
#[test]
439
1
fn test_is_proxy_returns_false_if_proxy_type_incorrect() {
440
1
	ExtBuilder::default()
441
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
442
1
		.build()
443
1
		.execute_with(|| {
444
1
			assert_ok!(RuntimeCall::Proxy(ProxyCall::add_proxy {
445
1
				delegate: Bob.into(),
446
1
				proxy_type: ProxyType::Something,
447
1
				delay: 0,
448
1
			})
449
1
			.dispatch(RuntimeOrigin::signed(Alice.into())));
450

            
451
1
			PrecompilesValue::get()
452
1
				.prepare_test(
453
1
					Alice,
454
1
					Precompile1,
455
1
					PCall::is_proxy {
456
1
						real: Address(Alice.into()),
457
1
						delegate: Address(Bob.into()),
458
1
						proxy_type: ProxyType::Any as u8,
459
1
						delay: 0,
460
1
					},
461
1
				)
462
1
				.execute_returns(false);
463
1
		})
464
1
}
465

            
466
#[test]
467
1
fn test_is_proxy_returns_false_if_proxy_delay_incorrect() {
468
1
	ExtBuilder::default()
469
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
470
1
		.build()
471
1
		.execute_with(|| {
472
1
			assert_ok!(RuntimeCall::Proxy(ProxyCall::add_proxy {
473
1
				delegate: Bob.into(),
474
1
				proxy_type: ProxyType::Something,
475
1
				delay: 1,
476
1
			})
477
1
			.dispatch(RuntimeOrigin::signed(Alice.into())));
478

            
479
1
			PrecompilesValue::get()
480
1
				.prepare_test(
481
1
					Alice,
482
1
					Precompile1,
483
1
					PCall::is_proxy {
484
1
						real: Address(Alice.into()),
485
1
						delegate: Address(Bob.into()),
486
1
						proxy_type: ProxyType::Any as u8,
487
1
						delay: 0,
488
1
					},
489
1
				)
490
1
				.execute_returns(false);
491
1
		})
492
1
}
493

            
494
#[test]
495
1
fn test_is_proxy_returns_true_if_proxy() {
496
1
	ExtBuilder::default()
497
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
498
1
		.build()
499
1
		.execute_with(|| {
500
1
			assert_ok!(RuntimeCall::Proxy(ProxyCall::add_proxy {
501
1
				delegate: Bob.into(),
502
1
				proxy_type: ProxyType::Something,
503
1
				delay: 1,
504
1
			})
505
1
			.dispatch(RuntimeOrigin::signed(Alice.into())));
506

            
507
1
			PrecompilesValue::get()
508
1
				.prepare_test(
509
1
					Alice,
510
1
					Precompile1,
511
1
					PCall::is_proxy {
512
1
						real: Address(Alice.into()),
513
1
						delegate: Address(Bob.into()),
514
1
						proxy_type: ProxyType::Something as u8,
515
1
						delay: 1,
516
1
					},
517
1
				)
518
1
				.execute_returns(true);
519
1
		})
520
1
}
521

            
522
#[test]
523
1
fn test_nested_evm_bypass_proxy_should_allow_elevating_proxy_type() {
524
1
	ExtBuilder::default()
525
1
		.with_balances(vec![(Alice.into(), 100000000), (Bob.into(), 100000000)])
526
1
		.build()
527
1
		.execute_with(|| {
528
1
			// make Bob a ProxyType::Something for Alice
529
1
			assert_ok!(RuntimeCall::Proxy(ProxyCall::add_proxy {
530
1
				delegate: Bob.into(),
531
1
				proxy_type: ProxyType::Something,
532
1
				delay: 0,
533
1
			})
534
1
			.dispatch(RuntimeOrigin::signed(Alice.into())));
535

            
536
			// construct the call wrapping the add_proxy precompile to escalate to ProxyType::Any
537
1
			let add_proxy_precompile = PCall::add_proxy {
538
1
				delegate: Address(Bob.into()),
539
1
				proxy_type: ProxyType::Any as u8,
540
1
				delay: 0,
541
1
			}
542
1
			.into();
543
1

            
544
1
			let evm_call = RuntimeCall::Evm(EvmCall::call {
545
1
				source: Alice.into(),
546
1
				target: Precompile1.into(),
547
1
				input: add_proxy_precompile,
548
1
				value: U256::zero(),
549
1
				gas_limit: u64::max_value(),
550
1
				max_fee_per_gas: 0.into(),
551
1
				max_priority_fee_per_gas: Some(U256::zero()),
552
1
				nonce: None,
553
1
				access_list: Vec::new(),
554
1
				authorization_list: Vec::new(),
555
1
			});
556
1

            
557
1
			// call the evm call in a proxy call
558
1
			assert_ok!(<ProxyPallet<Runtime>>::proxy(
559
1
				RuntimeOrigin::signed(Bob.into()),
560
1
				Alice.into(),
561
1
				None,
562
1
				Box::new(evm_call)
563
1
			));
564

            
565
			// assert Bob was not assigned ProxyType::Any
566
1
			assert_event_not_emitted!(RuntimeEvent::Proxy(ProxyEvent::ProxyAdded {
567
1
				delegator: Alice.into(),
568
1
				delegatee: Bob.into(),
569
1
				proxy_type: ProxyType::Any,
570
1
				delay: 0,
571
1
			}));
572
1
		})
573
1
}
574

            
575
#[test]
576
1
fn fails_if_called_by_smart_contract() {
577
1
	ExtBuilder::default()
578
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
579
1
		.build()
580
1
		.execute_with(|| {
581
1
			// Set code to Alice address as it if was a smart contract.
582
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
583
1
			pallet_evm::AccountCodesMetadata::<Runtime>::insert(
584
1
				H160::from(Alice),
585
1
				pallet_evm::CodeMetadata {
586
1
					size: 10,
587
1
					hash: H256::default(),
588
1
				},
589
1
			);
590
1

            
591
1
			PrecompilesValue::get()
592
1
				.prepare_test(
593
1
					Alice,
594
1
					Precompile1,
595
1
					PCall::add_proxy {
596
1
						delegate: Address(Bob.into()),
597
1
						proxy_type: ProxyType::Something as u8,
598
1
						delay: 1,
599
1
					},
600
1
				)
601
1
				.execute_reverts(|output| output == b"Function not callable by smart contracts");
602
1
		})
603
1
}
604

            
605
#[test]
606
1
fn succeed_if_called_by_precompile() {
607
1
	ExtBuilder::default()
608
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
609
1
		.build()
610
1
		.execute_with(|| {
611
1
			// Set dummy code to Alice address as it if was a precompile.
612
1
			pallet_evm::AccountCodes::<Runtime>::insert(
613
1
				H160::from(Alice),
614
1
				vec![0x60, 0x00, 0x60, 0x00, 0xfd],
615
1
			);
616
1

            
617
1
			PrecompilesValue::get()
618
1
				.prepare_test(
619
1
					Alice,
620
1
					Precompile1,
621
1
					PCall::add_proxy {
622
1
						delegate: Address(Bob.into()),
623
1
						proxy_type: ProxyType::Something as u8,
624
1
						delay: 1,
625
1
					},
626
1
				)
627
1
				.execute_returns(());
628
1
		})
629
1
}
630

            
631
#[test]
632
1
fn succeed_if_is_proxy_called_by_smart_contract() {
633
1
	ExtBuilder::default()
634
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
635
1
		.build()
636
1
		.execute_with(|| {
637
1
			// Set code to Alice address as it if was a smart contract.
638
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
639
1

            
640
1
			PrecompilesValue::get()
641
1
				.prepare_test(
642
1
					Alice,
643
1
					Precompile1,
644
1
					PCall::is_proxy {
645
1
						real: Address(Alice.into()),
646
1
						delegate: Address(Bob.into()),
647
1
						proxy_type: ProxyType::Something as u8,
648
1
						delay: 1,
649
1
					},
650
1
				)
651
1
				.execute_returns(false);
652
1
		})
653
1
}
654

            
655
#[test]
656
1
fn proxy_proxy_should_fail_if_called_by_precompile() {
657
1
	ExtBuilder::default()
658
1
		.with_balances(vec![
659
1
			(AddressU64::<1>::get().into(), 1000),
660
1
			(Bob.into(), 1000),
661
1
		])
662
1
		.build()
663
1
		.execute_with(|| {
664
1
			PrecompilesValue::get()
665
1
				.prepare_test(
666
1
					AddressU64::<1>::get(),
667
1
					Precompile1,
668
1
					PCall::proxy {
669
1
						real: Address(Alice.into()),
670
1
						call_to: Address(Bob.into()),
671
1
						call_data: BoundedBytes::from([]),
672
1
					},
673
1
				)
674
1
				.execute_reverts(|output| output == b"Function not callable by precompiles");
675
1
		})
676
1
}
677

            
678
#[test]
679
1
fn proxy_proxy_should_succeed_if_called_by_allowed_precompile() {
680
1
	// "Not proxy" means that the security filter has passed, so the call to proxy.proxy would work
681
1
	// if we had done a proxy.add_proxy before.
682
1
	ExtBuilder::default()
683
1
		.with_balances(vec![
684
1
			(AddressU64::<1>::get().into(), 1000),
685
1
			(Bob.into(), 1000),
686
1
		])
687
1
		.build()
688
1
		.execute_with(|| {
689
1
			PrecompilesValue::get()
690
1
				.prepare_test(
691
1
					// Address<2> allowed in mock.rs
692
1
					AddressU64::<2>::get(),
693
1
					Precompile1,
694
1
					PCall::proxy {
695
1
						real: Address(Alice.into()),
696
1
						call_to: Address(Bob.into()),
697
1
						call_data: BoundedBytes::from([]),
698
1
					},
699
1
				)
700
1
				.execute_reverts(|output| output == b"Not proxy");
701
1
		})
702
1
}
703

            
704
#[test]
705
1
fn proxy_proxy_should_succeed_if_called_by_smart_contract() {
706
1
	ExtBuilder::default()
707
1
		.with_balances(vec![
708
1
			(AddressU64::<1>::get().into(), 1000),
709
1
			(Bob.into(), 1000),
710
1
		])
711
1
		.build()
712
1
		.execute_with(|| {
713
1
			// Set code to Alice address as it if was a smart contract.
714
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
715
1

            
716
1
			// Bob allows Alice to make calls on his behalf
717
1
			assert_ok!(RuntimeCall::Proxy(ProxyCall::add_proxy {
718
1
				delegate: Alice.into(),
719
1
				proxy_type: ProxyType::Any,
720
1
				delay: 0,
721
1
			})
722
1
			.dispatch(RuntimeOrigin::signed(Bob.into())));
723

            
724
1
			let inside = Rc::new(Cell::new(false));
725
1
			let inside2 = inside.clone();
726
1

            
727
1
			// The smart contract calls proxy.proxy to call address Charlie as if it was Bob
728
1
			PrecompilesValue::get()
729
1
				.prepare_test(
730
1
					Alice,
731
1
					Precompile1,
732
1
					PCall::proxy {
733
1
						real: Address(Bob.into()),
734
1
						call_to: Address(Charlie.into()),
735
1
						call_data: BoundedBytes::from([1]),
736
1
					},
737
1
				)
738
1
				.with_subcall_handle(move |subcall| {
739
1
					let Subcall {
740
1
						address,
741
1
						transfer,
742
1
						input,
743
1
						target_gas: _,
744
1
						is_static,
745
1
						context,
746
1
					} = subcall;
747
1

            
748
1
					assert_eq!(context.caller, Bob.into());
749
1
					assert_eq!(address, Charlie.into());
750
1
					assert_eq!(is_static, false);
751

            
752
1
					assert!(transfer.is_none());
753

            
754
1
					assert_eq!(context.address, Charlie.into());
755
1
					assert_eq!(context.apparent_value, 0u8.into());
756

            
757
1
					assert_eq!(&input, &[1]);
758

            
759
1
					inside2.set(true);
760
1

            
761
1
					SubcallOutput {
762
1
						output: b"TEST".to_vec(),
763
1
						cost: 13,
764
1
						logs: vec![log1(Bob, H256::repeat_byte(0x11), vec![])],
765
1
						..SubcallOutput::succeed()
766
1
					}
767
1
				})
768
1
				.execute_returns(());
769
1

            
770
1
			// Ensure that the subcall was actually called.
771
1
			// proxy.proxy does not propagate the return value, so we cannot check for the return
772
1
			// value "TEST"
773
1
			assert!(inside.get(), "subcall not called");
774
1
		})
775
1
}
776

            
777
#[test]
778
1
fn proxy_proxy_should_fail_if_called_by_smart_contract_for_a_non_eoa_account() {
779
1
	ExtBuilder::default()
780
1
		.with_balances(vec![
781
1
			(AddressU64::<1>::get().into(), 1000),
782
1
			(Bob.into(), 1000),
783
1
		])
784
1
		.build()
785
1
		.execute_with(|| {
786
1
			// Set code to Alice & Bob addresses as if they are smart contracts.
787
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
788
1
			pallet_evm::AccountCodesMetadata::<Runtime>::insert(
789
1
				H160::from(Alice),
790
1
				pallet_evm::CodeMetadata {
791
1
					size: 10,
792
1
					hash: H256::default(),
793
1
				},
794
1
			);
795
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Bob), vec![10u8]);
796
1
			pallet_evm::AccountCodesMetadata::<Runtime>::insert(
797
1
				H160::from(Bob),
798
1
				pallet_evm::CodeMetadata {
799
1
					size: 10,
800
1
					hash: H256::default(),
801
1
				},
802
1
			);
803
1

            
804
1
			// Bob allows Alice to make calls on his behalf
805
1
			assert_ok!(RuntimeCall::Proxy(ProxyCall::add_proxy {
806
1
				delegate: Alice.into(),
807
1
				proxy_type: ProxyType::Any,
808
1
				delay: 0,
809
1
			})
810
1
			.dispatch(RuntimeOrigin::signed(Bob.into())));
811

            
812
1
			let inside = Rc::new(Cell::new(false));
813
1
			let inside2 = inside.clone();
814
1

            
815
1
			// The smart contract calls proxy.proxy to call address Charlie as if it was Bob
816
1
			PrecompilesValue::get()
817
1
				.prepare_test(
818
1
					Alice,
819
1
					Precompile1,
820
1
					PCall::proxy {
821
1
						real: Address(Bob.into()),
822
1
						call_to: Address(Charlie.into()),
823
1
						call_data: BoundedBytes::from([1]),
824
1
					},
825
1
				)
826
1
				.with_subcall_handle(move |subcall| {
827
					let Subcall {
828
						address,
829
						transfer,
830
						input,
831
						target_gas: _,
832
						is_static,
833
						context,
834
					} = subcall;
835

            
836
					assert_eq!(context.caller, Bob.into());
837
					assert_eq!(address, Charlie.into());
838
					assert_eq!(is_static, false);
839

            
840
					assert!(transfer.is_none());
841

            
842
					assert_eq!(context.address, Charlie.into());
843
					assert_eq!(context.apparent_value, 0u8.into());
844

            
845
					assert_eq!(&input, &[1]);
846

            
847
					inside2.set(true);
848

            
849
					SubcallOutput {
850
						output: b"TEST".to_vec(),
851
						cost: 13,
852
						logs: vec![log1(Bob, H256::repeat_byte(0x11), vec![])],
853
						..SubcallOutput::succeed()
854
					}
855
1
				})
856
1
				.execute_reverts(|output| output == b"real address must be EOA");
857
1
		})
858
1
}
859

            
860
#[test]
861
1
fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() {
862
1
	check_precompile_implements_solidity_interfaces(&["Proxy.sol"], PCall::supports_selector)
863
1
}