git.delta.rocks / unique-network / refs/commits / 9efe15d9b711

difftreelog

Add vesting e2e test and helpers

Max Andreev2022-11-24parent: #e8d45f9.patch.diff
in: master

2 files changed

modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
2145 return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;2145 return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;
2146 }2146 }
2147
2148 /**
2149 * Get latest relay block
2150 * @returns {number} relay block
2151 */
2152 async getRelayBlockNumber(): Promise<bigint> {
2153 const blockNumber = (await this.helper.callRpc('api.query.parachainSystem.validationData')).toJSON().relayParentNumber;
2154 return BigInt(blockNumber);
2155 }
21472156
2148 /**2157 /**
2149 * Get account nonce2158 * Get account nonce
2327 return isSuccess;2336 return isSuccess;
2328 }2337 }
2338
2339 /**
2340 * Transfer tokens with the unlock period
2341 * @param signer signers Keyring
2342 * @param address Substrate address of recipient
2343 * @param schedule Schedule params
2344 * @example vestedTransfer(signer, recepient.address, 20000, 100, 10, 50 * nominal); // total amount of vested tokens will be 100 * 50 = 5000
2345 */
2346 async vestedTransfer(signer: TSigner, address: TSubstrateAccount, schedule: {startRelayBlock: bigint, periodBlocks: bigint, periodCount: bigint, perPeriod: bigint}): Promise<void> {
2347 const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.vestedTransfer', [address, {start: schedule.startRelayBlock, period: schedule.periodBlocks, periodCount: schedule.periodCount, perPeriod: schedule.perPeriod}]);
2348 const event = result.result.events
2349 .find(e => e.event.section === 'vesting' &&
2350 e.event.method === 'VestingScheduleAdded' &&
2351 e.event.data[0].toHuman() === signer.address);
2352 if (!event) throw Error('Cannot find transfer in events');
2353 }
2354
2355 /**
2356 * Get schedule for recepient of vested transfer
2357 * @param address Substrate address of recipient
2358 * @returns
2359 */
2360 async getVestingSchedules(address: TSubstrateAccount): Promise<{startRelayBlock: bigint, periodBlocks: bigint, periodCount: bigint, perPeriod: bigint}[]> {
2361 const schedule = (await this.helper.callRpc('api.query.vesting.vestingSchedules', [address])).toJSON();
2362 return schedule.map((schedule: any) => {
2363 return {
2364 startRelayBlock: BigInt(schedule.start),
2365 periodBlocks: BigInt(schedule.period),
2366 periodCount: BigInt(schedule.periodCount),
2367 perPeriod: BigInt(schedule.perPeriod),
2368 };
2369 });
2370 }
2371
2372 /**
2373 * Claim vested tokens
2374 * @param signer signers Keyring
2375 */
2376 async claim(signer: TSigner) {
2377 const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.claim', []);
2378 const event = result.result.events
2379 .find(e => e.event.section === 'vesting' &&
2380 e.event.method === 'Claimed' &&
2381 e.event.data[0].toHuman() === signer.address);
2382 if (!event) throw Error('Cannot find claim in events');
2383 }
2329}2384}
23302385
2331class AddressGroup extends HelperGroup<ChainHelperBase> {2386class AddressGroup extends HelperGroup<ChainHelperBase> {
addedtests/src/vesting.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/vesting.test.ts
@@ -0,0 +1,104 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {IKeyringPair} from '@polkadot/types/types';
+import {itSub, usingPlaygrounds, expect} from './util';
+
+describe('Vesting', () => {
+  let donor: IKeyringPair;
+  let nominal: bigint;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({filename: __filename});
+      nominal = helper.balance.getOneTokenNominal();
+    });
+  });
+
+  itSub.only('can perform vestedTransfer and claim tokens', async ({helper}) => {
+    // arrange
+    const [sender, recepient] = await helper.arrange.createAccounts([1000n, 1n], donor);
+    const currentRelayBlock = await helper.chain.getRelayBlockNumber();
+    const schedule1 = {startRelayBlock: currentRelayBlock + 4n, periodBlocks: 4n, periodCount: 2n, perPeriod: 50n * nominal};
+    const schedule2 = {startRelayBlock: currentRelayBlock + 8n, periodBlocks: 8n, periodCount: 2n, perPeriod: 100n * nominal};
+
+    // act
+    await helper.balance.vestedTransfer(sender, recepient.address, schedule1);
+    await helper.balance.vestedTransfer(sender, recepient.address, schedule2);
+    let schedule = await helper.balance.getVestingSchedules(recepient.address);
+
+    // check senders balance after vesting:
+    let balanceSender = await helper.balance.getSubstrateFull(sender.address);
+    expect(balanceSender.free / nominal).to.eq(699n);
+    expect(balanceSender.feeFrozen).to.eq(0n);
+    expect(balanceSender.miscFrozen).to.eq(0n);
+    expect(balanceSender.reserved).to.eq(0n);
+
+    // check recepient balance after vesting:
+    let balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
+    expect(balanceRecepient.free).to.eq(301n * nominal);
+    expect(balanceRecepient.feeFrozen).to.eq(300n * nominal);
+    expect(balanceRecepient.miscFrozen).to.eq(300n * nominal);
+    expect(balanceRecepient.reserved).to.eq(0n);
+
+    // Schedules list correct:
+    expect(schedule).to.has.length(2);
+    expect(schedule[0]).to.deep.eq(schedule1);
+    expect(schedule[1]).to.deep.eq(schedule2);
+
+    await helper.wait.forRelayBlockNumber(currentRelayBlock + 8n);
+    await helper.balance.claim(recepient);
+
+    // check recepient balance after claim (50 tokens claimed):
+    balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
+    expect(balanceRecepient.free / nominal).to.eq(300n);
+    expect(balanceRecepient.feeFrozen).to.eq(250n * nominal);
+    expect(balanceRecepient.miscFrozen).to.eq(250n * nominal);
+    expect(balanceRecepient.reserved).to.eq(0n);
+    
+    await helper.wait.forRelayBlockNumber(currentRelayBlock + 16n);
+    await helper.balance.claim(recepient);
+
+    // check recepient balance after second claim (150 tokens claimed):
+    balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
+    expect(balanceRecepient.free / nominal).to.eq(300n);
+    expect(balanceRecepient.feeFrozen).to.eq(100n * nominal);
+    expect(balanceRecepient.miscFrozen).to.eq(100n * nominal);
+    expect(balanceRecepient.reserved).to.eq(0n);
+    
+    // Schedules list contain 1 vesting:
+    schedule = await helper.balance.getVestingSchedules(recepient.address);
+    expect(schedule).to.has.length(1);
+    expect(schedule[0]).to.deep.eq(schedule2);
+
+    await helper.wait.forRelayBlockNumber(currentRelayBlock + 24n);
+    await helper.balance.claim(recepient);
+
+    // check recepient balance after second claim (100 tokens claimed):
+    balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
+    expect(balanceRecepient.free / nominal).to.eq(300n);
+    expect(balanceRecepient.feeFrozen).to.eq(0n);
+    expect(balanceRecepient.miscFrozen).to.eq(0n);
+    expect(balanceRecepient.reserved).to.eq(0n);
+    
+    // check sender balance does not changed:
+    balanceSender = await helper.balance.getSubstrateFull(sender.address);
+    expect(balanceSender.free / nominal).to.eq(699n);
+    expect(balanceSender.feeFrozen).to.eq(0n);
+    expect(balanceSender.miscFrozen).to.eq(0n);
+    expect(balanceSender.reserved).to.eq(0n);
+  });
+});