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
13
fn precompiles() -> Precompiles<Runtime> {
28
13
	PrecompilesValue::get()
29
13
}
30

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

            
35
#[test]
36
1
fn selectors() {
37
1
	assert!(PCall::dispatch_selectors().contains(&0xb5ea0966));
38
1
	assert!(PCall::nonces_selectors().contains(&0x7ecebe00));
39
1
	assert!(PCall::domain_separator_selectors().contains(&0x3644e515));
40
1
}
41

            
42
#[test]
43
1
fn modifiers() {
44
1
	ExtBuilder::default()
45
1
		.with_balances(vec![(CryptoAlith.into(), 1000)])
46
1
		.build()
47
1
		.execute_with(|| {
48
1
			let mut tester = PrecompilesModifierTester::new(precompiles(), CryptoAlith, CallPermit);
49
1

            
50
1
			tester.test_default_modifier(PCall::dispatch_selectors());
51
1
			tester.test_view_modifier(PCall::nonces_selectors());
52
1
			tester.test_view_modifier(PCall::domain_separator_selectors());
53
1
		});
54
1
}
55

            
56
#[test]
57
1
fn valid_permit_returns() {
58
1
	ExtBuilder::default()
59
1
		.with_balances(vec![(CryptoAlith.into(), 1000)])
60
1
		.build()
61
1
		.execute_with(|| {
62
1
			let from: H160 = CryptoAlith.into();
63
1
			let to: H160 = Bob.into();
64
1
			let value: U256 = 42u8.into();
65
1
			let data: Vec<u8> = b"Test".to_vec();
66
1
			let gas_limit = 100_000u64;
67
1
			let nonce: U256 = 0u8.into();
68
1
			let deadline: U256 = 1_000u32.into();
69
1
			let permit = CallPermitPrecompile::<Runtime>::generate_permit(
70
1
				CallPermit.into(),
71
1
				from,
72
1
				to,
73
1
				value,
74
1
				data.clone(),
75
1
				gas_limit,
76
1
				nonce,
77
1
				deadline,
78
1
			);
79
1

            
80
1
			let secret_key = SecretKey::parse(&alith_secret_key()).unwrap();
81
1
			let message = Message::parse(&permit);
82
1
			let (rs, v) = sign(&message, &secret_key);
83
1

            
84
1
			precompiles()
85
1
				.prepare_test(
86
1
					CryptoAlith,
87
1
					CallPermit,
88
1
					PCall::nonces {
89
1
						owner: Address(CryptoAlith.into()),
90
1
					},
91
1
				)
92
1
				.expect_cost(0) // TODO: Test db read/write costs
93
1
				.expect_no_logs()
94
1
				.execute_returns(U256::from(0u8));
95
1

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

            
98
1
			precompiles()
99
1
				.prepare_test(
100
1
					Charlie, // can be anyone
101
1
					CallPermit,
102
1
					PCall::dispatch {
103
1
						from: Address(from),
104
1
						to: Address(to),
105
1
						value,
106
1
						data: data.into(),
107
1
						gas_limit,
108
1
						deadline,
109
1
						v: v.serialize(),
110
1
						r: H256::from(rs.r.b32()),
111
1
						s: H256::from(rs.s.b32()),
112
1
					},
113
1
				)
114
1
				.with_subcall_handle(move |subcall| {
115
1
					let Subcall {
116
1
						address,
117
1
						transfer,
118
1
						input,
119
1
						target_gas,
120
1
						is_static,
121
1
						context,
122
1
					} = subcall;
123
1

            
124
1
					// Called on the behalf of the permit maker.
125
1
					assert_eq!(context.caller, CryptoAlith.into());
126
1
					assert_eq!(address, Bob.into());
127
1
					assert_eq!(is_static, false);
128
1
					assert_eq!(target_gas, Some(100_000), "forward requested gas");
129

            
130
1
					let transfer = transfer.expect("there is a transfer");
131
1
					assert_eq!(transfer.source, CryptoAlith.into());
132
1
					assert_eq!(transfer.target, Bob.into());
133
1
					assert_eq!(transfer.value, 42u8.into());
134

            
135
1
					assert_eq!(context.address, Bob.into());
136
1
					assert_eq!(context.apparent_value, 42u8.into());
137

            
138
1
					assert_eq!(&input, b"Test");
139

            
140
1
					SubcallOutput {
141
1
						output: b"TEST".to_vec(),
142
1
						cost: 13,
143
1
						logs: vec![log1(Bob, H256::repeat_byte(0x11), vec![])],
144
1
						..SubcallOutput::succeed()
145
1
					}
146
1
				})
147
1
				.with_target_gas(Some(call_cost + 100_000 + dispatch_cost()))
148
1
				.expect_cost(call_cost + 13 + dispatch_cost())
149
1
				.expect_log(log1(Bob, H256::repeat_byte(0x11), vec![]))
150
1
				.execute_returns(UnboundedBytes::from(b"TEST"));
151
1
		})
152
1
}
153

            
154
#[test]
155
1
fn valid_permit_reverts() {
156
1
	ExtBuilder::default()
157
1
		.with_balances(vec![(CryptoAlith.into(), 1000)])
158
1
		.build()
159
1
		.execute_with(|| {
160
1
			let from: H160 = CryptoAlith.into();
161
1
			let to: H160 = Bob.into();
162
1
			let value: U256 = 42u8.into();
163
1
			let data: Vec<u8> = b"Test".to_vec();
164
1
			let gas_limit = 100_000u64;
165
1
			let nonce: U256 = 0u8.into();
166
1
			let deadline: U256 = 1_000u32.into();
167
1

            
168
1
			let permit = CallPermitPrecompile::<Runtime>::generate_permit(
169
1
				CallPermit.into(),
170
1
				from,
171
1
				to,
172
1
				value,
173
1
				data.clone(),
174
1
				gas_limit,
175
1
				nonce,
176
1
				deadline,
177
1
			);
178
1

            
179
1
			let secret_key = SecretKey::parse(&alith_secret_key()).unwrap();
180
1
			let message = Message::parse(&permit);
181
1
			let (rs, v) = sign(&message, &secret_key);
182
1

            
183
1
			precompiles()
184
1
				.prepare_test(
185
1
					CryptoAlith,
186
1
					CallPermit,
187
1
					PCall::nonces {
188
1
						owner: Address(CryptoAlith.into()),
189
1
					},
190
1
				)
191
1
				.expect_cost(0) // TODO: Test db read/write costs
192
1
				.expect_no_logs()
193
1
				.execute_returns(U256::from(0u8));
194
1

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

            
197
1
			precompiles()
198
1
				.prepare_test(
199
1
					Charlie, // can be anyone
200
1
					CallPermit,
201
1
					PCall::dispatch {
202
1
						from: Address(from),
203
1
						to: Address(to),
204
1
						value,
205
1
						data: data.into(),
206
1
						gas_limit,
207
1
						deadline,
208
1
						v: v.serialize(),
209
1
						r: H256::from(rs.r.b32()),
210
1
						s: H256::from(rs.s.b32()),
211
1
					},
212
1
				)
213
1
				.with_subcall_handle(move |subcall| {
214
1
					let Subcall {
215
1
						address,
216
1
						transfer,
217
1
						input,
218
1
						target_gas,
219
1
						is_static,
220
1
						context,
221
1
					} = subcall;
222
1

            
223
1
					// Called on the behalf of the permit maker.
224
1
					assert_eq!(context.caller, CryptoAlith.into());
225
1
					assert_eq!(address, Bob.into());
226
1
					assert_eq!(is_static, false);
227
1
					assert_eq!(target_gas, Some(100_000), "forward requested gas");
228

            
229
1
					let transfer = transfer.expect("there is a transfer");
230
1
					assert_eq!(transfer.source, CryptoAlith.into());
231
1
					assert_eq!(transfer.target, Bob.into());
232
1
					assert_eq!(transfer.value, 42u8.into());
233

            
234
1
					assert_eq!(context.address, Bob.into());
235
1
					assert_eq!(context.apparent_value, 42u8.into());
236

            
237
1
					assert_eq!(&input, b"Test");
238

            
239
1
					SubcallOutput {
240
1
						output: revert_as_bytes("TEST"),
241
1
						cost: 13,
242
1
						..SubcallOutput::revert()
243
1
					}
244
1
				})
245
1
				.with_target_gas(Some(call_cost + 100_000 + dispatch_cost()))
246
1
				.expect_cost(call_cost + 13 + dispatch_cost())
247
1
				.expect_no_logs()
248
1
				.execute_reverts(|x| x == b"TEST".to_vec());
249
1
		})
250
1
}
251

            
252
#[test]
253
1
fn invalid_permit_nonce() {
254
1
	ExtBuilder::default()
255
1
		.with_balances(vec![(CryptoAlith.into(), 1000)])
256
1
		.build()
257
1
		.execute_with(|| {
258
1
			let from: H160 = CryptoAlith.into();
259
1
			let to: H160 = Bob.into();
260
1
			let value: U256 = 42u8.into();
261
1
			let data: Vec<u8> = b"Test".to_vec();
262
1
			let gas_limit = 100_000u64;
263
1
			let nonce: U256 = 1u8.into(); // WRONG NONCE
264
1
			let deadline: U256 = 1_000u32.into();
265
1

            
266
1
			let permit = CallPermitPrecompile::<Runtime>::generate_permit(
267
1
				CallPermit.into(),
268
1
				from,
269
1
				to,
270
1
				value,
271
1
				data.clone(),
272
1
				gas_limit,
273
1
				nonce,
274
1
				deadline,
275
1
			);
276
1

            
277
1
			let secret_key = SecretKey::parse(&alith_secret_key()).unwrap();
278
1
			let message = Message::parse(&permit);
279
1
			let (rs, v) = sign(&message, &secret_key);
280
1

            
281
1
			precompiles()
282
1
				.prepare_test(
283
1
					CryptoAlith,
284
1
					CallPermit,
285
1
					PCall::nonces {
286
1
						owner: Address(CryptoAlith.into()),
287
1
					},
288
1
				)
289
1
				.expect_cost(0) // TODO: Test db read/write costs
290
1
				.expect_no_logs()
291
1
				.execute_returns(U256::from(0u8));
292
1

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

            
295
1
			precompiles()
296
1
				.prepare_test(
297
1
					Charlie, // can be anyone
298
1
					CallPermit,
299
1
					PCall::dispatch {
300
1
						from: Address(from),
301
1
						to: Address(to),
302
1
						value,
303
1
						data: data.into(),
304
1
						gas_limit,
305
1
						deadline,
306
1
						v: v.serialize(),
307
1
						r: H256::from(rs.r.b32()),
308
1
						s: H256::from(rs.s.b32()),
309
1
					},
310
1
				)
311
1
				.with_subcall_handle(move |_| panic!("should not perform subcall"))
312
1
				.with_target_gas(Some(call_cost + 100_000 + dispatch_cost()))
313
1
				.expect_cost(dispatch_cost())
314
1
				.execute_reverts(|x| x == b"Invalid permit");
315
1
		})
316
1
}
317

            
318
#[test]
319
1
fn invalid_permit_gas_limit_too_low() {
320
1
	ExtBuilder::default()
321
1
		.with_balances(vec![(CryptoAlith.into(), 1000)])
322
1
		.build()
323
1
		.execute_with(|| {
324
1
			let from: H160 = CryptoAlith.into();
325
1
			let to: H160 = Bob.into();
326
1
			let value: U256 = 42u8.into();
327
1
			let data: Vec<u8> = b"Test".to_vec();
328
1
			let gas_limit = 100_000u64;
329
1
			let nonce: U256 = 0u8.into();
330
1
			let deadline: U256 = 1_000u32.into();
331
1

            
332
1
			let permit = CallPermitPrecompile::<Runtime>::generate_permit(
333
1
				CallPermit.into(),
334
1
				from,
335
1
				to,
336
1
				value,
337
1
				data.clone(),
338
1
				gas_limit,
339
1
				nonce,
340
1
				deadline,
341
1
			);
342
1

            
343
1
			let secret_key = SecretKey::parse(&alith_secret_key()).unwrap();
344
1
			let message = Message::parse(&permit);
345
1
			let (rs, v) = sign(&message, &secret_key);
346
1

            
347
1
			precompiles()
348
1
				.prepare_test(
349
1
					CryptoAlith,
350
1
					CallPermit,
351
1
					PCall::nonces {
352
1
						owner: Address(CryptoAlith.into()),
353
1
					},
354
1
				)
355
1
				.expect_cost(0) // TODO: Test db read/write costs
356
1
				.expect_no_logs()
357
1
				.execute_returns(U256::from(0u8));
358
1

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

            
361
1
			precompiles()
362
1
				.prepare_test(
363
1
					Charlie, // can be anyone
364
1
					CallPermit,
365
1
					PCall::dispatch {
366
1
						from: Address(from),
367
1
						to: Address(to),
368
1
						value,
369
1
						data: data.into(),
370
1
						gas_limit,
371
1
						deadline,
372
1
						v: v.serialize(),
373
1
						r: H256::from(rs.r.b32()),
374
1
						s: H256::from(rs.s.b32()),
375
1
					},
376
1
				)
377
1
				.with_subcall_handle(move |_| panic!("should not perform subcall"))
378
1
				.with_target_gas(Some(call_cost + 99_999 + dispatch_cost()))
379
1
				.expect_cost(dispatch_cost())
380
1
				.execute_reverts(|x| x == b"Gaslimit is too low to dispatch provided call");
381
1
		})
382
1
}
383

            
384
#[test]
385
1
fn invalid_permit_gas_limit_overflow() {
386
1
	ExtBuilder::default()
387
1
		.with_balances(vec![(CryptoAlith.into(), 1000)])
388
1
		.build()
389
1
		.execute_with(|| {
390
1
			let from: H160 = CryptoAlith.into();
391
1
			let to: H160 = Bob.into();
392
1
			let value: U256 = 42u8.into();
393
1
			let data: Vec<u8> = b"Test".to_vec();
394
1
			let gas_limit = u64::MAX;
395
1
			let nonce: U256 = 0u8.into();
396
1
			let deadline: U256 = 1_000u32.into();
397
1

            
398
1
			let permit = CallPermitPrecompile::<Runtime>::generate_permit(
399
1
				CallPermit.into(),
400
1
				from,
401
1
				to,
402
1
				value,
403
1
				data.clone(),
404
1
				gas_limit,
405
1
				nonce,
406
1
				deadline,
407
1
			);
408
1

            
409
1
			dbg!(H256::from(permit));
410
1

            
411
1
			let secret_key = SecretKey::parse(&alith_secret_key()).unwrap();
412
1
			let message = Message::parse(&permit);
413
1
			let (rs, v) = sign(&message, &secret_key);
414
1

            
415
1
			precompiles()
416
1
				.prepare_test(
417
1
					CryptoAlith,
418
1
					CallPermit,
419
1
					PCall::nonces {
420
1
						owner: Address(CryptoAlith.into()),
421
1
					},
422
1
				)
423
1
				.expect_cost(0) // TODO: Test db read/write costs
424
1
				.expect_no_logs()
425
1
				.execute_returns(U256::from(0u8));
426
1

            
427
1
			precompiles()
428
1
				.prepare_test(
429
1
					Charlie, // can be anyone
430
1
					CallPermit,
431
1
					PCall::dispatch {
432
1
						from: Address(from),
433
1
						to: Address(to),
434
1
						value,
435
1
						data: data.into(),
436
1
						gas_limit,
437
1
						deadline,
438
1
						v: v.serialize(),
439
1
						r: H256::from(rs.r.b32()),
440
1
						s: H256::from(rs.s.b32()),
441
1
					},
442
1
				)
443
1
				.with_subcall_handle(move |_| panic!("should not perform subcall"))
444
1
				.with_target_gas(Some(100_000 + dispatch_cost()))
445
1
				.expect_cost(dispatch_cost())
446
1
				.execute_reverts(|x| x == b"Call require too much gas (uint64 overflow)");
447
1
		})
448
1
}
449

            
450
// // This test checks the validity of a metamask signed message against the permit precompile
451
// // The code used to generate the signature is the following.
452
// // You will need to import CryptoAlith_PRIV_KEY in metamask.
453
// // If you put this code in the developer tools console, it will log the signature
454

            
455
// await window.ethereum.enable();
456
// const accounts = await window.ethereum.request({ method: "eth_requestAccounts" });
457

            
458
// const from = accounts[0];
459
// const to = "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
460
// const value = 42;
461
// const data = "0xdeadbeef";
462
// const gaslimit = 100000;
463
// const nonce = 0;
464
// const deadline = 1000;
465

            
466
// const createPermitMessageData = function () {
467
// 	const message = {
468
// 	from: from,
469
// 	to: to,
470
// 	value: value,
471
//    data: data,
472
//    gaslimit: gaslimit,
473
// 	nonce: nonce,
474
// 	deadline: deadline,
475
// 	};
476

            
477
// 	const typedData = JSON.stringify({
478
// 	types: {
479
// 		EIP712Domain: [
480
// 		{
481
// 			name: "name",
482
// 			type: "string",
483
// 		},
484
// 		{
485
// 			name: "version",
486
// 			type: "string",
487
// 		},
488
// 		{
489
// 			name: "chainId",
490
// 			type: "uint256",
491
// 		},
492
// 		{
493
// 			name: "verifyingContract",
494
// 			type: "address",
495
// 		},
496
// 		],
497
// 		CallPermit: [
498
// 		{
499
// 			name: "from",
500
// 			type: "address",
501
// 		},
502
// 		{
503
// 			name: "to",
504
// 			type: "address",
505
// 		},
506
// 		{
507
// 			name: "value",
508
// 			type: "uint256",
509
// 		},
510
//       {
511
// 			name: "data",
512
// 			type: "bytes",
513
// 		},
514
// 		{
515
// 			name: "gaslimit",
516
// 			type: "uint64",
517
// 		},
518
// 		{
519
// 			name: "nonce",
520
// 			type: "uint256",
521
// 		},
522
// 		{
523
// 			name: "deadline",
524
// 			type: "uint256",
525
// 		},
526
// 		],
527
// 	},
528
// 	primaryType: "CallPermit",
529
// 	domain: {
530
// 		name: "Call Permit CallPermit",
531
// 		version: "1",
532
// 		chainId: 0,
533
// 		verifyingContract: "0x0000000000000000000000000000000000000001",
534
// 	},
535
// 	message: message,
536
// 	});
537

            
538
// 	return {
539
// 		typedData,
540
// 		message,
541
// 	};
542
// };
543

            
544
// const method = "eth_signTypedData_v4"
545
// const messageData = createPermitMessageData();
546
// const params = [from, messageData.typedData];
547

            
548
// web3.currentProvider.sendAsync(
549
// 	{
550
// 		method,
551
// 		params,
552
// 		from,
553
// 	},
554
// 	function (err, result) {
555
// 		if (err) return console.dir(err);
556
// 		if (result.error) {
557
// 			alert(result.error.message);
558
// 		}
559
// 		if (result.error) return console.error('ERROR', result);
560
// 		console.log('TYPED SIGNED:' + JSON.stringify(result.result));
561

            
562
// 		const recovered = sigUtil.recoverTypedSignature_v4({
563
// 			data: JSON.parse(msgParams),
564
// 			sig: result.result,
565
// 		});
566

            
567
// 		if (
568
// 			ethUtil.toChecksumAddress(recovered) === ethUtil.toChecksumAddress(from)
569
// 		) {
570
// 			alert('Successfully recovered signer as ' + from);
571
// 		} else {
572
// 			alert(
573
// 				'Failed to verify signer when comparing ' + result + ' to ' + from
574
// 			);
575
// 		}
576
// 	}
577
// );
578
#[test]
579
1
fn valid_permit_returns_with_metamask_signed_data() {
580
1
	ExtBuilder::default()
581
1
		.with_balances(vec![(CryptoAlith.into(), 2000)])
582
1
		.build()
583
1
		.execute_with(|| {
584
1
			let from: H160 = CryptoAlith.into();
585
1
			let to: H160 = Bob.into();
586
1
			let value: U256 = 42u8.into();
587
1
			let data: Vec<u8> = hex_literal::hex!("deadbeef").to_vec();
588
1
			let gas_limit = 100_000u64;
589
1
			let deadline: U256 = 1_000u32.into();
590
1

            
591
1
			// Made with MetaMask
592
1
			let rsv = hex_literal::hex!(
593
1
				"56b497d556cb1b57a16aac6e8d53f3cbf1108df467ffcb937a3744369a27478f608de05
594
1
				34b8e0385e55ffd97cbafcfeac12ab52d0b74a2dea582bc8de46f257d1c"
595
1
			)
596
1
			.as_slice();
597
1
			let (r, sv) = rsv.split_at(32);
598
1
			let (s, v) = sv.split_at(32);
599
1
			let v_real = v[0];
600
1
			let r_real: [u8; 32] = r.try_into().unwrap();
601
1
			let s_real: [u8; 32] = s.try_into().unwrap();
602
1

            
603
1
			precompiles()
604
1
				.prepare_test(
605
1
					CryptoAlith,
606
1
					CallPermit,
607
1
					PCall::nonces {
608
1
						owner: Address(CryptoAlith.into()),
609
1
					},
610
1
				)
611
1
				.expect_cost(0) // TODO: Test db read/write costs
612
1
				.expect_no_logs()
613
1
				.execute_returns(U256::from(0u8));
614
1

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

            
617
1
			precompiles()
618
1
				.prepare_test(
619
1
					Charlie, // can be anyone
620
1
					CallPermit,
621
1
					PCall::dispatch {
622
1
						from: Address(from),
623
1
						to: Address(to),
624
1
						value,
625
1
						data: data.clone().into(),
626
1
						gas_limit,
627
1
						deadline,
628
1
						v: v_real,
629
1
						r: r_real.into(),
630
1
						s: s_real.into(),
631
1
					},
632
1
				)
633
1
				.with_subcall_handle(move |subcall| {
634
1
					let Subcall {
635
1
						address,
636
1
						transfer,
637
1
						input,
638
1
						target_gas,
639
1
						is_static,
640
1
						context,
641
1
					} = subcall;
642
1

            
643
1
					// Called on the behalf of the permit maker.
644
1
					assert_eq!(context.caller, CryptoAlith.into());
645
1
					assert_eq!(address, Bob.into());
646
1
					assert_eq!(is_static, false);
647
1
					assert_eq!(target_gas, Some(100_000), "forward requested gas");
648

            
649
1
					let transfer = transfer.expect("there is a transfer");
650
1
					assert_eq!(transfer.source, CryptoAlith.into());
651
1
					assert_eq!(transfer.target, Bob.into());
652
1
					assert_eq!(transfer.value, 42u8.into());
653

            
654
1
					assert_eq!(context.address, Bob.into());
655
1
					assert_eq!(context.apparent_value, 42u8.into());
656

            
657
1
					assert_eq!(&input, &data);
658

            
659
1
					SubcallOutput {
660
1
						output: b"TEST".to_vec(),
661
1
						cost: 13,
662
1
						logs: vec![log1(Bob, H256::repeat_byte(0x11), vec![])],
663
1
						..SubcallOutput::succeed()
664
1
					}
665
1
				})
666
1
				.with_target_gas(Some(call_cost + 100_000 + dispatch_cost()))
667
1
				.expect_cost(call_cost + 13 + dispatch_cost())
668
1
				.expect_log(log1(Bob, H256::repeat_byte(0x11), vec![]))
669
1
				.execute_returns(UnboundedBytes::from(b"TEST"));
670
1
		})
671
1
}
672

            
673
#[test]
674
1
fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() {
675
1
	check_precompile_implements_solidity_interfaces(&["CallPermit.sol"], PCall::supports_selector)
676
1
}