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 super::*;
18

            
19
pool_test!(
20
    fn rebalance_increase<P>() {
21
2
        ExtBuilder::default().build().execute_with(|| {
22
2
            // Preparation:
23
2
            // We naturaly delegate towards a candidate.
24
2
            let initial_amount = 2 * SHARE_INIT;
25
2
            let rewards = 5 * KILO;
26
2
            let final_amount = initial_amount + rewards;
27
2

            
28
2
            FullDelegation {
29
2
                candidate: ACCOUNT_CANDIDATE_1,
30
2
                delegator: ACCOUNT_DELEGATOR_1,
31
2
                request_amount: initial_amount,
32
2
                expected_increase: initial_amount,
33
2
                ..default()
34
2
            }
35
2
            .test::<P>();
36
2

            
37
2
            // We then artificialy distribute rewards by increasing the value of the pool
38
2
            // and minting currency to the staking account (this is not how manual rewards would
39
2
            // be distributed but whatever).
40
2
            assert_ok!(Balances::mint_into(&ACCOUNT_STAKING, rewards));
41
2
            assert_ok!(P::share_stake_among_holders(
42
2
                &ACCOUNT_CANDIDATE_1,
43
2
                Stake(rewards)
44
2
            ));
45
2
            assert_ok!(Candidates::<Runtime>::add_total_stake(
46
2
                &ACCOUNT_CANDIDATE_1,
47
2
                &Stake(rewards)
48
2
            ));
49
2
            assert_eq!(total_balance(&ACCOUNT_STAKING), DEFAULT_BALANCE + rewards);
50

            
51
            // Holds should not change but the computed stake should increase.
52
2
            assert_eq!(total_balance(&ACCOUNT_DELEGATOR_1), 1 * DEFAULT_BALANCE);
53
2
            assert_eq!(balance_hold(&ACCOUNT_DELEGATOR_1), initial_amount);
54
2
            assert_eq!(
55
2
                P::hold(&ACCOUNT_CANDIDATE_1, &ACCOUNT_DELEGATOR_1),
56
2
                Stake(initial_amount)
57
2
            );
58
2
            assert_eq!(
59
2
                P::shares(&ACCOUNT_CANDIDATE_1, &ACCOUNT_DELEGATOR_1),
60
2
                Shares(2)
61
2
            );
62
2
            assert_eq!(
63
2
                P::computed_stake(&ACCOUNT_CANDIDATE_1, &ACCOUNT_DELEGATOR_1)
64
2
                    .unwrap()
65
2
                    .0,
66
2
                final_amount
67
2
            );
68
2
            assert_eq!(
69
2
                Candidates::<Runtime>::total_stake(&ACCOUNT_CANDIDATE_1),
70
2
                Stake(final_amount)
71
2
            );
72

            
73
            // We perform the rebalancing and check it works.
74
2
            do_rebalance_hold::<P>(
75
2
                ACCOUNT_CANDIDATE_1,
76
2
                ACCOUNT_DELEGATOR_1,
77
2
                P::target_pool().into(),
78
2
                SignedBalance::Positive(rewards),
79
2
            );
80
2
        })
81
    }
82
);
83

            
84
pool_test!(
85
    fn rebalance_decrease<P>() {
86
2
        ExtBuilder::default().build().execute_with(|| {
87
2
            // Preparation:
88
2
            // We naturaly delegate towards a candidate.
89
2
            let initial_amount = 2 * SHARE_INIT;
90
2
            let slash = 5 * KILO;
91
2
            let final_amount = initial_amount - slash;
92
2

            
93
2
            FullDelegation {
94
2
                candidate: ACCOUNT_CANDIDATE_1,
95
2
                delegator: ACCOUNT_DELEGATOR_1,
96
2
                request_amount: initial_amount,
97
2
                expected_increase: initial_amount,
98
2
                ..default()
99
2
            }
100
2
            .test::<P>();
101
2

            
102
2
            // We then artificialy slash by decreasing the value of the pool.
103
2
            assert_ok!(P::slash_stake_among_holders(
104
2
                &ACCOUNT_CANDIDATE_1,
105
2
                Stake(slash)
106
2
            ));
107
2
            assert_ok!(Candidates::<Runtime>::sub_total_stake(
108
2
                &ACCOUNT_CANDIDATE_1,
109
2
                Stake(slash)
110
2
            ));
111
2
            assert_eq!(total_balance(&ACCOUNT_STAKING), DEFAULT_BALANCE); // didn't change
112

            
113
            // Holds should not change but the computed stake should decrease.
114
2
            assert_eq!(total_balance(&ACCOUNT_DELEGATOR_1), 1 * DEFAULT_BALANCE);
115
2
            assert_eq!(balance_hold(&ACCOUNT_DELEGATOR_1), initial_amount);
116
2
            assert_eq!(
117
2
                P::hold(&ACCOUNT_CANDIDATE_1, &ACCOUNT_DELEGATOR_1),
118
2
                Stake(initial_amount)
119
2
            );
120
2
            assert_eq!(
121
2
                P::shares(&ACCOUNT_CANDIDATE_1, &ACCOUNT_DELEGATOR_1),
122
2
                Shares(2)
123
2
            );
124
2
            assert_eq!(
125
2
                P::computed_stake(&ACCOUNT_CANDIDATE_1, &ACCOUNT_DELEGATOR_1)
126
2
                    .unwrap()
127
2
                    .0,
128
2
                final_amount
129
2
            );
130
2
            assert_eq!(
131
2
                Candidates::<Runtime>::total_stake(&ACCOUNT_CANDIDATE_1),
132
2
                Stake(final_amount)
133
2
            );
134

            
135
            // We perform the rebalancing and check it works.
136
2
            do_rebalance_hold::<P>(
137
2
                ACCOUNT_CANDIDATE_1,
138
2
                ACCOUNT_DELEGATOR_1,
139
2
                P::target_pool().into(),
140
2
                SignedBalance::Negative(slash),
141
2
            );
142
2
        })
143
    }
144
);
145

            
146
pool_test!(
147
    fn rebalance_noop<P>() {
148
2
        ExtBuilder::default().build().execute_with(|| {
149
2
            // Preparation:
150
2
            // We naturaly delegate towards a candidate.
151
2
            let initial_amount = 2 * SHARE_INIT;
152
2

            
153
2
            FullDelegation {
154
2
                candidate: ACCOUNT_CANDIDATE_1,
155
2
                delegator: ACCOUNT_DELEGATOR_1,
156
2
                request_amount: initial_amount,
157
2
                expected_increase: initial_amount,
158
2
                ..default()
159
2
            }
160
2
            .test::<P>();
161
2

            
162
2
            // We perform the rebalancing and check nothing happen.
163
2
            do_rebalance_hold::<P>(
164
2
                ACCOUNT_CANDIDATE_1,
165
2
                ACCOUNT_DELEGATOR_1,
166
2
                P::target_pool().into(),
167
2
                SignedBalance::Positive(0),
168
2
            );
169
2
        })
170
    }
171
);
172

            
173
pool_test!(
174
    fn rebalance_in_undelegation_request<P>() {
175
2
        ExtBuilder::default().build().execute_with(|| {
176
2
            let joining_amount = 2 * SHARE_INIT;
177
2
            let rewards = 5 * KILO;
178
2
            let leaving_requested_amount = joining_amount + rewards;
179
2
            let leaving_amount = round_down(leaving_requested_amount, 3); // test leaving rounding
180
2

            
181
2
            FullDelegation {
182
2
                candidate: ACCOUNT_CANDIDATE_1,
183
2
                delegator: ACCOUNT_DELEGATOR_1,
184
2
                request_amount: joining_amount,
185
2
                expected_increase: joining_amount,
186
2
                ..default()
187
2
            }
188
2
            .test::<P>();
189
2

            
190
2
            // We then artificialy distribute rewards by increasing the value of the pool
191
2
            // and minting currency to the staking account (this is not how manual rewards would
192
2
            // be distributed but whatever).
193
2
            assert_ok!(Balances::mint_into(&ACCOUNT_STAKING, rewards));
194
2
            assert_ok!(P::share_stake_among_holders(
195
2
                &ACCOUNT_CANDIDATE_1,
196
2
                Stake(rewards)
197
2
            ));
198
2
            assert_ok!(Candidates::<Runtime>::add_total_stake(
199
2
                &ACCOUNT_CANDIDATE_1,
200
2
                &Stake(rewards)
201
2
            ));
202
2
            assert_eq!(total_balance(&ACCOUNT_STAKING), DEFAULT_BALANCE + rewards);
203

            
204
            // We then do the undelegation
205
2
            RequestUndelegation {
206
2
                candidate: ACCOUNT_CANDIDATE_1,
207
2
                delegator: ACCOUNT_DELEGATOR_1,
208
2
                request_amount: SharesOrStake::Stake(leaving_requested_amount),
209
2
                expected_removed: leaving_requested_amount,
210
2
                expected_leaving: leaving_amount,
211
2
                expected_hold_rebalance: rewards,
212
2
                ..default()
213
2
            }
214
2
            .test::<P>();
215
2
        })
216
    }
217
);
218

            
219
pool_test!(
220
    fn rebalance_in_swap<P>() {
221
2
        ExtBuilder::default().build().execute_with(|| {
222
2
            FullDelegation {
223
2
                candidate: ACCOUNT_CANDIDATE_1,
224
2
                delegator: ACCOUNT_DELEGATOR_1,
225
2
                request_amount: 10 * SHARE_INIT,
226
2
                expected_increase: 10 * SHARE_INIT,
227
2
                ..default()
228
2
            }
229
2
            .test::<P>();
230
2

            
231
2
            // We then artificialy distribute rewards to the source pool by increasing the value of the pool
232
2
            // and minting currency to the staking account (this is not how manual rewards would
233
2
            // be distributed but whatever).
234
2
            let rewards = 2 * SHARE_INIT;
235
2
            assert_ok!(Balances::mint_into(&ACCOUNT_STAKING, rewards));
236
2
            assert_ok!(P::share_stake_among_holders(
237
2
                &ACCOUNT_CANDIDATE_1,
238
2
                Stake(rewards)
239
2
            ));
240
2
            assert_ok!(Candidates::<Runtime>::add_total_stake(
241
2
                &ACCOUNT_CANDIDATE_1,
242
2
                &Stake(rewards)
243
2
            ));
244

            
245
2
            Swap {
246
2
                candidate: ACCOUNT_CANDIDATE_1,
247
2
                delegator: ACCOUNT_DELEGATOR_1,
248
2
                requested_amount: SharesOrStake::Shares(9),
249
2
                expected_removed: 10_800_000,
250
2
                expected_restaked: 10_000_000,
251
2
                expected_leaving: 799998,
252
2
                expected_released: 2,
253
2
                expected_hold_rebalance: rewards,
254
2
            }
255
2
            .test::<P>();
256
2
        })
257
    }
258
);