difftreelog
Add new helpers for new unstaking api
in: master
3 files changed
tests/src/sub/appPromotion/appPromotion.test.tsdiffbeforeafterboth--- a/tests/src/sub/appPromotion/appPromotion.test.ts
+++ b/tests/src/sub/appPromotion/appPromotion.test.ts
@@ -86,7 +86,7 @@
await expect(helper.staking.stake(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.NoPermission');
// After unstake can stake again
- await helper.staking.unstake(staker);
+ await helper.staking.unstakeAll(staker);
await helper.staking.stake(staker, 100n * nominal);
expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.equal(100n * nominal);
});
@@ -109,7 +109,7 @@
expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(1199n * nominal);
// staker can unstake
- await helper.staking.unstake(staker);
+ await helper.staking.unstakeAll(staker);
expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(1199n * nominal);
const [pendingUnstake] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
await helper.wait.forParachainBlockNumber(pendingUnstake.block);
@@ -152,7 +152,7 @@
const [staker, recepient] = [accounts.pop()!, accounts.pop()!];
const totalStakedBefore = await helper.staking.getTotalStaked();
await helper.staking.stake(staker, 900n * nominal);
- await helper.staking.unstake(staker);
+ await helper.staking.unstakeAll(staker);
// Right after unstake tokens are still locked
expect(await helper.balance.getLocked(staker.address)).to.deep.eq([{id: 'appstake', amount: 900n * nominal, reasons: 'All'}]);
@@ -167,7 +167,7 @@
itSub('should unlock balance after unlocking period ends and remove it from "pendingUnstake"', async ({helper}) => {
const staker = accounts.pop()!;
await helper.staking.stake(staker, 100n * nominal);
- await helper.staking.unstake(staker);
+ await helper.staking.unstakeAll(staker);
const [pendingUnstake] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
// Wait for unstaking period. Balance now free ~1000; reserved, frozen, miscFrozeb: 0n
@@ -197,7 +197,7 @@
expect(stakes[2].amount).to.equal(300n * nominal);
// Can unstake multiple stakes
- await helper.staking.unstake(staker);
+ await helper.staking.unstakeAll(staker);
pendingUnstake = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
totalPendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address});
stakes = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
@@ -216,7 +216,7 @@
const staker = accounts.pop()!;
// unstake has no effect if no stakes at all
- await helper.staking.unstake(staker);
+ await helper.staking.unstakeAll(staker);
expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(0n);
expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); // TODO bigint closeTo helper
@@ -224,8 +224,8 @@
// can't unstake if there are only pendingUnstakes
await helper.staking.stake(staker, 100n * nominal);
- await helper.staking.unstake(staker);
- await helper.staking.unstake(staker);
+ await helper.staking.unstakeAll(staker);
+ await helper.staking.unstakeAll(staker);
expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal);
expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n);
@@ -234,9 +234,9 @@
itSub('should keep different unlocking block for each unlocking stake', async ({helper}) => {
const staker = accounts.pop()!;
await helper.staking.stake(staker, 100n * nominal);
- await helper.staking.unstake(staker);
+ await helper.staking.unstakeAll(staker);
await helper.staking.stake(staker, 120n * nominal);
- await helper.staking.unstake(staker);
+ await helper.staking.unstakeAll(staker);
const unstakingPerBlock = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
expect(unstakingPerBlock).has.length(2);
@@ -248,7 +248,7 @@
const stakers = [accounts.pop()!, accounts.pop()!, accounts.pop()!];
await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));
- await Promise.all(stakers.map(staker => helper.staking.unstake(staker)));
+ await Promise.all(stakers.map(staker => helper.staking.unstakeAll(staker)));
await Promise.all(stakers.map(async (staker) => {
expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal);
@@ -261,7 +261,7 @@
const stakers = await helper.arrange.createAccounts([200n,200n,200n,200n,200n,200n,200n,200n,200n,200n], donor);
await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));
- const unstakingResults = await Promise.allSettled(stakers.map(staker => helper.staking.unstake(staker)));
+ const unstakingResults = await Promise.allSettled(stakers.map(staker => helper.staking.unstakeAll(staker)));
const successfulUnstakes = unstakingResults.filter(result => result.status === 'fulfilled');
expect(successfulUnstakes).to.have.length(3);
@@ -597,7 +597,7 @@
const totalStakedAfter = await helper.staking.getTotalStaked();
expect(totalStakedAfter).to.equal(totalStakedBefore + (100n * nominal) + totalPayout);
// staker can unstake
- await helper.staking.unstake(staker);
+ await helper.staking.unstakeAll(staker);
expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedAfter - calculateIncome(100n * nominal));
});
@@ -651,7 +651,7 @@
await helper.staking.stake(staker, 100n * nominal);
const [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD);
- await helper.staking.unstake(staker);
+ await helper.staking.unstakeAll(staker);
// so he did not receive any rewards
const totalBalanceBefore = await helper.balance.getSubstrate(staker.address);
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 IEvent {7 section: string;8 method: string;9 index: [number, number] | string;10 data: any[];11 phase: {applyExtrinsic: number} | 'Initialization',12}1314export interface ITransactionResult {15 status: 'Fail' | 'Success';16 result: {17 dispatchError: any,18 events: {19 phase: any, // {ApplyExtrinsic: number} | 'Initialization',20 event: IEvent;21 }[];22 },23 moduleError?: string;24}2526export interface ISubscribeBlockEventsData {27 number: number;28 hash: string;29 timestamp: number;30 events: IEvent[];31}3233export interface ILogger {34 log: (msg: any, level?: string) => void;35 level: {36 ERROR: 'ERROR';37 WARNING: 'WARNING';38 INFO: 'INFO';39 [key: string]: string;40 }41}4243export interface IUniqueHelperLog {44 executedAt: number;45 executionTime: number;46 type: 'extrinsic' | 'rpc';47 status: 'Fail' | 'Success';48 call: string;49 params: any[];50 moduleError?: string;51 dispatchError?: any;52 events?: any;53}5455export interface IApiListeners {56 connected?: (...args: any[]) => any;57 disconnected?: (...args: any[]) => any;58 error?: (...args: any[]) => any;59 ready?: (...args: any[]) => any;60 decorated?: (...args: any[]) => any;61}6263export interface ICrossAccountId {64 Substrate?: TSubstrateAccount;65 Ethereum?: TEthereumAccount;66}6768export interface ICrossAccountIdLower {69 substrate?: TSubstrateAccount;70 ethereum?: TEthereumAccount;71}7273export interface IEthCrossAccountId {74 0: TEthereumAccount;75 1: TSubstrateAccount;76 eth: TEthereumAccount;77 sub: TSubstrateAccount;78}7980export interface ICollectionLimits {81 accountTokenOwnershipLimit?: number | null;82 sponsoredDataSize?: number | null;83 sponsoredDataRateLimit?: {blocks: number} | {sponsoringDisabled: null} | null;84 tokenLimit?: number | null;85 sponsorTransferTimeout?: number | null;86 sponsorApproveTimeout?: number | null;87 ownerCanTransfer?: boolean | null;88 ownerCanDestroy?: boolean | null;89 transfersEnabled?: boolean | null;90}9192export interface INestingPermissions {93 tokenOwner?: boolean;94 collectionAdmin?: boolean;95 restricted?: number[] | null;96}9798export interface ICollectionPermissions {99 access?: 'Normal' | 'AllowList';100 mintMode?: boolean;101 nesting?: INestingPermissions;102}103104export interface IProperty {105 key: string;106 value?: string;107}108109export interface ITokenPropertyPermission {110 key: string;111 permission: {112 mutable?: boolean;113 tokenOwner?: boolean;114 collectionAdmin?: boolean;115 }116}117118export interface IToken {119 collectionId: number;120 tokenId: number;121}122123export interface IBlock {124 extrinsics: IExtrinsic[]125 header: {126 parentHash: string,127 number: number,128 };129}130131export interface IExtrinsic {132 isSigned: boolean,133 method: {134 method: string,135 section: string,136 args: any[]137 }138}139140export interface ICollectionCreationOptions {141 name?: string | number[];142 description?: string | number[];143 tokenPrefix?: string | number[];144 mode?: {145 nft?: null;146 refungible?: null;147 fungible?: number;148 }149 permissions?: ICollectionPermissions;150 properties?: IProperty[];151 tokenPropertyPermissions?: ITokenPropertyPermission[];152 limits?: ICollectionLimits;153 pendingSponsor?: TSubstrateAccount;154}155156export interface IChainProperties {157 ss58Format: number;158 tokenDecimals: number[];159 tokenSymbol: string[]160}161162export interface ISubstrateBalance {163 free: bigint,164 reserved: bigint,165 miscFrozen: bigint,166 feeFrozen: bigint167}168169export interface IStakingInfo {170 block: bigint,171 amount: bigint,172}173174export interface IPovInfo {175 proofSize: number,176 compactProofSize: number,177 compressedProofSize: number,178 results: any[],179 kv: any,180}181182export interface ISchedulerOptions {183 scheduledId?: string,184 priority?: number,185 periodic?: {186 period: number,187 repetitions: number,188 },189}190191export interface IForeignAssetMetadata {192 name?: number | Uint8Array,193 symbol?: string,194 decimals?: number,195 minimalBalance?: bigint,196}197198export interface MoonbeamAssetInfo {199 location: any,200 metadata: {201 name: string,202 symbol: string,203 decimals: number,204 isFrozen: boolean,205 minimalBalance: bigint,206 },207 existentialDeposit: bigint,208 isSufficient: boolean,209 unitsPerSecond: bigint,210 numAssetsWeightHint: number,211}212213export interface AcalaAssetMetadata {214 name: string,215 symbol: string,216 decimals: number,217 minimalBalance: bigint,218}219220export interface DemocracyStandardAccountVote {221 balance: bigint,222 vote: {223 aye: boolean,224 conviction: number,225 },226}227228export type TSubstrateAccount = string;229export type TEthereumAccount = string;230export type TApiAllowedListeners = 'connected' | 'disconnected' | 'error' | 'ready' | 'decorated';231export type TUniqueNetworks = 'opal' | 'quartz' | 'unique';232export type TSiblingNetworkds = 'moonbeam' | 'moonriver' | 'acala' | 'karura' | 'westmint';233export type TRelayNetworks = 'rococo' | 'westend';234export type TNetworks = TUniqueNetworks | TSiblingNetworkds | TRelayNetworks;235export type TSigner = IKeyringPair; // | 'string'236export type TCollectionMode = 'nft' | 'rft' | 'ft';1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.034import {IKeyringPair} from '@polkadot/types/types';56export interface IEvent {7 section: string;8 method: string;9 index: [number, number] | string;10 data: any[];11 phase: {applyExtrinsic: number} | 'Initialization',12}1314export interface ITransactionResult {15 status: 'Fail' | 'Success';16 result: {17 dispatchError: any,18 events: {19 phase: any, // {ApplyExtrinsic: number} | 'Initialization',20 event: IEvent;21 }[];22 },23 blockHash: string,24 moduleError?: string;25}2627export interface ISubscribeBlockEventsData {28 number: number;29 hash: string;30 timestamp: number;31 events: IEvent[];32}3334export interface ILogger {35 log: (msg: any, level?: string) => void;36 level: {37 ERROR: 'ERROR';38 WARNING: 'WARNING';39 INFO: 'INFO';40 [key: string]: string;41 }42}4344export interface IUniqueHelperLog {45 executedAt: number;46 executionTime: number;47 type: 'extrinsic' | 'rpc';48 status: 'Fail' | 'Success';49 call: string;50 params: any[];51 moduleError?: string;52 dispatchError?: any;53 events?: any;54}5556export interface IApiListeners {57 connected?: (...args: any[]) => any;58 disconnected?: (...args: any[]) => any;59 error?: (...args: any[]) => any;60 ready?: (...args: any[]) => any;61 decorated?: (...args: any[]) => any;62}6364export interface ICrossAccountId {65 Substrate?: TSubstrateAccount;66 Ethereum?: TEthereumAccount;67}6869export interface ICrossAccountIdLower {70 substrate?: TSubstrateAccount;71 ethereum?: TEthereumAccount;72}7374export interface IEthCrossAccountId {75 0: TEthereumAccount;76 1: TSubstrateAccount;77 eth: TEthereumAccount;78 sub: TSubstrateAccount;79}8081export interface ICollectionLimits {82 accountTokenOwnershipLimit?: number | null;83 sponsoredDataSize?: number | null;84 sponsoredDataRateLimit?: {blocks: number} | {sponsoringDisabled: null} | null;85 tokenLimit?: number | null;86 sponsorTransferTimeout?: number | null;87 sponsorApproveTimeout?: number | null;88 ownerCanTransfer?: boolean | null;89 ownerCanDestroy?: boolean | null;90 transfersEnabled?: boolean | null;91}9293export interface INestingPermissions {94 tokenOwner?: boolean;95 collectionAdmin?: boolean;96 restricted?: number[] | null;97}9899export interface ICollectionPermissions {100 access?: 'Normal' | 'AllowList';101 mintMode?: boolean;102 nesting?: INestingPermissions;103}104105export interface IProperty {106 key: string;107 value?: string;108}109110export interface ITokenPropertyPermission {111 key: string;112 permission: {113 mutable?: boolean;114 tokenOwner?: boolean;115 collectionAdmin?: boolean;116 }117}118119export interface IToken {120 collectionId: number;121 tokenId: number;122}123124export interface IBlock {125 extrinsics: IExtrinsic[]126 header: {127 parentHash: string,128 number: number,129 };130}131132export interface IExtrinsic {133 isSigned: boolean,134 method: {135 method: string,136 section: string,137 args: any[]138 }139}140141export interface ICollectionCreationOptions {142 name?: string | number[];143 description?: string | number[];144 tokenPrefix?: string | number[];145 mode?: {146 nft?: null;147 refungible?: null;148 fungible?: number;149 }150 permissions?: ICollectionPermissions;151 properties?: IProperty[];152 tokenPropertyPermissions?: ITokenPropertyPermission[];153 limits?: ICollectionLimits;154 pendingSponsor?: TSubstrateAccount;155}156157export interface IChainProperties {158 ss58Format: number;159 tokenDecimals: number[];160 tokenSymbol: string[]161}162163export interface ISubstrateBalance {164 free: bigint,165 reserved: bigint,166 miscFrozen: bigint,167 feeFrozen: bigint168}169170export interface IStakingInfo {171 block: bigint,172 amount: bigint,173}174175export interface IPovInfo {176 proofSize: number,177 compactProofSize: number,178 compressedProofSize: number,179 results: any[],180 kv: any,181}182183export interface ISchedulerOptions {184 scheduledId?: string,185 priority?: number,186 periodic?: {187 period: number,188 repetitions: number,189 },190}191192export interface IForeignAssetMetadata {193 name?: number | Uint8Array,194 symbol?: string,195 decimals?: number,196 minimalBalance?: bigint,197}198199export interface MoonbeamAssetInfo {200 location: any,201 metadata: {202 name: string,203 symbol: string,204 decimals: number,205 isFrozen: boolean,206 minimalBalance: bigint,207 },208 existentialDeposit: bigint,209 isSufficient: boolean,210 unitsPerSecond: bigint,211 numAssetsWeightHint: number,212}213214export interface AcalaAssetMetadata {215 name: string,216 symbol: string,217 decimals: number,218 minimalBalance: bigint,219}220221export interface DemocracyStandardAccountVote {222 balance: bigint,223 vote: {224 aye: boolean,225 conviction: number,226 },227}228229export type TSubstrateAccount = string;230export type TEthereumAccount = string;231export type TApiAllowedListeners = 'connected' | 'disconnected' | 'error' | 'ready' | 'decorated';232export type TUniqueNetworks = 'opal' | 'quartz' | 'unique';233export type TSiblingNetworkds = 'moonbeam' | 'moonriver' | 'acala' | 'karura' | 'westmint';234export type TRelayNetworks = 'rococo' | 'westend';235export type TNetworks = TUniqueNetworks | TSiblingNetworkds | TRelayNetworks;236export type TSigner = IKeyringPair; // | 'string'237export type TCollectionMode = 'nft' | 'rft' | 'ft';tests/src/util/playgrounds/unique.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -561,7 +561,7 @@
if (status === this.transactionStatus.SUCCESS) {
this.logger.log(`${label} successful`);
unsub();
- resolve({result, status});
+ resolve({result, status, blockHash: result.status.asInBlock.toHuman()});
} else if (status === this.transactionStatus.FAIL) {
let moduleError = null;
@@ -2657,20 +2657,35 @@
}
/**
- * Unstake tokens for App Promotion
+ * Unstake all staked tokens
* @param signer keyring of signer
* @param amountToUnstake amount of tokens to unstake
* @param label extra label for log
- * @returns block number where balances will be unlocked
+ * @returns block hash where unstake happened
*/
- async unstake(signer: TSigner, label?: string): Promise<number> {
+ async unstakeAll(signer: TSigner, label?: string): Promise<string> {
if(typeof label === 'undefined') label = `${signer.address}`;
- const _unstakeResult = await this.helper.executeExtrinsic(
- signer, 'api.tx.appPromotion.unstake',
+ const unstakeResult = await this.helper.executeExtrinsic(
+ signer, 'api.tx.appPromotion.unstakeAll',
[], true,
);
- // TODO extract block number fron events
- return 1;
+ return unstakeResult.blockHash;
+ }
+
+ /**
+ * Unstake the part of a staked tokens
+ * @param signer keyring of signer
+ * @param amount amount of tokens to unstake
+ * @param label extra label for log
+ * @returns block hash where unstake happened
+ */
+ async unstakePartial(signer: TSigner, amount: bigint, label?: string): Promise<string> {
+ if(typeof label === 'undefined') label = `${signer.address}`;
+ const unstakeResult = await this.helper.executeExtrinsic(
+ signer, 'api.tx.appPromotion.unstakePartial',
+ [amount], true,
+ );
+ return unstakeResult.blockHash;
}
/**