1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

            
4
// Tanssi 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
// Tanssi 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 Tanssi.  If not, see <http://www.gnu.org/licenses/>
16

            
17
use {
18
    super::*,
19
    crate::{
20
        assert_eq_last_events, CandidateSummaries, CandidateSummary, DelegatorCandidateSummaries,
21
        DelegatorCandidateSummary, PausePoolsExtrinsics,
22
    },
23
};
24

            
25
pool_test!(
26
    fn empty_delegation<P>() {
27
2
        ExtBuilder::default().build().execute_with(|| {
28
2
            let before = State::extract(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1);
29
2
            let pool_before =
30
2
                PoolState::extract::<Joining>(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1);
31

            
32
2
            assert_noop!(
33
2
                Staking::request_delegate(
34
2
                    RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
35
                    ACCOUNT_CANDIDATE_1,
36
2
                    P::target_pool(),
37
                    0
38
                ),
39
2
                Error::<Runtime>::StakeMustBeNonZero
40
            );
41

            
42
2
            let after = State::extract(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1);
43
2
            let pool_after =
44
2
                PoolState::extract::<Joining>(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1);
45

            
46
2
            assert_eq!(before, after);
47
2
            assert_eq!(pool_before, pool_after);
48

            
49
2
            assert_eq_events!(Vec::<Event<Runtime>>::new());
50
2
        })
51
    }
52
);
53

            
54
pool_test!(
55
    fn delegation_request<P>() {
56
2
        ExtBuilder::default().build().execute_with(|| {
57
2
            let amount = 3324;
58
2
            RequestDelegation {
59
2
                candidate: ACCOUNT_CANDIDATE_1,
60
2
                delegator: ACCOUNT_DELEGATOR_1,
61
2
                pool: P::target_pool(),
62
2
                amount: amount + 1, // to test joining rounding
63
2
                expected_joining: amount,
64
2
            }
65
2
            .test();
66

            
67
2
            assert_eq!(
68
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(ACCOUNT_DELEGATOR_1)
69
2
                    .count(),
70
                1
71
            );
72
2
            assert_eq!(
73
2
                DelegatorCandidateSummaries::<Runtime>::get(
74
                    ACCOUNT_DELEGATOR_1,
75
                    ACCOUNT_CANDIDATE_1
76
                ),
77
2
                DelegatorCandidateSummary::new().with_joining(true)
78
            );
79
2
            assert_eq!(
80
2
                CandidateSummaries::<Runtime>::get(ACCOUNT_CANDIDATE_1),
81
2
                CandidateSummary {
82
2
                    delegators: 1,
83
2
                    joining_delegators: 1,
84
2
                    ..default()
85
2
                }
86
            );
87

            
88
2
            assert_eq_events!(vec![Event::RequestedDelegate {
89
2
                candidate: ACCOUNT_CANDIDATE_1,
90
2
                delegator: ACCOUNT_DELEGATOR_1,
91
2
                pool: P::target_pool(),
92
2
                pending: amount
93
2
            },]);
94
2
        })
95
    }
96
);
97

            
98
pool_test!(
99
    fn delegation_request_more_than_available<P>() {
100
2
        ExtBuilder::default().build().execute_with(|| {
101
2
            let amount = DEFAULT_BALANCE; // not enough to keep ED
102

            
103
2
            let before = State::extract(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1);
104
2
            let pool_before =
105
2
                PoolState::extract::<Joining>(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1);
106

            
107
2
            assert_noop!(
108
2
                Staking::request_delegate(
109
2
                    RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
110
                    ACCOUNT_CANDIDATE_1,
111
2
                    P::target_pool(),
112
2
                    amount,
113
                ),
114
2
                TokenError::FundsUnavailable
115
            );
116

            
117
2
            let after = State::extract(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1);
118
2
            let pool_after =
119
2
                PoolState::extract::<Joining>(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1);
120

            
121
2
            assert_eq!(before, after);
122
2
            assert_eq!(pool_before, pool_after);
123

            
124
2
            assert_eq_events!(Vec::<Event<Runtime>>::new());
125
2
        })
126
    }
127
);
128

            
129
pool_test!(
130
    fn delegation_execution<P>() {
131
2
        ExtBuilder::default().build().execute_with(|| {
132
2
            let final_amount = 2 * SHARE_INIT;
133
2
            let requested_amount = final_amount + 10; // test share rounding
134

            
135
2
            FullDelegation {
136
2
                candidate: ACCOUNT_CANDIDATE_1,
137
2
                delegator: ACCOUNT_DELEGATOR_1,
138
2
                request_amount: requested_amount,
139
2
                expected_increase: final_amount,
140
2
                ..default()
141
2
            }
142
2
            .test::<P>();
143

            
144
2
            assert_eq!(
145
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(ACCOUNT_DELEGATOR_1)
146
2
                    .count(),
147
                1
148
            );
149
2
            assert_eq!(
150
2
                DelegatorCandidateSummaries::<Runtime>::get(
151
                    ACCOUNT_DELEGATOR_1,
152
                    ACCOUNT_CANDIDATE_1
153
                ),
154
2
                DelegatorCandidateSummary::new().with_pool(P::pool_kind(), true)
155
            );
156
2
            assert_eq!(
157
2
                CandidateSummaries::<Runtime>::get(ACCOUNT_CANDIDATE_1),
158
2
                CandidateSummary {
159
2
                    delegators: 1,
160
2
                    ..default()
161
2
                }
162
2
                .with_pool(P::pool_kind(), 1)
163
            );
164

            
165
2
            assert_eq_events!(vec![
166
2
                Event::RequestedDelegate {
167
2
                    candidate: ACCOUNT_CANDIDATE_1,
168
2
                    delegator: ACCOUNT_DELEGATOR_1,
169
2
                    pool: P::target_pool(),
170
2
                    pending: requested_amount,
171
2
                },
172
2
                P::event_staked(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1, 2, final_amount),
173
2
                Event::ExecutedDelegate {
174
2
                    candidate: ACCOUNT_CANDIDATE_1,
175
2
                    delegator: ACCOUNT_DELEGATOR_1,
176
2
                    pool: P::target_pool(),
177
2
                    staked: final_amount,
178
2
                    released: 10,
179
2
                },
180
            ]);
181
2
        })
182
    }
183
);
184

            
185
pool_test!(
186
    fn delegation_execution_too_soon<P>() {
187
2
        ExtBuilder::default().build().execute_with(|| {
188
2
            let final_amount = 2 * SHARE_INIT;
189
2
            let block_number = block_number();
190

            
191
2
            RequestDelegation {
192
2
                candidate: ACCOUNT_CANDIDATE_1,
193
2
                delegator: ACCOUNT_DELEGATOR_1,
194
2
                pool: P::target_pool(),
195
2
                amount: final_amount,
196
2
                expected_joining: final_amount,
197
2
            }
198
2
            .test();
199
2
            run_to_block(block_number + BLOCKS_TO_WAIT - 1); // too soon
200

            
201
2
            assert_noop!(
202
2
                Staking::execute_pending_operations(
203
2
                    RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
204
2
                    vec![PendingOperationQuery {
205
2
                        delegator: ACCOUNT_DELEGATOR_1,
206
2
                        operation: P::joining_operation_key(ACCOUNT_CANDIDATE_1, block_number)
207
2
                    }]
208
                ),
209
2
                Error::<Runtime>::RequestCannotBeExecuted(0)
210
            );
211
2
        })
212
    }
213
);
214

            
215
pool_test!(
216
    fn undelegation_execution_too_soon<P>() {
217
2
        ExtBuilder::default().build().execute_with(|| {
218
2
            let final_amount = 2 * SHARE_INIT;
219
2
            let leaving_amount = round_down(final_amount, 3); // test leaving rounding
220

            
221
2
            FullDelegation {
222
2
                candidate: ACCOUNT_CANDIDATE_1,
223
2
                delegator: ACCOUNT_DELEGATOR_1,
224
2
                request_amount: final_amount,
225
2
                expected_increase: final_amount,
226
2
                ..default()
227
2
            }
228
2
            .test::<P>();
229

            
230
2
            let block_number = block_number();
231

            
232
2
            RequestUndelegation {
233
2
                candidate: ACCOUNT_CANDIDATE_1,
234
2
                delegator: ACCOUNT_DELEGATOR_1,
235
2
                request_amount: SharesOrStake::Stake(final_amount),
236
2
                expected_removed: final_amount,
237
2
                expected_leaving: leaving_amount,
238
2
                ..default()
239
2
            }
240
2
            .test::<P>();
241

            
242
2
            run_to_block(block_number + BLOCKS_TO_WAIT - 1); // too soon
243
2
            assert_noop!(
244
2
                Staking::execute_pending_operations(
245
2
                    RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
246
2
                    vec![PendingOperationQuery {
247
2
                        delegator: ACCOUNT_DELEGATOR_1,
248
2
                        operation: PendingOperationKey::Leaving {
249
2
                            candidate: ACCOUNT_CANDIDATE_1,
250
2
                            at: block_number,
251
2
                        }
252
2
                    }]
253
                ),
254
2
                Error::<Runtime>::RequestCannotBeExecuted(0)
255
            );
256
2
        })
257
    }
258
);
259

            
260
pool_test!(
261
    fn undelegation_execution<P>() {
262
2
        ExtBuilder::default().build().execute_with(|| {
263
2
            let final_amount = 2 * SHARE_INIT;
264
2
            let requested_amount = final_amount + 10; // test share rounding
265
2
            let leaving_amount = round_down(final_amount, 3); // test leaving rounding
266

            
267
2
            assert_eq!(leaving_amount, 1_999_998);
268

            
269
2
            FullDelegation {
270
2
                candidate: ACCOUNT_CANDIDATE_1,
271
2
                delegator: ACCOUNT_DELEGATOR_1,
272
2
                request_amount: requested_amount,
273
2
                expected_increase: final_amount,
274
2
                ..default()
275
2
            }
276
2
            .test::<P>();
277

            
278
2
            assert_eq!(
279
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(ACCOUNT_DELEGATOR_1)
280
2
                    .count(),
281
                1
282
            );
283
2
            assert_eq!(
284
2
                DelegatorCandidateSummaries::<Runtime>::get(
285
                    ACCOUNT_DELEGATOR_1,
286
                    ACCOUNT_CANDIDATE_1
287
                ),
288
2
                DelegatorCandidateSummary::new().with_pool(P::pool_kind(), true)
289
            );
290
2
            assert_eq!(
291
2
                CandidateSummaries::<Runtime>::get(ACCOUNT_CANDIDATE_1),
292
2
                CandidateSummary {
293
2
                    delegators: 1,
294
2
                    ..default()
295
2
                }
296
2
                .with_pool(P::pool_kind(), 1)
297
            );
298

            
299
2
            FullUndelegation {
300
2
                candidate: ACCOUNT_CANDIDATE_1,
301
2
                delegator: ACCOUNT_DELEGATOR_1,
302
2
                request_amount: SharesOrStake::Stake(final_amount),
303
2
                expected_removed: final_amount,
304
2
                expected_leaving: leaving_amount,
305
2
                ..default()
306
2
            }
307
2
            .test::<P>();
308

            
309
2
            assert_eq!(
310
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(ACCOUNT_DELEGATOR_1)
311
2
                    .count(),
312
                0
313
            );
314
2
            assert_eq!(
315
2
                CandidateSummaries::<Runtime>::get(ACCOUNT_CANDIDATE_1),
316
2
                CandidateSummary::default(),
317
            );
318

            
319
2
            assert_eq_events!(vec![
320
                // delegate request
321
2
                Event::RequestedDelegate {
322
2
                    candidate: ACCOUNT_CANDIDATE_1,
323
2
                    delegator: ACCOUNT_DELEGATOR_1,
324
2
                    pool: P::target_pool(),
325
2
                    pending: requested_amount
326
2
                },
327
                // delegate exec
328
2
                P::event_staked(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1, 2, final_amount),
329
2
                Event::ExecutedDelegate {
330
2
                    candidate: ACCOUNT_CANDIDATE_1,
331
2
                    delegator: ACCOUNT_DELEGATOR_1,
332
2
                    pool: P::target_pool(),
333
2
                    staked: final_amount,
334
2
                    released: 10,
335
2
                },
336
                // undelegate request
337
2
                Event::RequestedUndelegate {
338
2
                    candidate: ACCOUNT_CANDIDATE_1,
339
2
                    delegator: ACCOUNT_DELEGATOR_1,
340
2
                    from: P::target_pool(),
341
2
                    pending: leaving_amount,
342
2
                    released: 2
343
2
                },
344
                // undelegate exec
345
2
                Event::ExecutedUndelegate {
346
2
                    candidate: ACCOUNT_CANDIDATE_1,
347
2
                    delegator: ACCOUNT_DELEGATOR_1,
348
2
                    released: leaving_amount,
349
2
                },
350
            ]);
351
2
        })
352
    }
353
);
354

            
355
pool_test!(
356
    fn undelegation_execution_amount_in_shares<P>() {
357
2
        ExtBuilder::default().build().execute_with(|| {
358
2
            let joining_amount = 2 * SHARE_INIT;
359
2
            let joining_requested_amount = joining_amount + 10; // test share rounding
360

            
361
2
            let leaving_requested_amount = SHARE_INIT;
362
2
            let leaving_amount = round_down(leaving_requested_amount, 3); // test leaving rounding
363

            
364
2
            assert_eq!(leaving_amount, 999_999);
365

            
366
2
            FullDelegation {
367
2
                candidate: ACCOUNT_CANDIDATE_1,
368
2
                delegator: ACCOUNT_DELEGATOR_1,
369
2
                request_amount: joining_requested_amount,
370
2
                expected_increase: joining_amount,
371
2
                ..default()
372
2
            }
373
2
            .test::<P>();
374

            
375
2
            FullUndelegation {
376
2
                candidate: ACCOUNT_CANDIDATE_1,
377
2
                delegator: ACCOUNT_DELEGATOR_1,
378
2
                request_amount: SharesOrStake::Shares(1),
379
2
                expected_removed: leaving_requested_amount,
380
2
                expected_leaving: leaving_amount,
381
2
                ..default()
382
2
            }
383
2
            .test::<P>();
384

            
385
2
            assert_eq_events!(vec![
386
                // delegate request
387
2
                Event::RequestedDelegate {
388
2
                    candidate: ACCOUNT_CANDIDATE_1,
389
2
                    delegator: ACCOUNT_DELEGATOR_1,
390
2
                    pool: P::target_pool(),
391
2
                    pending: joining_requested_amount
392
2
                },
393
                // delegate exec
394
2
                P::event_staked(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1, 2, joining_amount),
395
2
                Event::ExecutedDelegate {
396
2
                    candidate: ACCOUNT_CANDIDATE_1,
397
2
                    delegator: ACCOUNT_DELEGATOR_1,
398
2
                    pool: P::target_pool(),
399
2
                    staked: joining_amount,
400
2
                    released: 10,
401
2
                },
402
                // undelegate request
403
2
                Event::RequestedUndelegate {
404
2
                    candidate: ACCOUNT_CANDIDATE_1,
405
2
                    delegator: ACCOUNT_DELEGATOR_1,
406
2
                    from: P::target_pool(),
407
2
                    pending: leaving_amount,
408
2
                    released: 1
409
2
                },
410
                // undelegate exec
411
2
                Event::ExecutedUndelegate {
412
2
                    candidate: ACCOUNT_CANDIDATE_1,
413
2
                    delegator: ACCOUNT_DELEGATOR_1,
414
2
                    released: leaving_amount,
415
2
                },
416
            ]);
417
2
        })
418
    }
419
);
420

            
421
pool_test!(
422
    fn partial_swap_works<P>() {
423
2
        ExtBuilder::default().build().execute_with(|| {
424
2
            FullDelegation {
425
2
                candidate: ACCOUNT_CANDIDATE_1,
426
2
                delegator: ACCOUNT_DELEGATOR_1,
427
2
                request_amount: 10 * SHARE_INIT,
428
2
                expected_increase: 10 * SHARE_INIT,
429
2
                ..default()
430
2
            }
431
2
            .test::<P>();
432

            
433
2
            assert_eq!(
434
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(ACCOUNT_DELEGATOR_1)
435
2
                    .count(),
436
                1
437
            );
438
2
            assert_eq!(
439
2
                DelegatorCandidateSummaries::<Runtime>::get(
440
                    ACCOUNT_DELEGATOR_1,
441
                    ACCOUNT_CANDIDATE_1
442
                ),
443
2
                DelegatorCandidateSummary::new().with_pool(P::pool_kind(), true)
444
            );
445
2
            assert_eq!(
446
2
                CandidateSummaries::<Runtime>::get(ACCOUNT_CANDIDATE_1),
447
2
                CandidateSummary {
448
2
                    delegators: 1,
449
2
                    ..default()
450
2
                }
451
2
                .with_pool(P::pool_kind(), 1)
452
            );
453

            
454
            // We swap only part of the stake.
455
2
            Swap {
456
2
                candidate: ACCOUNT_CANDIDATE_1,
457
2
                delegator: ACCOUNT_DELEGATOR_1,
458
2
                requested_amount: SharesOrStake::Stake(5 * SHARE_INIT + 10),
459
2
                expected_removed: 5 * SHARE_INIT,
460
2
                expected_restaked: 5 * SHARE_INIT,
461
2
                ..default()
462
2
            }
463
2
            .test::<P>();
464

            
465
2
            assert_eq!(
466
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(ACCOUNT_DELEGATOR_1)
467
2
                    .count(),
468
                1
469
            );
470
2
            assert_eq!(
471
2
                DelegatorCandidateSummaries::<Runtime>::get(
472
                    ACCOUNT_DELEGATOR_1,
473
                    ACCOUNT_CANDIDATE_1
474
                ),
475
2
                DelegatorCandidateSummary::new()
476
2
                    .with_pool(P::pool_kind(), true)
477
2
                    .with_pool(P::OppositePool::pool_kind(), true)
478
            );
479
2
            assert_eq!(
480
2
                CandidateSummaries::<Runtime>::get(ACCOUNT_CANDIDATE_1),
481
2
                CandidateSummary {
482
2
                    delegators: 1,
483
2
                    ..default()
484
2
                }
485
2
                .with_pool(P::pool_kind(), 1)
486
2
                .with_pool(P::OppositePool::pool_kind(), 1)
487
            );
488

            
489
2
            assert_eq_last_events!(vec![Event::<Runtime>::SwappedPool {
490
2
                candidate: ACCOUNT_CANDIDATE_1,
491
2
                delegator: ACCOUNT_DELEGATOR_1,
492
2
                source_pool: P::target_pool(),
493
2
                source_shares: 5,
494
2
                source_stake: 5 * SHARE_INIT,
495
2
                target_shares: 5,
496
2
                target_stake: 5 * SHARE_INIT,
497
2
                pending_leaving: 0,
498
2
                released: 0,
499
2
            }]);
500
2
        })
501
    }
502
);
503

            
504
pool_test!(
505
    fn full_swap_works<P>() {
506
2
        ExtBuilder::default().build().execute_with(|| {
507
2
            FullDelegation {
508
2
                candidate: ACCOUNT_CANDIDATE_1,
509
2
                delegator: ACCOUNT_DELEGATOR_1,
510
2
                request_amount: 10 * SHARE_INIT,
511
2
                expected_increase: 10 * SHARE_INIT,
512
2
                ..default()
513
2
            }
514
2
            .test::<P>();
515

            
516
2
            assert_eq!(
517
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(ACCOUNT_DELEGATOR_1)
518
2
                    .count(),
519
                1
520
            );
521
2
            assert_eq!(
522
2
                DelegatorCandidateSummaries::<Runtime>::get(
523
                    ACCOUNT_DELEGATOR_1,
524
                    ACCOUNT_CANDIDATE_1
525
                ),
526
2
                DelegatorCandidateSummary::new().with_pool(P::pool_kind(), true)
527
            );
528
2
            assert_eq!(
529
2
                CandidateSummaries::<Runtime>::get(ACCOUNT_CANDIDATE_1),
530
2
                CandidateSummary {
531
2
                    delegators: 1,
532
2
                    ..default()
533
2
                }
534
2
                .with_pool(P::pool_kind(), 1)
535
            );
536

            
537
            // All stake is swapped, so original pool should be empty
538
2
            Swap {
539
2
                candidate: ACCOUNT_CANDIDATE_1,
540
2
                delegator: ACCOUNT_DELEGATOR_1,
541
2
                requested_amount: SharesOrStake::Stake(10 * SHARE_INIT),
542
2
                expected_removed: 10 * SHARE_INIT,
543
2
                expected_restaked: 10 * SHARE_INIT,
544
2
                ..default()
545
2
            }
546
2
            .test::<P>();
547

            
548
2
            assert_eq!(
549
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(ACCOUNT_DELEGATOR_1)
550
2
                    .count(),
551
                1
552
            );
553
2
            assert_eq!(
554
2
                DelegatorCandidateSummaries::<Runtime>::get(
555
                    ACCOUNT_DELEGATOR_1,
556
                    ACCOUNT_CANDIDATE_1
557
                ),
558
2
                DelegatorCandidateSummary::new().with_pool(P::OppositePool::pool_kind(), true)
559
            );
560
2
            assert_eq!(
561
2
                CandidateSummaries::<Runtime>::get(ACCOUNT_CANDIDATE_1),
562
2
                CandidateSummary {
563
2
                    delegators: 1,
564
2
                    ..default()
565
2
                }
566
2
                .with_pool(P::OppositePool::pool_kind(), 1)
567
            );
568

            
569
2
            assert_eq_last_events!(vec![Event::<Runtime>::SwappedPool {
570
2
                candidate: ACCOUNT_CANDIDATE_1,
571
2
                delegator: ACCOUNT_DELEGATOR_1,
572
2
                source_pool: P::target_pool(),
573
2
                source_shares: 10,
574
2
                source_stake: 10 * SHARE_INIT,
575
2
                target_shares: 10,
576
2
                target_stake: 10 * SHARE_INIT,
577
2
                pending_leaving: 0,
578
2
                released: 0,
579
2
            }]);
580
2
        })
581
    }
582
);
583

            
584
pool_test!(
585
    fn swap_too_much<P>() {
586
2
        ExtBuilder::default().build().execute_with(|| {
587
2
            FullDelegation {
588
2
                candidate: ACCOUNT_CANDIDATE_1,
589
2
                delegator: ACCOUNT_DELEGATOR_1,
590
2
                request_amount: 10 * SHARE_INIT,
591
2
                expected_increase: 10 * SHARE_INIT,
592
2
                ..default()
593
2
            }
594
2
            .test::<P>();
595

            
596
2
            assert_noop!(
597
2
                Staking::swap_pool(
598
2
                    RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
599
                    ACCOUNT_CANDIDATE_1,
600
2
                    P::target_pool(),
601
2
                    SharesOrStake::Shares(11),
602
                ),
603
2
                Error::<Runtime>::MathUnderflow
604
            );
605
2
        })
606
    }
607
);
608

            
609
pool_test!(
610
    fn swap_with_rounding<P>() {
611
2
        ExtBuilder::default().build().execute_with(|| {
612
2
            FullDelegation {
613
2
                candidate: ACCOUNT_CANDIDATE_1,
614
2
                delegator: ACCOUNT_DELEGATOR_1,
615
2
                request_amount: 10 * SHARE_INIT,
616
2
                expected_increase: 10 * SHARE_INIT,
617
2
                ..default()
618
2
            }
619
2
            .test::<P>();
620

            
621
2
            FullDelegation {
622
2
                candidate: ACCOUNT_CANDIDATE_1,
623
2
                delegator: ACCOUNT_DELEGATOR_1,
624
2
                request_amount: 1 * SHARE_INIT,
625
2
                expected_increase: 1 * SHARE_INIT,
626
2
                ..default()
627
2
            }
628
2
            .test::<P::OppositePool>();
629

            
630
2
            assert_eq!(
631
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(ACCOUNT_DELEGATOR_1)
632
2
                    .count(),
633
                1
634
            );
635
2
            assert_eq!(
636
2
                DelegatorCandidateSummaries::<Runtime>::get(
637
                    ACCOUNT_DELEGATOR_1,
638
                    ACCOUNT_CANDIDATE_1
639
                ),
640
2
                DelegatorCandidateSummary::new()
641
2
                    .with_pool(P::pool_kind(), true)
642
2
                    .with_pool(P::OppositePool::pool_kind(), true)
643
            );
644
2
            assert_eq!(
645
2
                CandidateSummaries::<Runtime>::get(ACCOUNT_CANDIDATE_1),
646
2
                CandidateSummary {
647
2
                    delegators: 1,
648
2
                    ..default()
649
2
                }
650
2
                .with_pool(P::pool_kind(), 1)
651
2
                .with_pool(P::OppositePool::pool_kind(), 1)
652
            );
653

            
654
            // We then artificialy distribute rewards to the target by increasing the value of the pool
655
            // and minting currency to the staking account (this is not how manual rewards would
656
            // be distributed but whatever).
657
2
            let rewards = 5 * KILO;
658
2
            assert_ok!(Balances::mint_into(&ACCOUNT_STAKING, rewards));
659
2
            assert_ok!(P::OppositePool::share_stake_among_holders(
660
2
                &ACCOUNT_CANDIDATE_1,
661
2
                Stake(rewards)
662
            ));
663
2
            assert_ok!(Candidates::<Runtime>::add_total_stake(
664
2
                &ACCOUNT_CANDIDATE_1,
665
2
                &Stake(rewards)
666
            ));
667

            
668
            // We swap only part of the stake.
669
2
            Swap {
670
2
                candidate: ACCOUNT_CANDIDATE_1,
671
2
                delegator: ACCOUNT_DELEGATOR_1,
672
2
                requested_amount: SharesOrStake::Stake(5 * SHARE_INIT + 10),
673
2
                expected_removed: 5 * SHARE_INIT,
674
2
                // due to 1 target share now being worth a bit more than SHARE_INIT,
675
2
                // only 4 target shares can be restaked
676
2
                expected_restaked: 4_020_000,
677
2
                // remaining amount is put in the leaving pool, rounded down
678
2
                // to the closest multiple of 3 (test leaving share init value)
679
2
                expected_leaving: 979_998,
680
2
                // thus the 2 stake that could not be put in the leaving pool
681
2
                // are directly released
682
2
                expected_released: 2,
683
2
                ..default()
684
2
            }
685
2
            .test::<P>();
686

            
687
2
            assert_eq!(
688
2
                DelegatorCandidateSummaries::<Runtime>::get(
689
                    ACCOUNT_DELEGATOR_1,
690
                    ACCOUNT_CANDIDATE_1
691
                ),
692
2
                DelegatorCandidateSummary::new()
693
2
                    .with_pool(P::pool_kind(), true)
694
2
                    .with_pool(P::OppositePool::pool_kind(), true)
695
2
                    .with_leaving(true) // leaving dust
696
            );
697
2
            assert_eq!(
698
2
                CandidateSummaries::<Runtime>::get(ACCOUNT_CANDIDATE_1),
699
2
                CandidateSummary {
700
2
                    delegators: 1,
701
2
                    leaving_delegators: 1,
702
2
                    auto_compounding_delegators: 1,
703
2
                    manual_rewards_delegators: 1,
704
2
                    ..default()
705
2
                }
706
            );
707

            
708
2
            assert_eq_last_events!(vec![Event::<Runtime>::SwappedPool {
709
2
                candidate: ACCOUNT_CANDIDATE_1,
710
2
                delegator: ACCOUNT_DELEGATOR_1,
711
2
                source_pool: P::target_pool(),
712
2
                source_shares: 5,
713
2
                source_stake: 5 * SHARE_INIT,
714
2
                target_shares: 4,
715
2
                target_stake: 4_020_000,
716
2
                pending_leaving: 979_998,
717
2
                released: 2,
718
2
            }]);
719
2
        })
720
    }
721
);
722

            
723
#[test]
724
1
fn paused_extrinsics() {
725
1
    ExtBuilder::default().build().execute_with(|| {
726
1
        PausePoolsExtrinsics::<Runtime>::put(true);
727

            
728
1
        assert_noop!(
729
1
            Staking::rebalance_hold(
730
1
                RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
731
                ACCOUNT_DELEGATOR_1,
732
                ACCOUNT_CANDIDATE_1,
733
1
                PoolKind::AutoCompounding
734
            ),
735
1
            Error::<Runtime>::NoOneIsStaking
736
        ); // no pause check
737

            
738
1
        assert_noop!(
739
1
            Staking::request_delegate(
740
1
                RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
741
                ACCOUNT_CANDIDATE_1,
742
1
                ActivePoolKind::AutoCompounding,
743
                42
744
            ),
745
1
            Error::<Runtime>::PoolsExtrinsicsArePaused
746
        );
747

            
748
1
        assert_noop!(
749
1
            Staking::execute_pending_operations(RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1), vec![]),
750
1
            Error::<Runtime>::PoolsExtrinsicsArePaused
751
        );
752

            
753
1
        assert_noop!(
754
1
            Staking::request_undelegate(
755
1
                RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
756
                ACCOUNT_CANDIDATE_1,
757
1
                ActivePoolKind::AutoCompounding,
758
1
                SharesOrStake::Shares(42),
759
            ),
760
1
            Error::<Runtime>::PoolsExtrinsicsArePaused
761
        );
762

            
763
1
        assert_ok!(Staking::claim_manual_rewards(
764
1
            RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
765
1
            vec![]
766
        ));
767

            
768
1
        assert_ok!(Staking::update_candidate_position(
769
1
            RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
770
1
            vec![]
771
        ));
772

            
773
1
        assert_noop!(
774
1
            Staking::swap_pool(
775
1
                RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
776
                ACCOUNT_CANDIDATE_1,
777
1
                ActivePoolKind::AutoCompounding,
778
1
                SharesOrStake::Shares(42),
779
            ),
780
1
            Error::<Runtime>::PoolsExtrinsicsArePaused
781
        );
782
1
    })
783
1
}