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
	mock::{CallPermit, ExtBuilder, PCall, Precompiles, PrecompilesValue, Runtime},
19
	CallPermitPrecompile,
20
};
21
use libsecp256k1::{sign, Message, SecretKey};
22
use precompile_utils::{
23
	evm::costs::call_cost, prelude::*, solidity::revert::revert_as_bytes, testing::*,
24
};
25
use sp_core::{H160, H256, U256};
26

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

            
31
12
fn dispatch_cost() -> u64 {
32
12
	CallPermitPrecompile::<Runtime>::dispatch_inherent_cost()
33
12
}
34

            
35
#[test]
36
1
fn expired_permit_deadline_milliseconds() {
37
1
	ExtBuilder::default()
38
1
		.with_balances(vec![(CryptoAlith.into(), 1000)])
39
1
		.build()
40
1
		.execute_with(|| {
41
			use pallet_timestamp::Pallet as TimestampPallet;
42

            
43
1
			let one_second = 1;
44

            
45
1
			let deadline: U256 = one_second.into();
46

            
47
1
			let from: H160 = CryptoAlith.into();
48
1
			let to: H160 = Bob.into();
49
1
			let value: U256 = 42u8.into();
50
1
			let data: Vec<u8> = b"Test".to_vec();
51
1
			let gas_limit = 100_000u64;
52
1
			let nonce: U256 = 0u8.into();
53

            
54
1
			let permit = CallPermitPrecompile::<Runtime>::generate_permit(
55
1
				CallPermit.into(),
56
1
				from,
57
1
				to,
58
1
				value,
59
1
				data.clone(),
60
1
				gas_limit,
61
1
				nonce,
62
1
				deadline,
63
			);
64

            
65
1
			let secret_key = SecretKey::parse(&alith_secret_key()).unwrap();
66
1
			let message = Message::parse(&permit);
67
1
			let (rs, v) = sign(&message, &secret_key);
68

            
69
			// (deadline_ms + 1ms) = should revert with "Permit expired"
70
1
			TimestampPallet::<Runtime>::set_timestamp((one_second * 1000) + 1);
71

            
72
1
			precompiles()
73
1
				.prepare_test(
74
1
					CryptoBaltathar,
75
1
					CallPermit,
76
1
					PCall::dispatch {
77
1
						from: Address(from),
78
1
						to: Address(to),
79
1
						value,
80
1
						data: data.into(),
81
1
						gas_limit,
82
1
						deadline,
83
1
						v: v.serialize(),
84
1
						r: H256::from(rs.r.b32()),
85
1
						s: H256::from(rs.s.b32()),
86
1
					},
87
				)
88
1
				.execute_reverts(|x| x == b"Permit expired");
89
1
		})
90
1
}
91

            
92
#[test]
93
1
fn selectors() {
94
1
	assert!(PCall::dispatch_selectors().contains(&0xb5ea0966));
95
1
	assert!(PCall::nonces_selectors().contains(&0x7ecebe00));
96
1
	assert!(PCall::domain_separator_selectors().contains(&0x3644e515));
97
1
}
98

            
99
#[test]
100
1
fn modifiers() {
101
1
	ExtBuilder::default()
102
1
		.with_balances(vec![(CryptoAlith.into(), 1000)])
103
1
		.build()
104
1
		.execute_with(|| {
105
1
			let mut tester = PrecompilesModifierTester::new(precompiles(), CryptoAlith, CallPermit);
106

            
107
1
			tester.test_default_modifier(PCall::dispatch_selectors());
108
1
			tester.test_view_modifier(PCall::nonces_selectors());
109
1
			tester.test_view_modifier(PCall::domain_separator_selectors());
110
1
		});
111
1
}
112

            
113
#[test]
114
1
fn valid_permit_returns() {
115
1
	ExtBuilder::default()
116
1
		.with_balances(vec![(CryptoAlith.into(), 1000)])
117
1
		.build()
118
1
		.execute_with(|| {
119
1
			let from: H160 = CryptoAlith.into();
120
1
			let to: H160 = Bob.into();
121
1
			let value: U256 = 42u8.into();
122
1
			let data: Vec<u8> = b"Test".to_vec();
123
1
			let gas_limit = 100_000u64;
124
1
			let nonce: U256 = 0u8.into();
125
1
			let deadline: U256 = 1_000u32.into();
126
1
			let permit = CallPermitPrecompile::<Runtime>::generate_permit(
127
1
				CallPermit.into(),
128
1
				from,
129
1
				to,
130
1
				value,
131
1
				data.clone(),
132
1
				gas_limit,
133
1
				nonce,
134
1
				deadline,
135
			);
136

            
137
1
			let secret_key = SecretKey::parse(&alith_secret_key()).unwrap();
138
1
			let message = Message::parse(&permit);
139
1
			let (rs, v) = sign(&message, &secret_key);
140

            
141
1
			precompiles()
142
1
				.prepare_test(
143
1
					CryptoAlith,
144
1
					CallPermit,
145
1
					PCall::nonces {
146
1
						owner: Address(CryptoAlith.into()),
147
1
					},
148
				)
149
1
				.expect_cost(0) // TODO: Test db read/write costs
150
1
				.expect_no_logs()
151
1
				.execute_returns(U256::from(0u8));
152

            
153
1
			let call_cost = call_cost(value, <Runtime as pallet_evm::Config>::config());
154

            
155
1
			precompiles()
156
1
				.prepare_test(
157
1
					Charlie, // can be anyone
158
1
					CallPermit,
159
1
					PCall::dispatch {
160
1
						from: Address(from),
161
1
						to: Address(to),
162
1
						value,
163
1
						data: data.into(),
164
1
						gas_limit,
165
1
						deadline,
166
1
						v: v.serialize(),
167
1
						r: H256::from(rs.r.b32()),
168
1
						s: H256::from(rs.s.b32()),
169
1
					},
170
				)
171
1
				.with_subcall_handle(move |subcall| {
172
					let Subcall {
173
1
						address,
174
1
						transfer,
175
1
						input,
176
1
						target_gas,
177
1
						is_static,
178
1
						context,
179
1
					} = subcall;
180

            
181
					// Called on the behalf of the permit maker.
182
1
					assert_eq!(context.caller, CryptoAlith.into());
183
1
					assert_eq!(address, Bob.into());
184
1
					assert_eq!(is_static, false);
185
1
					assert_eq!(target_gas, Some(100_000), "forward requested gas");
186

            
187
1
					let transfer = transfer.expect("there is a transfer");
188
1
					assert_eq!(transfer.source, CryptoAlith.into());
189
1
					assert_eq!(transfer.target, Bob.into());
190
1
					assert_eq!(transfer.value, 42u8.into());
191

            
192
1
					assert_eq!(context.address, Bob.into());
193
1
					assert_eq!(context.apparent_value, 42u8.into());
194

            
195
1
					assert_eq!(&input, b"Test");
196

            
197
1
					SubcallOutput {
198
1
						output: b"TEST".to_vec(),
199
1
						cost: 13,
200
1
						logs: vec![log1(Bob, H256::repeat_byte(0x11), vec![])],
201
1
						..SubcallOutput::succeed()
202
1
					}
203
1
				})
204
1
				.with_target_gas(Some(call_cost + 100_000 + dispatch_cost()))
205
1
				.expect_cost(call_cost + 13 + dispatch_cost())
206
1
				.expect_log(log1(Bob, H256::repeat_byte(0x11), vec![]))
207
1
				.execute_returns(UnboundedBytes::from(b"TEST"));
208
1
		})
209
1
}
210

            
211
#[test]
212
1
fn valid_permit_reverts() {
213
1
	ExtBuilder::default()
214
1
		.with_balances(vec![(CryptoAlith.into(), 1000)])
215
1
		.build()
216
1
		.execute_with(|| {
217
1
			let from: H160 = CryptoAlith.into();
218
1
			let to: H160 = Bob.into();
219
1
			let value: U256 = 42u8.into();
220
1
			let data: Vec<u8> = b"Test".to_vec();
221
1
			let gas_limit = 100_000u64;
222
1
			let nonce: U256 = 0u8.into();
223
1
			let deadline: U256 = 1_000u32.into();
224

            
225
1
			let permit = CallPermitPrecompile::<Runtime>::generate_permit(
226
1
				CallPermit.into(),
227
1
				from,
228
1
				to,
229
1
				value,
230
1
				data.clone(),
231
1
				gas_limit,
232
1
				nonce,
233
1
				deadline,
234
			);
235

            
236
1
			let secret_key = SecretKey::parse(&alith_secret_key()).unwrap();
237
1
			let message = Message::parse(&permit);
238
1
			let (rs, v) = sign(&message, &secret_key);
239

            
240
1
			precompiles()
241
1
				.prepare_test(
242
1
					CryptoAlith,
243
1
					CallPermit,
244
1
					PCall::nonces {
245
1
						owner: Address(CryptoAlith.into()),
246
1
					},
247
				)
248
1
				.expect_cost(0) // TODO: Test db read/write costs
249
1
				.expect_no_logs()
250
1
				.execute_returns(U256::from(0u8));
251

            
252
1
			let call_cost = call_cost(value, <Runtime as pallet_evm::Config>::config());
253

            
254
1
			precompiles()
255
1
				.prepare_test(
256
1
					Charlie, // can be anyone
257
1
					CallPermit,
258
1
					PCall::dispatch {
259
1
						from: Address(from),
260
1
						to: Address(to),
261
1
						value,
262
1
						data: data.into(),
263
1
						gas_limit,
264
1
						deadline,
265
1
						v: v.serialize(),
266
1
						r: H256::from(rs.r.b32()),
267
1
						s: H256::from(rs.s.b32()),
268
1
					},
269
				)
270
1
				.with_subcall_handle(move |subcall| {
271
					let Subcall {
272
1
						address,
273
1
						transfer,
274
1
						input,
275
1
						target_gas,
276
1
						is_static,
277
1
						context,
278
1
					} = subcall;
279

            
280
					// Called on the behalf of the permit maker.
281
1
					assert_eq!(context.caller, CryptoAlith.into());
282
1
					assert_eq!(address, Bob.into());
283
1
					assert_eq!(is_static, false);
284
1
					assert_eq!(target_gas, Some(100_000), "forward requested gas");
285

            
286
1
					let transfer = transfer.expect("there is a transfer");
287
1
					assert_eq!(transfer.source, CryptoAlith.into());
288
1
					assert_eq!(transfer.target, Bob.into());
289
1
					assert_eq!(transfer.value, 42u8.into());
290

            
291
1
					assert_eq!(context.address, Bob.into());
292
1
					assert_eq!(context.apparent_value, 42u8.into());
293

            
294
1
					assert_eq!(&input, b"Test");
295

            
296
1
					SubcallOutput {
297
1
						output: revert_as_bytes("TEST"),
298
1
						cost: 13,
299
1
						..SubcallOutput::revert()
300
1
					}
301
1
				})
302
1
				.with_target_gas(Some(call_cost + 100_000 + dispatch_cost()))
303
1
				.expect_cost(call_cost + 13 + dispatch_cost())
304
1
				.expect_no_logs()
305
1
				.execute_reverts(|x| x == b"TEST".to_vec());
306
1
		})
307
1
}
308

            
309
#[test]
310
1
fn invalid_permit_nonce() {
311
1
	ExtBuilder::default()
312
1
		.with_balances(vec![(CryptoAlith.into(), 1000)])
313
1
		.build()
314
1
		.execute_with(|| {
315
1
			let from: H160 = CryptoAlith.into();
316
1
			let to: H160 = Bob.into();
317
1
			let value: U256 = 42u8.into();
318
1
			let data: Vec<u8> = b"Test".to_vec();
319
1
			let gas_limit = 100_000u64;
320
1
			let nonce: U256 = 1u8.into(); // WRONG NONCE
321
1
			let deadline: U256 = 1_000u32.into();
322

            
323
1
			let permit = CallPermitPrecompile::<Runtime>::generate_permit(
324
1
				CallPermit.into(),
325
1
				from,
326
1
				to,
327
1
				value,
328
1
				data.clone(),
329
1
				gas_limit,
330
1
				nonce,
331
1
				deadline,
332
			);
333

            
334
1
			let secret_key = SecretKey::parse(&alith_secret_key()).unwrap();
335
1
			let message = Message::parse(&permit);
336
1
			let (rs, v) = sign(&message, &secret_key);
337

            
338
1
			precompiles()
339
1
				.prepare_test(
340
1
					CryptoAlith,
341
1
					CallPermit,
342
1
					PCall::nonces {
343
1
						owner: Address(CryptoAlith.into()),
344
1
					},
345
				)
346
1
				.expect_cost(0) // TODO: Test db read/write costs
347
1
				.expect_no_logs()
348
1
				.execute_returns(U256::from(0u8));
349

            
350
1
			let call_cost = call_cost(value, <Runtime as pallet_evm::Config>::config());
351

            
352
1
			precompiles()
353
1
				.prepare_test(
354
1
					Charlie, // can be anyone
355
1
					CallPermit,
356
1
					PCall::dispatch {
357
1
						from: Address(from),
358
1
						to: Address(to),
359
1
						value,
360
1
						data: data.into(),
361
1
						gas_limit,
362
1
						deadline,
363
1
						v: v.serialize(),
364
1
						r: H256::from(rs.r.b32()),
365
1
						s: H256::from(rs.s.b32()),
366
1
					},
367
				)
368
1
				.with_subcall_handle(move |_| panic!("should not perform subcall"))
369
1
				.with_target_gas(Some(call_cost + 100_000 + dispatch_cost()))
370
1
				.expect_cost(dispatch_cost())
371
1
				.execute_reverts(|x| x == b"Invalid permit");
372
1
		})
373
1
}
374

            
375
#[test]
376
1
fn invalid_permit_gas_limit_too_low() {
377
1
	ExtBuilder::default()
378
1
		.with_balances(vec![(CryptoAlith.into(), 1000)])
379
1
		.build()
380
1
		.execute_with(|| {
381
1
			let from: H160 = CryptoAlith.into();
382
1
			let to: H160 = Bob.into();
383
1
			let value: U256 = 42u8.into();
384
1
			let data: Vec<u8> = b"Test".to_vec();
385
1
			let gas_limit = 100_000u64;
386
1
			let nonce: U256 = 0u8.into();
387
1
			let deadline: U256 = 1_000u32.into();
388

            
389
1
			let permit = CallPermitPrecompile::<Runtime>::generate_permit(
390
1
				CallPermit.into(),
391
1
				from,
392
1
				to,
393
1
				value,
394
1
				data.clone(),
395
1
				gas_limit,
396
1
				nonce,
397
1
				deadline,
398
			);
399

            
400
1
			let secret_key = SecretKey::parse(&alith_secret_key()).unwrap();
401
1
			let message = Message::parse(&permit);
402
1
			let (rs, v) = sign(&message, &secret_key);
403

            
404
1
			precompiles()
405
1
				.prepare_test(
406
1
					CryptoAlith,
407
1
					CallPermit,
408
1
					PCall::nonces {
409
1
						owner: Address(CryptoAlith.into()),
410
1
					},
411
				)
412
1
				.expect_cost(0) // TODO: Test db read/write costs
413
1
				.expect_no_logs()
414
1
				.execute_returns(U256::from(0u8));
415

            
416
1
			let call_cost = call_cost(value, <Runtime as pallet_evm::Config>::config());
417

            
418
1
			precompiles()
419
1
				.prepare_test(
420
1
					Charlie, // can be anyone
421
1
					CallPermit,
422
1
					PCall::dispatch {
423
1
						from: Address(from),
424
1
						to: Address(to),
425
1
						value,
426
1
						data: data.into(),
427
1
						gas_limit,
428
1
						deadline,
429
1
						v: v.serialize(),
430
1
						r: H256::from(rs.r.b32()),
431
1
						s: H256::from(rs.s.b32()),
432
1
					},
433
				)
434
1
				.with_subcall_handle(move |_| panic!("should not perform subcall"))
435
1
				.with_target_gas(Some(call_cost + 99_999 + dispatch_cost()))
436
1
				.expect_cost(dispatch_cost())
437
1
				.execute_reverts(|x| x == b"Gaslimit is too low to dispatch provided call");
438
1
		})
439
1
}
440

            
441
#[test]
442
1
fn invalid_permit_gas_limit_overflow() {
443
1
	ExtBuilder::default()
444
1
		.with_balances(vec![(CryptoAlith.into(), 1000)])
445
1
		.build()
446
1
		.execute_with(|| {
447
1
			let from: H160 = CryptoAlith.into();
448
1
			let to: H160 = Bob.into();
449
1
			let value: U256 = 42u8.into();
450
1
			let data: Vec<u8> = b"Test".to_vec();
451
1
			let gas_limit = u64::MAX;
452
1
			let nonce: U256 = 0u8.into();
453
1
			let deadline: U256 = 1_000u32.into();
454

            
455
1
			let permit = CallPermitPrecompile::<Runtime>::generate_permit(
456
1
				CallPermit.into(),
457
1
				from,
458
1
				to,
459
1
				value,
460
1
				data.clone(),
461
1
				gas_limit,
462
1
				nonce,
463
1
				deadline,
464
			);
465

            
466
1
			dbg!(H256::from(permit));
467

            
468
1
			let secret_key = SecretKey::parse(&alith_secret_key()).unwrap();
469
1
			let message = Message::parse(&permit);
470
1
			let (rs, v) = sign(&message, &secret_key);
471

            
472
1
			precompiles()
473
1
				.prepare_test(
474
1
					CryptoAlith,
475
1
					CallPermit,
476
1
					PCall::nonces {
477
1
						owner: Address(CryptoAlith.into()),
478
1
					},
479
				)
480
1
				.expect_cost(0) // TODO: Test db read/write costs
481
1
				.expect_no_logs()
482
1
				.execute_returns(U256::from(0u8));
483

            
484
1
			precompiles()
485
1
				.prepare_test(
486
1
					Charlie, // can be anyone
487
1
					CallPermit,
488
1
					PCall::dispatch {
489
1
						from: Address(from),
490
1
						to: Address(to),
491
1
						value,
492
1
						data: data.into(),
493
1
						gas_limit,
494
1
						deadline,
495
1
						v: v.serialize(),
496
1
						r: H256::from(rs.r.b32()),
497
1
						s: H256::from(rs.s.b32()),
498
1
					},
499
				)
500
1
				.with_subcall_handle(move |_| panic!("should not perform subcall"))
501
1
				.with_target_gas(Some(100_000 + dispatch_cost()))
502
1
				.expect_cost(dispatch_cost())
503
1
				.execute_reverts(|x| x == b"Call require too much gas (uint64 overflow)");
504
1
		})
505
1
}
506

            
507
// // This test checks the validity of a metamask signed message against the permit precompile
508
// // The code used to generate the signature is the following.
509
// // You will need to import CryptoAlith_PRIV_KEY in metamask.
510
// // If you put this code in the developer tools console, it will log the signature
511

            
512
// await window.ethereum.enable();
513
// const accounts = await window.ethereum.request({ method: "eth_requestAccounts" });
514

            
515
// const from = accounts[0];
516
// const to = "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
517
// const value = 42;
518
// const data = "0xdeadbeef";
519
// const gaslimit = 100000;
520
// const nonce = 0;
521
// const deadline = 1000;
522

            
523
// const createPermitMessageData = function () {
524
// 	const message = {
525
// 	from: from,
526
// 	to: to,
527
// 	value: value,
528
//    data: data,
529
//    gaslimit: gaslimit,
530
// 	nonce: nonce,
531
// 	deadline: deadline,
532
// 	};
533

            
534
// 	const typedData = JSON.stringify({
535
// 	types: {
536
// 		EIP712Domain: [
537
// 		{
538
// 			name: "name",
539
// 			type: "string",
540
// 		},
541
// 		{
542
// 			name: "version",
543
// 			type: "string",
544
// 		},
545
// 		{
546
// 			name: "chainId",
547
// 			type: "uint256",
548
// 		},
549
// 		{
550
// 			name: "verifyingContract",
551
// 			type: "address",
552
// 		},
553
// 		],
554
// 		CallPermit: [
555
// 		{
556
// 			name: "from",
557
// 			type: "address",
558
// 		},
559
// 		{
560
// 			name: "to",
561
// 			type: "address",
562
// 		},
563
// 		{
564
// 			name: "value",
565
// 			type: "uint256",
566
// 		},
567
//       {
568
// 			name: "data",
569
// 			type: "bytes",
570
// 		},
571
// 		{
572
// 			name: "gaslimit",
573
// 			type: "uint64",
574
// 		},
575
// 		{
576
// 			name: "nonce",
577
// 			type: "uint256",
578
// 		},
579
// 		{
580
// 			name: "deadline",
581
// 			type: "uint256",
582
// 		},
583
// 		],
584
// 	},
585
// 	primaryType: "CallPermit",
586
// 	domain: {
587
// 		name: "Call Permit CallPermit",
588
// 		version: "1",
589
// 		chainId: 0,
590
// 		verifyingContract: "0x0000000000000000000000000000000000000001",
591
// 	},
592
// 	message: message,
593
// 	});
594

            
595
// 	return {
596
// 		typedData,
597
// 		message,
598
// 	};
599
// };
600

            
601
// const method = "eth_signTypedData_v4"
602
// const messageData = createPermitMessageData();
603
// const params = [from, messageData.typedData];
604

            
605
// web3.currentProvider.sendAsync(
606
// 	{
607
// 		method,
608
// 		params,
609
// 		from,
610
// 	},
611
// 	function (err, result) {
612
// 		if (err) return console.dir(err);
613
// 		if (result.error) {
614
// 			alert(result.error.message);
615
// 		}
616
// 		if (result.error) return console.error('ERROR', result);
617
// 		console.log('TYPED SIGNED:' + JSON.stringify(result.result));
618

            
619
// 		const recovered = sigUtil.recoverTypedSignature_v4({
620
// 			data: JSON.parse(msgParams),
621
// 			sig: result.result,
622
// 		});
623

            
624
// 		if (
625
// 			ethUtil.toChecksumAddress(recovered) === ethUtil.toChecksumAddress(from)
626
// 		) {
627
// 			alert('Successfully recovered signer as ' + from);
628
// 		} else {
629
// 			alert(
630
// 				'Failed to verify signer when comparing ' + result + ' to ' + from
631
// 			);
632
// 		}
633
// 	}
634
// );
635
#[test]
636
1
fn valid_permit_returns_with_metamask_signed_data() {
637
1
	ExtBuilder::default()
638
1
		.with_balances(vec![(CryptoAlith.into(), 2000)])
639
1
		.build()
640
1
		.execute_with(|| {
641
1
			let from: H160 = CryptoAlith.into();
642
1
			let to: H160 = Bob.into();
643
1
			let value: U256 = 42u8.into();
644
1
			let data: Vec<u8> = hex_literal::hex!("deadbeef").to_vec();
645
1
			let gas_limit = 100_000u64;
646
1
			let deadline: U256 = 1_000u32.into();
647

            
648
			// Made with MetaMask
649
1
			let rsv = hex_literal::hex!(
650
				"56b497d556cb1b57a16aac6e8d53f3cbf1108df467ffcb937a3744369a27478f608de05
651
				34b8e0385e55ffd97cbafcfeac12ab52d0b74a2dea582bc8de46f257d1c"
652
			)
653
1
			.as_slice();
654
1
			let (r, sv) = rsv.split_at(32);
655
1
			let (s, v) = sv.split_at(32);
656
1
			let v_real = v[0];
657
1
			let r_real: [u8; 32] = r.try_into().unwrap();
658
1
			let s_real: [u8; 32] = s.try_into().unwrap();
659

            
660
1
			precompiles()
661
1
				.prepare_test(
662
1
					CryptoAlith,
663
1
					CallPermit,
664
1
					PCall::nonces {
665
1
						owner: Address(CryptoAlith.into()),
666
1
					},
667
				)
668
1
				.expect_cost(0) // TODO: Test db read/write costs
669
1
				.expect_no_logs()
670
1
				.execute_returns(U256::from(0u8));
671

            
672
1
			let call_cost = call_cost(value, <Runtime as pallet_evm::Config>::config());
673

            
674
1
			precompiles()
675
1
				.prepare_test(
676
1
					Charlie, // can be anyone
677
1
					CallPermit,
678
1
					PCall::dispatch {
679
1
						from: Address(from),
680
1
						to: Address(to),
681
1
						value,
682
1
						data: data.clone().into(),
683
1
						gas_limit,
684
1
						deadline,
685
1
						v: v_real,
686
1
						r: r_real.into(),
687
1
						s: s_real.into(),
688
1
					},
689
				)
690
1
				.with_subcall_handle(move |subcall| {
691
					let Subcall {
692
1
						address,
693
1
						transfer,
694
1
						input,
695
1
						target_gas,
696
1
						is_static,
697
1
						context,
698
1
					} = subcall;
699

            
700
					// Called on the behalf of the permit maker.
701
1
					assert_eq!(context.caller, CryptoAlith.into());
702
1
					assert_eq!(address, Bob.into());
703
1
					assert_eq!(is_static, false);
704
1
					assert_eq!(target_gas, Some(100_000), "forward requested gas");
705

            
706
1
					let transfer = transfer.expect("there is a transfer");
707
1
					assert_eq!(transfer.source, CryptoAlith.into());
708
1
					assert_eq!(transfer.target, Bob.into());
709
1
					assert_eq!(transfer.value, 42u8.into());
710

            
711
1
					assert_eq!(context.address, Bob.into());
712
1
					assert_eq!(context.apparent_value, 42u8.into());
713

            
714
1
					assert_eq!(&input, &data);
715

            
716
1
					SubcallOutput {
717
1
						output: b"TEST".to_vec(),
718
1
						cost: 13,
719
1
						logs: vec![log1(Bob, H256::repeat_byte(0x11), vec![])],
720
1
						..SubcallOutput::succeed()
721
1
					}
722
1
				})
723
1
				.with_target_gas(Some(call_cost + 100_000 + dispatch_cost()))
724
1
				.expect_cost(call_cost + 13 + dispatch_cost())
725
1
				.expect_log(log1(Bob, H256::repeat_byte(0x11), vec![]))
726
1
				.execute_returns(UnboundedBytes::from(b"TEST"));
727
1
		})
728
1
}
729

            
730
#[test]
731
1
fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() {
732
1
	check_precompile_implements_solidity_interfaces(&["CallPermit.sol"], PCall::supports_selector)
733
1
}