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 fp_evm::MAX_TRANSACTION_GAS_LIMIT;
25
use frame_support::assert_ok;
26
use pallet_evm::Call as EvmCall;
27
use pallet_proxy::{
28
	Call as ProxyCall, Event as ProxyEvent, Pallet as ProxyPallet, ProxyDefinition,
29
};
30
use precompile_utils::{precompile_set::AddressU64, prelude::*, testing::*};
31
use sp_core::{Get, H160, H256, U256};
32
use sp_runtime::traits::Dispatchable;
33
use std::cell::Cell;
34
use std::rc::Rc;
35
use std::str::from_utf8;
36

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
606
#[test]
607
1
fn succeed_if_called_by_precompile() {
608
1
	ExtBuilder::default()
609
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
610
1
		.build()
611
1
		.execute_with(|| {
612
1
			PrecompilesValue::get()
613
1
				.prepare_test(
614
1
					Alice,
615
1
					Precompile1,
616
1
					PCall::add_proxy {
617
1
						delegate: Address(Bob.into()),
618
1
						proxy_type: ProxyType::Something as u8,
619
1
						delay: 1,
620
1
					},
621
				)
622
1
				.execute_returns(());
623
1
		})
624
1
}
625

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

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

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

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

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

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

            
719
1
			let inside = Rc::new(Cell::new(false));
720
1
			let inside2 = inside.clone();
721

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

            
743
1
					assert_eq!(context.caller, Bob.into());
744
1
					assert_eq!(address, Charlie.into());
745
1
					assert_eq!(is_static, false);
746

            
747
1
					assert!(transfer.is_none());
748

            
749
1
					assert_eq!(context.address, Charlie.into());
750
1
					assert_eq!(context.apparent_value, 0u8.into());
751

            
752
1
					assert_eq!(&input, &[1]);
753

            
754
1
					inside2.set(true);
755

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

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

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

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

            
807
1
			let inside = Rc::new(Cell::new(false));
808
1
			let inside2 = inside.clone();
809

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

            
831
					assert_eq!(context.caller, Bob.into());
832
					assert_eq!(address, Charlie.into());
833
					assert_eq!(is_static, false);
834

            
835
					assert!(transfer.is_none());
836

            
837
					assert_eq!(context.address, Charlie.into());
838
					assert_eq!(context.apparent_value, 0u8.into());
839

            
840
					assert_eq!(&input, &[1]);
841

            
842
					inside2.set(true);
843

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

            
855
#[test]
856
1
fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() {
857
1
	check_precompile_implements_solidity_interfaces(&["Proxy.sol"], PCall::supports_selector)
858
1
}