git.delta.rocks / unique-network / refs/commits / bbf501e6dfeb

difftreelog

Return (un)staking info in more convinient view

Max Andreev2022-09-14parent: #85b56f6.patch.diff
in: master
- Refactor getTotalStakedPerBlock getPendingUnstakePerBlock methods.
- Add docs

3 files changed

modifiedtests/src/app-promotion.test.tsdiffbeforeafterboth
--- a/tests/src/app-promotion.test.ts
+++ b/tests/src/app-promotion.test.ts
@@ -81,7 +81,9 @@
       
       await helper.staking.stake(staker, 200n * nominal);
       expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(300n * nominal);
-      expect((await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map((x) => x[1])).to.be.deep.equal([100n * nominal, 200n * nominal]);
+      const totalStakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      expect(totalStakedPerBlock[0].amount).to.equal(100n * nominal);
+      expect(totalStakedPerBlock[1].amount).to.equal(200n * nominal);
     });
   });
 
@@ -159,10 +161,10 @@
       const staker = accounts.pop()!;
       await helper.staking.stake(staker, 100n * nominal);
       await helper.staking.unstake(staker);
-      const unstakedInBlock = (await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}))[0][0];
+      const [pendingUnstake] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
 
       // Wait for unstaking period. Balance now free ~1000; reserved, frozen, miscFrozeb: 0n
-      await helper.wait.forParachainBlockNumber(unstakedInBlock);
+      await helper.wait.forParachainBlockNumber(pendingUnstake.block);
       expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, miscFrozen: 0n, feeFrozen: 0n});
       expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n);
 
@@ -180,25 +182,26 @@
       await helper.staking.stake(staker, 300n * nominal);
 
       // staked: [100, 200, 300]; unstaked: 0
-      let pendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address});
-      let unstakedPerBlock = (await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address})).map(stake => stake[1]);
-      let stakedPerBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map(stake => stake[1]);
-      expect(pendingUnstake).to.be.deep.equal(0n);
-      expect(unstakedPerBlock).to.be.deep.equal([]);
-      expect(stakedPerBlock).to.be.deep.equal([100n * nominal, 200n * nominal, 300n * nominal]);
+      let totalPendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address});
+      let pendingUnstake = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
+      let stakes = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      expect(totalPendingUnstake).to.be.deep.equal(0n);
+      expect(pendingUnstake).to.be.deep.equal([]);
+      expect(stakes[0].amount).to.equal(100n * nominal);
+      expect(stakes[1].amount).to.equal(200n * nominal);
+      expect(stakes[2].amount).to.equal(300n * nominal);
      
       // Can unstake multiple stakes
       await helper.staking.unstake(staker);
-      const unstakingBlock = (await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}))[0][0];
-      pendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address});
-      unstakedPerBlock = (await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address})).map(stake => stake[1]);
-      stakedPerBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map(stake => stake[1]);
-      expect(pendingUnstake).to.be.equal(600n * nominal);
-      expect(stakedPerBlock).to.be.deep.equal([]);
-      expect(unstakedPerBlock).to.be.deep.equal([600n * nominal]);
+      pendingUnstake = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
+      totalPendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address});
+      stakes = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      expect(totalPendingUnstake).to.be.equal(600n * nominal);
+      expect(stakes).to.be.deep.equal([]);
+      expect(pendingUnstake[0].amount).to.equal(600n * nominal);
 
       expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 600n * nominal, feeFrozen: 0n, miscFrozen: 0n});
-      await helper.wait.forParachainBlockNumber(unstakingBlock);
+      await helper.wait.forParachainBlockNumber(pendingUnstake[0].block);
       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);
     });
@@ -235,8 +238,8 @@
 
       const unstakingPerBlock = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
       expect(unstakingPerBlock).has.length(2);
-      expect(unstakingPerBlock[0][1]).to.equal(100n * nominal);
-      expect(unstakingPerBlock[1][1]).to.equal(120n * nominal);
+      expect(unstakingPerBlock[0].amount).to.equal(100n * nominal);
+      expect(unstakingPerBlock[1].amount).to.equal(120n * nominal);
     });
   });
 
@@ -661,15 +664,15 @@
     });
   });
 
-  it('should increase total staked', async() => {
+  it('should increase total staked', async () => {
     await usingPlaygrounds(async (helper) => {
       const staker = accounts.pop()!;
       const totalStakedBefore = await helper.staking.getTotalStaked();
       await helper.staking.stake(staker, 100n * nominal);
 
       // Wait for rewards and pay
-      const stakedInBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}))[0][0];
-      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock));
+      const [stakedInBlock] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock.block));
       await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100));
 
       const totalStakedAfter = await helper.staking.getTotalStaked();
@@ -691,13 +694,14 @@
       await helper.staking.stake(staker, 200n * nominal);
 
       // wait rewards are available:
-      const stakedInBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}))[1][0];
-      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock));
+      const [_, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block));
 
       await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100));
 
-      const totalStakedPerBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map(s => s[1]);
-      expect(totalStakedPerBlock).to.be.deep.equal([calculateIncome(100n * nominal, 10n), calculateIncome(200n * nominal, 10n)]);
+      const totalStakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      expect(totalStakedPerBlock[0].amount).to.equal(calculateIncome(100n * nominal, 10n));
+      expect(totalStakedPerBlock[1].amount).to.equal(calculateIncome(200n * nominal, 10n));
     });
   });
 
@@ -707,13 +711,13 @@
 
       await helper.staking.stake(staker, 100n * nominal);
       // wait for two rewards are available:
-      const stakedInBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}))[0][0];
-      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock) + LOCKING_PERIOD);
+      let [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD);
 
       await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100));
-      const stakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
       const frozenBalanceShouldBe = calculateIncome(100n * nominal, 10n, 2);
-      expect(stakedPerBlock[0][1]).to.be.equal(frozenBalanceShouldBe);
+      expect(stake.amount).to.be.equal(frozenBalanceShouldBe);
 
       const stakerFullBalance = await helper.balance.getSubstrateFull(staker.address);
 
@@ -726,8 +730,8 @@
       // staker unstakes before rewards has been payed
       const staker = accounts.pop()!;
       await helper.staking.stake(staker, 100n * nominal);
-      const stakedInBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}))[0][0];
-      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock) + LOCKING_PERIOD);
+      const [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD);
       await helper.staking.unstake(staker);
       
       // so he did not receive any rewards
@@ -745,17 +749,17 @@
             
       await helper.staking.stake(staker, 100n * nominal);
 
-      const stakedInBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}))[0][0];
-      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock));
+      let [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block));
       
       await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100));
-      let totalStakedPerBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map(s => s[1]);
-      expect(totalStakedPerBlock).to.deep.equal([calculateIncome(100n * nominal, 10n)]);
+      [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      expect(stake.amount).to.equal(calculateIncome(100n * nominal, 10n));
       
-      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock) + LOCKING_PERIOD);
+      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD);
       await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100));
-      totalStakedPerBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map(s => s[1]);
-      expect(totalStakedPerBlock).to.deep.equal([calculateIncome(100n * nominal, 10n, 2)]);      
+      [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      expect(stake.amount).to.equal(calculateIncome(100n * nominal, 10n, 2));
     });
   });
 
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 IBlock {105  extrinsics: IExtrinsic[]106  header: {107    parentHash: string,108    number: number,109  };110}111112export interface IExtrinsic {113  isSigned: boolean,114  method: {115    method: string,116    section: string,117    args: any[]118  }119}120121export interface ICollectionCreationOptions {122  name?: string | number[];123  description?: string | number[];124  tokenPrefix?: string | number[];125  mode?: {126    nft?: null;127    refungible?: null;128    fungible?: number;129  }130  permissions?: ICollectionPermissions;131  properties?: IProperty[];132  tokenPropertyPermissions?: ITokenPropertyPermission[];133  limits?: ICollectionLimits;134  pendingSponsor?: TSubstrateAccount;135}136137export interface IChainProperties {138  ss58Format: number;139  tokenDecimals: number[];140  tokenSymbol: string[]141}142143export interface ISubstrateBalance {144  free: bigint,145  reserved: bigint,146  miscFrozen: bigint,147  feeFrozen: bigint148}149150export type TSubstrateAccount = string;151export type TEthereumAccount = string;152export type TApiAllowedListeners = 'connected' | 'disconnected' | 'error' | 'ready' | 'decorated';153export type TUniqueNetworks = 'opal' | 'quartz' | 'unique';154export type TSigner = IKeyringPair; // | 'string'
after · 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 IBlock {105  extrinsics: IExtrinsic[]106  header: {107    parentHash: string,108    number: number,109  };110}111112export interface IExtrinsic {113  isSigned: boolean,114  method: {115    method: string,116    section: string,117    args: any[]118  }119}120121export interface ICollectionCreationOptions {122  name?: string | number[];123  description?: string | number[];124  tokenPrefix?: string | number[];125  mode?: {126    nft?: null;127    refungible?: null;128    fungible?: number;129  }130  permissions?: ICollectionPermissions;131  properties?: IProperty[];132  tokenPropertyPermissions?: ITokenPropertyPermission[];133  limits?: ICollectionLimits;134  pendingSponsor?: TSubstrateAccount;135}136137export interface IChainProperties {138  ss58Format: number;139  tokenDecimals: number[];140  tokenSymbol: string[]141}142143export interface ISubstrateBalance {144  free: bigint,145  reserved: bigint,146  miscFrozen: bigint,147  feeFrozen: bigint148}149150export interface IStakingInfo {151  block: bigint,152  amount: bigint,153}154155export type TSubstrateAccount = string;156export type TEthereumAccount = string;157export type TApiAllowedListeners = 'connected' | 'disconnected' | 'error' | 'ready' | 'decorated';158export type TUniqueNetworks = 'opal' | 'quartz' | 'unique';159export 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, IBlock, IChainEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, TUniqueNetworks} from './types';
+import {IApiListeners, IBlock, IChainEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, IStakingInfo, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, TUniqueNetworks} from './types';
 
 export const crossAccountIdFromLower = (lowerAddress: ICrossAccountIdLower): ICrossAccountId => {
   const address = {} as ICrossAccountId;
@@ -2029,21 +2029,54 @@
     return 1;
   }
 
+  /**
+   * Get total staked amount for address
+   * @param address substrate or ethereum address
+   * @returns total staked amount
+   */
   async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {
     if (address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();
     return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();
   }
 
-  async getTotalStakedPerBlock(address: ICrossAccountId): Promise<bigint[][]> {
-    return (await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address])).map(([block, amount]: any[]) => [block.toBigInt(), amount.toBigInt()]);
+  /**
+   * Get total staked per block
+   * @param address substrate or ethereum address
+   * @returns array of stakes. `block` – the number of the block in which the stake was made. `amount` - the number of tokens staked in the block
+   */
+  async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {
+    const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);
+    return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => {
+      return { 
+        block: block.toBigInt(),
+        amount: amount.toBigInt(),
+      };
+    });
   }
 
+  /**
+   * Get total pending unstake amount for address
+   * @param address substrate or ethereum address
+   * @returns total pending unstake amount
+   */
   async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {
     return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();
   }
 
-  async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<bigint[][]> {
-    return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address])).map(([block, amount]: any[]) => [block.toBigInt(), amount.toBigInt()]);
+  /**
+   * Get pending unstake amount per block for address
+   * @param address substrate or ethereum address
+   * @returns array of pending stakes. `block` – the number of the block in which the unstake was made. `amount` - the number of tokens unstaked in the block
+   */
+  async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {
+    const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);
+    const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => {
+      return {
+        block: block.toBigInt(),
+        amount: amount.toBigInt(),
+      };
+    });
+    return result;
   }
 }