git.delta.rocks / unique-network / refs/commits / 8d226d67b0fa

difftreelog

source

tests/src/app-promotion.test.ts12.9 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {default as usingApi, submitTransactionAsync} from './substrate/substrate-api';18import {IKeyringPair, ITuple} from '@polkadot/types/types';19import {20  21  createMultipleItemsExpectSuccess,22  isTokenExists,23  getLastTokenId,24  getAllowance,25  approve,26  transferFrom,27  createCollection,28  transfer,29  burnItem,30  normalizeAccountId,31  CrossAccountId,32  createFungibleItemExpectSuccess,33  U128_MAX,34  burnFromExpectSuccess,35  UNIQUE,36  getModuleNames,37  Pallets,38  getBlockNumber,39} from './util/helpers';4041import chai, {use} from 'chai';42import chaiAsPromised from 'chai-as-promised';43import getBalance, {getBalanceSingle} from './substrate/get-balance';44import {unique} from './interfaces/definitions';45import {usingPlaygrounds} from './util/playgrounds';46import {default as waitNewBlocks} from './substrate/wait-new-blocks';4748import BN from 'bn.js';49import {mnemonicGenerate} from '@polkadot/util-crypto';50import {UniqueHelper} from './util/playgrounds/unique';51chai.use(chaiAsPromised);52const expect = chai.expect;5354let alice: IKeyringPair;55let bob: IKeyringPair;56let palletAdmin: IKeyringPair;57let nominal: bigint; 5859describe('integration test: AppPromotion', () => {60  before(async function() {61    await usingPlaygrounds(async (helper, privateKeyWrapper) => {62      if (!getModuleNames(helper.api!).includes(Pallets.AppPromotion)) this.skip();63      alice = privateKeyWrapper('//Alice');64      bob = privateKeyWrapper('//Bob');65      palletAdmin = privateKeyWrapper('//palletAdmin');66      const tx = helper.api!.tx.sudo.sudo(helper.api!.tx.promotion.setAdminAddress(palletAdmin.addressRaw));67      nominal = helper.balance.getOneTokenNominal();68      await submitTransactionAsync(alice, tx);69    });70  });71  it('will change balance state to "locked", add it to "staked" map, and increase "totalStaked" amount', async () => {72    // arrange: Alice balance = 100073    // act:     Alice calls appPromotion.stake(100)74    // assert:  Alice locked balance equal 10075    // assert:  Alice free balance closeTo 90076    // assert:  query appPromotion.staked(Alice) equal [100]77    // assert:  query appPromotion.totalStaked() increased by 10078    // act:     Alice extrinsic appPromotion.stake(200)79  80    // assert:  Alice locked balance equal 30081    // assert:  query appPromotion.staked(Alice) equal [100, 200]82    // assert:  query appPromotion.totalStaked() increased by 20083    84    await usingPlaygrounds(async (helper, privateKeyWrapper) => {85      const totalStakedBefore = (await helper.api!.rpc.unique.totalStaked()).toBigInt();86      const staker = await createUser();87   88      const firstStakedBlock = await helper.chain.getLatestBlockNumber();89      90      await expect(submitTransactionAsync(staker, helper.api!.tx.promotion.stake(1n * nominal))).to.be.eventually.fulfilled;91      expect((await helper.api!.rpc.unique.totalStakingLocked(normalizeAccountId(staker))).toBigInt()).to.be.equal(nominal);92      expect(9n * nominal - await helper.balance.getSubstrate(staker.address) <= nominal / 2n).to.be.true;93      expect((await helper.api!.rpc.unique.totalStaked(normalizeAccountId(staker))).toBigInt()).to.be.equal(nominal);94      expect((await helper.api!.rpc.unique.totalStaked()).toBigInt()).to.be.equal(totalStakedBefore + nominal);95      96      await waitNewBlocks(helper.api!, 1);97      const secondStakedBlock = await helper.chain.getLatestBlockNumber();98      99      await expect(submitTransactionAsync(staker, helper.api!.tx.promotion.stake(2n * nominal))).to.be.eventually.fulfilled;100      expect((await helper.api!.rpc.unique.totalStakingLocked(normalizeAccountId(staker))).toBigInt()).to.be.equal(3n * nominal);101      102      const stakedPerBlock = (await helper.api!.rpc.unique.totalStakedPerBlock(normalizeAccountId(staker))).map(([block, amount]) => [block.toBigInt(), amount.toBigInt()]);103      expect(stakedPerBlock.map((x) => x[1])).to.be.deep.equal([nominal, 2n * nominal]);104    });105  });106  107  it('will throws if stake amount is more than total free balance', async () => {108    // arrange: Alice balance = 1000109    // assert:  Alice calls appPromotion.stake(1000) throws /// because Alice needs some fee110111    // act:     Alice calls appPromotion.stake(700)112    // assert:  Alice calls appPromotion.stake(400) throws /// because Alice has ~300 free QTZ and 700 locked113114    await usingPlaygrounds(async helper => { 115      116      const staker = await createUser();117      118      await expect(submitTransactionAsync(staker, helper.api!.tx.promotion.stake(10n * nominal))).to.be.eventually.rejected;119      await expect(submitTransactionAsync(staker, helper.api!.tx.promotion.stake(7n * nominal))).to.be.eventually.fulfilled;120      await expect(submitTransactionAsync(staker, helper.api!.tx.promotion.stake(4n * nominal))).to.be.eventually.rejected;121      122    });123  });124  125  it('for different accounts in one block is possible', async () => {126    // arrange: Alice, Bob, Charlie, Dave balance = 1000127    // arrange: Alice, Bob, Charlie, Dave calls appPromotion.stake(100) in the same time128129    // assert:  query appPromotion.staked(Alice/Bob/Charlie/Dave) equal [100]130    await usingPlaygrounds(async helper => {131      // const userOne = await createUser();132      // const userTwo = await createUser();133      // const userThree = await createUser();134      // const userFour = await createUser();135      const crowd = [];136      for(let i = 4; i--;) crowd.push(await createUser());137      // const crowd = await creteAccounts([10n, 10n, 10n, 10n], alice, helper);138      // const crowd = [userOne, userTwo, userThree, userFour];139      140      141      const promises = crowd.map(async user => submitTransactionAsync(user, helper.api!.tx.promotion.stake(nominal)));142      await expect(Promise.all(promises)).to.be.eventually.fulfilled;143    144      for (let i = 0; i < crowd.length; i++){145        expect((await helper.api!.rpc.unique.totalStaked(normalizeAccountId(crowd[i]))).toBigInt()).to.be.equal(nominal);  146      }147    });148  });149150});151152describe('unstake balance extrinsic', () => {153  before(async function () {154    await usingPlaygrounds(async (helper, privateKeyWrapper) => {155      if (!getModuleNames(helper.api!).includes(Pallets.AppPromotion)) this.skip();156      alice = privateKeyWrapper('//Alice');157      bob = privateKeyWrapper('//Bob');158      palletAdmin = privateKeyWrapper('//palletAdmin');159      const tx = helper.api!.tx.sudo.sudo(helper.api!.tx.promotion.setAdminAddress(palletAdmin.addressRaw));160      nominal = helper.balance.getOneTokenNominal();161      await submitTransactionAsync(alice, tx);162    });163  });164  165  it('will change balance state to "reserved", add it to "pendingUnstake" map, and subtract it from totalStaked', async () => {166    // arrange: Alice balance = 1000167    // arrange: Alice calls appPromotion.stake(Alice, 500)168169    // act:     Alice calls appPromotion.unstake(300)170    // assert:  Alice reserved balance to equal 300171    // assert:  query appPromotion.staked(Alice) equal [200] /// 500 - 300172    // assert:  query appPromotion.pendingUnstake(Alice) to equal [300]173    // assert:  query appPromotion.totalStaked() decreased by 300174    await usingPlaygrounds(async helper => {175      const totalStakedBefore = (await helper.api!.rpc.unique.totalStaked()).toBigInt();176      const staker = await createUser();177      await expect(submitTransactionAsync(staker, helper.api!.tx.promotion.stake(5n * nominal))).to.be.eventually.fulfilled;178      await expect(submitTransactionAsync(staker, helper.api!.tx.promotion.unstake(3n * nominal))).to.be.eventually.fulfilled;179      expect((await helper.api!.rpc.unique.pendingUnstake(normalizeAccountId(staker.address))).toBigInt()).to.be.equal(3n * nominal);180      expect((await helper.api!.rpc.unique.totalStaked(normalizeAccountId(staker))).toBigInt()).to.be.equal(2n * nominal);181      expect((await helper.api!.rpc.unique.totalStaked()).toBigInt()).to.be.equal(totalStakedBefore + 2n * nominal);182    });183  });184  it('will remove from the "staked" map starting from the oldest entry', async () => {185    // arrange: Alice balance = 1000186    // arrange: Alice stakes 100187    // arrange: Alice stakes 200188    // arrange: Alice stakes 300189190    // assert Alice stake is [100, 200, 300]191192193    // act:     Alice calls appPromotion.unstake(30)194    // assert:  query appPromotion.staked(Alice) to equal [70, 200, 300] /// Can unstake part of stake195    // assert:  query appPromotion.pendingUnstake(Alice) to equal [30]196197    // act:     Alice calls appPromotion.unstake(170)198    // assert:  query appPromotion.staked(Alice) to equal [100, 300] /// Can unstake one stake totally and one more partialy199    // assert:  query appPromotion.pendingUnstake(Alice) to equal [30, 170]200201    // act:     Alice calls appPromotion.unstake(400)202    // assert:  query appPromotion.staked(Alice) to equal [100, 300] /// Can totally unstake 2 stakes in one tx203    // assert:  query appPromotion.pendingUnstake(Alice) to equal [30, 170, 400]204    await usingPlaygrounds(async helper => {205      const totalStakedBefore = (await helper.api!.rpc.unique.totalStaked()).toBigInt();206      const staker = await createUser();207      await expect(submitTransactionAsync(staker, helper.api!.tx.promotion.stake(1n * nominal))).to.be.eventually.fulfilled;208      await expect(submitTransactionAsync(staker, helper.api!.tx.promotion.stake(2n * nominal))).to.be.eventually.fulfilled;209      await expect(submitTransactionAsync(staker, helper.api!.tx.promotion.stake(3n * nominal))).to.be.eventually.fulfilled;210      let stakedPerBlock = (await helper.api!.rpc.unique.totalStakedPerBlock(normalizeAccountId(staker))).map(([_, amount]) => amount.toBigInt());211      expect(stakedPerBlock).to.be.deep.equal([nominal, 2n * nominal, 3n * nominal]);212      await expect(submitTransactionAsync(staker, helper.api!.tx.promotion.unstake(3n * nominal / 10n))).to.be.eventually.fulfilled;213      expect((await helper.api!.rpc.unique.pendingUnstake(normalizeAccountId(staker.address))).toBigInt()).to.be.equal(3n * nominal / 10n);214      stakedPerBlock = (await helper.api!.rpc.unique.totalStakedPerBlock(normalizeAccountId(staker))).map(([_, amount]) => amount.toBigInt());215      216      expect(stakedPerBlock).to.be.deep.equal([7n * nominal / 10n, 2n * nominal, 3n * nominal]);217      218      await expect(submitTransactionAsync(staker, helper.api!.tx.promotion.unstake(17n * nominal / 10n))).to.be.eventually.fulfilled;219      stakedPerBlock = (await helper.api!.rpc.unique.totalStakedPerBlock(normalizeAccountId(staker))).map(([_, amount]) => amount.toBigInt());220      expect(stakedPerBlock).to.be.deep.equal([nominal, 3n * nominal]);221      const unstakedPerBlock = (await helper.api!.rpc.unique.pendingUnstakePerBlock(normalizeAccountId(staker))).map(([_, amount]) => amount.toBigInt());222      223      expect(unstakedPerBlock).to.be.deep.equal([3n * nominal / 10n, 17n * nominal / 10n]);224      225      await waitNewBlocks(helper.api!, 1);226      await expect(submitTransactionAsync(staker, helper.api!.tx.promotion.unstake(4n * nominal))).to.be.eventually.fulfilled;227      expect((await helper.api!.rpc.unique.totalStakedPerBlock(normalizeAccountId(staker))).map(([_, amount]) => amount.toBigInt())).to.be.deep.equal([]);228      expect((await helper.api!.rpc.unique.pendingUnstakePerBlock(normalizeAccountId(staker))).map(([_, amount]) => amount.toBigInt())).to.be.deep.equal([3n * nominal / 10n, 17n * nominal / 10n, 4n * nominal]);229    });230    231  });232});233234235async function createUser(amount?: bigint) {236  return await usingPlaygrounds(async (helper, privateKeyWrapper) => {237    const user: IKeyringPair = privateKeyWrapper(`//Alice+${(new Date()).getTime()}`);238    await helper.balance.transferToSubstrate(alice, user.address, amount ? amount : 10n * helper.balance.getOneTokenNominal());239    return user;240  });241}242243const creteAccounts = async (balances: bigint[], donor: IKeyringPair, helper: UniqueHelper) => {244  let nonce = await helper.chain.getNonce(donor.address);245  const tokenNominal = helper.balance.getOneTokenNominal();246  const transactions = [];247  const accounts = [];248  for (const balance of balances) {249    const recepient = helper.util.fromSeed(mnemonicGenerate());250    accounts.push(recepient);251    if (balance !== 0n){252      const tx = helper.constructApiCall('api.tx.balances.transfer', [{Id: recepient.address}, balance * tokenNominal]);253      transactions.push(helper.signTransaction(donor, tx, 'account generation', {nonce}));254      nonce++;255    }256  }257258  await Promise.all(transactions);259  return accounts;260};