difftreelog
Return (un)staking info in more convinient view
in: master
- Refactor getTotalStakedPerBlock getPendingUnstakePerBlock methods. - Add docs
3 files changed
tests/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));
});
});
tests/src/util/playgrounds/types.tsdiffbeforeafterboth1// 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'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'tests/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;
}
}