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::mock::{
18
	events, roll_to, roll_to_round_begin, set_points, ExtBuilder, PCall, ParachainStaking,
19
	Precompiles, PrecompilesValue, Runtime, RuntimeCall, RuntimeOrigin,
20
};
21
use core::str::from_utf8;
22
use frame_support::assert_ok;
23
use frame_support::sp_runtime::Percent;
24
use pallet_evm::Call as EvmCall;
25
use pallet_parachain_staking::Event as StakingEvent;
26
use precompile_utils::{prelude::*, testing::*};
27
use sp_core::{H160, U256};
28
use sp_runtime::traits::Dispatchable;
29

            
30
48
fn precompiles() -> Precompiles<Runtime> {
31
48
	PrecompilesValue::get()
32
48
}
33

            
34
24
fn evm_call(source: impl Into<H160>, input: Vec<u8>) -> EvmCall<Runtime> {
35
24
	EvmCall::call {
36
24
		source: source.into(),
37
24
		target: Precompile1.into(),
38
24
		input,
39
24
		value: U256::zero(), // No value sent in EVM
40
24
		gas_limit: u64::max_value(),
41
24
		max_fee_per_gas: 0.into(),
42
24
		max_priority_fee_per_gas: Some(U256::zero()),
43
24
		nonce: None, // Use the next nonce
44
24
		access_list: Vec::new(),
45
24
	}
46
24
}
47

            
48
#[test]
49
1
fn selectors() {
50
1
	assert!(PCall::is_delegator_selectors().contains(&0xfd8ab482));
51
1
	assert!(PCall::is_candidate_selectors().contains(&0xd51b9e93));
52
1
	assert!(PCall::is_selected_candidate_selectors().contains(&0x740d7d2a));
53
1
	assert!(PCall::delegation_amount_selectors().contains(&0xa73e51bc));
54
1
	assert!(PCall::is_in_top_delegations_selectors().contains(&0x91cc8657));
55
1
	assert!(PCall::points_selectors().contains(&0x9799b4e7));
56
1
	assert!(PCall::min_delegation_selectors().contains(&0x02985992));
57
1
	assert!(PCall::candidate_count_selectors().contains(&0xa9a981a3));
58
1
	assert!(PCall::round_selectors().contains(&0x146ca531));
59
1
	assert!(PCall::candidate_delegation_count_selectors().contains(&0x2ec087eb));
60
1
	assert!(PCall::candidate_auto_compounding_delegation_count_selectors().contains(&0x905f0806));
61
1
	assert!(PCall::delegator_delegation_count_selectors().contains(&0x067ec822));
62
1
	assert!(PCall::selected_candidates_selectors().contains(&0xbcf868a6));
63
1
	assert!(PCall::delegation_request_is_pending_selectors().contains(&0x3b16def8));
64
1
	assert!(PCall::candidate_exit_is_pending_selectors().contains(&0x43443682));
65
1
	assert!(PCall::candidate_request_is_pending_selectors().contains(&0xd0deec11));
66
1
	assert!(PCall::join_candidates_selectors().contains(&0x1f2f83ad));
67
1
	assert!(PCall::schedule_leave_candidates_selectors().contains(&0xb1a3c1b7));
68
1
	assert!(PCall::execute_leave_candidates_selectors().contains(&0x3867f308));
69
1
	assert!(PCall::cancel_leave_candidates_selectors().contains(&0x9c76ebb4));
70
1
	assert!(PCall::go_offline_selectors().contains(&0xa6485ccd));
71
1
	assert!(PCall::go_online_selectors().contains(&0x6e5b676b));
72
1
	assert!(PCall::candidate_bond_more_selectors().contains(&0xa52c8643));
73
1
	assert!(PCall::schedule_candidate_bond_less_selectors().contains(&0x60744ae0));
74
1
	assert!(PCall::execute_candidate_bond_less_selectors().contains(&0x2e290290));
75
1
	assert!(PCall::cancel_candidate_bond_less_selectors().contains(&0xb5ad5f07));
76
1
	assert!(PCall::delegate_with_auto_compound_selectors().contains(&0x4b8bc9bf));
77
1
	assert!(PCall::schedule_revoke_delegation_selectors().contains(&0x1a1c740c));
78
1
	assert!(PCall::delegator_bond_more_selectors().contains(&0x0465135b));
79
1
	assert!(PCall::schedule_delegator_bond_less_selectors().contains(&0xc172fd2b));
80
1
	assert!(PCall::execute_delegation_request_selectors().contains(&0xe98c8abe));
81
1
	assert!(PCall::cancel_delegation_request_selectors().contains(&0xc90eee83));
82
1
	assert!(PCall::get_delegator_total_staked_selectors().contains(&0xe6861713));
83
1
	assert!(PCall::get_candidate_total_counted_selectors().contains(&0xbc5a1043));
84
1
}
85

            
86
#[test]
87
1
fn modifiers() {
88
1
	ExtBuilder::default().build().execute_with(|| {
89
1
		let mut tester = PrecompilesModifierTester::new(precompiles(), Alice, Precompile1);
90
1

            
91
1
		tester.test_view_modifier(PCall::is_delegator_selectors());
92
1
		tester.test_view_modifier(PCall::is_candidate_selectors());
93
1
		tester.test_view_modifier(PCall::is_selected_candidate_selectors());
94
1
		tester.test_view_modifier(PCall::points_selectors());
95
1
		tester.test_view_modifier(PCall::delegation_amount_selectors());
96
1
		tester.test_view_modifier(PCall::is_in_top_delegations_selectors());
97
1
		tester.test_view_modifier(PCall::min_delegation_selectors());
98
1
		tester.test_view_modifier(PCall::candidate_count_selectors());
99
1
		tester.test_view_modifier(PCall::round_selectors());
100
1
		tester.test_view_modifier(PCall::candidate_delegation_count_selectors());
101
1
		tester.test_view_modifier(PCall::delegator_delegation_count_selectors());
102
1
		tester.test_view_modifier(PCall::selected_candidates_selectors());
103
1
		tester.test_view_modifier(PCall::delegation_request_is_pending_selectors());
104
1
		tester.test_view_modifier(PCall::candidate_exit_is_pending_selectors());
105
1
		tester.test_view_modifier(PCall::candidate_request_is_pending_selectors());
106
1
		tester.test_default_modifier(PCall::join_candidates_selectors());
107
1
		tester.test_default_modifier(PCall::schedule_leave_candidates_selectors());
108
1
		tester.test_default_modifier(PCall::execute_leave_candidates_selectors());
109
1
		tester.test_default_modifier(PCall::cancel_leave_candidates_selectors());
110
1
		tester.test_default_modifier(PCall::go_offline_selectors());
111
1
		tester.test_default_modifier(PCall::go_online_selectors());
112
1
		tester.test_default_modifier(PCall::candidate_bond_more_selectors());
113
1
		tester.test_default_modifier(PCall::schedule_candidate_bond_less_selectors());
114
1
		tester.test_default_modifier(PCall::execute_candidate_bond_less_selectors());
115
1
		tester.test_default_modifier(PCall::cancel_candidate_bond_less_selectors());
116
1
		tester.test_default_modifier(PCall::delegate_with_auto_compound_selectors());
117
1
		tester.test_default_modifier(PCall::schedule_revoke_delegation_selectors());
118
1
		tester.test_default_modifier(PCall::delegator_bond_more_selectors());
119
1
		tester.test_default_modifier(PCall::schedule_delegator_bond_less_selectors());
120
1
		tester.test_default_modifier(PCall::execute_delegation_request_selectors());
121
1
		tester.test_default_modifier(PCall::cancel_delegation_request_selectors());
122
1
		tester.test_view_modifier(PCall::get_delegator_total_staked_selectors());
123
1
		tester.test_view_modifier(PCall::get_candidate_total_counted_selectors());
124
1
	});
125
1
}
126

            
127
#[test]
128
1
fn selector_less_than_four_bytes() {
129
1
	ExtBuilder::default().build().execute_with(|| {
130
1
		precompiles()
131
1
			.prepare_test(Alice, Precompile1, vec![1u8, 2u8, 3u8])
132
1
			.execute_reverts(|output| output == b"Tried to read selector out of bounds");
133
1
	});
134
1
}
135

            
136
#[test]
137
1
fn no_selector_exists_but_length_is_right() {
138
1
	ExtBuilder::default().build().execute_with(|| {
139
1
		precompiles()
140
1
			.prepare_test(Alice, Precompile1, vec![1u8, 2u8, 3u8, 4u8])
141
1
			.execute_reverts(|output| output == b"Unknown selector");
142
1
	});
143
1
}
144

            
145
#[test]
146
1
fn min_delegation_works() {
147
1
	ExtBuilder::default().build().execute_with(|| {
148
1
		precompiles()
149
1
			.prepare_test(Alice, Precompile1, PCall::min_delegation {})
150
1
			.expect_cost(0) // TODO: Test db read/write costs
151
1
			.expect_no_logs()
152
1
			.execute_returns(3u32)
153
1
	});
154
1
}
155

            
156
#[test]
157
1
fn points_zero() {
158
1
	ExtBuilder::default()
159
1
		.with_balances(vec![(Alice.into(), 1_000)])
160
1
		.with_candidates(vec![(Alice.into(), 1_000)])
161
1
		.build()
162
1
		.execute_with(|| {
163
1
			precompiles()
164
1
				// Assert that there are total 0 points in round 1
165
1
				.prepare_test(Alice, Precompile1, PCall::points { round: 1.into() })
166
1
				.expect_cost(0) // TODO: Test db read/write costs
167
1
				.expect_no_logs()
168
1
				.execute_returns(0u32);
169
1
		});
170
1
}
171

            
172
#[test]
173
1
fn points_non_zero() {
174
1
	ExtBuilder::default()
175
1
		.with_balances(vec![(Alice.into(), 1_000)])
176
1
		.with_candidates(vec![(Alice.into(), 1_000)])
177
1
		.build()
178
1
		.execute_with(|| {
179
1
			set_points(1u32, Alice, 100);
180
1

            
181
1
			// Assert that there are total 100 points in round 1
182
1
			precompiles()
183
1
				.prepare_test(Alice, Precompile1, PCall::points { round: 1.into() })
184
1
				.expect_cost(0) // TODO: Test db read/write costs
185
1
				.expect_no_logs()
186
1
				.execute_returns(100u32);
187
1
		});
188
1
}
189

            
190
#[test]
191
1
fn awarded_points_zero() {
192
1
	ExtBuilder::default()
193
1
		.with_balances(vec![(Alice.into(), 1_000)])
194
1
		.with_candidates(vec![(Alice.into(), 1_000)])
195
1
		.build()
196
1
		.execute_with(|| {
197
1
			set_points(1u32, Alice, 100);
198
1

            
199
1
			precompiles()
200
1
				.prepare_test(
201
1
					Alice,
202
1
					Precompile1,
203
1
					PCall::awarded_points {
204
1
						round: 1u32.into(),
205
1
						candidate: Address(Bob.into()),
206
1
					},
207
1
				)
208
1
				.expect_cost(0)
209
1
				.expect_no_logs()
210
1
				.execute_returns(0u32);
211
1
		});
212
1
}
213

            
214
#[test]
215
1
fn awarded_points_non_zero() {
216
1
	ExtBuilder::default()
217
1
		.with_balances(vec![(Alice.into(), 1_000)])
218
1
		.with_candidates(vec![(Alice.into(), 1_000)])
219
1
		.build()
220
1
		.execute_with(|| {
221
1
			set_points(1u32, Alice, 100);
222
1
			set_points(1u32, Bob, 10);
223
1

            
224
1
			precompiles()
225
1
				.prepare_test(
226
1
					Alice,
227
1
					Precompile1,
228
1
					PCall::awarded_points {
229
1
						round: 1u32.into(),
230
1
						candidate: Address(Alice.into()),
231
1
					},
232
1
				)
233
1
				.expect_cost(0)
234
1
				.expect_no_logs()
235
1
				.execute_returns(100u32);
236
1
		});
237
1
}
238

            
239
#[test]
240
1
fn delegation_amount_zero() {
241
1
	ExtBuilder::default()
242
1
		.with_balances(vec![(Alice.into(), 1_000)])
243
1
		.build()
244
1
		.execute_with(|| {
245
1
			precompiles()
246
1
				.prepare_test(
247
1
					Alice,
248
1
					Precompile1,
249
1
					PCall::delegation_amount {
250
1
						delegator: Address(Alice.into()),
251
1
						candidate: Address(Alice.into()),
252
1
					},
253
1
				)
254
1
				.expect_cost(0) // TODO: Test db read/write costs
255
1
				.expect_no_logs()
256
1
				.execute_returns(0u32);
257
1
		});
258
1
}
259

            
260
#[test]
261
1
fn delegation_amount_nonzero() {
262
1
	ExtBuilder::default()
263
1
		.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
264
1
		.with_candidates(vec![(Alice.into(), 1_000)])
265
1
		.with_delegations(vec![(Bob.into(), Alice.into(), 1_000)])
266
1
		.build()
267
1
		.execute_with(|| {
268
1
			precompiles()
269
1
				.prepare_test(
270
1
					Alice,
271
1
					Precompile1,
272
1
					PCall::delegation_amount {
273
1
						delegator: Address(Bob.into()),
274
1
						candidate: Address(Alice.into()),
275
1
					},
276
1
				)
277
1
				.expect_cost(0) // TODO: Test db read/write costs
278
1
				.expect_no_logs()
279
1
				.execute_returns(1000u32);
280
1
		});
281
1
}
282

            
283
#[test]
284
1
fn is_not_in_top_delegations_when_delegation_dne() {
285
1
	ExtBuilder::default()
286
1
		.with_balances(vec![(Alice.into(), 1_000)])
287
1
		.build()
288
1
		.execute_with(|| {
289
1
			precompiles()
290
1
				.prepare_test(
291
1
					Alice,
292
1
					Precompile1,
293
1
					PCall::delegation_amount {
294
1
						delegator: Address(Alice.into()),
295
1
						candidate: Address(Alice.into()),
296
1
					},
297
1
				)
298
1
				.expect_cost(0) // TODO: Test db read/write costs
299
1
				.expect_no_logs()
300
1
				.execute_returns(false);
301
1
		});
302
1
}
303

            
304
#[test]
305
1
fn is_not_in_top_delegations_because_not_in_top() {
306
1
	ExtBuilder::default()
307
1
		.with_balances(vec![
308
1
			(Alice.into(), 1_000),
309
1
			(Bob.into(), 500),
310
1
			(Charlie.into(), 501),
311
1
			(David.into(), 502),
312
1
		])
313
1
		.with_candidates(vec![(Alice.into(), 1_000)])
314
1
		.with_delegations(vec![
315
1
			(Bob.into(), Alice.into(), 500),
316
1
			(Charlie.into(), Alice.into(), 501),
317
1
			(David.into(), Alice.into(), 502),
318
1
		])
319
1
		.build()
320
1
		.execute_with(|| {
321
1
			precompiles()
322
1
				.prepare_test(
323
1
					Alice,
324
1
					Precompile1,
325
1
					PCall::is_in_top_delegations {
326
1
						delegator: Address(Bob.into()),
327
1
						candidate: Address(Alice.into()),
328
1
					},
329
1
				)
330
1
				.expect_cost(0) // TODO: Test db read/write costs
331
1
				.expect_no_logs()
332
1
				.execute_returns(false);
333
1
		});
334
1
}
335

            
336
#[test]
337
1
fn is_in_top_delegations() {
338
1
	ExtBuilder::default()
339
1
		.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 500)])
340
1
		.with_candidates(vec![(Alice.into(), 1_000)])
341
1
		.with_delegations(vec![(Bob.into(), Alice.into(), 500)])
342
1
		.build()
343
1
		.execute_with(|| {
344
1
			precompiles()
345
1
				.prepare_test(
346
1
					Alice,
347
1
					Precompile1,
348
1
					PCall::is_in_top_delegations {
349
1
						delegator: Address(Bob.into()),
350
1
						candidate: Address(Alice.into()),
351
1
					},
352
1
				)
353
1
				.expect_cost(0) // TODO: Test db read/write costs
354
1
				.expect_no_logs()
355
1
				.execute_returns(true);
356
1
		});
357
1
}
358

            
359
#[test]
360
1
fn round_works() {
361
1
	ExtBuilder::default().build().execute_with(|| {
362
1
		precompiles()
363
1
			.prepare_test(Alice, Precompile1, PCall::round {})
364
1
			.expect_cost(0) // TODO: Test db read/write costs
365
1
			.expect_no_logs()
366
1
			.execute_returns(1u32);
367

            
368
		// test next `ROUNDS_TO_TEST` rounds
369
		const ROUNDS_TO_TEST: u32 = 10;
370
1
		let mut current_round = 1;
371
10
		while current_round < ROUNDS_TO_TEST {
372
9
			current_round += 1;
373
9
			roll_to_round_begin(current_round);
374
9

            
375
9
			// Assert that round is equal to expectation
376
9
			precompiles()
377
9
				.prepare_test(Alice, Precompile1, PCall::round {})
378
9
				.expect_cost(0) // TODO: Test db read/write costs
379
9
				.expect_no_logs()
380
9
				.execute_returns(current_round);
381
9
		}
382
1
	});
383
1
}
384

            
385
#[test]
386
1
fn candidate_delegation_count_works() {
387
1
	ExtBuilder::default()
388
1
		.with_balances(vec![
389
1
			(Alice.into(), 1_000),
390
1
			(Bob.into(), 50),
391
1
			(Charlie.into(), 50),
392
1
			(David.into(), 50),
393
1
		])
394
1
		.with_candidates(vec![(Alice.into(), 1_000)])
395
1
		.with_delegations(vec![
396
1
			(Bob.into(), Alice.into(), 50),
397
1
			(Charlie.into(), Alice.into(), 50),
398
1
			(David.into(), Alice.into(), 50),
399
1
		])
400
1
		.build()
401
1
		.execute_with(|| {
402
1
			// Assert that there 3 delegations to Alice
403
1
			precompiles()
404
1
				.prepare_test(
405
1
					Alice,
406
1
					Precompile1,
407
1
					PCall::candidate_delegation_count {
408
1
						candidate: Address(Alice.into()),
409
1
					},
410
1
				)
411
1
				.expect_cost(0) // TODO: Test db read/write costs
412
1
				.expect_no_logs()
413
1
				.execute_returns(3u32);
414
1
		});
415
1
}
416

            
417
#[test]
418
1
fn candidate_auto_compounding_delegation_count_works() {
419
1
	ExtBuilder::default()
420
1
		.with_balances(vec![
421
1
			(Alice.into(), 1_000),
422
1
			(Bob.into(), 50),
423
1
			(Charlie.into(), 50),
424
1
			(David.into(), 50),
425
1
		])
426
1
		.with_candidates(vec![(Alice.into(), 1_000)])
427
1
		.with_delegations(vec![
428
1
			(Bob.into(), Alice.into(), 50),
429
1
			(Charlie.into(), Alice.into(), 50),
430
1
			(David.into(), Alice.into(), 50),
431
1
		])
432
1
		.with_auto_compounding_delegations(vec![(
433
1
			Charlie.into(),
434
1
			Alice.into(),
435
1
			50,
436
1
			Percent::from_percent(50),
437
1
		)])
438
1
		.build()
439
1
		.execute_with(|| {
440
1
			// Assert that there 1 auto compounding delegations to Alice
441
1
			precompiles()
442
1
				.prepare_test(
443
1
					Alice,
444
1
					Precompile1,
445
1
					PCall::candidate_auto_compounding_delegation_count {
446
1
						candidate: Address(Alice.into()),
447
1
					},
448
1
				)
449
1
				.expect_cost(0) // TODO: Test db read/write costs
450
1
				.expect_no_logs()
451
1
				.execute_returns(1u32);
452
1
		});
453
1
}
454

            
455
#[test]
456
1
fn candidate_auto_compounding_elegation_count_works_with_zero() {
457
1
	ExtBuilder::default()
458
1
		.with_balances(vec![
459
1
			(Alice.into(), 1_000),
460
1
			(Bob.into(), 50),
461
1
			(Charlie.into(), 50),
462
1
			(David.into(), 50),
463
1
		])
464
1
		.with_candidates(vec![(Alice.into(), 1_000)])
465
1
		.with_delegations(vec![
466
1
			(Bob.into(), Alice.into(), 50),
467
1
			(Charlie.into(), Alice.into(), 50),
468
1
			(David.into(), Alice.into(), 50),
469
1
		])
470
1
		.build()
471
1
		.execute_with(|| {
472
1
			// Assert that there 0 auto compounding delegations to Alice
473
1
			precompiles()
474
1
				.prepare_test(
475
1
					Alice,
476
1
					Precompile1,
477
1
					PCall::candidate_auto_compounding_delegation_count {
478
1
						candidate: Address(Alice.into()),
479
1
					},
480
1
				)
481
1
				.expect_cost(0) // TODO: Test db read/write costs
482
1
				.expect_no_logs()
483
1
				.execute_returns(0u32);
484
1
		});
485
1
}
486

            
487
#[test]
488
1
fn delegator_delegation_count_works() {
489
1
	ExtBuilder::default()
490
1
		.with_balances(vec![
491
1
			(Alice.into(), 1_000),
492
1
			(Bob.into(), 1_000),
493
1
			(Charlie.into(), 200),
494
1
		])
495
1
		.with_candidates(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
496
1
		.with_delegations(vec![
497
1
			(Charlie.into(), Alice.into(), 100),
498
1
			(Charlie.into(), Bob.into(), 100),
499
1
		])
500
1
		.build()
501
1
		.execute_with(|| {
502
1
			// Assert that Charlie has 2 outstanding nominations
503
1
			precompiles()
504
1
				.prepare_test(
505
1
					Alice,
506
1
					Precompile1,
507
1
					PCall::delegator_delegation_count {
508
1
						delegator: Address(Charlie.into()),
509
1
					},
510
1
				)
511
1
				.expect_cost(0) // TODO: Test db read/write costs
512
1
				.expect_no_logs()
513
1
				.execute_returns(2u32);
514
1
		});
515
1
}
516

            
517
#[test]
518
1
fn is_delegator_false() {
519
1
	ExtBuilder::default().build().execute_with(|| {
520
1
		// Assert that Charlie is not a delegator
521
1
		precompiles()
522
1
			.prepare_test(
523
1
				Alice,
524
1
				Precompile1,
525
1
				PCall::is_delegator {
526
1
					delegator: Address(Charlie.into()),
527
1
				},
528
1
			)
529
1
			.expect_cost(0) // TODO: Test db read/write costs
530
1
			.expect_no_logs()
531
1
			.execute_returns(false);
532
1
	});
533
1
}
534

            
535
#[test]
536
1
fn is_delegator_true() {
537
1
	ExtBuilder::default()
538
1
		.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 50)])
539
1
		.with_candidates(vec![(Alice.into(), 1_000)])
540
1
		.with_delegations(vec![(Bob.into(), Alice.into(), 50)])
541
1
		.build()
542
1
		.execute_with(|| {
543
1
			// Assert that Bob is a delegator
544
1
			precompiles()
545
1
				.prepare_test(
546
1
					Alice,
547
1
					Precompile1,
548
1
					PCall::is_delegator {
549
1
						delegator: Address(Bob.into()),
550
1
					},
551
1
				)
552
1
				.expect_cost(0) // TODO: Test db read/write costs
553
1
				.expect_no_logs()
554
1
				.execute_returns(true);
555
1
		});
556
1
}
557

            
558
#[test]
559
1
fn is_candidate_false() {
560
1
	ExtBuilder::default().build().execute_with(|| {
561
1
		// Assert that Alice is not a candidate
562
1
		precompiles()
563
1
			.prepare_test(
564
1
				Alice,
565
1
				Precompile1,
566
1
				PCall::is_candidate {
567
1
					candidate: Address(Alice.into()),
568
1
				},
569
1
			)
570
1
			.expect_cost(0) // TODO: Test db read/write costs
571
1
			.expect_no_logs()
572
1
			.execute_returns(false);
573
1
	});
574
1
}
575

            
576
#[test]
577
1
fn is_candidate_true() {
578
1
	ExtBuilder::default()
579
1
		.with_balances(vec![(Alice.into(), 1_000)])
580
1
		.with_candidates(vec![(Alice.into(), 1_000)])
581
1
		.build()
582
1
		.execute_with(|| {
583
1
			// Assert that Alice is a candidate
584
1
			precompiles()
585
1
				.prepare_test(
586
1
					Alice,
587
1
					Precompile1,
588
1
					PCall::is_candidate {
589
1
						candidate: Address(Alice.into()),
590
1
					},
591
1
				)
592
1
				.expect_cost(0) // TODO: Test db read/write costs
593
1
				.expect_no_logs()
594
1
				.execute_returns(true);
595
1
		});
596
1
}
597

            
598
#[test]
599
1
fn is_selected_candidate_false() {
600
1
	ExtBuilder::default().build().execute_with(|| {
601
1
		// Assert that Alice is not a selected candidate
602
1
		precompiles()
603
1
			.prepare_test(
604
1
				Alice,
605
1
				Precompile1,
606
1
				PCall::is_selected_candidate {
607
1
					candidate: Address(Alice.into()),
608
1
				},
609
1
			)
610
1
			.expect_cost(0) // TODO: Test db read/write costs
611
1
			.expect_no_logs()
612
1
			.execute_returns(false);
613
1
	});
614
1
}
615

            
616
#[test]
617
1
fn is_selected_candidate_true() {
618
1
	ExtBuilder::default()
619
1
		.with_balances(vec![(Alice.into(), 1_000)])
620
1
		.with_candidates(vec![(Alice.into(), 1_000)])
621
1
		.build()
622
1
		.execute_with(|| {
623
1
			// Assert that Alice is not a selected candidate
624
1
			precompiles()
625
1
				.prepare_test(
626
1
					Alice,
627
1
					Precompile1,
628
1
					PCall::is_selected_candidate {
629
1
						candidate: Address(Alice.into()),
630
1
					},
631
1
				)
632
1
				.expect_cost(0) // TODO: Test db read/write costs
633
1
				.expect_no_logs()
634
1
				.execute_returns(true);
635
1
		});
636
1
}
637

            
638
#[test]
639
1
fn selected_candidates_works() {
640
1
	ExtBuilder::default()
641
1
		.with_balances(vec![(Alice.into(), 1_000)])
642
1
		.with_candidates(vec![(Alice.into(), 1_000)])
643
1
		.build()
644
1
		.execute_with(|| {
645
1
			precompiles()
646
1
				.prepare_test(Alice, Precompile1, PCall::selected_candidates {})
647
1
				.expect_cost(0) // TODO: Test db read/write costs
648
1
				.expect_no_logs()
649
1
				.execute_returns(vec![Address(Alice.into())]);
650
1
		});
651
1
}
652

            
653
#[test]
654
1
fn delegation_request_is_pending_works() {
655
1
	ExtBuilder::default()
656
1
		.with_balances(vec![
657
1
			(Alice.into(), 1_000),
658
1
			(Charlie.into(), 50),
659
1
			(David.into(), 50),
660
1
		])
661
1
		.with_candidates(vec![(Alice.into(), 1_000)])
662
1
		.with_delegations(vec![(Charlie.into(), Alice.into(), 50)])
663
1
		.build()
664
1
		.execute_with(|| {
665
1
			// Assert that we dont have pending requests
666
1
			precompiles()
667
1
				.prepare_test(
668
1
					Alice,
669
1
					Precompile1,
670
1
					PCall::delegation_request_is_pending {
671
1
						delegator: Address(Charlie.into()),
672
1
						candidate: Address(Alice.into()),
673
1
					},
674
1
				)
675
1
				.expect_cost(0) // TODO: Test db read/write costs
676
1
				.expect_no_logs()
677
1
				.execute_returns(false);
678
1

            
679
1
			// Schedule Revoke request
680
1
			precompiles()
681
1
				.prepare_test(
682
1
					Charlie,
683
1
					Precompile1,
684
1
					PCall::schedule_revoke_delegation {
685
1
						candidate: Address(Alice.into()),
686
1
					},
687
1
				)
688
1
				.expect_cost(272791237)
689
1
				.expect_no_logs()
690
1
				.execute_returns(());
691
1

            
692
1
			// Assert that we have pending requests
693
1
			precompiles()
694
1
				.prepare_test(
695
1
					Alice,
696
1
					Precompile1,
697
1
					PCall::delegation_request_is_pending {
698
1
						delegator: Address(Charlie.into()),
699
1
						candidate: Address(Alice.into()),
700
1
					},
701
1
				)
702
1
				.expect_cost(0) // TODO: Test db read/write costs
703
1
				.expect_no_logs()
704
1
				.execute_returns(true);
705
1
		})
706
1
}
707

            
708
#[test]
709
1
fn delegation_request_is_pending_returns_false_for_non_existing_delegator() {
710
1
	ExtBuilder::default().build().execute_with(|| {
711
1
		// Expected false because delegator Bob does not exist
712
1
		precompiles()
713
1
			.prepare_test(
714
1
				Alice,
715
1
				Precompile1,
716
1
				PCall::delegation_request_is_pending {
717
1
					delegator: Address(Bob.into()),
718
1
					candidate: Address(Alice.into()),
719
1
				},
720
1
			)
721
1
			.expect_cost(0) // TODO: Test db read/write costs
722
1
			.expect_no_logs()
723
1
			.execute_returns(false);
724
1
	})
725
1
}
726

            
727
#[test]
728
1
fn candidate_exit_is_pending_works() {
729
1
	ExtBuilder::default()
730
1
		.with_balances(vec![(Alice.into(), 1_000)])
731
1
		.with_candidates(vec![(Alice.into(), 1_000)])
732
1
		.build()
733
1
		.execute_with(|| {
734
1
			// Assert that we don't have pending requests
735
1
			precompiles()
736
1
				.prepare_test(
737
1
					Alice,
738
1
					Precompile1,
739
1
					PCall::candidate_exit_is_pending {
740
1
						candidate: Address(Alice.into()),
741
1
					},
742
1
				)
743
1
				.expect_cost(0) // TODO: Test db read/write costs
744
1
				.expect_no_logs()
745
1
				.execute_returns(false);
746
1

            
747
1
			// Schedule exit request
748
1
			precompiles()
749
1
				.prepare_test(
750
1
					Alice,
751
1
					Precompile1,
752
1
					PCall::schedule_leave_candidates {
753
1
						candidate_count: 1.into(),
754
1
					},
755
1
				)
756
1
				.expect_cost(264694393)
757
1
				.expect_no_logs()
758
1
				.execute_returns(());
759
1

            
760
1
			// Assert that we have pending exit
761
1
			precompiles()
762
1
				.prepare_test(
763
1
					Alice,
764
1
					Precompile1,
765
1
					PCall::candidate_exit_is_pending {
766
1
						candidate: Address(Alice.into()),
767
1
					},
768
1
				)
769
1
				.expect_cost(0) // TODO: Test db read/write costs
770
1
				.expect_no_logs()
771
1
				.execute_returns(true);
772
1
		})
773
1
}
774

            
775
#[test]
776
1
fn candidate_exit_is_pending_returns_false_for_non_existing_delegator() {
777
1
	ExtBuilder::default().build().execute_with(|| {
778
1
		// Expected false because candidate Bob does not exist
779
1
		precompiles()
780
1
			.prepare_test(
781
1
				Alice,
782
1
				Precompile1,
783
1
				PCall::candidate_exit_is_pending {
784
1
					candidate: Address(Bob.into()),
785
1
				},
786
1
			)
787
1
			.expect_cost(0) // TODO: Test db read/write costs
788
1
			.expect_no_logs()
789
1
			.execute_returns(false);
790
1
	})
791
1
}
792

            
793
#[test]
794
1
fn candidate_request_is_pending_works() {
795
1
	ExtBuilder::default()
796
1
		.with_balances(vec![(Alice.into(), 1_050)])
797
1
		.with_candidates(vec![(Alice.into(), 1_050)])
798
1
		.build()
799
1
		.execute_with(|| {
800
1
			// Assert that we dont have pending requests
801
1
			precompiles()
802
1
				.prepare_test(
803
1
					Alice,
804
1
					Precompile1,
805
1
					PCall::candidate_request_is_pending {
806
1
						candidate: Address(Alice.into()),
807
1
					},
808
1
				)
809
1
				.expect_cost(0) // TODO: Test db read/write costs
810
1
				.expect_no_logs()
811
1
				.execute_returns(false);
812
1

            
813
1
			// Schedule bond less request
814
1
			precompiles()
815
1
				.prepare_test(
816
1
					Alice,
817
1
					Precompile1,
818
1
					PCall::schedule_candidate_bond_less { less: 0.into() },
819
1
				)
820
1
				.expect_cost(136000000)
821
1
				.expect_no_logs()
822
1
				.execute_returns(());
823
1

            
824
1
			// Assert that we have pending requests
825
1
			precompiles()
826
1
				.prepare_test(
827
1
					Alice,
828
1
					Precompile1,
829
1
					PCall::candidate_request_is_pending {
830
1
						candidate: Address(Alice.into()),
831
1
					},
832
1
				)
833
1
				.expect_cost(0) // TODO: Test db read/write costs
834
1
				.expect_no_logs()
835
1
				.execute_returns(true);
836
1
		})
837
1
}
838

            
839
#[test]
840
1
fn candidate_request_is_pending_returns_false_for_non_existing_candidate() {
841
1
	ExtBuilder::default().build().execute_with(|| {
842
1
		// Expected false because candidate Bob does not exist
843
1
		precompiles()
844
1
			.prepare_test(
845
1
				Alice,
846
1
				Precompile1,
847
1
				PCall::candidate_request_is_pending {
848
1
					candidate: Address(Bob.into()),
849
1
				},
850
1
			)
851
1
			.expect_cost(0) // TODO: Test db read/write costs
852
1
			.expect_no_logs()
853
1
			.execute_returns(false);
854
1
	})
855
1
}
856

            
857
#[test]
858
1
fn delegation_auto_compound_returns_value_if_set() {
859
1
	ExtBuilder::default()
860
1
		.with_balances(vec![(Alice.into(), 1_000), (Charlie.into(), 50)])
861
1
		.with_candidates(vec![(Alice.into(), 1_000)])
862
1
		.with_auto_compounding_delegations(vec![(
863
1
			Charlie.into(),
864
1
			Alice.into(),
865
1
			50,
866
1
			Percent::from_percent(50),
867
1
		)])
868
1
		.build()
869
1
		.execute_with(|| {
870
1
			precompiles()
871
1
				.prepare_test(
872
1
					Alice,
873
1
					Precompile1,
874
1
					PCall::delegation_auto_compound {
875
1
						delegator: Address(Charlie.into()),
876
1
						candidate: Address(Alice.into()),
877
1
					},
878
1
				)
879
1
				.expect_cost(0)
880
1
				.expect_no_logs()
881
1
				.execute_returns(50u8);
882
1
		})
883
1
}
884

            
885
#[test]
886
1
fn delegation_auto_compound_returns_zero_if_not_set() {
887
1
	ExtBuilder::default()
888
1
		.with_balances(vec![(Alice.into(), 1_000), (Charlie.into(), 50)])
889
1
		.with_candidates(vec![(Alice.into(), 1_000)])
890
1
		.with_delegations(vec![(Charlie.into(), Alice.into(), 50)])
891
1
		.build()
892
1
		.execute_with(|| {
893
1
			precompiles()
894
1
				.prepare_test(
895
1
					Alice,
896
1
					Precompile1,
897
1
					PCall::delegation_auto_compound {
898
1
						delegator: Address(Charlie.into()),
899
1
						candidate: Address(Alice.into()),
900
1
					},
901
1
				)
902
1
				.expect_cost(0)
903
1
				.expect_no_logs()
904
1
				.execute_returns(0u8);
905
1
		})
906
1
}
907

            
908
#[test]
909
1
fn join_candidates_works() {
910
1
	ExtBuilder::default()
911
1
		.with_balances(vec![(Alice.into(), 1_000)])
912
1
		.build()
913
1
		.execute_with(|| {
914
1
			let input_data = PCall::join_candidates {
915
1
				amount: 1000.into(),
916
1
				candidate_count: 0.into(),
917
1
			}
918
1
			.into();
919
1

            
920
1
			// Make sure the call goes through successfully
921
1
			assert_ok!(
922
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
923
1
			);
924

            
925
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::JoinedCollatorCandidates {
926
1
				account: Alice.into(),
927
1
				amount_locked: 1000,
928
1
				new_total_amt_locked: 1000,
929
1
			}
930
1
			.into();
931
1
			// Assert that the events vector contains the one expected
932
1
			println!("{:?}", events());
933
1
			assert!(events().contains(&expected));
934
1
		});
935
1
}
936

            
937
#[test]
938
1
fn schedule_leave_candidates_works() {
939
1
	ExtBuilder::default()
940
1
		.with_balances(vec![(Alice.into(), 1_000)])
941
1
		.with_candidates(vec![(Alice.into(), 1_000)])
942
1
		.build()
943
1
		.execute_with(|| {
944
1
			let input_data = PCall::schedule_leave_candidates {
945
1
				candidate_count: 1.into(),
946
1
			}
947
1
			.into();
948
1

            
949
1
			// Make sure the call goes through successfully
950
1
			assert_ok!(
951
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
952
1
			);
953

            
954
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CandidateScheduledExit {
955
1
				exit_allowed_round: 1,
956
1
				candidate: Alice.into(),
957
1
				scheduled_exit: 3,
958
1
			}
959
1
			.into();
960
1
			// Assert that the events vector contains the one expected
961
1
			assert!(events().contains(&expected));
962
1
		});
963
1
}
964

            
965
#[test]
966
1
fn execute_leave_candidates_works() {
967
1
	ExtBuilder::default()
968
1
		.with_balances(vec![(Alice.into(), 1_000)])
969
1
		.with_candidates(vec![(Alice.into(), 1_000)])
970
1
		.build()
971
1
		.execute_with(|| {
972
1
			assert_ok!(ParachainStaking::schedule_leave_candidates(
973
1
				RuntimeOrigin::signed(Alice.into()),
974
1
				1
975
1
			));
976
1
			roll_to(10);
977
1

            
978
1
			let input_data = PCall::execute_leave_candidates {
979
1
				candidate: Address(Alice.into()),
980
1
				candidate_count: 0.into(),
981
1
			}
982
1
			.into();
983
1

            
984
1
			// Make sure the call goes through successfully
985
1
			assert_ok!(
986
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
987
1
			);
988

            
989
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CandidateLeft {
990
1
				ex_candidate: Alice.into(),
991
1
				unlocked_amount: 1_000,
992
1
				new_total_amt_locked: 0,
993
1
			}
994
1
			.into();
995
1
			// Assert that the events vector contains the one expected
996
1
			assert!(events().contains(&expected));
997
1
		});
998
1
}
999

            
#[test]
1
fn cancel_leave_candidates_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1_000)])
1
		.with_candidates(vec![(Alice.into(), 1_000)])
1
		.build()
1
		.execute_with(|| {
1
			assert_ok!(ParachainStaking::schedule_leave_candidates(
1
				RuntimeOrigin::signed(Alice.into()),
1
				1
1
			));
1
			let input_data = PCall::cancel_leave_candidates {
1
				candidate_count: 0.into(),
1
			}
1
			.into();
1

            
1
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
1
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CancelledCandidateExit {
1
				candidate: Alice.into(),
1
			}
1
			.into();
1
			// Assert that the events vector contains the one expected
1
			assert!(events().contains(&expected));
1
		});
1
}
#[test]
1
fn go_online_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1_000)])
1
		.with_candidates(vec![(Alice.into(), 1_000)])
1
		.build()
1
		.execute_with(|| {
1
			assert_ok!(ParachainStaking::go_offline(RuntimeOrigin::signed(
1
				Alice.into()
1
			)));
1
			let input_data = PCall::go_online {}.into();
1

            
1
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
1
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CandidateBackOnline {
1
				candidate: Alice.into(),
1
			}
1
			.into();
1
			// Assert that the events vector contains the one expected
1
			assert!(events().contains(&expected));
1
		});
1
}
#[test]
1
fn go_offline_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1_000)])
1
		.with_candidates(vec![(Alice.into(), 1_000)])
1
		.build()
1
		.execute_with(|| {
1
			let input_data = PCall::go_offline {}.into();
1
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
1
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CandidateWentOffline {
1
				candidate: Alice.into(),
1
			}
1
			.into();
1
			// Assert that the events vector contains the one expected
1
			assert!(events().contains(&expected));
1
		});
1
}
#[test]
1
fn candidate_bond_more_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1_500)])
1
		.with_candidates(vec![(Alice.into(), 1_000)])
1
		.build()
1
		.execute_with(|| {
1
			let input_data = PCall::candidate_bond_more { more: 500.into() }.into();
1

            
1
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
1
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CandidateBondedMore {
1
				candidate: Alice.into(),
1
				amount: 500,
1
				new_total_bond: 1500,
1
			}
1
			.into();
1
			// Assert that the events vector contains the one expected
1
			assert!(events().contains(&expected));
1
		});
1
}
#[test]
1
fn schedule_candidate_bond_less_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1_000)])
1
		.with_candidates(vec![(Alice.into(), 1_000)])
1
		.build()
1
		.execute_with(|| {
1
			let input_data = PCall::schedule_candidate_bond_less { less: 500.into() }.into();
1

            
1
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
1
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CandidateBondLessRequested {
1
				candidate: Alice.into(),
1
				amount_to_decrease: 500,
1
				execute_round: 3,
1
			}
1
			.into();
1
			// Assert that the events vector contains the one expected
1
			assert!(events().contains(&expected));
1
		});
1
}
#[test]
1
fn execute_candidate_bond_less_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1_500)])
1
		.with_candidates(vec![(Alice.into(), 1_500)])
1
		.build()
1
		.execute_with(|| {
1
			assert_ok!(ParachainStaking::schedule_candidate_bond_less(
1
				RuntimeOrigin::signed(Alice.into()),
1
				500
1
			));
1
			roll_to(10);
1

            
1
			// Make sure the call goes through successfully
1
			let input_data = PCall::execute_candidate_bond_less {
1
				candidate: Address(Alice.into()),
1
			}
1
			.into();
1

            
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
1
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CandidateBondedLess {
1
				candidate: Alice.into(),
1
				amount: 500,
1
				new_bond: 1000,
1
			}
1
			.into();
1
			// Assert that the events vector contains the one expected
1
			assert!(events().contains(&expected));
1
		});
1
}
#[test]
1
fn cancel_candidate_bond_less_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1_200)])
1
		.with_candidates(vec![(Alice.into(), 1_200)])
1
		.build()
1
		.execute_with(|| {
1
			assert_ok!(ParachainStaking::schedule_candidate_bond_less(
1
				RuntimeOrigin::signed(Alice.into()),
1
				200
1
			));
1
			let input_data = PCall::cancel_candidate_bond_less {}.into();
1

            
1
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
1
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CancelledCandidateBondLess {
1
				candidate: Alice.into(),
1
				amount: 200,
1
				execute_round: 3,
1
			}
1
			.into();
1
			// Assert that the events vector contains the one expected
1
			assert!(events().contains(&expected));
1
		});
1
}
#[test]
1
fn delegate_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
1
		.with_candidates(vec![(Alice.into(), 1_000)])
1
		.build()
1
		.execute_with(|| {
1
			let input_data = PCall::delegate_with_auto_compound {
1
				candidate: Address(Alice.into()),
1
				amount: 1_000.into(),
1
				auto_compound: 0.into(),
1
				candidate_delegation_count: 0.into(),
1
				candidate_auto_compounding_delegation_count: 0.into(),
1
				delegator_delegation_count: 0.into(),
1
			}
1
			.into();
1

            
1
			// Make sure the call goes through successfully
1
			assert_ok!(RuntimeCall::Evm(evm_call(Bob, input_data)).dispatch(RuntimeOrigin::root()));
1
			assert!(ParachainStaking::is_delegator(&Bob.into()));
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::Delegation {
1
				delegator: Bob.into(),
1
				locked_amount: 1_000,
1
				candidate: Alice.into(),
1
				delegator_position: pallet_parachain_staking::DelegatorAdded::AddedToTop {
1
					new_total: 2_000,
1
				},
1
				auto_compound: Percent::zero(),
1
			}
1
			.into();
1
			// Assert that the events vector contains the one expected
1
			assert!(events().contains(&expected));
1
		});
1
}
#[test]
1
fn schedule_revoke_delegation_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
1
		.with_candidates(vec![(Alice.into(), 1_000)])
1
		.with_delegations(vec![(Bob.into(), Alice.into(), 1_000)])
1
		.build()
1
		.execute_with(|| {
1
			let input_data = PCall::schedule_revoke_delegation {
1
				candidate: Address(Alice.into()),
1
			}
1
			.into();
1

            
1
			// Make sure the call goes through successfully
1
			assert_ok!(RuntimeCall::Evm(evm_call(Bob, input_data)).dispatch(RuntimeOrigin::root()));
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::DelegationRevocationScheduled {
1
				round: 1,
1
				delegator: Bob.into(),
1
				candidate: Alice.into(),
1
				scheduled_exit: 3,
1
			}
1
			.into();
1
			// Assert that the events vector contains the one expected
1
			assert!(events().contains(&expected));
1
		});
1
}
#[test]
1
fn delegator_bond_more_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 1_500)])
1
		.with_candidates(vec![(Alice.into(), 1_000)])
1
		.with_delegations(vec![(Bob.into(), Alice.into(), 500)])
1
		.build()
1
		.execute_with(|| {
1
			let input_data = PCall::delegator_bond_more {
1
				candidate: Address(Alice.into()),
1
				more: 500.into(),
1
			}
1
			.into();
1

            
1
			assert_ok!(RuntimeCall::Evm(evm_call(Bob, input_data)).dispatch(RuntimeOrigin::root()));
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::DelegationIncreased {
1
				delegator: Bob.into(),
1
				candidate: Alice.into(),
1
				amount: 500,
1
				in_top: true,
1
			}
1
			.into();
1
			// Assert that the events vector contains the one expected
1
			assert!(events().contains(&expected));
1
		});
1
}
#[test]
1
fn schedule_delegator_bond_less_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 1_500)])
1
		.with_candidates(vec![(Alice.into(), 1_000)])
1
		.with_delegations(vec![(Bob.into(), Alice.into(), 1_500)])
1
		.build()
1
		.execute_with(|| {
1
			let input_data = PCall::schedule_delegator_bond_less {
1
				candidate: Address(Alice.into()),
1
				less: 500.into(),
1
			}
1
			.into();
1

            
1
			assert_ok!(RuntimeCall::Evm(evm_call(Bob, input_data)).dispatch(RuntimeOrigin::root()));
			// Check for the right events.
1
			let expected_event: crate::mock::RuntimeEvent =
1
				StakingEvent::DelegationDecreaseScheduled {
1
					delegator: Bob.into(),
1
					candidate: Alice.into(),
1
					amount_to_decrease: 500,
1
					execute_round: 3,
1
				}
1
				.into();
1

            
1
			assert!(events().contains(&expected_event));
1
		});
1
}
#[test]
1
fn execute_revoke_delegation_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
1
		.with_candidates(vec![(Alice.into(), 1_000)])
1
		.with_delegations(vec![(Bob.into(), Alice.into(), 1_000)])
1
		.build()
1
		.execute_with(|| {
1
			assert_ok!(ParachainStaking::schedule_revoke_delegation(
1
				RuntimeOrigin::signed(Bob.into()),
1
				Alice.into()
1
			));
1
			roll_to(10);
1

            
1
			let input_data = PCall::execute_delegation_request {
1
				delegator: Address(Bob.into()),
1
				candidate: Address(Alice.into()),
1
			}
1
			.into();
1

            
1
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
1
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::DelegationRevoked {
1
				delegator: Bob.into(),
1
				candidate: Alice.into(),
1
				unstaked_amount: 1_000,
1
			}
1
			.into();
1
			// Assert that the events vector contains the one expected
1
			assert!(events().contains(&expected));
1
		});
1
}
#[test]
1
fn execute_delegator_bond_less_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
1
		.with_candidates(vec![(Alice.into(), 1_000)])
1
		.with_delegations(vec![(Bob.into(), Alice.into(), 1_000)])
1
		.build()
1
		.execute_with(|| {
1
			assert_ok!(ParachainStaking::schedule_delegator_bond_less(
1
				RuntimeOrigin::signed(Bob.into()),
1
				Alice.into(),
1
				500
1
			));
1
			roll_to(10);
1

            
1
			let input_data = PCall::execute_delegation_request {
1
				delegator: Address(Bob.into()),
1
				candidate: Address(Alice.into()),
1
			}
1
			.into();
1

            
1
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
1
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::DelegationDecreased {
1
				delegator: Bob.into(),
1
				candidate: Alice.into(),
1
				amount: 500,
1
				in_top: true,
1
			}
1
			.into();
1
			// Assert that the events vector contains the one expected
1
			assert!(events().contains(&expected));
1
		});
1
}
#[test]
1
fn cancel_revoke_delegation_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
1
		.with_candidates(vec![(Alice.into(), 1_000)])
1
		.with_delegations(vec![(Bob.into(), Alice.into(), 1_000)])
1
		.build()
1
		.execute_with(|| {
1
			assert_ok!(ParachainStaking::schedule_revoke_delegation(
1
				RuntimeOrigin::signed(Bob.into()),
1
				Alice.into()
1
			));
1
			let input_data = PCall::cancel_delegation_request {
1
				candidate: Address(Alice.into()),
1
			}
1
			.into();
1

            
1
			// Make sure the call goes through successfully
1
			assert_ok!(RuntimeCall::Evm(evm_call(Bob, input_data)).dispatch(RuntimeOrigin::root()));
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CancelledDelegationRequest {
1
				delegator: Bob.into(),
1
				collator: Alice.into(),
1
				cancelled_request: pallet_parachain_staking::CancelledScheduledRequest {
1
					when_executable: 3,
1
					action: pallet_parachain_staking::DelegationAction::Revoke(1_000),
1
				},
1
			}
1
			.into();
1
			// Assert that the events vector contains the one expected
1
			assert!(events().contains(&expected));
1
		});
1
}
#[test]
1
fn cancel_delegator_bonded_less_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
1
		.with_candidates(vec![(Alice.into(), 1_000)])
1
		.with_delegations(vec![(Bob.into(), Alice.into(), 1_000)])
1
		.build()
1
		.execute_with(|| {
1
			assert_ok!(ParachainStaking::schedule_delegator_bond_less(
1
				RuntimeOrigin::signed(Bob.into()),
1
				Alice.into(),
1
				500
1
			));
1
			let input_data = PCall::cancel_delegation_request {
1
				candidate: Address(Alice.into()),
1
			}
1
			.into();
1

            
1
			// Make sure the call goes through successfully
1
			assert_ok!(RuntimeCall::Evm(evm_call(Bob, input_data)).dispatch(RuntimeOrigin::root()));
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CancelledDelegationRequest {
1
				delegator: Bob.into(),
1
				collator: Alice.into(),
1
				cancelled_request: pallet_parachain_staking::CancelledScheduledRequest {
1
					when_executable: 3,
1
					action: pallet_parachain_staking::DelegationAction::Decrease(500),
1
				},
1
			}
1
			.into();
1
			// Assert that the events vector contains the one expected
1
			assert!(events().contains(&expected));
1
		});
1
}
#[test]
1
fn delegate_with_auto_compound_works() {
4
	for auto_compound_percent in [0, 50, 100] {
3
		ExtBuilder::default()
3
			.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
3
			.with_candidates(vec![(Alice.into(), 1_000)])
3
			.build()
3
			.execute_with(|| {
3
				let input_data = PCall::delegate_with_auto_compound {
3
					candidate: Address(Alice.into()),
3
					amount: 1_000.into(),
3
					auto_compound: auto_compound_percent,
3
					candidate_delegation_count: 0.into(),
3
					candidate_auto_compounding_delegation_count: 0.into(),
3
					delegator_delegation_count: 0.into(),
3
				}
3
				.into();
3

            
3
				// Make sure the call goes through successfully
3
				assert_ok!(
3
					RuntimeCall::Evm(evm_call(Bob, input_data)).dispatch(RuntimeOrigin::root())
3
				);
3
				assert!(ParachainStaking::is_delegator(&Bob.into()));
3
				let expected: crate::mock::RuntimeEvent = StakingEvent::Delegation {
3
					delegator: Bob.into(),
3
					locked_amount: 1_000,
3
					candidate: Alice.into(),
3
					delegator_position: pallet_parachain_staking::DelegatorAdded::AddedToTop {
3
						new_total: 2_000,
3
					},
3
					auto_compound: Percent::from_percent(auto_compound_percent),
3
				}
3
				.into();
3
				// Assert that the events vector contains the one expected
3
				assert!(events().contains(&expected));
3
			});
3
	}
1
}
#[test]
1
fn delegate_with_auto_compound_returns_error_if_percent_above_hundred() {
3
	for auto_compound_percent in [101, 255] {
2
		ExtBuilder::default()
2
			.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
2
			.with_candidates(vec![(Alice.into(), 1_000)])
2
			.build()
2
			.execute_with(|| {
2
				PrecompilesValue::get()
2
					.prepare_test(
2
						Bob,
2
						Precompile1,
2
						PCall::delegate_with_auto_compound {
2
							candidate: Address(Alice.into()),
2
							amount: 1_000.into(),
2
							auto_compound: auto_compound_percent,
2
							candidate_delegation_count: 0.into(),
2
							candidate_auto_compounding_delegation_count: 0.into(),
2
							delegator_delegation_count: 0.into(),
2
						},
2
					)
2
					.execute_reverts(|output| {
2
						from_utf8(&output).unwrap().contains(
2
							"auto_compound: Must be an integer between 0 and 100 included",
2
						)
2
					});
2
			});
2
	}
1
}
#[test]
1
fn set_auto_compound_works_if_delegation() {
4
	for auto_compound_percent in [0, 50, 100] {
3
		ExtBuilder::default()
3
			.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
3
			.with_candidates(vec![(Alice.into(), 1_000)])
3
			.with_delegations(vec![(Bob.into(), Alice.into(), 1_000)])
3
			.build()
3
			.execute_with(|| {
3
				let input_data = PCall::set_auto_compound {
3
					candidate: Address(Alice.into()),
3
					value: auto_compound_percent,
3
					candidate_auto_compounding_delegation_count: 0.into(),
3
					delegator_delegation_count: 1.into(),
3
				}
3
				.into();
3

            
3
				// Make sure the call goes through successfully
3
				assert_ok!(
3
					RuntimeCall::Evm(evm_call(Bob, input_data)).dispatch(RuntimeOrigin::root())
3
				);
3
				assert_eq!(
3
					ParachainStaking::delegation_auto_compound(&Alice.into(), &Bob.into()),
3
					Percent::from_percent(auto_compound_percent)
3
				);
3
				let expected: crate::mock::RuntimeEvent = StakingEvent::AutoCompoundSet {
3
					candidate: Alice.into(),
3
					delegator: Bob.into(),
3
					value: Percent::from_percent(auto_compound_percent),
3
				}
3
				.into();
3
				// Assert that the events vector contains the one expected
3
				assert!(events().contains(&expected));
3
			});
3
	}
1
}
#[test]
1
fn set_auto_compound_returns_error_if_value_above_hundred_percent() {
3
	for auto_compound_percent in [101, 255] {
2
		ExtBuilder::default()
2
			.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
2
			.with_candidates(vec![(Alice.into(), 1_000)])
2
			.with_delegations(vec![(Bob.into(), Alice.into(), 1_000)])
2
			.build()
2
			.execute_with(|| {
2
				PrecompilesValue::get()
2
					.prepare_test(
2
						Bob,
2
						Precompile1,
2
						PCall::set_auto_compound {
2
							candidate: Address(Alice.into()),
2
							value: auto_compound_percent,
2
							candidate_auto_compounding_delegation_count: 0.into(),
2
							delegator_delegation_count: 1.into(),
2
						},
2
					)
2
					.execute_reverts(|output| {
2
						from_utf8(&output)
2
							.unwrap()
2
							.contains("value: Must be an integer between 0 and 100 included")
2
					});
2
			});
2
	}
1
}
#[test]
1
fn set_auto_compound_fails_if_not_delegation() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(Alice.into(), 1000), (Bob.into(), 1000)])
1
		.with_candidates(vec![(Alice.into(), 1_000)])
1
		.build()
1
		.execute_with(|| {
1
			PrecompilesValue::get()
1
				.prepare_test(
1
					Alice,
1
					Precompile1,
1
					PCall::set_auto_compound {
1
						candidate: Address(Alice.into()),
1
						value: 50,
1
						candidate_auto_compounding_delegation_count: 0.into(),
1
						delegator_delegation_count: 0.into(),
1
					},
1
				)
1
				.execute_reverts(|output| from_utf8(&output).unwrap().contains("DelegatorDNE"));
1
		});
1
}
#[test]
1
fn get_delegator_total_staked_getter() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(Alice.into(), 1_000),
1
			(Bob.into(), 1_000),
1
			(Charlie.into(), 1_500),
1
		])
1
		.with_candidates(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
1
		.with_delegations(vec![
1
			(Charlie.into(), Alice.into(), 1_000),
1
			(Charlie.into(), Bob.into(), 499),
1
		])
1
		.build()
1
		.execute_with(|| {
1
			PrecompilesValue::get()
1
				.prepare_test(
1
					Alice,
1
					Precompile1,
1
					PCall::get_delegator_total_staked {
1
						delegator: Address(Charlie.into()),
1
					},
1
				)
1
				.execute_returns(U256::from(1_499));
1
		});
1
}
#[test]
1
fn get_delegator_total_staked_getter_unknown() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(Alice.into(), 1_000),
1
			(Bob.into(), 1_000),
1
			(Charlie.into(), 1_500),
1
		])
1
		.with_candidates(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
1
		.build()
1
		.execute_with(|| {
1
			PrecompilesValue::get()
1
				.prepare_test(
1
					Alice,
1
					Precompile1,
1
					PCall::get_delegator_total_staked {
1
						delegator: Address(Charlie.into()),
1
					},
1
				)
1
				.execute_returns(U256::zero());
1
		});
1
}
#[test]
1
fn get_candidate_total_counted_getter() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(Alice.into(), 1_000),
1
			(Bob.into(), 1_000),
1
			(Charlie.into(), 1_500),
1
		])
1
		.with_candidates(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
1
		.with_delegations(vec![
1
			(Charlie.into(), Alice.into(), 1_000),
1
			(Charlie.into(), Bob.into(), 499),
1
		])
1
		.build()
1
		.execute_with(|| {
1
			PrecompilesValue::get()
1
				.prepare_test(
1
					Alice,
1
					Precompile1,
1
					PCall::get_candidate_total_counted {
1
						candidate: Address(Alice.into()),
1
					},
1
				)
1
				.execute_returns(U256::from(2_000));
1
		});
1
}
#[test]
1
fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() {
1
	check_precompile_implements_solidity_interfaces(
1
		&["StakingInterface.sol"],
1
		PCall::supports_selector,
1
	)
1
}
#[test]
1
fn test_deprecated_solidity_selectors_are_supported() {
26
	for deprecated_function in [
		"min_delegation()",
1
		"candidate_count()",
1
		"candidate_delegation_count(address)",
1
		"delegator_delegation_count(address)",
1
		"selected_candidates()",
1
		"is_delegator(address)",
1
		"is_candidate(address)",
1
		"is_selected_candidate(address)",
1
		"delegation_request_is_pending(address,address)",
1
		"candidate_exit_is_pending(address)",
1
		"candidate_request_is_pending(address)",
1
		"join_candidates(uint256,uint256)",
1
		"schedule_leave_candidates(uint256)",
1
		"execute_leave_candidates(address,uint256)",
1
		"cancel_leave_candidates(uint256)",
1
		"go_offline()",
1
		"go_online()",
1
		"schedule_candidate_bond_less(uint256)",
1
		"candidate_bond_more(uint256)",
1
		"execute_candidate_bond_less(address)",
1
		"cancel_candidate_bond_less()",
1
		"schedule_revoke_delegation(address)",
1
		"schedule_delegator_bond_less(address,uint256)",
1
		"delegator_bond_more(address,uint256)",
1
		"execute_delegation_request(address,address)",
1
		"cancel_delegation_request(address)",
	] {
26
		let selector = compute_selector(deprecated_function);
26
		if !PCall::supports_selector(selector) {
			panic!(
				"failed decoding selector 0x{:x} => '{}' as Action",
				selector, deprecated_function,
			)
26
		}
	}
1
}