difftreelog
Add vesting e2e test and helpers
in: master
2 files changed
tests/src/util/playgrounds/unique.tsdiffbeforeafterboth2145 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 }21472148 /**2149 * Get latest relay block2150 * @returns {number} relay block2151 */2152 async getRelayBlockNumber(): Promise<bigint> {2153 const blockNumber = (await this.helper.callRpc('api.query.parachainSystem.validationData')).toJSON().relayParentNumber;2154 return BigInt(blockNumber);2155 }214721562148 /**2157 /**2149 * Get account nonce2158 * Get account nonce2327 return isSuccess;2336 return isSuccess;2328 }2337 }23382339 /**2340 * Transfer tokens with the unlock period2341 * @param signer signers Keyring2342 * @param address Substrate address of recipient2343 * @param schedule Schedule params2344 * @example vestedTransfer(signer, recepient.address, 20000, 100, 10, 50 * nominal); // total amount of vested tokens will be 100 * 50 = 50002345 */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.events2349 .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 }23542355 /**2356 * Get schedule for recepient of vested transfer2357 * @param address Substrate address of recipient2358 * @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 }23712372 /**2373 * Claim vested tokens2374 * @param signer signers Keyring2375 */2376 async claim(signer: TSigner) {2377 const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.claim', []);2378 const event = result.result.events2379 .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}233023852331class AddressGroup extends HelperGroup<ChainHelperBase> {2386class AddressGroup extends HelperGroup<ChainHelperBase> {tests/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);
+ });
+});