1234567891011121314151617import {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 73 74 75 76 77 78 79 80 81 82 83 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 109 110111 112 113114 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 127 128129 130 await usingPlaygrounds(async helper => {131 132 133 134 135 const crowd = [];136 for(let i = 4; i--;) crowd.push(await createUser());137 138 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 167 168169 170 171 172 173 174 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 186 187 188 189190 191192193 194 195 196197 198 199 200201 202 203 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};