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
		authorization_list: Vec::new(),
46
24
	}
47
24
}
48

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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