git.delta.rocks / unique-network / refs/commits / 7a09794fa753

difftreelog

Tests: add balance state asserts

Maksandre2022-09-01parent: #0889383.patch.diff
in: master

3 files changed

modifiedtests/src/app-promotion.test.tsdiffbeforeafterboth
--- a/tests/src/app-promotion.test.ts
+++ b/tests/src/app-promotion.test.ts
@@ -63,16 +63,21 @@
 });
 
 describe('app-promotions.stake extrinsic', () => {
-  it('should change balance state to "locked", add it to "staked" map, and increase "totalStaked" amount', async () => {
+  it('should "lock" some balance in system.account, add it to "staked" map, and increase "totalStaked" amount', async () => {
     await usingPlaygrounds(async (helper) => {
       const totalStakedBefore = await helper.staking.getTotalStaked();
-      const [staker] = await helper.arrange.createAccounts([400n], alice);
+      const [staker, recepient] = await helper.arrange.createAccounts([400n, 0n], alice);
    
       // Minimum stake amount is 100:
       await expect(helper.staking.stake(staker, 100n * nominal - 1n)).to.be.eventually.rejected;
       await helper.staking.stake(staker, 100n * nominal);
+
+      // Staker balance is: miscFrozen: 100, feeFrozen: 100, reserved: 0n, free less than 300...
+      // ...so he can not transfer 300
+      expect (await helper.balance.getSubstrateFull(staker.address)).to.contain({miscFrozen: 100n * nominal, feeFrozen: 100n * nominal, reserved: 0n});
+      await expect(helper.balance.transferToSubstrate(staker, recepient.address, 300n * nominal)).to.be.rejected;
+      
       expect(await helper.staking.getTotalStakingLocked({Substrate: staker.address})).to.be.equal(100n * nominal);
-
       // TODO add helpers to assert bigints. Check balance close to 100
       expect(await helper.balance.getSubstrate(staker.address) - 99n * nominal >= (nominal / 2n)).to.be.true;
       expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(100n * nominal);
@@ -121,17 +126,27 @@
   it('should change balance state to "reserved", add it to "pendingUnstake" map, and subtract it from totalStaked', async () => {
     await usingPlaygrounds(async helper => {
       const totalStakedBefore = await helper.staking.getTotalStaked();
-      const [staker] = await helper.arrange.createAccounts([1000n], alice);
+      const [staker, recepient] = await helper.arrange.createAccounts([600n, 0n], alice);
       await helper.staking.stake(staker, 500n * nominal);
       await helper.staking.unstake(staker);
 
+      // Stakers balance now: {free: <100n, reserved: 500n, miscFrozen: 0, feeFrozen: 0};
+      // Staker can not transfer 
+      // TODO expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 500n * nominal, miscFrozen: 0n, feeFrozen: 0n});
+      await expect(helper.balance.transferToSubstrate(staker, recepient.address, 100n * nominal)).to.be.rejected;
+
       expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(500n * nominal);
       expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n);
       expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore);
+
+      // Wait for unstaking period. Balance now free ~600, and reserved, frozen, miscFrozeb 0n
+      await waitForRelayBlock(helper.api!, 20);
+      expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n * nominal, miscFrozen: 0n, feeFrozen: 0n});
+      expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(599n);
     });
   });
 
-  it('should remove multiple stakes', async () => {
+  it('should successfully unstake multiple stakes', async () => {
     await usingPlaygrounds(async helper => {
       const [staker] = await helper.arrange.createAccounts([1000n], alice);
       await helper.staking.stake(staker, 100n * nominal);
@@ -154,6 +169,11 @@
       expect(pendingUnstake).to.be.equal(600n * nominal);
       expect(stakedPerBlock).to.be.deep.equal([]);
       expect(unstakedPerBlock).to.be.deep.equal([600n * nominal]);
+
+      expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, feeFrozen: 600n * nominal, miscFrozen: 600n * nominal});
+      await waitForRelayBlock(helper.api!, 20);
+      expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, feeFrozen: 0n, miscFrozen: 0n});
+      expect (await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n);
     });
   });
 
@@ -200,6 +220,8 @@
       const [staker] = await helper.arrange.createAccounts([1000n], alice);
       await helper.staking.stake(staker, 100n * nominal);
       await helper.staking.unstake(staker);
+      await waitForRelayBlock(helper.api!, 20);
+      // expect balance unlocked
       expect.fail('Not implemented');
     });
   });
modifiedtests/src/util/playgrounds/types.tsdiffbeforeafterboth
before · tests/src/util/playgrounds/types.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.034import {IKeyringPair} from '@polkadot/types/types';56export interface IChainEvent {7  data: any;8  method: string;9  section: string;10}1112export interface ITransactionResult {13    status: 'Fail' | 'Success';14    result: {15        events: {16          event: IChainEvent17        }[];18    },19    moduleError?: string;20}2122export interface ILogger {23  log: (msg: any, level?: string) => void;24  level: {25    ERROR: 'ERROR';26    WARNING: 'WARNING';27    INFO: 'INFO';28    [key: string]: string;29  }30}3132export interface IUniqueHelperLog {33  executedAt: number;34  executionTime: number;35  type: 'extrinsic' | 'rpc';36  status: 'Fail' | 'Success';37  call: string;38  params: any[];39  moduleError?: string;40  events?: any;41}4243export interface IApiListeners {44  connected?: (...args: any[]) => any;45  disconnected?: (...args: any[]) => any;46  error?: (...args: any[]) => any;47  ready?: (...args: any[]) => any; 48  decorated?: (...args: any[]) => any;49}5051export interface ICrossAccountId {52  Substrate?: TSubstrateAccount;53  Ethereum?: TEthereumAccount;54}5556export interface ICrossAccountIdLower {57  substrate?: TSubstrateAccount;58  ethereum?: TEthereumAccount;59}6061export interface ICollectionLimits {62  accountTokenOwnershipLimit?: number | null;63  sponsoredDataSize?: number | null;64  sponsoredDataRateLimit?: {blocks: number} | {sponsoringDisabled: null} | null;65  tokenLimit?: number | null;66  sponsorTransferTimeout?: number | null;67  sponsorApproveTimeout?: number | null;68  ownerCanTransfer?: boolean | null;69  ownerCanDestroy?: boolean | null;70  transfersEnabled?: boolean | null;71}7273export interface INestingPermissions {74  tokenOwner?: boolean;75  collectionAdmin?: boolean;76  restricted?: number[] | null;77}7879export interface ICollectionPermissions {80  access?: 'Normal' | 'AllowList';81  mintMode?: boolean;82  nesting?: INestingPermissions;83}8485export interface IProperty {86  key: string;87  value: string;88}8990export interface ITokenPropertyPermission {91  key: string;92  permission: {93    mutable: boolean;94    tokenOwner: boolean;95    collectionAdmin: boolean;96  }97}9899export interface IToken {100  collectionId: number;101  tokenId: number;102}103104export interface ICollectionCreationOptions {105  name: string | number[];106  description: string | number[];107  tokenPrefix: string | number[];108  mode?: {109    nft?: null;110    refungible?: null;111    fungible?: number;112  }113  permissions?: ICollectionPermissions;114  properties?: IProperty[];115  tokenPropertyPermissions?: ITokenPropertyPermission[];116  limits?: ICollectionLimits;117  pendingSponsor?: TSubstrateAccount;118}119120export interface IChainProperties {121  ss58Format: number;122  tokenDecimals: number[];123  tokenSymbol: string[]124}125126export type TSubstrateAccount = string;127export type TEthereumAccount = string;128export type TApiAllowedListeners = 'connected' | 'disconnected' | 'error' | 'ready' | 'decorated';129export type TUniqueNetworks = 'opal' | 'quartz' | 'unique';130export type TSigner = IKeyringPair; // | 'string'
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -9,7 +9,7 @@
 import {ApiInterfaceEvents} from '@polkadot/api/types';
 import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm} from '@polkadot/util-crypto';
 import {IKeyringPair} from '@polkadot/types/types';
-import {IApiListeners, IChainEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, TUniqueNetworks} from './types';
+import {IApiListeners, IChainEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, TUniqueNetworks} from './types';
 
 const crossAccountIdFromLower = (lowerAddress: ICrossAccountIdLower): ICrossAccountId => {
   const address = {} as ICrossAccountId;
@@ -1894,6 +1894,16 @@
   }
 
   /**
+   * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved
+   * @param address substrate address
+   * @returns 
+   */
+  async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {
+    const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;
+    return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};
+  }
+
+  /**
    * Get ethereum address balance
    * @param address ethereum address
    * @example getEthereum("0x9F0583DbB855d...")