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
			// Set dummy code to Alice address as it if was a precompile.
613
1
			pallet_evm::AccountCodes::<Runtime>::insert(
614
1
				H160::from(Alice),
615
1
				vec![0x60, 0x00, 0x60, 0x00, 0xfd],
616
			);
617

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

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

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

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

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

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

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

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

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

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

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

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

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

            
760
1
					inside2.set(true);
761

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

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

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

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

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

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

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

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

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

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

            
848
					inside2.set(true);
849

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

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