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
2

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

            
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
2

            
67
2
            assert_eq!(
68
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(&ACCOUNT_DELEGATOR_1)
69
2
                    .count(),
70
2
                1
71
2
            );
72
2
            assert_eq!(
73
2
                DelegatorCandidateSummaries::<Runtime>::get(
74
2
                    &ACCOUNT_DELEGATOR_1,
75
2
                    &ACCOUNT_CANDIDATE_1
76
2
                ),
77
2
                DelegatorCandidateSummary::new().with_joining(true)
78
2
            );
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
2
            );
87

            
88
2
            assert_eq_events!(vec![
89
2
                Event::IncreasedStake {
90
2
                    candidate: ACCOUNT_CANDIDATE_1,
91
2
                    stake_diff: amount,
92
2
                },
93
2
                Event::UpdatedCandidatePosition {
94
2
                    candidate: ACCOUNT_CANDIDATE_1,
95
2
                    stake: amount,
96
2
                    self_delegation: 0,
97
2
                    before: None,
98
2
                    after: None,
99
2
                },
100
2
                Event::RequestedDelegate {
101
2
                    candidate: ACCOUNT_CANDIDATE_1,
102
2
                    delegator: ACCOUNT_DELEGATOR_1,
103
2
                    pool: P::target_pool(),
104
2
                    pending: amount
105
2
                },
106
2
            ]);
107
2
        })
108
    }
109
);
110

            
111
pool_test!(
112
    fn delegation_request_more_than_available<P>() {
113
2
        ExtBuilder::default().build().execute_with(|| {
114
2
            let amount = DEFAULT_BALANCE; // not enough to keep ED
115
2

            
116
2
            let before = State::extract(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1);
117
2
            let pool_before =
118
2
                PoolState::extract::<Joining>(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1);
119
2

            
120
2
            assert_noop!(
121
2
                Staking::request_delegate(
122
2
                    RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
123
2
                    ACCOUNT_CANDIDATE_1,
124
2
                    P::target_pool(),
125
2
                    amount,
126
2
                ),
127
2
                TokenError::FundsUnavailable
128
2
            );
129

            
130
2
            let after = State::extract(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1);
131
2
            let pool_after =
132
2
                PoolState::extract::<Joining>(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1);
133
2

            
134
2
            assert_eq!(before, after);
135
2
            assert_eq!(pool_before, pool_after);
136

            
137
2
            assert_eq_events!(Vec::<Event<Runtime>>::new());
138
2
        })
139
    }
140
);
141

            
142
pool_test!(
143
    fn delegation_execution<P>() {
144
2
        ExtBuilder::default().build().execute_with(|| {
145
2
            let final_amount = 2 * SHARE_INIT;
146
2
            let requested_amount = final_amount + 10; // test share rounding
147
2

            
148
2
            FullDelegation {
149
2
                candidate: ACCOUNT_CANDIDATE_1,
150
2
                delegator: ACCOUNT_DELEGATOR_1,
151
2
                request_amount: requested_amount,
152
2
                expected_increase: final_amount,
153
2
                ..default()
154
2
            }
155
2
            .test::<P>();
156
2

            
157
2
            assert_eq!(
158
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(&ACCOUNT_DELEGATOR_1)
159
2
                    .count(),
160
2
                1
161
2
            );
162
2
            assert_eq!(
163
2
                DelegatorCandidateSummaries::<Runtime>::get(
164
2
                    &ACCOUNT_DELEGATOR_1,
165
2
                    &ACCOUNT_CANDIDATE_1
166
2
                ),
167
2
                DelegatorCandidateSummary::new().with_pool(P::pool_kind(), true)
168
2
            );
169
2
            assert_eq!(
170
2
                CandidateSummaries::<Runtime>::get(&ACCOUNT_CANDIDATE_1),
171
2
                CandidateSummary {
172
2
                    delegators: 1,
173
2
                    ..default()
174
2
                }
175
2
                .with_pool(P::pool_kind(), 1)
176
2
            );
177

            
178
2
            assert_eq_events!(vec![
179
2
                Event::IncreasedStake {
180
2
                    candidate: ACCOUNT_CANDIDATE_1,
181
2
                    stake_diff: requested_amount,
182
2
                },
183
2
                Event::UpdatedCandidatePosition {
184
2
                    candidate: ACCOUNT_CANDIDATE_1,
185
2
                    stake: requested_amount,
186
2
                    self_delegation: 0,
187
2
                    before: None,
188
2
                    after: None,
189
2
                },
190
2
                Event::RequestedDelegate {
191
2
                    candidate: ACCOUNT_CANDIDATE_1,
192
2
                    delegator: ACCOUNT_DELEGATOR_1,
193
2
                    pool: P::target_pool(),
194
2
                    pending: requested_amount,
195
2
                },
196
2
                Event::DecreasedStake {
197
2
                    candidate: ACCOUNT_CANDIDATE_1,
198
2
                    stake_diff: 10,
199
2
                },
200
2
                Event::UpdatedCandidatePosition {
201
2
                    candidate: ACCOUNT_CANDIDATE_1,
202
2
                    stake: final_amount,
203
2
                    self_delegation: 0,
204
2
                    before: None,
205
2
                    after: None,
206
2
                },
207
2
                P::event_staked(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1, 2, final_amount),
208
2
                Event::ExecutedDelegate {
209
2
                    candidate: ACCOUNT_CANDIDATE_1,
210
2
                    delegator: ACCOUNT_DELEGATOR_1,
211
2
                    pool: P::target_pool(),
212
2
                    staked: final_amount,
213
2
                    released: 10,
214
2
                },
215
2
            ]);
216
2
        })
217
    }
218
);
219

            
220
pool_test!(
221
    fn delegation_execution_too_soon<P>() {
222
2
        ExtBuilder::default().build().execute_with(|| {
223
2
            let final_amount = 2 * SHARE_INIT;
224
2
            let block_number = block_number();
225
2

            
226
2
            RequestDelegation {
227
2
                candidate: ACCOUNT_CANDIDATE_1,
228
2
                delegator: ACCOUNT_DELEGATOR_1,
229
2
                pool: P::target_pool(),
230
2
                amount: final_amount,
231
2
                expected_joining: final_amount,
232
2
            }
233
2
            .test();
234
2
            roll_to(block_number + BLOCKS_TO_WAIT - 1); // too soon
235
2

            
236
2
            assert_noop!(
237
2
                Staking::execute_pending_operations(
238
2
                    RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
239
2
                    vec![PendingOperationQuery {
240
2
                        delegator: ACCOUNT_DELEGATOR_1,
241
2
                        operation: P::joining_operation_key(ACCOUNT_CANDIDATE_1, block_number)
242
2
                    }]
243
2
                ),
244
2
                Error::<Runtime>::RequestCannotBeExecuted(0)
245
2
            );
246
2
        })
247
    }
248
);
249

            
250
pool_test!(
251
    fn undelegation_execution_too_soon<P>() {
252
2
        ExtBuilder::default().build().execute_with(|| {
253
2
            let final_amount = 2 * SHARE_INIT;
254
2
            let leaving_amount = round_down(final_amount, 3); // test leaving rounding
255
2

            
256
2
            FullDelegation {
257
2
                candidate: ACCOUNT_CANDIDATE_1,
258
2
                delegator: ACCOUNT_DELEGATOR_1,
259
2
                request_amount: final_amount,
260
2
                expected_increase: final_amount,
261
2
                ..default()
262
2
            }
263
2
            .test::<P>();
264
2

            
265
2
            let block_number = block_number();
266
2

            
267
2
            RequestUndelegation {
268
2
                candidate: ACCOUNT_CANDIDATE_1,
269
2
                delegator: ACCOUNT_DELEGATOR_1,
270
2
                request_amount: SharesOrStake::Stake(final_amount),
271
2
                expected_removed: final_amount,
272
2
                expected_leaving: leaving_amount,
273
2
                ..default()
274
2
            }
275
2
            .test::<P>();
276
2

            
277
2
            roll_to(block_number + BLOCKS_TO_WAIT - 1); // too soon
278
2
            assert_noop!(
279
2
                Staking::execute_pending_operations(
280
2
                    RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
281
2
                    vec![PendingOperationQuery {
282
2
                        delegator: ACCOUNT_DELEGATOR_1,
283
2
                        operation: PendingOperationKey::Leaving {
284
2
                            candidate: ACCOUNT_CANDIDATE_1,
285
2
                            at: block_number,
286
2
                        }
287
2
                    }]
288
2
                ),
289
2
                Error::<Runtime>::RequestCannotBeExecuted(0)
290
2
            );
291
2
        })
292
    }
293
);
294

            
295
pool_test!(
296
    fn undelegation_execution<P>() {
297
2
        ExtBuilder::default().build().execute_with(|| {
298
2
            let final_amount = 2 * SHARE_INIT;
299
2
            let requested_amount = final_amount + 10; // test share rounding
300
2
            let leaving_amount = round_down(final_amount, 3); // test leaving rounding
301
2

            
302
2
            assert_eq!(leaving_amount, 1_999_998);
303

            
304
2
            FullDelegation {
305
2
                candidate: ACCOUNT_CANDIDATE_1,
306
2
                delegator: ACCOUNT_DELEGATOR_1,
307
2
                request_amount: requested_amount,
308
2
                expected_increase: final_amount,
309
2
                ..default()
310
2
            }
311
2
            .test::<P>();
312
2

            
313
2
            assert_eq!(
314
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(&ACCOUNT_DELEGATOR_1)
315
2
                    .count(),
316
2
                1
317
2
            );
318
2
            assert_eq!(
319
2
                DelegatorCandidateSummaries::<Runtime>::get(
320
2
                    &ACCOUNT_DELEGATOR_1,
321
2
                    &ACCOUNT_CANDIDATE_1
322
2
                ),
323
2
                DelegatorCandidateSummary::new().with_pool(P::pool_kind(), true)
324
2
            );
325
2
            assert_eq!(
326
2
                CandidateSummaries::<Runtime>::get(&ACCOUNT_CANDIDATE_1),
327
2
                CandidateSummary {
328
2
                    delegators: 1,
329
2
                    ..default()
330
2
                }
331
2
                .with_pool(P::pool_kind(), 1)
332
2
            );
333

            
334
2
            FullUndelegation {
335
2
                candidate: ACCOUNT_CANDIDATE_1,
336
2
                delegator: ACCOUNT_DELEGATOR_1,
337
2
                request_amount: SharesOrStake::Stake(final_amount),
338
2
                expected_removed: final_amount,
339
2
                expected_leaving: leaving_amount,
340
2
                ..default()
341
2
            }
342
2
            .test::<P>();
343
2

            
344
2
            assert_eq!(
345
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(&ACCOUNT_DELEGATOR_1)
346
2
                    .count(),
347
2
                0
348
2
            );
349
2
            assert_eq!(
350
2
                CandidateSummaries::<Runtime>::get(&ACCOUNT_CANDIDATE_1),
351
2
                CandidateSummary::default(),
352
2
            );
353

            
354
2
            assert_eq_events!(vec![
355
2
                // delegate request
356
2
                Event::IncreasedStake {
357
2
                    candidate: ACCOUNT_CANDIDATE_1,
358
2
                    stake_diff: requested_amount,
359
2
                },
360
2
                Event::UpdatedCandidatePosition {
361
2
                    candidate: ACCOUNT_CANDIDATE_1,
362
2
                    stake: requested_amount,
363
2
                    self_delegation: 0,
364
2
                    before: None,
365
2
                    after: None,
366
2
                },
367
2
                Event::RequestedDelegate {
368
2
                    candidate: ACCOUNT_CANDIDATE_1,
369
2
                    delegator: ACCOUNT_DELEGATOR_1,
370
2
                    pool: P::target_pool(),
371
2
                    pending: requested_amount
372
2
                },
373
2
                // delegate exec
374
2
                Event::DecreasedStake {
375
2
                    candidate: ACCOUNT_CANDIDATE_1,
376
2
                    stake_diff: 10,
377
2
                },
378
2
                Event::UpdatedCandidatePosition {
379
2
                    candidate: ACCOUNT_CANDIDATE_1,
380
2
                    stake: final_amount,
381
2
                    self_delegation: 0,
382
2
                    before: None,
383
2
                    after: None,
384
2
                },
385
2
                P::event_staked(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1, 2, final_amount),
386
2
                Event::ExecutedDelegate {
387
2
                    candidate: ACCOUNT_CANDIDATE_1,
388
2
                    delegator: ACCOUNT_DELEGATOR_1,
389
2
                    pool: P::target_pool(),
390
2
                    staked: final_amount,
391
2
                    released: 10,
392
2
                },
393
2
                // undelegate request
394
2
                Event::DecreasedStake {
395
2
                    candidate: ACCOUNT_CANDIDATE_1,
396
2
                    stake_diff: final_amount,
397
2
                },
398
2
                Event::UpdatedCandidatePosition {
399
2
                    candidate: ACCOUNT_CANDIDATE_1,
400
2
                    stake: 0,
401
2
                    self_delegation: 0,
402
2
                    before: None,
403
2
                    after: None,
404
2
                },
405
2
                Event::RequestedUndelegate {
406
2
                    candidate: ACCOUNT_CANDIDATE_1,
407
2
                    delegator: ACCOUNT_DELEGATOR_1,
408
2
                    from: P::target_pool(),
409
2
                    pending: leaving_amount,
410
2
                    released: 2
411
2
                },
412
2
                // undelegate exec
413
2
                Event::ExecutedUndelegate {
414
2
                    candidate: ACCOUNT_CANDIDATE_1,
415
2
                    delegator: ACCOUNT_DELEGATOR_1,
416
2
                    released: leaving_amount,
417
2
                },
418
2
            ]);
419
2
        })
420
    }
421
);
422

            
423
pool_test!(
424
    fn undelegation_execution_amount_in_shares<P>() {
425
2
        ExtBuilder::default().build().execute_with(|| {
426
2
            let joining_amount = 2 * SHARE_INIT;
427
2
            let joining_requested_amount = joining_amount + 10; // test share rounding
428
2

            
429
2
            let leaving_requested_amount = SHARE_INIT;
430
2
            let leaving_amount = round_down(leaving_requested_amount, 3); // test leaving rounding
431
2

            
432
2
            assert_eq!(leaving_amount, 999_999);
433

            
434
2
            FullDelegation {
435
2
                candidate: ACCOUNT_CANDIDATE_1,
436
2
                delegator: ACCOUNT_DELEGATOR_1,
437
2
                request_amount: joining_requested_amount,
438
2
                expected_increase: joining_amount,
439
2
                ..default()
440
2
            }
441
2
            .test::<P>();
442
2

            
443
2
            FullUndelegation {
444
2
                candidate: ACCOUNT_CANDIDATE_1,
445
2
                delegator: ACCOUNT_DELEGATOR_1,
446
2
                request_amount: SharesOrStake::Shares(1),
447
2
                expected_removed: leaving_requested_amount,
448
2
                expected_leaving: leaving_amount,
449
2
                ..default()
450
2
            }
451
2
            .test::<P>();
452
2

            
453
2
            assert_eq_events!(vec![
454
2
                // delegate request
455
2
                Event::IncreasedStake {
456
2
                    candidate: ACCOUNT_CANDIDATE_1,
457
2
                    stake_diff: joining_requested_amount,
458
2
                },
459
2
                Event::UpdatedCandidatePosition {
460
2
                    candidate: ACCOUNT_CANDIDATE_1,
461
2
                    stake: joining_requested_amount,
462
2
                    self_delegation: 0,
463
2
                    before: None,
464
2
                    after: None,
465
2
                },
466
2
                Event::RequestedDelegate {
467
2
                    candidate: ACCOUNT_CANDIDATE_1,
468
2
                    delegator: ACCOUNT_DELEGATOR_1,
469
2
                    pool: P::target_pool(),
470
2
                    pending: joining_requested_amount
471
2
                },
472
2
                // delegate exec
473
2
                Event::DecreasedStake {
474
2
                    candidate: ACCOUNT_CANDIDATE_1,
475
2
                    stake_diff: 10,
476
2
                },
477
2
                Event::UpdatedCandidatePosition {
478
2
                    candidate: ACCOUNT_CANDIDATE_1,
479
2
                    stake: joining_amount,
480
2
                    self_delegation: 0,
481
2
                    before: None,
482
2
                    after: None,
483
2
                },
484
2
                P::event_staked(ACCOUNT_CANDIDATE_1, ACCOUNT_DELEGATOR_1, 2, joining_amount),
485
2
                Event::ExecutedDelegate {
486
2
                    candidate: ACCOUNT_CANDIDATE_1,
487
2
                    delegator: ACCOUNT_DELEGATOR_1,
488
2
                    pool: P::target_pool(),
489
2
                    staked: joining_amount,
490
2
                    released: 10,
491
2
                },
492
2
                // undelegate request
493
2
                Event::DecreasedStake {
494
2
                    candidate: ACCOUNT_CANDIDATE_1,
495
2
                    stake_diff: leaving_requested_amount,
496
2
                },
497
2
                Event::UpdatedCandidatePosition {
498
2
                    candidate: ACCOUNT_CANDIDATE_1,
499
2
                    stake: joining_amount - leaving_requested_amount,
500
2
                    self_delegation: 0,
501
2
                    before: None,
502
2
                    after: None,
503
2
                },
504
2
                Event::RequestedUndelegate {
505
2
                    candidate: ACCOUNT_CANDIDATE_1,
506
2
                    delegator: ACCOUNT_DELEGATOR_1,
507
2
                    from: P::target_pool(),
508
2
                    pending: leaving_amount,
509
2
                    released: 1
510
2
                },
511
2
                // undelegate exec
512
2
                Event::ExecutedUndelegate {
513
2
                    candidate: ACCOUNT_CANDIDATE_1,
514
2
                    delegator: ACCOUNT_DELEGATOR_1,
515
2
                    released: leaving_amount,
516
2
                },
517
2
            ]);
518
2
        })
519
    }
520
);
521

            
522
pool_test!(
523
    fn partial_swap_works<P>() {
524
2
        ExtBuilder::default().build().execute_with(|| {
525
2
            FullDelegation {
526
2
                candidate: ACCOUNT_CANDIDATE_1,
527
2
                delegator: ACCOUNT_DELEGATOR_1,
528
2
                request_amount: 10 * SHARE_INIT,
529
2
                expected_increase: 10 * SHARE_INIT,
530
2
                ..default()
531
2
            }
532
2
            .test::<P>();
533
2

            
534
2
            assert_eq!(
535
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(&ACCOUNT_DELEGATOR_1)
536
2
                    .count(),
537
2
                1
538
2
            );
539
2
            assert_eq!(
540
2
                DelegatorCandidateSummaries::<Runtime>::get(
541
2
                    &ACCOUNT_DELEGATOR_1,
542
2
                    &ACCOUNT_CANDIDATE_1
543
2
                ),
544
2
                DelegatorCandidateSummary::new().with_pool(P::pool_kind(), true)
545
2
            );
546
2
            assert_eq!(
547
2
                CandidateSummaries::<Runtime>::get(&ACCOUNT_CANDIDATE_1),
548
2
                CandidateSummary {
549
2
                    delegators: 1,
550
2
                    ..default()
551
2
                }
552
2
                .with_pool(P::pool_kind(), 1)
553
2
            );
554

            
555
            // We swap only part of the stake.
556
2
            Swap {
557
2
                candidate: ACCOUNT_CANDIDATE_1,
558
2
                delegator: ACCOUNT_DELEGATOR_1,
559
2
                requested_amount: SharesOrStake::Stake(5 * SHARE_INIT + 10),
560
2
                expected_removed: 5 * SHARE_INIT,
561
2
                expected_restaked: 5 * SHARE_INIT,
562
2
                ..default()
563
2
            }
564
2
            .test::<P>();
565
2

            
566
2
            assert_eq!(
567
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(&ACCOUNT_DELEGATOR_1)
568
2
                    .count(),
569
2
                1
570
2
            );
571
2
            assert_eq!(
572
2
                DelegatorCandidateSummaries::<Runtime>::get(
573
2
                    &ACCOUNT_DELEGATOR_1,
574
2
                    &ACCOUNT_CANDIDATE_1
575
2
                ),
576
2
                DelegatorCandidateSummary::new()
577
2
                    .with_pool(P::pool_kind(), true)
578
2
                    .with_pool(P::OppositePool::pool_kind(), true)
579
2
            );
580
2
            assert_eq!(
581
2
                CandidateSummaries::<Runtime>::get(&ACCOUNT_CANDIDATE_1),
582
2
                CandidateSummary {
583
2
                    delegators: 1,
584
2
                    ..default()
585
2
                }
586
2
                .with_pool(P::pool_kind(), 1)
587
2
                .with_pool(P::OppositePool::pool_kind(), 1)
588
2
            );
589

            
590
2
            assert_eq_last_events!(vec![Event::<Runtime>::SwappedPool {
591
2
                candidate: ACCOUNT_CANDIDATE_1,
592
2
                delegator: ACCOUNT_DELEGATOR_1,
593
2
                source_pool: P::target_pool(),
594
2
                source_shares: 5,
595
2
                source_stake: 5 * SHARE_INIT,
596
2
                target_shares: 5,
597
2
                target_stake: 5 * SHARE_INIT,
598
2
                pending_leaving: 0,
599
2
                released: 0,
600
2
            }]);
601
2
        })
602
    }
603
);
604

            
605
pool_test!(
606
    fn full_swap_works<P>() {
607
2
        ExtBuilder::default().build().execute_with(|| {
608
2
            FullDelegation {
609
2
                candidate: ACCOUNT_CANDIDATE_1,
610
2
                delegator: ACCOUNT_DELEGATOR_1,
611
2
                request_amount: 10 * SHARE_INIT,
612
2
                expected_increase: 10 * SHARE_INIT,
613
2
                ..default()
614
2
            }
615
2
            .test::<P>();
616
2

            
617
2
            assert_eq!(
618
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(&ACCOUNT_DELEGATOR_1)
619
2
                    .count(),
620
2
                1
621
2
            );
622
2
            assert_eq!(
623
2
                DelegatorCandidateSummaries::<Runtime>::get(
624
2
                    &ACCOUNT_DELEGATOR_1,
625
2
                    &ACCOUNT_CANDIDATE_1
626
2
                ),
627
2
                DelegatorCandidateSummary::new().with_pool(P::pool_kind(), true)
628
2
            );
629
2
            assert_eq!(
630
2
                CandidateSummaries::<Runtime>::get(&ACCOUNT_CANDIDATE_1),
631
2
                CandidateSummary {
632
2
                    delegators: 1,
633
2
                    ..default()
634
2
                }
635
2
                .with_pool(P::pool_kind(), 1)
636
2
            );
637

            
638
            // All stake is swapped, so original pool should be empty
639
2
            Swap {
640
2
                candidate: ACCOUNT_CANDIDATE_1,
641
2
                delegator: ACCOUNT_DELEGATOR_1,
642
2
                requested_amount: SharesOrStake::Stake(10 * SHARE_INIT),
643
2
                expected_removed: 10 * SHARE_INIT,
644
2
                expected_restaked: 10 * SHARE_INIT,
645
2
                ..default()
646
2
            }
647
2
            .test::<P>();
648
2

            
649
2
            assert_eq!(
650
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(&ACCOUNT_DELEGATOR_1)
651
2
                    .count(),
652
2
                1
653
2
            );
654
2
            assert_eq!(
655
2
                DelegatorCandidateSummaries::<Runtime>::get(
656
2
                    &ACCOUNT_DELEGATOR_1,
657
2
                    &ACCOUNT_CANDIDATE_1
658
2
                ),
659
2
                DelegatorCandidateSummary::new().with_pool(P::OppositePool::pool_kind(), true)
660
2
            );
661
2
            assert_eq!(
662
2
                CandidateSummaries::<Runtime>::get(&ACCOUNT_CANDIDATE_1),
663
2
                CandidateSummary {
664
2
                    delegators: 1,
665
2
                    ..default()
666
2
                }
667
2
                .with_pool(P::OppositePool::pool_kind(), 1)
668
2
            );
669

            
670
2
            assert_eq_last_events!(vec![Event::<Runtime>::SwappedPool {
671
2
                candidate: ACCOUNT_CANDIDATE_1,
672
2
                delegator: ACCOUNT_DELEGATOR_1,
673
2
                source_pool: P::target_pool(),
674
2
                source_shares: 10,
675
2
                source_stake: 10 * SHARE_INIT,
676
2
                target_shares: 10,
677
2
                target_stake: 10 * SHARE_INIT,
678
2
                pending_leaving: 0,
679
2
                released: 0,
680
2
            }]);
681
2
        })
682
    }
683
);
684

            
685
pool_test!(
686
    fn swap_too_much<P>() {
687
2
        ExtBuilder::default().build().execute_with(|| {
688
2
            FullDelegation {
689
2
                candidate: ACCOUNT_CANDIDATE_1,
690
2
                delegator: ACCOUNT_DELEGATOR_1,
691
2
                request_amount: 10 * SHARE_INIT,
692
2
                expected_increase: 10 * SHARE_INIT,
693
2
                ..default()
694
2
            }
695
2
            .test::<P>();
696
2

            
697
2
            assert_noop!(
698
2
                Staking::swap_pool(
699
2
                    RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
700
2
                    ACCOUNT_CANDIDATE_1,
701
2
                    P::target_pool(),
702
2
                    SharesOrStake::Shares(11),
703
2
                ),
704
2
                Error::<Runtime>::MathUnderflow
705
2
            );
706
2
        })
707
    }
708
);
709

            
710
pool_test!(
711
    fn swap_with_rounding<P>() {
712
2
        ExtBuilder::default().build().execute_with(|| {
713
2
            FullDelegation {
714
2
                candidate: ACCOUNT_CANDIDATE_1,
715
2
                delegator: ACCOUNT_DELEGATOR_1,
716
2
                request_amount: 10 * SHARE_INIT,
717
2
                expected_increase: 10 * SHARE_INIT,
718
2
                ..default()
719
2
            }
720
2
            .test::<P>();
721
2

            
722
2
            FullDelegation {
723
2
                candidate: ACCOUNT_CANDIDATE_1,
724
2
                delegator: ACCOUNT_DELEGATOR_1,
725
2
                request_amount: 1 * SHARE_INIT,
726
2
                expected_increase: 1 * SHARE_INIT,
727
2
                ..default()
728
2
            }
729
2
            .test::<P::OppositePool>();
730
2

            
731
2
            assert_eq!(
732
2
                DelegatorCandidateSummaries::<Runtime>::iter_key_prefix(&ACCOUNT_DELEGATOR_1)
733
2
                    .count(),
734
2
                1
735
2
            );
736
2
            assert_eq!(
737
2
                DelegatorCandidateSummaries::<Runtime>::get(
738
2
                    &ACCOUNT_DELEGATOR_1,
739
2
                    &ACCOUNT_CANDIDATE_1
740
2
                ),
741
2
                DelegatorCandidateSummary::new()
742
2
                    .with_pool(P::pool_kind(), true)
743
2
                    .with_pool(P::OppositePool::pool_kind(), true)
744
2
            );
745
2
            assert_eq!(
746
2
                CandidateSummaries::<Runtime>::get(&ACCOUNT_CANDIDATE_1),
747
2
                CandidateSummary {
748
2
                    delegators: 1,
749
2
                    ..default()
750
2
                }
751
2
                .with_pool(P::pool_kind(), 1)
752
2
                .with_pool(P::OppositePool::pool_kind(), 1)
753
2
            );
754

            
755
            // We then artificialy distribute rewards to the target by increasing the value of the pool
756
            // and minting currency to the staking account (this is not how manual rewards would
757
            // be distributed but whatever).
758
2
            let rewards = 5 * KILO;
759
2
            assert_ok!(Balances::mint_into(&ACCOUNT_STAKING, rewards));
760
2
            assert_ok!(P::OppositePool::share_stake_among_holders(
761
2
                &ACCOUNT_CANDIDATE_1,
762
2
                Stake(rewards)
763
2
            ));
764
2
            assert_ok!(Candidates::<Runtime>::add_total_stake(
765
2
                &ACCOUNT_CANDIDATE_1,
766
2
                &Stake(rewards)
767
2
            ));
768

            
769
            // We swap only part of the stake.
770
2
            Swap {
771
2
                candidate: ACCOUNT_CANDIDATE_1,
772
2
                delegator: ACCOUNT_DELEGATOR_1,
773
2
                requested_amount: SharesOrStake::Stake(5 * SHARE_INIT + 10),
774
2
                expected_removed: 5 * SHARE_INIT,
775
2
                // due to 1 target share now being worth a bit more than SHARE_INIT,
776
2
                // only 4 target shares can be restaked
777
2
                expected_restaked: 4_020_000,
778
2
                // remaining amount is put in the leaving pool, rounded down
779
2
                // to the closest multiple of 3 (test leaving share init value)
780
2
                expected_leaving: 979_998,
781
2
                // thus the 2 stake that could not be put in the leaving pool
782
2
                // are directly released
783
2
                expected_released: 2,
784
2
                ..default()
785
2
            }
786
2
            .test::<P>();
787
2

            
788
2
            assert_eq!(
789
2
                DelegatorCandidateSummaries::<Runtime>::get(
790
2
                    &ACCOUNT_DELEGATOR_1,
791
2
                    &ACCOUNT_CANDIDATE_1
792
2
                ),
793
2
                DelegatorCandidateSummary::new()
794
2
                    .with_pool(P::pool_kind(), true)
795
2
                    .with_pool(P::OppositePool::pool_kind(), true)
796
2
                    .with_leaving(true) // leaving dust
797
2
            );
798
2
            assert_eq!(
799
2
                CandidateSummaries::<Runtime>::get(&ACCOUNT_CANDIDATE_1),
800
2
                CandidateSummary {
801
2
                    delegators: 1,
802
2
                    leaving_delegators: 1,
803
2
                    auto_compounding_delegators: 1,
804
2
                    manual_rewards_delegators: 1,
805
2
                    ..default()
806
2
                }
807
2
            );
808

            
809
2
            assert_eq_last_events!(vec![Event::<Runtime>::SwappedPool {
810
2
                candidate: ACCOUNT_CANDIDATE_1,
811
2
                delegator: ACCOUNT_DELEGATOR_1,
812
2
                source_pool: P::target_pool(),
813
2
                source_shares: 5,
814
2
                source_stake: 5 * SHARE_INIT,
815
2
                target_shares: 4,
816
2
                target_stake: 4_020_000,
817
2
                pending_leaving: 979_998,
818
2
                released: 2,
819
2
            }]);
820
2
        })
821
    }
822
);
823

            
824
#[test]
825
1
fn paused_extrinsics() {
826
1
    ExtBuilder::default().build().execute_with(|| {
827
1
        PausePoolsExtrinsics::<Runtime>::put(true);
828
1

            
829
1
        assert_noop!(
830
1
            Staking::rebalance_hold(
831
1
                RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
832
1
                ACCOUNT_DELEGATOR_1,
833
1
                ACCOUNT_CANDIDATE_1,
834
1
                PoolKind::AutoCompounding
835
1
            ),
836
1
            Error::<Runtime>::NoOneIsStaking
837
1
        ); // no pause check
838

            
839
1
        assert_noop!(
840
1
            Staking::request_delegate(
841
1
                RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
842
1
                ACCOUNT_CANDIDATE_1,
843
1
                ActivePoolKind::AutoCompounding,
844
1
                42
845
1
            ),
846
1
            Error::<Runtime>::PoolsExtrinsicsArePaused
847
1
        );
848

            
849
1
        assert_noop!(
850
1
            Staking::execute_pending_operations(RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1), vec![]),
851
1
            Error::<Runtime>::PoolsExtrinsicsArePaused
852
1
        );
853

            
854
1
        assert_noop!(
855
1
            Staking::request_undelegate(
856
1
                RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
857
1
                ACCOUNT_CANDIDATE_1,
858
1
                ActivePoolKind::AutoCompounding,
859
1
                SharesOrStake::Shares(42),
860
1
            ),
861
1
            Error::<Runtime>::PoolsExtrinsicsArePaused
862
1
        );
863

            
864
1
        assert_ok!(Staking::claim_manual_rewards(
865
1
            RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
866
1
            vec![]
867
1
        ));
868

            
869
1
        assert_ok!(Staking::update_candidate_position(
870
1
            RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
871
1
            vec![]
872
1
        ));
873

            
874
1
        assert_noop!(
875
1
            Staking::swap_pool(
876
1
                RuntimeOrigin::signed(ACCOUNT_DELEGATOR_1),
877
1
                ACCOUNT_CANDIDATE_1,
878
1
                ActivePoolKind::AutoCompounding,
879
1
                SharesOrStake::Shares(42),
880
1
            ),
881
1
            Error::<Runtime>::PoolsExtrinsicsArePaused
882
1
        );
883
1
    })
884
1
}