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 fp_evm::MAX_TRANSACTION_GAS_LIMIT;
23
use frame_support::assert_ok;
24
use frame_support::sp_runtime::Percent;
25
use pallet_evm::Call as EvmCall;
26
use pallet_parachain_staking::Event as StakingEvent;
27
use precompile_utils::{prelude::*, testing::*};
28
use sp_core::{H160, U256};
29
use sp_runtime::traits::Dispatchable;
30

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
991
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CandidateLeft {
992
1
				ex_candidate: Alice.into(),
993
1
				unlocked_amount: 1_000,
994
1
				new_total_amt_locked: 0,
995
1
			}
996
1
			.into();
997
			// Assert that the events vector contains the one expected
998
1
			assert!(events().contains(&expected));
999
1
		});
1
}
#[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
			let input_data = PCall::cancel_leave_candidates {
1
				candidate_count: 0.into(),
1
			}
1
			.into();
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CancelledCandidateExit {
1
				candidate: Alice.into(),
1
			}
1
			.into();
			// 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
			let input_data = PCall::go_online {}.into();
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CandidateBackOnline {
1
				candidate: Alice.into(),
1
			}
1
			.into();
			// 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();
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CandidateWentOffline {
1
				candidate: Alice.into(),
1
			}
1
			.into();
			// 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();
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CandidateBondedMore {
1
				candidate: Alice.into(),
1
				amount: 500,
1
				new_total_bond: 1500,
1
			}
1
			.into();
			// 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();
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CandidateBondLessRequested {
1
				candidate: Alice.into(),
1
				amount_to_decrease: 500,
1
				execute_round: 3,
1
			}
1
			.into();
			// 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()),
				500
			));
1
			roll_to(10);
			// 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
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CandidateBondedLess {
1
				candidate: Alice.into(),
1
				amount: 500,
1
				new_bond: 1000,
1
			}
1
			.into();
			// 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()),
				200
			));
1
			let input_data = PCall::cancel_candidate_bond_less {}.into();
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::CancelledCandidateBondLess {
1
				candidate: Alice.into(),
1
				amount: 200,
1
				execute_round: 3,
1
			}
1
			.into();
			// 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();
			// 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();
			// 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();
			// 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();
			// 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
			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();
			// 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
			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
			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
			roll_to(10);
1
			let input_data = PCall::execute_delegation_request {
1
				delegator: Address(Bob.into()),
1
				candidate: Address(Alice.into()),
1
			}
1
			.into();
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
			);
1
			let expected: crate::mock::RuntimeEvent = StakingEvent::DelegationRevoked {
1
				delegator: Bob.into(),
1
				candidate: Alice.into(),
1
				unstaked_amount: 1_000,
1
			}
1
			.into();
			// 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(),
				500
			));
1
			roll_to(10);
1
			let input_data = PCall::execute_delegation_request {
1
				delegator: Address(Bob.into()),
1
				candidate: Address(Alice.into()),
1
			}
1
			.into();
			// Make sure the call goes through successfully
1
			assert_ok!(
1
				RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root())
			);
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();
			// 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
			let input_data = PCall::cancel_delegation_request {
1
				candidate: Address(Alice.into()),
1
			}
1
			.into();
			// 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();
			// 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(),
				500
			));
1
			let input_data = PCall::cancel_delegation_request {
1
				candidate: Address(Alice.into()),
1
			}
1
			.into();
			// 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();
			// 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();
				// Make sure the call goes through successfully
3
				assert_ok!(
3
					RuntimeCall::Evm(evm_call(Bob, input_data)).dispatch(RuntimeOrigin::root())
				);
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();
				// Assert that the events vector contains the one expected
3
				assert!(events().contains(&expected));
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
					.execute_reverts(|output| {
2
						from_utf8(&output).unwrap().contains(
							"auto_compound: Must be an integer between 0 and 100 included",
						)
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();
				// Make sure the call goes through successfully
3
				assert_ok!(
3
					RuntimeCall::Evm(evm_call(Bob, input_data)).dispatch(RuntimeOrigin::root())
				);
3
				assert_eq!(
3
					ParachainStaking::delegation_auto_compound(&Alice.into(), &Bob.into()),
3
					Percent::from_percent(auto_compound_percent)
				);
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();
				// Assert that the events vector contains the one expected
3
				assert!(events().contains(&expected));
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
					.execute_reverts(|output| {
2
						from_utf8(&output)
2
							.unwrap()
2
							.contains("value: Must be an integer between 0 and 100 included")
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
				.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
				.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
				.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
				.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"],
		PCall::supports_selector,
	)
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
}