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
			});
555
1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
758
1
					inside2.set(true);
759
1

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

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

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

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

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

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

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

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

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

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

            
846
					inside2.set(true);
847

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

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