git.delta.rocks / unique-network / refs/commits / ea43c5ee413d

difftreelog

Check both unstakeAll and unstakePartial

Max Andreev2023-02-13parent: #7976517.patch.diff
in: master

2 files changed

modifiedtests/src/sub/appPromotion/appPromotion.test.tsdiffbeforeafterboth
147 });147 });
148 });148 });
149149
150 describe('unstake extrinsic', () => {150 describe('Unstaking', () => {
151 [
152 {method: 'unstakeAll' as const},
153 {method: 'unstakePartial' as const},
154 ].map(testCase => {
151 itSub('should move tokens to "pendingUnstake" map and subtract it from totalStaked', async ({helper}) => {155 itSub(`[${testCase.method}] should move tokens to "pendingUnstake" and subtract it from totalStaked`, async ({helper}) => {
152 const [staker, recepient] = [accounts.pop()!, accounts.pop()!];156 const [staker, recepient] = [accounts.pop()!, accounts.pop()!];
153 const totalStakedBefore = await helper.staking.getTotalStaked();157 const totalStakedBefore = await helper.staking.getTotalStaked();
158 const STAKE_AMOUNT = 900n * nominal;
159
154 await helper.staking.stake(staker, 900n * nominal);160 await helper.staking.stake(staker, STAKE_AMOUNT);
161 testCase.method === 'unstakeAll'
155 await helper.staking.unstakeAll(staker);162 ? await helper.staking.unstakeAll(staker)
163 : await helper.staking.unstakePartial(staker, STAKE_AMOUNT);
156164
157 // Right after unstake tokens are still locked165 // Right after unstake tokens are still locked
166 expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0);
158 expect(await helper.balance.getLocked(staker.address)).to.deep.eq([{id: 'appstake', amount: 900n * nominal, reasons: 'All'}]);167 expect(await helper.balance.getLocked(staker.address)).to.deep.eq([{id: 'appstake', amount: STAKE_AMOUNT, reasons: 'All'}]);
159 expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, miscFrozen: 900n * nominal, feeFrozen: 900n * nominal});168 expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, miscFrozen: STAKE_AMOUNT, feeFrozen: STAKE_AMOUNT});
160 // Staker can not transfer169 // Staker can not transfer
161 await expect(helper.balance.transferToSubstrate(staker, recepient.address, 100n * nominal)).to.be.rejectedWith('balances.LiquidityRestrictions');170 await expect(helper.balance.transferToSubstrate(staker, recepient.address, 100n * nominal)).to.be.rejectedWith('balances.LiquidityRestrictions');
162 expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(900n * nominal);171 expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(STAKE_AMOUNT);
163 expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n);172 expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n);
164 expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore);173 expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore);
165 });174 });
166175 });
176
177 [
178 {method: 'unstakeAll' as const},
179 {method: 'unstakePartial' as const},
180 ].map(testCase => {
167 itSub('should unlock balance after unlocking period ends and remove it from "pendingUnstake"', async ({helper}) => {181 itSub(`[${testCase.method}] should unlock balance after unlocking period ends and remove it from "pendingUnstake"`, async ({helper}) => {
168 const staker = accounts.pop()!;182 const staker = accounts.pop()!;
169 await helper.staking.stake(staker, 100n * nominal);183 await helper.staking.stake(staker, 100n * nominal);
184 testCase.method === 'unstakeAll'
170 await helper.staking.unstakeAll(staker);185 ? await helper.staking.unstakeAll(staker)
186 : await helper.staking.unstakePartial(staker, 100n * nominal);
171 const [pendingUnstake] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});187 const [pendingUnstake] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
172188
173 // Wait for unstaking period. Balance now free ~1000; reserved, frozen, miscFrozeb: 0n189 // Wait for unstaking period. Balance now free ~1000; reserved, frozen, miscFrozeb: 0n
178 // staker can transfer:194 // staker can transfer:
179 await helper.balance.transferToSubstrate(staker, donor.address, 998n * nominal);195 await helper.balance.transferToSubstrate(staker, donor.address, 998n * nominal);
180 expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(1n);196 expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(1n);
181 });197 });
182198 });
199
200 [
201 {method: 'unstakeAll' as const},
202 {method: 'unstakePartial' as const},
203 ].map(testCase => {
183 itSub('should successfully unstake multiple stakes', async ({helper}) => {204 itSub(`[${testCase.method}] should successfully unstake multiple stakes`, async ({helper}) => {
184 const staker = accounts.pop()!;205 const staker = accounts.pop()!;
185 await helper.staking.stake(staker, 100n * nominal);206 await helper.staking.stake(staker, 100n * nominal);
186 await helper.staking.stake(staker, 200n * nominal);207 await helper.staking.stake(staker, 200n * nominal);
197 expect(stakes[2].amount).to.equal(300n * nominal);218 expect(stakes[2].amount).to.equal(300n * nominal);
198219
199 // Can unstake multiple stakes220 // Can unstake multiple stakes
221 testCase.method === 'unstakeAll'
200 await helper.staking.unstakeAll(staker);222 ? await helper.staking.unstakeAll(staker)
223 : await helper.staking.unstakePartial(staker, 600n * nominal);
224
201 pendingUnstake = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});225 pendingUnstake = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
202 totalPendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address});226 totalPendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address});
210 await helper.wait.forParachainBlockNumber(pendingUnstake[0].block);234 await helper.wait.forParachainBlockNumber(pendingUnstake[0].block);
211 expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, feeFrozen: 0n, miscFrozen: 0n});235 expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, feeFrozen: 0n, miscFrozen: 0n});
212 expect (await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n);236 expect (await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n);
213 });237 });
214238 });
239
240 [
241 {method: 'unstakeAll' as const},
242 {method: 'unstakePartial' as const},
243 ].map(testCase => {
215 itSub('should not have any effects if no active stakes', async ({helper}) => {244 itSub(`[${testCase.method}] should not have any effects if no active stakes`, async ({helper}) => {
216 const staker = accounts.pop()!;245 const staker = accounts.pop()!;
217246
218 // unstake has no effect if no stakes at all247 // unstake has no effect if no stakes at all
248 testCase.method === 'unstakeAll'
219 await helper.staking.unstakeAll(staker);249 ? await helper.staking.unstakeAll(staker)
250 : await helper.staking.unstakePartial(staker, 100n * nominal);
251
220 expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(0n);252 expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(0n);
221 expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); // TODO bigint closeTo helper253 expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); // TODO bigint closeTo helper
225 // can't unstake if there are only pendingUnstakes257 // can't unstake if there are only pendingUnstakes
226 await helper.staking.stake(staker, 100n * nominal);258 await helper.staking.stake(staker, 100n * nominal);
259
260 if (testCase.method === 'unstakeAll') {
227 await helper.staking.unstakeAll(staker);261 await helper.staking.unstakeAll(staker);
228 await helper.staking.unstakeAll(staker);262 await helper.staking.unstakeAll(staker);
229263 } else {
264 await helper.staking.unstakePartial(staker, 100n * nominal);
265 await helper.staking.unstakePartial(staker, 100n * nominal);
266 }
267
268 expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0);
230 expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal);269 expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal);
231 expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n);270 expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n);
232 });271 });
233272 });
273
274 [
275 {method: 'unstakeAll' as const},
276 {method: 'unstakePartial' as const},
277 ].map(testCase => {
234 itSub('should keep different unlocking block for each unlocking stake', async ({helper}) => {278 itSub(`[${testCase.method}] should create different pending-unlock for each unlocking stake`, async ({helper}) => {
235 const staker = accounts.pop()!;279 const staker = accounts.pop()!;
236 await helper.staking.stake(staker, 100n * nominal);280 await helper.staking.stake(staker, 100n * nominal);
281 testCase.method === 'unstakeAll'
237 await helper.staking.unstakeAll(staker);282 ? await helper.staking.unstakeAll(staker)
283 : await helper.staking.unstakePartial(staker, 100n * nominal);
238 await helper.staking.stake(staker, 120n * nominal);284 await helper.staking.stake(staker, 120n * nominal);
285 testCase.method === 'unstakeAll'
239 await helper.staking.unstakeAll(staker);286 ? await helper.staking.unstakeAll(staker)
287 : await helper.staking.unstakePartial(staker, 120n * nominal);
240288
241 const unstakingPerBlock = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});289 const unstakingPerBlock = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
242 expect(unstakingPerBlock).has.length(2);290 expect(unstakingPerBlock).has.length(2);
243 expect(unstakingPerBlock[0].amount).to.equal(100n * nominal);291 expect(unstakingPerBlock[0].amount).to.equal(100n * nominal);
244 expect(unstakingPerBlock[1].amount).to.equal(120n * nominal);292 expect(unstakingPerBlock[1].amount).to.equal(120n * nominal);
293 expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.equal(0);
245 });294 });
246295 });
296
297 [
298 {method: 'unstakeAll' as const},
299 {method: 'unstakePartial' as const},
300 ].map(testCase => {
247 itSub('should be possible for 3 accounts in one block', async ({helper}) => {301 itSub(`[${testCase.method}] should be possible for 3 accounts in one block`, async ({helper}) => {
248 const stakers = [accounts.pop()!, accounts.pop()!, accounts.pop()!];302 const stakers = [accounts.pop()!, accounts.pop()!, accounts.pop()!];
249303
250 await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));304 await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));
251 await Promise.all(stakers.map(staker => helper.staking.unstakeAll(staker)));305 await Promise.all(stakers.map(staker => {
306 return testCase.method === 'unstakeAll'
307 ? helper.staking.unstakeAll(staker)
308 : helper.staking.unstakePartial(staker, 100n * nominal);
309 }));
252310
253 await Promise.all(stakers.map(async (staker) => {311 await Promise.all(stakers.map(async (staker) => {
254 expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal);312 expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal);
255 expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n);313 expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n);
256 }));314 }));
257 });315 });
316 });
258317
259 itSub('should not be possible for more than 3 accounts in one block', async ({helper}) => {318 itSub('should not be possible for more than 3 accounts in one block', async ({helper}) => {
260 if (!await helper.arrange.isDevNode()) {319 if (!await helper.arrange.isDevNode()) {
261 const stakers = await helper.arrange.createAccounts([200n,200n,200n,200n,200n,200n,200n,200n,200n,200n], donor);320 const stakers = await helper.arrange.createAccounts([200n,200n,200n,200n,200n,200n,200n,200n,200n,200n], donor);
262321
263 await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));322 await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));
264 const unstakingResults = await Promise.allSettled(stakers.map(staker => helper.staking.unstakeAll(staker)));323 const unstakingResults = await Promise.allSettled(stakers.map((staker, i) => {
324 return i % 2 === 0
325 ? helper.staking.unstakeAll(staker)
326 : helper.staking.unstakePartial(staker, 100n * nominal);
327 }));
265328
266 const successfulUnstakes = unstakingResults.filter(result => result.status === 'fulfilled');329 const successfulUnstakes = unstakingResults.filter(result => result.status === 'fulfilled');
267 expect(successfulUnstakes).to.have.length(3);330 expect(successfulUnstakes).to.have.length(3);
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
2688 return unstakeResult.blockHash;2688 return unstakeResult.blockHash;
2689 }2689 }
2690
2691 /**
2692 * Get total number of active stakes
2693 * @param address substrate address
2694 * @returns {number}
2695 */
2696 async getStakesNumber(address: ICrossAccountId): Promise<number> {
2697 if (address.Ethereum) throw Error('only substrate address');
2698 return (await this.helper.callRpc('api.query.appPromotion.stakesPerAccount', [address.Substrate])).toNumber();
2699 }
26902700
2691 /**2701 /**
2692 * Get total staked amount for address2702 * Get total staked amount for address