difftreelog
Merge pull request #1051 from UniqueNetwork/feature/playgrounds-refactor
in: master
154 files changed
.github/workflows/xcm.ymldiffbeforeafterboth--- a/.github/workflows/xcm.yml
+++ b/.github/workflows/xcm.yml
@@ -368,9 +368,9 @@
yarn add mochawesome
- name: Call HRMP initialization
- working-directory: js-packages/tests
+ working-directory: js-packages/scripts
run: |
- yarn node --no-warnings=ExperimentalWarning --loader ts-node/esm util/createHrmp.ts ${{matrix.network}}
+ yarn node --no-warnings=ExperimentalWarning --loader ts-node/esm createHrmp.ts ${{matrix.network}}
- name: Run XCM tests
working-directory: js-packages/tests
js-packages/package.jsondiffbeforeafterboth--- a/js-packages/package.json
+++ b/js-packages/package.json
@@ -26,8 +26,9 @@
"@types/node": "^20.8.10",
"@typescript-eslint/eslint-plugin": "^6.10.0",
"@typescript-eslint/parser": "^6.10.0",
- "@unique/opal-types": "workspace:*",
- "@unique/playgrounds": "workspace:*",
+ "@unique-nft/opal-testnet-types": "workspace:*",
+ "@unique-nft/playgrounds": "workspace:*",
+ "@unique/test-utils": "workspace:*",
"chai": "^4.3.10",
"chai-subset": "^1.6.0",
"eslint": "^8.53.0",
@@ -59,6 +60,7 @@
"types",
"playgrounds",
"scripts",
+ "test-utils",
"tests"
]
}
js-packages/playgrounds/package.jsondiffbeforeafterboth--- a/js-packages/playgrounds/package.json
+++ b/js-packages/playgrounds/package.json
@@ -1,18 +1,17 @@
{
- "author": "",
- "license": "SEE LICENSE IN ../../../LICENSE",
- "description": "Playground scripts",
+ "author": "Unique Network",
+ "license": "Apache 2.0",
+ "description": "Helpers for Unique Network chain",
"engines": {
- "node": ">=16"
+ "node": ">=18"
},
- "name": "@unique/playgrounds",
+ "name": "@unique-nft/playgrounds",
"type": "module",
"version": "1.0.0",
"main": "unique.js",
"dependencies": {
"@polkadot/api": "10.10.1",
"@polkadot/util": "^12.5.1",
- "@polkadot/util-crypto": "^12.5.1",
- "@unique/opal-types": "workspace:*"
+ "@polkadot/util-crypto": "^12.5.1"
}
}
js-packages/playgrounds/types.xcm.tsdiffbeforeafterboth--- a/js-packages/playgrounds/types.xcm.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-export interface AcalaAssetMetadata {
- name: string,
- symbol: string,
- decimals: number,
- minimalBalance: bigint,
-}
-
-export interface MoonbeamAssetInfo {
- location: any,
- metadata: {
- name: string,
- symbol: string,
- decimals: number,
- isFrozen: boolean,
- minimalBalance: bigint,
- },
- existentialDeposit: bigint,
- isSufficient: boolean,
- unitsPerSecond: bigint,
- numAssetsWeightHint: number,
-}
-
-export interface DemocracyStandardAccountVote {
- balance: bigint,
- vote: {
- aye: boolean,
- conviction: number,
- },
-}
js-packages/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/js-packages/playgrounds/unique.dev.ts
+++ /dev/null
@@ -1,1646 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-import '@unique/opal-types/augment-api.js';
-import '@unique/opal-types/augment-types.js';
-import '@unique/opal-types/types-lookup.js';
-
-import {stringToU8a} from '@polkadot/util';
-import {blake2AsHex, encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto';
-import type {ChainHelperBaseConstructor, UniqueHelperConstructor} from './unique.js';
-import {UniqueHelper, ChainHelperBase, HelperGroup} from './unique.js';
-import {ApiPromise, Keyring, WsProvider} from '@polkadot/api';
-import * as defs from '@unique/opal-types/definitions.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-import type {EventRecord} from '@polkadot/types/interfaces';
-import type {ICrossAccountId, ILogger, IPovInfo, ISchedulerOptions, ITransactionResult, TSigner} from './types.js';
-import type {FrameSystemEventRecord, StagingXcmV2TraitsError, StagingXcmV3TraitsOutcome} from '@polkadot/types/lookup';
-import type {SignerOptions, VoidFn} from '@polkadot/api/types';
-import {spawnSync} from 'child_process';
-import {AcalaHelper, AstarHelper, MoonbeamHelper, PolkadexHelper, RelayHelper, WestmintHelper, ForeignAssetsGroup, XcmGroup, XTokensGroup, TokensGroup, HydraDxHelper} from './unique.xcm.js';
-import {CollectiveGroup, CollectiveMembershipGroup, DemocracyGroup, RankedCollectiveGroup, ReferendaGroup} from './unique.governance.js';
-import type {ICollectiveGroup, IFellowshipGroup} from './unique.governance.js';
-
-export class SilentLogger {
- log(_msg: any, _level: any): void { }
- level = {
- ERROR: 'ERROR' as const,
- WARNING: 'WARNING' as const,
- INFO: 'INFO' as const,
- };
-}
-
-export class SilentConsole {
- // TODO: Remove, this is temporary: Filter unneeded API output
- // (Jaco promised it will be removed in the next version)
- consoleErr: any;
- consoleLog: any;
- consoleWarn: any;
-
- constructor() {
- this.consoleErr = console.error;
- this.consoleLog = console.log;
- this.consoleWarn = console.warn;
- }
-
- enable() {
- const outFn = (printer: any) => (...args: any[]) => {
- for(const arg of args) {
- if(typeof arg !== 'string')
- continue;
- const skippedWarnings = ['1000:: Normal connection closure', 'Not decorating unknown runtime apis:', 'RPC methods not decorated:', 'Not decorating runtime apis', 'Bad input data provided to validate_transaction', 'account balance too low', '1006:: Abnormal Closure'];
- const needToSkip = skippedWarnings.reduce((a, b) => a || arg.includes(b), false);
- if(needToSkip || arg === 'Normal connection closure')
- return;
- }
- printer(...args);
- };
-
- console.error = outFn(this.consoleErr.bind(console));
- console.log = outFn(this.consoleLog.bind(console));
- console.warn = outFn(this.consoleWarn.bind(console));
- }
-
- disable() {
- console.error = this.consoleErr;
- console.log = this.consoleLog;
- console.warn = this.consoleWarn;
- }
-}
-
-export interface IEventHelper {
- section(): string;
-
- method(): string;
-
- wrapEvent(data: any[]): any;
-}
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-function EventHelper(section: string, method: string, wrapEvent: (data: any[]) => any) {
- const helperClass = class implements IEventHelper {
- wrapEvent: (data: any[]) => any;
- _section: string;
- _method: string;
-
- constructor() {
- this.wrapEvent = wrapEvent;
- this._section = section;
- this._method = method;
- }
-
- section(): string {
- return this._section;
- }
-
- method(): string {
- return this._method;
- }
-
- filter(txres: ITransactionResult) {
- return txres.result.events.filter(e => e.event.section === section && e.event.method === method)
- .map(e => this.wrapEvent(e.event.data));
- }
-
- find(txres: ITransactionResult) {
- const e = txres.result.events.find(e => e.event.section === section && e.event.method === method);
- return e ? this.wrapEvent(e.event.data) : null;
- }
-
- expect(txres: ITransactionResult) {
- const e = this.find(txres);
- if(e) {
- return e;
- } else {
- throw Error(`Expected event ${section}.${method}`);
- }
- }
- };
-
- return helperClass;
-}
-
-function eventJsonData<T = any>(data: any[], index: number) {
- return data[index].toJSON() as T;
-}
-
-function eventHumanData(data: any[], index: number) {
- return data[index].toHuman();
-}
-
-function eventData<T = any>(data: any[], index: number) {
- return data[index] as T;
-}
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-function EventSection(section: string) {
- return class Section {
- static section = section;
-
- static Method(name: string, wrapEvent: (data: any[]) => any = () => {}) {
- const helperClass = EventHelper(Section.section, name, wrapEvent);
- return new helperClass();
- }
- };
-}
-
-function schedulerSection(schedulerInstance: string) {
- return class extends EventSection(schedulerInstance) {
- static Dispatched = this.Method('Dispatched', data => ({
- task: eventJsonData(data, 0),
- id: eventHumanData(data, 1),
- result: data[2],
- }));
-
- static PriorityChanged = this.Method('PriorityChanged', data => ({
- task: eventJsonData(data, 0),
- priority: eventJsonData(data, 1),
- }));
- };
-}
-
-export class Event {
- static Democracy = class extends EventSection('democracy') {
- static Proposed = this.Method('Proposed', data => ({
- proposalIndex: eventJsonData<number>(data, 0),
- }));
-
- static ExternalTabled = this.Method('ExternalTabled');
-
- static Started = this.Method('Started', data => ({
- referendumIndex: eventJsonData<number>(data, 0),
- threshold: eventHumanData(data, 1),
- }));
-
- static Voted = this.Method('Voted', data => ({
- voter: eventJsonData(data, 0),
- referendumIndex: eventJsonData<number>(data, 1),
- vote: eventJsonData(data, 2),
- }));
-
- static Passed = this.Method('Passed', data => ({
- referendumIndex: eventJsonData<number>(data, 0),
- }));
-
- static ProposalCanceled = this.Method('ProposalCanceled', data => ({
- propIndex: eventJsonData<number>(data, 0),
- }));
-
- static Cancelled = this.Method('Cancelled', data => ({
- propIndex: eventJsonData<number>(data, 0),
- }));
-
- static Vetoed = this.Method('Vetoed', data => ({
- who: eventHumanData(data, 0),
- proposalHash: eventHumanData(data, 1),
- until: eventJsonData<number>(data, 1),
- }));
- };
-
- static Council = class extends EventSection('council') {
- static Proposed = this.Method('Proposed', data => ({
- account: eventHumanData(data, 0),
- proposalIndex: eventJsonData<number>(data, 1),
- proposalHash: eventHumanData(data, 2),
- threshold: eventJsonData<number>(data, 3),
- }));
- static Closed = this.Method('Closed', data => ({
- proposalHash: eventHumanData(data, 0),
- yes: eventJsonData<number>(data, 1),
- no: eventJsonData<number>(data, 2),
- }));
- static Executed = this.Method('Executed', data => ({
- proposalHash: eventHumanData(data, 0),
- }));
- };
-
- static TechnicalCommittee = class extends EventSection('technicalCommittee') {
- static Proposed = this.Method('Proposed', data => ({
- account: eventHumanData(data, 0),
- proposalIndex: eventJsonData<number>(data, 1),
- proposalHash: eventHumanData(data, 2),
- threshold: eventJsonData<number>(data, 3),
- }));
- static Closed = this.Method('Closed', data => ({
- proposalHash: eventHumanData(data, 0),
- yes: eventJsonData<number>(data, 1),
- no: eventJsonData<number>(data, 2),
- }));
- static Approved = this.Method('Approved', data => ({
- proposalHash: eventHumanData(data, 0),
- }));
- static Executed = this.Method('Executed', data => ({
- proposalHash: eventHumanData(data, 0),
- result: eventHumanData(data, 1),
- }));
- };
-
- static FellowshipReferenda = class extends EventSection('fellowshipReferenda') {
- static Submitted = this.Method('Submitted', data => ({
- referendumIndex: eventJsonData<number>(data, 0),
- trackId: eventJsonData<number>(data, 1),
- proposal: eventJsonData(data, 2),
- }));
-
- static Cancelled = this.Method('Cancelled', data => ({
- index: eventJsonData<number>(data, 0),
- tally: eventJsonData(data, 1),
- }));
- };
-
- static UniqueScheduler = schedulerSection('uniqueScheduler');
- static Scheduler = schedulerSection('scheduler');
-
- static XcmpQueue = class extends EventSection('xcmpQueue') {
- static XcmpMessageSent = this.Method('XcmpMessageSent', data => ({
- messageHash: eventJsonData(data, 0),
- }));
-
- static Success = this.Method('Success', data => ({
- messageHash: eventJsonData(data, 0),
- }));
-
- static Fail = this.Method('Fail', data => ({
- messageHash: eventJsonData(data, 0),
- outcome: eventData<StagingXcmV2TraitsError>(data, 2),
- }));
- };
-
- static DmpQueue = class extends EventSection('dmpQueue') {
- static ExecutedDownward = this.Method('ExecutedDownward', data => ({
- outcome: eventData<StagingXcmV3TraitsOutcome>(data, 2),
- }));
- };
-}
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {
- return class extends Base {
- constructor(...args: any[]) {
- super(...args);
- }
-
- override async executeExtrinsic(
- sender: IKeyringPair,
- extrinsic: string,
- params: any[],
- expectSuccess?: boolean,
- options: Partial<SignerOptions> | null = null,
- ): Promise<ITransactionResult> {
- const call = this.constructApiCall(extrinsic, params);
- const result = await super.executeExtrinsic(
- sender,
- 'api.tx.sudo.sudo',
- [call],
- expectSuccess,
- options,
- );
-
- if(result.status === 'Fail') return result;
-
- const data = (result.result.events.find(x => x.event.section == 'sudo' && x.event.method == 'Sudid')?.event.data as any).sudoResult;
- if(data.isErr) {
- if(data.asErr.isModule) {
- const error = (result.result.events[1].event.data as any).sudoResult.asErr.asModule;
- const metaError = super.getApi()?.registry.findMetaError(error);
- throw new Error(`${metaError.section}.${metaError.name}`);
- } else if(data.asErr.isToken) {
- throw new Error(`Token: ${data.asErr.asToken}`);
- }
- // May be [object Object] in case of unhandled non-unit enum
- throw new Error(`Misc: ${data.asErr.toHuman()}`);
- }
- return result;
- }
- override async executeExtrinsicUncheckedWeight(
- sender: IKeyringPair,
- extrinsic: string,
- params: any[],
- expectSuccess?: boolean,
- options: Partial<SignerOptions> | null = null,
- ): Promise<ITransactionResult> {
- const call = this.constructApiCall(extrinsic, params);
- const result = await super.executeExtrinsic(
- sender,
- 'api.tx.sudo.sudoUncheckedWeight',
- [call, {refTime: 0, proofSize: 0}],
- expectSuccess,
- options,
- );
-
- if(result.status === 'Fail') return result;
-
- const data = (result.result.events.find(x => x.event.section == 'sudo' && x.event.method == 'Sudid')?.event.data as any).sudoResult;
- if(data.isErr) {
- if(data.asErr.isModule) {
- const error = (result.result.events[1].event.data as any).sudoResult.asErr.asModule;
- const metaError = super.getApi()?.registry.findMetaError(error);
- throw new Error(`${metaError.section}.${metaError.name}`);
- } else if(data.asErr.isToken) {
- throw new Error(`Token: ${data.asErr.asToken}`);
- }
- // May be [object Object] in case of unhandled non-unit enum
- throw new Error(`Misc: ${data.asErr.toHuman()}`);
- }
- return result;
- }
- };
-}
-
-class SchedulerGroup extends HelperGroup<UniqueHelper> {
- constructor(helper: UniqueHelper) {
- super(helper);
- }
-
- cancelScheduled(signer: TSigner, scheduledId: string) {
- return this.helper.executeExtrinsic(
- signer,
- 'api.tx.scheduler.cancelNamed',
- [scheduledId],
- true,
- );
- }
-
- changePriority(signer: TSigner, scheduledId: string, priority: number) {
- return this.helper.executeExtrinsic(
- signer,
- 'api.tx.scheduler.changeNamedPriority',
- [scheduledId, priority],
- true,
- );
- }
-
- scheduleAt<T extends DevUniqueHelper>(
- executionBlockNumber: number,
- options: ISchedulerOptions = {},
- ) {
- return this.schedule<T>('schedule', executionBlockNumber, options);
- }
-
- scheduleAfter<T extends DevUniqueHelper>(
- blocksBeforeExecution: number,
- options: ISchedulerOptions = {},
- ) {
- return this.schedule<T>('scheduleAfter', blocksBeforeExecution, options);
- }
-
- schedule<T extends UniqueHelper>(
- scheduleFn: 'schedule' | 'scheduleAfter',
- blocksNum: number,
- options: ISchedulerOptions = {},
- ) {
- // eslint-disable-next-line @typescript-eslint/naming-convention
- const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);
- return this.helper.clone(ScheduledHelperType, {
- scheduleFn,
- blocksNum,
- options,
- }) as T;
- }
-}
-
-class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {
- //todo:collator documentation
- addInvulnerable(signer: TSigner, address: string) {
- return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.addInvulnerable', [address]);
- }
-
- removeInvulnerable(signer: TSigner, address: string) {
- return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.removeInvulnerable', [address]);
- }
-
- async getInvulnerables(): Promise<string[]> {
- return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());
- }
-
- /** and also total max invulnerables */
- maxCollators(): number {
- return (this.helper.getApi().consts.configuration.defaultCollatorSelectionMaxCollators.toJSON() as number);
- }
-
- async getDesiredCollators(): Promise<number> {
- return (await this.helper.callRpc('api.query.configuration.collatorSelectionDesiredCollatorsOverride')).toNumber();
- }
-
- setLicenseBond(signer: TSigner, amount: bigint) {
- return this.helper.executeExtrinsic(signer, 'api.tx.configuration.setCollatorSelectionLicenseBond', [amount]);
- }
-
- async getLicenseBond(): Promise<bigint> {
- return (await this.helper.callRpc('api.query.configuration.collatorSelectionLicenseBondOverride')).toBigInt();
- }
-
- obtainLicense(signer: TSigner) {
- return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.getLicense', []);
- }
-
- releaseLicense(signer: TSigner) {
- return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.releaseLicense', []);
- }
-
- forceReleaseLicense(signer: TSigner, released: string) {
- return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.forceReleaseLicense', [released]);
- }
-
- async hasLicense(address: string): Promise<bigint> {
- return (await this.helper.callRpc('api.query.collatorSelection.licenseDepositOf', [address])).toBigInt();
- }
-
- onboard(signer: TSigner) {
- return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.onboard', []);
- }
-
- offboard(signer: TSigner) {
- return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.offboard', []);
- }
-
- async getCandidates(): Promise<string[]> {
- return (await this.helper.callRpc('api.query.collatorSelection.candidates')).map((x: any) => x.toHuman());
- }
-}
-
-export class DevUniqueHelper extends UniqueHelper {
- /**
- * Arrange methods for tests
- */
- arrange: ArrangeGroup;
- wait: WaitGroup;
- admin: AdminGroup;
- session: SessionGroup;
- testUtils: TestUtilGroup;
- foreignAssets: ForeignAssetsGroup;
- xcm: XcmGroup<DevUniqueHelper>;
- xTokens: XTokensGroup<DevUniqueHelper>;
- tokens: TokensGroup<DevUniqueHelper>;
- scheduler: SchedulerGroup;
- collatorSelection: CollatorSelectionGroup;
- council: ICollectiveGroup;
- technicalCommittee: ICollectiveGroup;
- fellowship: IFellowshipGroup;
- democracy: DemocracyGroup;
-
- constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
- options.helperBase = options.helperBase ?? DevUniqueHelper;
-
- super(logger, options);
- this.arrange = new ArrangeGroup(this);
- this.wait = new WaitGroup(this);
- this.admin = new AdminGroup(this);
- this.testUtils = new TestUtilGroup(this);
- this.session = new SessionGroup(this);
- this.foreignAssets = new ForeignAssetsGroup(this);
- this.xcm = new XcmGroup(this, 'polkadotXcm');
- this.xTokens = new XTokensGroup(this);
- this.tokens = new TokensGroup(this);
- this.scheduler = new SchedulerGroup(this);
- this.collatorSelection = new CollatorSelectionGroup(this);
- this.council = {
- collective: new CollectiveGroup(this, 'council'),
- membership: new CollectiveMembershipGroup(this, 'councilMembership'),
- };
- this.technicalCommittee = {
- collective: new CollectiveGroup(this, 'technicalCommittee'),
- membership: new CollectiveMembershipGroup(this, 'technicalCommitteeMembership'),
- };
- this.fellowship = {
- collective: new RankedCollectiveGroup(this, 'fellowshipCollective'),
- referenda: new ReferendaGroup(this, 'fellowshipReferenda'),
- };
- this.democracy = new DemocracyGroup(this);
- }
-
- override async connect(wsEndpoint: string, _listeners?: any): Promise<void> {
- if(!wsEndpoint) throw new Error('wsEndpoint was not set');
- const wsProvider = new WsProvider(wsEndpoint);
- this.api = new ApiPromise({
- provider: wsProvider,
- signedExtensions: {
- ContractHelpers: {
- extrinsic: {},
- payload: {},
- },
- CheckMaintenance: {
- extrinsic: {},
- payload: {},
- },
- DisableIdentityCalls: {
- extrinsic: {},
- payload: {},
- },
- FakeTransactionFinalizer: {
- extrinsic: {},
- payload: {},
- },
- },
- rpc: {
- unique: defs.unique.rpc,
- appPromotion: defs.appPromotion.rpc,
- povinfo: defs.povinfo.rpc,
- eth: {
- feeHistory: {
- description: 'Dummy',
- params: [],
- type: 'u8',
- },
- maxPriorityFeePerGas: {
- description: 'Dummy',
- params: [],
- type: 'u8',
- },
- },
- },
- });
- await this.api.isReadyOrError;
- this.network = await UniqueHelper.detectNetwork(this.api);
- this.wsEndpoint = wsEndpoint;
- }
- getSudo<T extends DevUniqueHelper>() {
- // eslint-disable-next-line @typescript-eslint/naming-convention
- const SudoHelperType = SudoHelper(this.helperBase);
- return this.clone(SudoHelperType) as T;
- }
-}
-
-export class DevRelayHelper extends RelayHelper {
- wait: WaitGroup;
-
- constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
- options.helperBase = options.helperBase ?? DevRelayHelper;
-
- super(logger, options);
- this.wait = new WaitGroup(this);
- }
-
- getSudo() {
- // eslint-disable-next-line @typescript-eslint/naming-convention
- const SudoHelperType = SudoHelper(this.helperBase);
- return this.clone(SudoHelperType) as DevRelayHelper;
- }
-}
-
-export class DevWestmintHelper extends WestmintHelper {
- wait: WaitGroup;
-
- constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
- options.helperBase = options.helperBase ?? DevWestmintHelper;
-
- super(logger, options);
- this.wait = new WaitGroup(this);
- }
-}
-
-export class DevStatemineHelper extends DevWestmintHelper {}
-
-export class DevStatemintHelper extends DevWestmintHelper {}
-
-export class DevMoonbeamHelper extends MoonbeamHelper {
- account: MoonbeamAccountGroup;
- wait: WaitGroup;
- fastDemocracy: MoonbeamFastDemocracyGroup;
-
- constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
- options.helperBase = options.helperBase ?? DevMoonbeamHelper;
- options.notePreimagePallet = options.notePreimagePallet ?? 'preimage';
-
- super(logger, options);
- this.account = new MoonbeamAccountGroup(this);
- this.wait = new WaitGroup(this);
- this.fastDemocracy = new MoonbeamFastDemocracyGroup(this);
- }
-}
-
-export class DevMoonriverHelper extends DevMoonbeamHelper {
- constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
- options.notePreimagePallet = options.notePreimagePallet ?? 'preimage';
- super(logger, options);
- }
-}
-
-export class DevAstarHelper extends AstarHelper {
- wait: WaitGroup;
-
- constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
- options.helperBase = options.helperBase ?? DevAstarHelper;
-
- super(logger, options);
- this.wait = new WaitGroup(this);
- }
-
- getSudo<T extends AstarHelper>() {
- // eslint-disable-next-line @typescript-eslint/naming-convention
- const SudoHelperType = SudoHelper(this.helperBase);
- return this.clone(SudoHelperType) as T;
- }
-}
-
-export class DevShidenHelper extends DevAstarHelper { }
-
-export class DevAcalaHelper extends AcalaHelper {
- wait: WaitGroup;
-
- constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
- options.helperBase = options.helperBase ?? DevAcalaHelper;
-
- super(logger, options);
- this.wait = new WaitGroup(this);
- }
- getSudo() {
- // eslint-disable-next-line @typescript-eslint/naming-convention
- const SudoHelperType = SudoHelper(this.helperBase);
- return this.clone(SudoHelperType) as DevAcalaHelper;
- }
-}
-
-export class DevPolkadexHelper extends PolkadexHelper {
- wait: WaitGroup;
- constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
- options.helperBase = options.helperBase ?? PolkadexHelper;
-
- super(logger, options);
- this.wait = new WaitGroup(this);
- }
-
- getSudo() {
- // eslint-disable-next-line @typescript-eslint/naming-convention
- const SudoHelperType = SudoHelper(this.helperBase);
- return this.clone(SudoHelperType) as DevPolkadexHelper;
- }
-}
-
-export class DevHydraDxHelper extends HydraDxHelper {
- wait: WaitGroup;
- fastDemocracy: HydraFastDemocracyGroup;
-
- constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
- options.helperBase = options.helperBase ?? DevHydraDxHelper;
-
- super(logger, options);
-
- this.wait = new WaitGroup(this);
- this.fastDemocracy = new HydraFastDemocracyGroup(this);
- }
-}
-
-export class DevKaruraHelper extends DevAcalaHelper {}
-
-export class ArrangeGroup {
- helper: DevUniqueHelper;
-
- scheduledIdSlider = 0;
-
- constructor(helper: DevUniqueHelper) {
- this.helper = helper;
- }
-
- /**
- * Generates accounts with the specified UNQ token balance
- * @param balances balances for generated accounts. Each balance will be multiplied by the token nominal.
- * @param donor donor account for balances
- * @returns array of newly created accounts
- * @example const [acc1, acc2, acc3] = await createAccounts([0n, 10n, 20n], donor);
- */
- createAccounts = async (balances: bigint[], donor: IKeyringPair): Promise<IKeyringPair[]> => {
- let nonce = await this.helper.chain.getNonce(donor.address);
- const wait = new WaitGroup(this.helper);
- const ss58Format = this.helper.chain.getChainProperties().ss58Format;
- const tokenNominal = this.helper.balance.getOneTokenNominal();
- const transactions = [];
- const accounts: IKeyringPair[] = [];
- for(const balance of balances) {
- const recipient = this.helper.util.fromSeed(mnemonicGenerate(), ss58Format);
- accounts.push(recipient);
- if(balance !== 0n) {
- const tx = this.helper.constructApiCall('api.tx.balances.transferKeepAlive', [{Id: recipient.address}, balance * tokenNominal]);
- transactions.push(this.helper.signTransaction(donor, tx, {nonce}, 'account generation'));
- nonce++;
- }
- }
-
- await Promise.all(transactions).catch(_e => {});
-
- //#region TODO remove this region, when nonce problem will be solved
- const checkBalances = async () => {
- let isSuccess = true;
- for(let i = 0; i < balances.length; i++) {
- const balance = await this.helper.balance.getSubstrate(accounts[i].address);
- if(balance !== balances[i] * tokenNominal) {
- isSuccess = false;
- break;
- }
- }
- return isSuccess;
- };
-
- let accountsCreated = false;
- const maxBlocksChecked = await this.helper.arrange.isDevNode() ? 50 : 5;
- // checkBalances retry up to 5-50 blocks
- for(let index = 0; index < maxBlocksChecked; index++) {
- accountsCreated = await checkBalances();
- if(accountsCreated) break;
- await wait.newBlocks(1);
- }
-
- if(!accountsCreated) throw Error('Accounts generation failed');
- //#endregion
-
- return accounts;
- };
-
- // TODO combine this method and createAccounts into one
- createCrowd = async (accountsToCreate: number, withBalance: bigint, donor: IKeyringPair): Promise<IKeyringPair[]> => {
- const createAsManyAsCan = async () => {
- let transactions: any = [];
- const accounts: IKeyringPair[] = [];
- let nonce = await this.helper.chain.getNonce(donor.address);
- const tokenNominal = this.helper.balance.getOneTokenNominal();
- const ss58Format = this.helper.chain.getChainProperties().ss58Format;
- for(let i = 0; i < accountsToCreate; i++) {
- if(i === 500) { // if there are too many accounts to create
- await Promise.allSettled(transactions); // wait while first 500 (should be 100 for devnode) tx will be settled
- transactions = []; //
- nonce = await this.helper.chain.getNonce(donor.address); // update nonce
- }
- const recipient = this.helper.util.fromSeed(mnemonicGenerate(), ss58Format);
- accounts.push(recipient);
- if(withBalance !== 0n) {
- const tx = this.helper.constructApiCall('api.tx.balances.transferKeepAlive', [{Id: recipient.address}, withBalance * tokenNominal]);
- transactions.push(this.helper.signTransaction(donor, tx, {nonce}, 'account generation'));
- nonce++;
- }
- }
-
- const fullfilledAccounts = [];
- await Promise.allSettled(transactions);
- for(const account of accounts) {
- const accountBalance = await this.helper.balance.getSubstrate(account.address);
- if(accountBalance === withBalance * tokenNominal) {
- fullfilledAccounts.push(account);
- }
- }
- return fullfilledAccounts;
- };
-
-
- const crowd: IKeyringPair[] = [];
- // do up to 5 retries
- for(let index = 0; index < 5 && accountsToCreate !== 0; index++) {
- const asManyAsCan = await createAsManyAsCan();
- crowd.push(...asManyAsCan);
- accountsToCreate -= asManyAsCan.length;
- }
-
- if(accountsToCreate !== 0) throw Error(`Crowd generation failed: ${accountsToCreate} accounts left`);
-
- return crowd;
- };
-
- /**
- * Generates one account with zero balance
- * @returns the newly generated account
- * @example const account = await helper.arrange.createEmptyAccount();
- */
- createEmptyAccount = (): IKeyringPair => {
- const ss58Format = this.helper.chain.getChainProperties().ss58Format;
- return this.helper.util.fromSeed(mnemonicGenerate(), ss58Format);
- };
-
- isDevNode = async () => {
- let blockNumber = (await this.helper.callRpc('api.query.system.number')).toJSON();
- if(blockNumber == 0) {
- await this.helper.wait.newBlocks(1);
- blockNumber = (await this.helper.callRpc('api.query.system.number')).toJSON();
- }
- const block2 = await this.helper.callRpc('api.rpc.chain.getBlock', [await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])]);
- const block1 = await this.helper.callRpc('api.rpc.chain.getBlock', [await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber - 1])]);
- const findCreationDate = (block: any) => {
- const humanBlock = block.toHuman();
- let date;
- humanBlock.block.extrinsics.forEach((ext: any) => {
- if(ext.method.section === 'timestamp') {
- date = Number(ext.method.args.now.replaceAll(',', ''));
- }
- });
- return date;
- };
- const block1date = await findCreationDate(block1);
- const block2date = await findCreationDate(block2);
- if(block2date! - block1date! < 9000) return true;
- return false;
- };
-
- async calculcateFee(payer: ICrossAccountId, promise: () => Promise<any>): Promise<bigint> {
- const address = 'Substrate' in payer ? payer.Substrate : this.helper.address.ethToSubstrate(payer.Ethereum);
- let balance = await this.helper.balance.getSubstrate(address);
-
- await promise();
-
- balance -= await this.helper.balance.getSubstrate(address);
-
- return balance;
- }
-
- async calculatePoVInfo(txs: any[]): Promise<IPovInfo> {
- const rawPovInfo = await this.helper.callRpc('api.rpc.povinfo.estimateExtrinsicPoV', [txs]);
-
- const kvJson: {[key: string]: string} = {};
-
- for(const kv of rawPovInfo.keyValues) {
- kvJson[kv.key.toHex()] = kv.value.toHex();
- }
-
- const kvStr = JSON.stringify(kvJson);
-
- const chainql = spawnSync(
- 'chainql',
- [
- `--tla-code=data=${kvStr}`,
- '-e', `function(data) cql.dump(cql.chain("${this.helper.getEndpoint()}").latest._meta, data, {omit_empty:true})`,
- ],
- );
-
- if(!chainql.stdout) {
- throw Error('unable to get an output from the `chainql`');
- }
-
- return {
- proofSize: rawPovInfo.proofSize.toNumber(),
- compactProofSize: rawPovInfo.compactProofSize.toNumber(),
- compressedProofSize: rawPovInfo.compressedProofSize.toNumber(),
- results: rawPovInfo.results,
- kv: JSON.parse(chainql.stdout.toString()),
- };
- }
-
- calculatePalletAddress(palletId: any) {
- const address = stringToU8a(('modl' + palletId).padEnd(32, '\0'));
- return encodeAddress(address, this.helper.chain.getChainProperties().ss58Format);
- }
-
- makeScheduledIds(num: number): string[] {
- function makeId(slider: number) {
- const scheduledIdSize = 64;
- const hexId = slider.toString(16);
- const prefixSize = scheduledIdSize - hexId.length;
-
- const scheduledId = '0x' + '0'.repeat(prefixSize) + hexId;
-
- return scheduledId;
- }
-
- const ids = [];
- for(let i = 0; i < num; i++) {
- ids.push(makeId(this.scheduledIdSlider));
- this.scheduledIdSlider += 1;
- }
-
- return ids;
- }
-
- makeScheduledId(): string {
- return (this.makeScheduledIds(1))[0];
- }
-
- async captureEvents(eventSection: string, eventMethod: string): Promise<EventCapture> {
- const capture = new EventCapture(this.helper, eventSection, eventMethod);
- await capture.startCapture();
-
- return capture;
- }
-
- makeXcmProgramWithdrawDeposit(beneficiary: Uint8Array, id: any, amount: bigint) {
- return {
- V2: [
- {
- WithdrawAsset: [
- {
- id,
- fun: {
- Fungible: amount,
- },
- },
- ],
- },
- {
- BuyExecution: {
- fees: {
- id,
- fun: {
- Fungible: amount,
- },
- },
- weightLimit: 'Unlimited',
- },
- },
- {
- DepositAsset: {
- assets: {
- Wild: 'All',
- },
- maxAssets: 1,
- beneficiary: {
- parents: 0,
- interior: {
- X1: {
- AccountId32: {
- network: 'Any',
- id: beneficiary,
- },
- },
- },
- },
- },
- },
- ],
- };
- }
-
- makeXcmProgramReserveAssetDeposited(beneficiary: Uint8Array, id: any, amount: bigint) {
- return {
- V2: [
- {
- ReserveAssetDeposited: [
- {
- id,
- fun: {
- Fungible: amount,
- },
- },
- ],
- },
- {
- BuyExecution: {
- fees: {
- id,
- fun: {
- Fungible: amount,
- },
- },
- weightLimit: 'Unlimited',
- },
- },
- {
- DepositAsset: {
- assets: {
- Wild: 'All',
- },
- maxAssets: 1,
- beneficiary: {
- parents: 0,
- interior: {
- X1: {
- AccountId32: {
- network: 'Any',
- id: beneficiary,
- },
- },
- },
- },
- },
- },
- ],
- };
- }
-
- makeUnpaidSudoTransactProgram(info: {weightMultiplier: number, call: string}) {
- return {
- V3: [
- {
- UnpaidExecution: {
- weightLimit: 'Unlimited',
- checkOrigin: null,
- },
- },
- {
- Transact: {
- originKind: 'Superuser',
- requireWeightAtMost: {
- refTime: info.weightMultiplier * 200000000,
- proofSize: info.weightMultiplier * 3000,
- },
- call: {
- encoded: info.call,
- },
- },
- },
- ],
- };
- }
-}
-
-class MoonbeamAccountGroup {
- helper: MoonbeamHelper;
-
- keyring: Keyring;
- _alithAccount: IKeyringPair;
- _baltatharAccount: IKeyringPair;
- _dorothyAccount: IKeyringPair;
-
- constructor(helper: MoonbeamHelper) {
- this.helper = helper;
-
- this.keyring = new Keyring({type: 'ethereum'});
- const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';
- const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';
- const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';
-
- this._alithAccount = this.keyring.addFromUri(alithPrivateKey, undefined, 'ethereum');
- this._baltatharAccount = this.keyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');
- this._dorothyAccount = this.keyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');
- }
-
- alithAccount() {
- return this._alithAccount;
- }
-
- baltatharAccount() {
- return this._baltatharAccount;
- }
-
- dorothyAccount() {
- return this._dorothyAccount;
- }
-
- create() {
- return this.keyring.addFromUri(mnemonicGenerate());
- }
-}
-
-class MoonbeamFastDemocracyGroup {
- helper: DevMoonbeamHelper;
-
- constructor(helper: DevMoonbeamHelper) {
- this.helper = helper;
- }
-
- async executeProposal(proposalDesciption: string, encodedProposal: string) {
- const proposalHash = blake2AsHex(encodedProposal);
-
- const alithAccount = this.helper.account.alithAccount();
- const baltatharAccount = this.helper.account.baltatharAccount();
- const dorothyAccount = this.helper.account.dorothyAccount();
-
- const councilVotingThreshold = 2;
- const technicalCommitteeThreshold = 2;
- const fastTrackVotingPeriod = 3;
- const fastTrackDelayPeriod = 0;
-
- console.log(`[democracy] executing '${proposalDesciption}' proposal`);
-
- // >>> Propose external motion through council >>>
- console.log('\t* Propose external motion through council.......');
- const externalMotion = this.helper.democracy.externalProposeMajority({Inline: encodedProposal});
- const encodedMotion = externalMotion?.method.toHex() || '';
- const motionHash = blake2AsHex(encodedMotion);
- console.log('\t* Motion hash is %s', motionHash);
-
- await this.helper.collective.council.propose(
- baltatharAccount,
- councilVotingThreshold,
- externalMotion,
- externalMotion.encodedLength,
- );
-
- const councilProposalIdx = await this.helper.collective.council.proposalCount() - 1;
- await this.helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);
- await this.helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);
-
- await this.helper.collective.council.close(
- dorothyAccount,
- motionHash,
- councilProposalIdx,
- {
- refTime: 1_000_000_000,
- proofSize: 1_000_000,
- },
- externalMotion.encodedLength,
- );
- console.log('\t* Propose external motion through council.......DONE');
- // <<< Propose external motion through council <<<
-
- // >>> Fast track proposal through technical committee >>>
- console.log('\t* Fast track proposal through technical committee.......');
- const fastTrack = this.helper.democracy.fastTrack(proposalHash, fastTrackVotingPeriod, fastTrackDelayPeriod);
- const encodedFastTrack = fastTrack?.method.toHex() || '';
- const fastTrackHash = blake2AsHex(encodedFastTrack);
- console.log('\t* FastTrack hash is %s', fastTrackHash);
-
- await this.helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);
-
- const techProposalIdx = await this.helper.collective.techCommittee.proposalCount() - 1;
- await this.helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);
- await this.helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);
-
- await this.helper.collective.techCommittee.close(
- baltatharAccount,
- fastTrackHash,
- techProposalIdx,
- {
- refTime: 1_000_000_000,
- proofSize: 1_000_000,
- },
- fastTrack.encodedLength,
- );
- console.log('\t* Fast track proposal through technical committee.......DONE');
- // <<< Fast track proposal through technical committee <<<
-
- const democracyStarted = await this.helper.wait.expectEvent(3, Event.Democracy.Started);
- const referendumIndex = democracyStarted.referendumIndex;
-
- // >>> Referendum voting >>>
- console.log(`\t* Referendum #${referendumIndex} voting.......`);
- await this.helper.democracy.referendumVote(dorothyAccount, referendumIndex, {
- balance: 10_000_000_000_000_000_000n,
- vote: {aye: true, conviction: 1},
- });
- console.log(`\t* Referendum #${referendumIndex} voting.......DONE`);
- // <<< Referendum voting <<<
-
- // Wait the proposal to pass
- await this.helper.wait.expectEvent(3, Event.Democracy.Passed, event => event.referendumIndex == referendumIndex);
-
- await this.helper.wait.newBlocks(1);
-
- console.log(`[democracy] executing '${proposalDesciption}' proposal.......DONE`);
- }
-}
-
-class HydraFastDemocracyGroup {
- helper: DevHydraDxHelper;
-
- constructor(helper: DevHydraDxHelper) {
- this.helper = helper;
- }
-
- async executeProposal(proposalDesciption: string, encodedProposal: string) {
- const proposalHash = blake2AsHex(encodedProposal);
- const aliceAccount = this.helper.util.fromSeed('//Alice');
- const bobAccount = this.helper.util.fromSeed('//Bob');
- const eveAccount = this.helper.util.fromSeed('//Eve');
-
- const councilVotingThreshold = 1;
- const technicalCommitteeThreshold = 3;
- const fastTrackVotingPeriod = 3;
- const fastTrackDelayPeriod = 0;
-
- console.log(`[democracy] executing '${proposalDesciption}' proposal`);
-
- // >>> Propose external motion through council >>>
- console.log('\t* Propose external motion through council.......');
- const externalMotion = this.helper.democracy.externalProposeMajority({Inline: encodedProposal});
- const encodedMotion = externalMotion?.method.toHex() || '';
- const motionHash = blake2AsHex(encodedMotion);
- console.log('\t* Motion hash is %s', motionHash);
-
- await this.helper.collective.council.propose(
- aliceAccount,
- councilVotingThreshold,
- externalMotion,
- externalMotion.encodedLength,
- );
-
- console.log('\t* Propose external motion through council.......DONE');
- // <<< Propose external motion through council <<<
-
- // >>> Fast track proposal through technical committee >>>
- console.log('\t* Fast track proposal through technical committee.......');
- const fastTrack = this.helper.democracy.fastTrack(proposalHash, fastTrackVotingPeriod, fastTrackDelayPeriod);
- const encodedFastTrack = fastTrack?.method.toHex() || '';
- const fastTrackHash = blake2AsHex(encodedFastTrack);
- console.log('\t* FastTrack hash is %s', fastTrackHash);
-
- await this.helper.collective.techCommittee.propose(aliceAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);
-
- const techProposalIdx = await this.helper.collective.techCommittee.proposalCount() - 1;
- await this.helper.collective.techCommittee.vote(aliceAccount, fastTrackHash, techProposalIdx, true);
- await this.helper.collective.techCommittee.vote(bobAccount, fastTrackHash, techProposalIdx, true);
- await this.helper.collective.techCommittee.vote(eveAccount, fastTrackHash, techProposalIdx, true);
-
- await this.helper.collective.techCommittee.close(
- bobAccount,
- fastTrackHash,
- techProposalIdx,
- {
- refTime: 1_000_000_000,
- proofSize: 1_000_000,
- },
- fastTrack.encodedLength,
- );
- console.log('\t* Fast track proposal through technical committee.......DONE');
- // <<< Fast track proposal through technical committee <<<
-
- const democracyStarted = await this.helper.wait.expectEvent(3, Event.Democracy.Started);
- const referendumIndex = democracyStarted.referendumIndex;
-
- // >>> Referendum voting >>>
- console.log(`\t* Referendum #${referendumIndex} voting.......`);
- await this.helper.democracy.referendumVote(eveAccount, referendumIndex, {
- balance: 10_000_000_000_000_000_000n,
- vote: {aye: true, conviction: 1},
- });
- console.log(`\t* Referendum #${referendumIndex} voting.......DONE`);
- // <<< Referendum voting <<<
-
- // Wait the proposal to pass
- await this.helper.wait.expectEvent(3, Event.Democracy.Passed, event => event.referendumIndex == referendumIndex);
-
- await this.helper.wait.newBlocks(1);
-
- console.log(`[democracy] executing '${proposalDesciption}' proposal.......DONE`);
- }
-}
-
-class WaitGroup {
- helper: ChainHelperBase;
-
- constructor(helper: ChainHelperBase) {
- this.helper = helper;
- }
-
- sleep(milliseconds: number) {
- return new Promise((resolve) => setTimeout(resolve, milliseconds));
- }
-
- private async waitWithTimeout(promise: Promise<any>, timeout: number) {
- let isBlock = false;
- promise.then(() => isBlock = true).catch(() => isBlock = true);
- let totalTime = 0;
- const step = 100;
- while(!isBlock) {
- await this.sleep(step);
- totalTime += step;
- if(totalTime >= timeout) throw Error('Blocks production failed');
- }
- return promise;
- }
-
- /**
- * Launch some async operation, or throw an error after some time. Note that it will still continue executing after the timeout.
- * @param promise async operation to race against the timeout
- * @param timeoutMS time after which to time out
- * @param timeoutError error message to throw
- * @returns promise of the same type the operation had
- */
- withTimeout<T>(
- promise: Promise<T>,
- timeoutMS = 30000,
- timeoutError = 'The operation has timed out!',
- ): Promise<T> {
- const timeout = new Promise<never>((_, reject) => {
- setTimeout(() => {
- reject(new Error(timeoutError));
- }, timeoutMS);
- });
-
- return Promise.race<T>([promise, timeout]).catch(e => {throw new Error(e);});
- }
-
- /**
- * Wait for specified number of blocks
- * @param blocksCount number of blocks to wait
- * @returns
- */
- async newBlocks(blocksCount = 1, timeout?: number): Promise<void> {
- timeout = timeout ?? blocksCount * 60_000;
- // eslint-disable-next-line no-async-promise-executor
- const promise = new Promise<void>(async (resolve) => {
- const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(() => {
- if(blocksCount > 0) {
- blocksCount--;
- } else {
- unsubscribe();
- resolve();
- }
- });
- });
- await this.waitWithTimeout(promise, timeout);
- return promise;
- }
-
- /**
- * Wait for the specified number of sessions to pass.
- * Only applicable if the Session pallet is turned on.
- * @param sessionCount number of sessions to wait
- * @param blockTimeout time in ms until panicking that the chain has stopped producing blocks
- * @returns
- */
- async newSessions(sessionCount = 1, blockTimeout = 60000): Promise<void> {
- console.log(`Waiting for ${sessionCount} new session${sessionCount > 1 ? 's' : ''}.`
- + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.');
-
- const expectedSessionIndex = await (this.helper as DevUniqueHelper).session.getIndex() + sessionCount;
- let currentSessionIndex = -1;
-
- while(currentSessionIndex < expectedSessionIndex) {
- // eslint-disable-next-line no-async-promise-executor
- currentSessionIndex = await this.withTimeout(new Promise(async (resolve) => {
- await this.newBlocks(1);
- const res = await (this.helper as DevUniqueHelper).session.getIndex();
- resolve(res);
- }), blockTimeout, 'The chain has stopped producing blocks!');
- }
- }
-
- async forParachainBlockNumber(blockNumber: bigint | number, timeout?: number) {
- timeout = timeout ?? 30 * 60 * 1000;
- // eslint-disable-next-line no-async-promise-executor
- const promise = new Promise<void>(async (resolve) => {
- const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads((data: any) => {
- if(data.number.toNumber() >= blockNumber) {
- unsubscribe();
- resolve();
- }
- });
- });
- await this.waitWithTimeout(promise, timeout);
- return promise;
- }
-
- async forRelayBlockNumber(blockNumber: bigint | number, timeout?: number) {
- timeout = timeout ?? 30 * 60 * 1000;
- // eslint-disable-next-line no-async-promise-executor
- const promise = new Promise<void>(async (resolve) => {
- const unsubscribe = await this.helper.getApi().query.parachainSystem.validationData((data: any) => {
- if(data.value.relayParentNumber.toNumber() >= blockNumber) {
- // @ts-ignore
- unsubscribe();
- resolve();
- }
- });
- });
- await this.waitWithTimeout(promise, timeout);
- return promise;
- }
-
- noScheduledTasks() {
- const api = this.helper.getApi();
-
- // eslint-disable-next-line no-async-promise-executor
- const promise = new Promise<void>(async resolve => {
- const unsubscribe = await api.rpc.chain.subscribeNewHeads(async () => {
- const areThereScheduledTasks = await api.query.scheduler.lookup.entries();
-
- if(areThereScheduledTasks.length == 0) {
- unsubscribe();
- resolve();
- }
- });
- });
-
- return promise;
- }
-
- parachainBlockMultiplesOf(val: bigint) {
- // eslint-disable-next-line no-async-promise-executor
- const promise = new Promise<void>(async resolve => {
- const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads((data: any) => {
- if(data.number.toBigInt() % val == 0n) {
- console.log(`from waiter: ${data.number.toBigInt()}`);
- unsubscribe();
- resolve();
- }
- });
- });
- return promise;
- }
-
- event<T extends IEventHelper>(
- maxBlocksToWait: number,
- eventHelper: T,
- filter: (_: any) => boolean = () => true,
- ): any {
- // eslint-disable-next-line no-async-promise-executor
- const promise = new Promise<T | null>(async (resolve) => {
- const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(async header => {
- const blockNumber = header.number.toJSON();
- const blockHash = header.hash;
- const eventIdStr = `${eventHelper.section()}.${eventHelper.method()}`;
- const waitLimitStr = `wait blocks remaining: ${maxBlocksToWait}`;
-
- this.helper.logger.log(`[Block #${blockNumber}] Waiting for event \`${eventIdStr}\` (${waitLimitStr})`);
-
- const apiAt = await this.helper.getApi().at(blockHash);
- const eventRecords = (await apiAt.query.system.events()) as any;
-
- const neededEvent = eventRecords.toArray()
- .filter((r: FrameSystemEventRecord) => r.event.section == eventHelper.section() && r.event.method == eventHelper.method())
- .map((r: FrameSystemEventRecord) => eventHelper.wrapEvent(r.event.data))
- .find(filter);
-
- if(neededEvent) {
- unsubscribe();
- resolve(neededEvent);
- } else if(maxBlocksToWait > 0) {
- maxBlocksToWait--;
- } else {
- this.helper.logger.log(`Eligible event \`${eventIdStr}\` is NOT found.
- The wait lasted until block ${blockNumber} inclusive`);
- unsubscribe();
- resolve(null);
- }
- });
- });
- return promise;
- }
-
- async expectEvent<T extends IEventHelper>(
- maxBlocksToWait: number,
- eventHelper: T,
- filter: (e: any) => boolean = () => true,
- ) {
- const e = await this.event(maxBlocksToWait, eventHelper, filter);
- if(e == null) {
- throw Error(`The event '${eventHelper.section()}.${eventHelper.method()}' is expected`);
- } else {
- return e;
- }
- }
-}
-
-class SessionGroup {
- helper: ChainHelperBase;
-
- constructor(helper: ChainHelperBase) {
- this.helper = helper;
- }
-
- //todo:collator documentation
- async getIndex(): Promise<number> {
- return (await this.helper.callRpc('api.query.session.currentIndex', [])).toNumber();
- }
-
- newSessions(sessionCount = 1, blockTimeout = 24000): Promise<void> {
- return (this.helper as DevUniqueHelper).wait.newSessions(sessionCount, blockTimeout);
- }
-
- setOwnKeys(signer: TSigner, key: string) {
- return this.helper.executeExtrinsic(
- signer,
- 'api.tx.session.setKeys',
- [key, '0x0'],
- true,
- );
- }
-
- setOwnKeysFromAddress(signer: TSigner) {
- return this.setOwnKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex'));
- }
-}
-
-class TestUtilGroup {
- helper: DevUniqueHelper;
-
- constructor(helper: DevUniqueHelper) {
- this.helper = helper;
- }
-
- async enable(testUtilsPalletName: string) {
- if(this.helper.fetchMissingPalletNames([testUtilsPalletName]).length != 0) {
- return;
- }
-
- const signer = this.helper.util.fromSeed('//Alice');
- await this.helper.getSudo<DevUniqueHelper>().executeExtrinsic(signer, 'api.tx.testUtils.enable', [], true);
- }
-
- async setTestValue(signer: TSigner, testVal: number) {
- await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.setTestValue', [testVal], true);
- }
-
- async incTestValue(signer: TSigner) {
- await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.incTestValue', [], true);
- }
-
- async setTestValueAndRollback(signer: TSigner, testVal: number) {
- await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.setTestValueAndRollback', [testVal], true);
- }
-
- async testValue(blockIdx?: number) {
- const api = blockIdx
- ? await this.helper.getApi().at(await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockIdx]))
- : this.helper.getApi();
-
- return (await api.query.testUtils.testValue()).toJSON();
- }
-
- async justTakeFee(signer: TSigner) {
- await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.justTakeFee', [], true);
- }
-
- async selfCancelingInc(signer: TSigner, scheduledId: string, maxTestVal: number) {
- await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.selfCancelingInc', [scheduledId, maxTestVal], true);
- }
-}
-
-class EventCapture {
- helper: DevUniqueHelper;
- eventSection: string;
- eventMethod: string;
- events: EventRecord[] = [];
- unsubscribe: VoidFn | null = null;
-
- constructor(
- helper: DevUniqueHelper,
- eventSection: string,
- eventMethod: string,
- ) {
- this.helper = helper;
- this.eventSection = eventSection;
- this.eventMethod = eventMethod;
- }
-
- async startCapture() {
- this.stopCapture();
- this.unsubscribe = (await this.helper.getApi().query.system.events((eventRecords: FrameSystemEventRecord[]) => {
- const newEvents = eventRecords.filter(r => r.event.section == this.eventSection && r.event.method == this.eventMethod);
-
- this.events.push(...newEvents);
- })) as any;
- }
-
- stopCapture() {
- if(this.unsubscribe !== null) {
- this.unsubscribe();
- }
- }
-
- extractCapturedEvents() {
- return this.events;
- }
-}
-
-class AdminGroup {
- helper: UniqueHelper;
-
- constructor(helper: UniqueHelper) {
- this.helper = helper;
- }
-
- async payoutStakers(signer: IKeyringPair, stakersToPayout: number): Promise<{staker: string, stake: bigint, payout: bigint}[]> {
- const payoutResult = await this.helper.executeExtrinsic(signer, 'api.tx.appPromotion.payoutStakers', [stakersToPayout], true);
- return payoutResult.result.events.filter(e => e.event.method === 'StakingRecalculation').map(e => ({
- staker: e.event.data[0].toString(),
- stake: e.event.data[1].toBigInt(),
- payout: e.event.data[2].toBigInt(),
- }));
- }
-}
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {
- return class extends Base {
- scheduleFn: 'schedule' | 'scheduleAfter';
- blocksNum: number;
- options: ISchedulerOptions;
-
- constructor(...args: any[]) {
- const logger = args[0] as ILogger;
- const options = args[1] as {
- scheduleFn: 'schedule' | 'scheduleAfter',
- blocksNum: number,
- options: ISchedulerOptions
- };
-
- super(logger);
-
- this.scheduleFn = options.scheduleFn;
- this.blocksNum = options.blocksNum;
- this.options = options.options;
- }
-
- override executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {
- const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);
-
- const mandatorySchedArgs = [
- this.blocksNum,
- this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,
- this.options.priority ?? null,
- scheduledTx,
- ];
-
- let schedArgs;
- let scheduleFn;
-
- if(this.options.scheduledId) {
- schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs];
-
- if(this.scheduleFn == 'schedule') {
- scheduleFn = 'scheduleNamed';
- } else if(this.scheduleFn == 'scheduleAfter') {
- scheduleFn = 'scheduleNamedAfter';
- }
- } else {
- schedArgs = mandatorySchedArgs;
- scheduleFn = this.scheduleFn;
- }
-
- const extrinsic = 'api.tx.scheduler.' + scheduleFn;
-
- return super.executeExtrinsic(
- sender,
- extrinsic as any,
- schedArgs,
- expectSuccess,
- );
- }
- };
-}
js-packages/playgrounds/unique.governance.tsdiffbeforeafterboth--- a/js-packages/playgrounds/unique.governance.ts
+++ /dev/null
@@ -1,531 +0,0 @@
-import {blake2AsHex} from '@polkadot/util-crypto';
-import type {PalletDemocracyConviction} from '@polkadot/types/lookup';
-import type {IPhasicEvent, TSigner} from './types.js';
-import {HelperGroup, UniqueHelper} from './unique.js';
-
-export class CollectiveGroup extends HelperGroup<UniqueHelper> {
- /**
- * Pallet name to make an API call to. Examples: 'council', 'technicalCommittee'
- */
- private collective: string;
-
- constructor(helper: UniqueHelper, collective: string) {
- super(helper);
- this.collective = collective;
- }
-
- /**
- * Check the result of a proposal execution for the success of the underlying proposed extrinsic.
- * @param events events of the proposal execution
- * @returns proposal hash
- */
- private checkExecutedEvent(events: IPhasicEvent[]): string {
- const executionEvents = events.filter(x =>
- x.event.section === this.collective && (x.event.method === 'Executed' || x.event.method === 'MemberExecuted'));
-
- if(executionEvents.length != 1) {
- if(events.filter(x => x.event.section === this.collective && x.event.method === 'Disapproved').length > 0)
- throw new Error(`Disapproved by ${this.collective}`);
- else
- throw new Error(`Expected one 'Executed' or 'MemberExecuted' event for ${this.collective}`);
- }
-
- const result = (executionEvents[0].event.data as any).result;
-
- if(result.isErr) {
- if(result.asErr.isModule) {
- const error = result.asErr.asModule;
- const metaError = this.helper.getApi()?.registry.findMetaError(error);
- throw new Error(`Proposal execution failed with ${metaError.section}.${metaError.name}`);
- } else {
- throw new Error('Proposal execution failed with ' + result.asErr.toHuman());
- }
- }
-
- return (executionEvents[0].event.data as any).proposalHash;
- }
-
- /**
- * Returns an array of members' addresses.
- */
- async getMembers() {
- return (await this.helper.callRpc(`api.query.${this.collective}.members`, [])).toHuman();
- }
-
- /**
- * Returns the optional address of the prime member of the collective.
- */
- async getPrimeMember() {
- return (await this.helper.callRpc(`api.query.${this.collective}.prime`, [])).toHuman();
- }
-
- /**
- * Returns an array of proposal hashes that are currently active for this collective.
- */
- async getProposals() {
- return (await this.helper.callRpc(`api.query.${this.collective}.proposals`, [])).toHuman();
- }
-
- /**
- * Returns the call originally encoded under the specified hash.
- * @param hash h256-encoded proposal
- * @returns the optional call that the proposal hash stands for.
- */
- async getProposalCallOf(hash: string) {
- return (await this.helper.callRpc(`api.query.${this.collective}.proposalOf`, [hash])).toHuman();
- }
-
- /**
- * Returns the total number of proposals so far.
- */
- async getTotalProposalsCount() {
- return (await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, [])).toNumber();
- }
-
- /**
- * Creates a new proposal up for voting. If the threshold is set to 1, the proposal will be executed immediately.
- * @param signer keyring of the proposer
- * @param proposal constructed call to be executed if the proposal is successful
- * @param voteThreshold minimal number of votes for the proposal to be verified and executed
- * @param lengthBound byte length of the encoded call
- * @returns promise of extrinsic execution and its result
- */
- async propose(signer: TSigner, proposal: any, voteThreshold: number, lengthBound = 10000) {
- return await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [voteThreshold, proposal, lengthBound]);
- }
-
- /**
- * Casts a vote to either approve or reject a proposal.
- * @param signer keyring of the voter
- * @param proposalHash hash of the proposal to be voted for
- * @param proposalIndex absolute index of the proposal used for absolutely nothing but throwing pointless errors
- * @param approve aye or nay
- * @returns promise of extrinsic execution and its result
- */
- vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve]);
- }
-
- /**
- * Executes a call immediately as a member of the collective. Needed for the Member origin.
- * @param signer keyring of the executor member
- * @param proposal constructed call to be executed by the member
- * @param lengthBound byte length of the encoded call
- * @returns promise of extrinsic execution
- */
- async execute(signer: TSigner, proposal: any, lengthBound = 10000) {
- const result = await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.execute`, [proposal, lengthBound]);
- this.checkExecutedEvent(result.result.events);
- return result;
- }
-
- /**
- * Attempt to close and execute a proposal. Note that there must already be enough votes to meet the threshold set when proposing.
- * @param signer keyring of the executor. Can be absolutely anyone.
- * @param proposalHash hash of the proposal to close
- * @param proposalIndex index of the proposal generated on its creation
- * @param weightBound weight of the proposed call. Can be obtained by calling `paymentInfo()` on the call.
- * @param lengthBound byte length of the encoded call
- * @returns promise of extrinsic execution and its result
- */
- async close(
- signer: TSigner,
- proposalHash: string,
- proposalIndex: number,
- weightBound: [number, number] | any = [20_000_000_000, 1000_000],
- lengthBound = 10_000,
- ) {
- const result = await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [
- proposalHash,
- proposalIndex,
- weightBound,
- lengthBound,
- ]);
- this.checkExecutedEvent(result.result.events);
- return result;
- }
-
- /**
- * Shut down a proposal, regardless of its current state.
- * @param signer keyring of the disapprover. Must be root
- * @param proposalHash hash of the proposal to close
- * @returns promise of extrinsic execution and its result
- */
- disapproveProposal(signer: TSigner, proposalHash: string) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.disapproveProposal`, [proposalHash]);
- }
-}
-
-export class CollectiveMembershipGroup extends HelperGroup<UniqueHelper> {
- /**
- * Pallet name to make an API call to. Examples: 'councilMembership', 'technicalCommitteeMembership'
- */
- private membership: string;
-
- constructor(helper: UniqueHelper, membership: string) {
- super(helper);
- this.membership = membership;
- }
-
- /**
- * Returns an array of members' addresses according to the membership pallet's perception.
- * Note that it does not recognize the original pallet's members set with `setMembers()`.
- */
- async getMembers() {
- return (await this.helper.callRpc(`api.query.${this.membership}.members`, [])).toHuman();
- }
-
- /**
- * Returns the optional address of the prime member of the collective.
- */
- async getPrimeMember() {
- return (await this.helper.callRpc(`api.query.${this.membership}.prime`, [])).toHuman();
- }
-
- /**
- * Add a member to the collective.
- * @param signer keyring of the setter. Must be root
- * @param member address of the member to add
- * @returns promise of extrinsic execution and its result
- */
- addMember(signer: TSigner, member: string) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.addMember`, [member]);
- }
-
- addMemberCall(member: string) {
- return this.helper.constructApiCall(`api.tx.${this.membership}.addMember`, [member]);
- }
-
- /**
- * Remove a member from the collective.
- * @param signer keyring of the setter. Must be root
- * @param member address of the member to remove
- * @returns promise of extrinsic execution and its result
- */
- removeMember(signer: TSigner, member: string) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.removeMember`, [member]);
- }
-
- removeMemberCall(member: string) {
- return this.helper.constructApiCall(`api.tx.${this.membership}.removeMember`, [member]);
- }
-
- /**
- * Set members of the collective to the given list of addresses.
- * @param signer keyring of the setter. Must be root (for the direct call, bypassing a public motion)
- * @param members addresses of the members to set
- * @returns promise of extrinsic execution and its result
- */
- resetMembers(signer: TSigner, members: string[]) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.resetMembers`, [members]);
- }
-
- /**
- * Set the collective's prime member to the given address.
- * @param signer keyring of the setter. Must be root (for the direct call, bypassing a public motion)
- * @param prime address of the prime member of the collective
- * @returns promise of extrinsic execution and its result
- */
- setPrime(signer: TSigner, prime: string) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.setPrime`, [prime]);
- }
-
- setPrimeCall(member: string) {
- return this.helper.constructApiCall(`api.tx.${this.membership}.setPrime`, [member]);
- }
-
- /**
- * Remove the collective's prime member.
- * @param signer keyring of the setter. Must be root (for the direct call, bypassing a public motion)
- * @returns promise of extrinsic execution and its result
- */
- clearPrime(signer: TSigner) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.clearPrime`, []);
- }
-
- clearPrimeCall() {
- return this.helper.constructApiCall(`api.tx.${this.membership}.clearPrime`, []);
- }
-}
-
-export class RankedCollectiveGroup extends HelperGroup<UniqueHelper> {
- /**
- * Pallet name to make an API call to. Examples: 'FellowshipCollective'
- */
- private collective: string;
-
- constructor(helper: UniqueHelper, collective: string) {
- super(helper);
- this.collective = collective;
- }
-
- addMember(signer: TSigner, newMember: string) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.addMember`, [newMember]);
- }
-
- addMemberCall(newMember: string) {
- return this.helper.constructApiCall(`api.tx.${this.collective}.addMember`, [newMember]);
- }
-
- removeMember(signer: TSigner, member: string, minRank: number) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.removeMember`, [member, minRank]);
- }
-
- removeMemberCall(newMember: string, minRank: number) {
- return this.helper.constructApiCall(`api.tx.${this.collective}.removeMember`, [newMember, minRank]);
- }
-
- promote(signer: TSigner, member: string) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.promoteMember`, [member]);
- }
-
- promoteCall(member: string) {
- return this.helper.constructApiCall(`api.tx.${this.collective}.promoteMember`, [member]);
- }
-
- demote(signer: TSigner, member: string) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.demoteMember`, [member]);
- }
-
- demoteCall(newMember: string) {
- return this.helper.constructApiCall(`api.tx.${this.collective}.demoteMember`, [newMember]);
- }
-
- vote(signer: TSigner, pollIndex: number, aye: boolean) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [pollIndex, aye]);
- }
-
- async getMembers() {
- return (await this.helper.getApi().query.fellowshipCollective.members.keys())
- .map((key) => key.args[0].toString());
- }
-
- async getMemberRank(member: string) {
- return (await this.helper.callRpc('api.query.fellowshipCollective.members', [member])).toJSON().rank;
- }
-}
-
-export class ReferendaGroup extends HelperGroup<UniqueHelper> {
- /**
- * Pallet name to make an API call to. Examples: 'FellowshipReferenda'
- */
- private referenda: string;
-
- constructor(helper: UniqueHelper, referenda: string) {
- super(helper);
- this.referenda = referenda;
- }
-
- submit(
- signer: TSigner,
- proposalOrigin: string,
- proposal: any,
- enactmentMoment: any,
- ) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.submit`, [
- {Origins: proposalOrigin},
- proposal,
- enactmentMoment,
- ]);
- }
-
- placeDecisionDeposit(signer: TSigner, referendumIndex: number) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.placeDecisionDeposit`, [referendumIndex]);
- }
-
- cancel(signer: TSigner, referendumIndex: number) {
- return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.cancel`, [referendumIndex]);
- }
-
- cancelCall(referendumIndex: number) {
- return this.helper.constructApiCall(`api.tx.${this.referenda}.cancel`, [referendumIndex]);
- }
-
- async referendumInfo(referendumIndex: number) {
- return (await this.helper.callRpc(`api.query.${this.referenda}.referendumInfoFor`, [referendumIndex])).toJSON();
- }
-
- async enactmentEventId(referendumIndex: number) {
- const api = await this.helper.getApi();
-
- const bytes = api.createType('([u8;8], Text, u32)', ['assembly', 'enactment', referendumIndex]).toU8a();
- return blake2AsHex(bytes, 256);
- }
-}
-
-export interface IFellowshipGroup {
- collective: RankedCollectiveGroup;
- referenda: ReferendaGroup;
-}
-
-export interface ICollectiveGroup {
- collective: CollectiveGroup;
- membership: CollectiveMembershipGroup;
-}
-
-export class DemocracyGroup extends HelperGroup<UniqueHelper> {
- // todo displace proposal into types?
- propose(signer: TSigner, call: any, deposit: bigint) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.propose', [{Inline: call.method.toHex()}, deposit]);
- }
-
- proposeWithPreimage(signer: TSigner, preimage: string, deposit: bigint) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.propose', [{Legacy: preimage}, deposit]);
- }
-
- proposeCall(call: any, deposit: bigint) {
- return this.helper.constructApiCall('api.tx.democracy.propose', [{Inline: call.method.toHex()}, deposit]);
- }
-
- second(signer: TSigner, proposalIndex: number) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.second', [proposalIndex]);
- }
-
- externalPropose(signer: TSigner, proposalCall: any) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalPropose', [{Inline: proposalCall.method.toHex()}]);
- }
-
- externalProposeMajority(signer: TSigner, proposalCall: any) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeMajority', [{Inline: proposalCall.method.toHex()}]);
- }
-
- externalProposeDefault(signer: TSigner, proposalCall: any) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeDefault', [{Inline: proposalCall.method.toHex()}]);
- }
-
- externalProposeDefaultWithPreimage(signer: TSigner, preimage: string) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeDefault', [{Legacy: preimage}]);
- }
-
- externalProposeCall(proposalCall: any) {
- return this.helper.constructApiCall('api.tx.democracy.externalPropose', [{Inline: proposalCall.method.toHex()}]);
- }
-
- externalProposeMajorityCall(proposalCall: any) {
- return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [{Inline: proposalCall.method.toHex()}]);
- }
-
- externalProposeDefaultCall(proposalCall: any) {
- return this.helper.constructApiCall('api.tx.democracy.externalProposeDefault', [{Inline: proposalCall.method.toHex()}]);
- }
-
- externalProposeDefaultWithPreimageCall(preimage: string) {
- return this.helper.constructApiCall('api.tx.democracy.externalProposeDefault', [{Legacy: preimage}]);
- }
-
- // ... and blacklist external proposal hash.
- vetoExternal(signer: TSigner, proposalHash: string) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vetoExternal', [proposalHash]);
- }
-
- vetoExternalCall(proposalHash: string) {
- return this.helper.constructApiCall('api.tx.democracy.vetoExternal', [proposalHash]);
- }
-
- blacklist(signer: TSigner, proposalHash: string, referendumIndex: number | null = null) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.blacklist', [proposalHash, referendumIndex]);
- }
-
- blacklistCall(proposalHash: string, referendumIndex: number | null = null) {
- return this.helper.constructApiCall('api.tx.democracy.blacklist', [proposalHash, referendumIndex]);
- }
-
- // proposal. CancelProposalOrigin (root or all techcom)
- cancelProposal(signer: TSigner, proposalIndex: number) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.cancelProposal', [proposalIndex]);
- }
-
- cancelProposalCall(proposalIndex: number) {
- return this.helper.constructApiCall('api.tx.democracy.cancelProposal', [proposalIndex]);
- }
-
- clearPublicProposals(signer: TSigner) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.clearPublicProposals', []);
- }
-
- fastTrack(signer: TSigner, proposalHash: string, votingPeriod: number, delayPeriod: number) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);
- }
-
- fastTrackCall(proposalHash: string, votingPeriod: number, delayPeriod: number) {
- return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);
- }
-
- // referendum. CancellationOrigin (TechCom member)
- emergencyCancel(signer: TSigner, referendumIndex: number) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.emergencyCancel', [referendumIndex]);
- }
-
- emergencyCancelCall(referendumIndex: number) {
- return this.helper.constructApiCall('api.tx.democracy.emergencyCancel', [referendumIndex]);
- }
-
- vote(signer: TSigner, referendumIndex: number, vote: any) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, vote]);
- }
-
- removeVote(signer: TSigner, referendumIndex: number, targetAccount?: string) {
- if(targetAccount) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.removeOtherVote', [targetAccount, referendumIndex]);
- } else {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.removeVote', [referendumIndex]);
- }
- }
-
- unlock(signer: TSigner, targetAccount: string) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.unlock', [targetAccount]);
- }
-
- delegate(signer: TSigner, toAccount: string, conviction: PalletDemocracyConviction, balance: bigint) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.delegate', [toAccount, conviction, balance]);
- }
-
- undelegate(signer: TSigner) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.undelegate', []);
- }
-
- async referendumInfo(referendumIndex: number) {
- return (await this.helper.callRpc('api.query.democracy.referendumInfoOf', [referendumIndex])).toJSON();
- }
-
- async publicProposals() {
- return (await this.helper.callRpc('api.query.democracy.publicProps', [])).toJSON();
- }
-
- async findPublicProposal(proposalIndex: number) {
- const proposalInfo = (await this.publicProposals()).find((proposalInfo: any[]) => proposalInfo[0] == proposalIndex);
-
- return proposalInfo ? proposalInfo[1] : null;
- }
-
- async expectPublicProposal(proposalIndex: number) {
- const proposal = await this.findPublicProposal(proposalIndex);
-
- if(proposal) {
- return proposal;
- } else {
- throw Error(`Proposal #${proposalIndex} is expected to exist`);
- }
- }
-
- async getExternalProposal() {
- return (await this.helper.callRpc('api.query.democracy.nextExternal', []));
- }
-
- async expectExternalProposal() {
- const proposal = await this.getExternalProposal();
-
- if(proposal) {
- return proposal;
- } else {
- throw Error('An external proposal is expected to exist');
- }
- }
-
- /* setMetadata? */
-
- /* todo?
- referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {
- return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);
- }*/
-}
js-packages/playgrounds/unique.xcm.tsdiffbeforeafterboth--- a/js-packages/playgrounds/unique.xcm.ts
+++ /dev/null
@@ -1,449 +0,0 @@
-import {ApiPromise, WsProvider} from '@polkadot/api';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {ChainHelperBase, EthereumBalanceGroup, HelperGroup, SubstrateBalanceGroup, UniqueHelper} from './unique.js';
-import type {ILogger, TSigner, TSubstrateAccount} from './types.js';
-import type {AcalaAssetMetadata, DemocracyStandardAccountVote, MoonbeamAssetInfo} from './types.xcm.js';
-
-
-export class XcmChainHelper extends ChainHelperBase {
- override async connect(wsEndpoint: string, _listeners?: any): Promise<void> {
- const wsProvider = new WsProvider(wsEndpoint);
- this.api = new ApiPromise({
- provider: wsProvider,
- });
- await this.api.isReadyOrError;
- this.network = await UniqueHelper.detectNetwork(this.api);
- }
-}
-
-class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {
- async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {
- await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);
- }
-}
-
-class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {
- makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {
- const apiPrefix = 'api.tx.assetManager.';
-
- const registerTx = this.helper.constructApiCall(
- apiPrefix + 'registerForeignAsset',
- [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],
- );
-
- const setUnitsTx = this.helper.constructApiCall(
- apiPrefix + 'setAssetUnitsPerSecond',
- [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],
- );
-
- const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);
- const encodedProposal = batchCall?.method.toHex() || '';
- return encodedProposal;
- }
-
- async assetTypeId(location: any) {
- return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);
- }
-}
-
-class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {
- notePreimagePallet: string;
-
- constructor(helper: MoonbeamHelper, options: { [key: string]: any } = {}) {
- super(helper);
- this.notePreimagePallet = options.notePreimagePallet;
- }
-
- async notePreimage(signer: TSigner, encodedProposal: string) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.notePreimagePallet}.notePreimage`, [encodedProposal], true);
- }
-
- externalProposeMajority(proposal: any) {
- return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposal]);
- }
-
- fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {
- return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);
- }
-
- async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {
- await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);
- }
-}
-
-class DemocracyGroup<T extends ChainHelperBase> extends HelperGroup<T> {
- notePreimagePallet: string;
-
- constructor(helper: T, options: { [key: string]: any } = {}) {
- super(helper);
- this.notePreimagePallet = options.notePreimagePallet;
- }
-
- async notePreimage(signer: TSigner, encodedProposal: string) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.notePreimagePallet}.notePreimage`, [encodedProposal], true);
- }
-
- externalProposeMajority(proposal: any) {
- return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposal]);
- }
-
- fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {
- return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);
- }
-
- async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {
- await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);
- }
-}
-
-class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {
- collective: string;
-
- constructor(helper: MoonbeamHelper, collective: string) {
- super(helper);
-
- this.collective = collective;
- }
-
- async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);
- }
-
- async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);
- }
-
- async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: any, lengthBound: number) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);
- }
-
- async proposalCount() {
- return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));
- }
-}
-
-class CollectiveGroup<T extends ChainHelperBase> extends HelperGroup<T> {
- collective: string;
-
- constructor(helper: T, palletName: string) {
- super(helper);
-
- this.collective = palletName;
- }
-
- async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);
- }
-
- async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);
- }
-
- async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: any, lengthBound: number) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);
- }
-
- async proposalCount() {
- return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));
- }
-}
-class PolkadexXcmHelperGroup<T extends ChainHelperBase> extends HelperGroup<T> {
- async whitelistToken(signer: TSigner, assetId: any) {
- await this.helper.executeExtrinsic(signer, 'api.tx.xcmHelper.whitelistToken', [assetId], true);
- }
-}
-
-export class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {
- async register(signer: TSigner, assetId: any, name: string, tokenPrefix: string, mode: 'NFT' | { Fungible: number }) {
- await this.helper.executeExtrinsic(
- signer,
- 'api.tx.foreignAssets.forceRegisterForeignAsset',
- [{V3: assetId}, this.helper.util.str2vec(name), tokenPrefix, mode],
- true,
- );
- }
-
- async foreignCollectionId(assetId: any) {
- return (await this.helper.callRpc('api.query.foreignAssets.foreignAssetToCollection', [assetId])).toJSON();
- }
-}
-
-export class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {
- palletName: string;
-
- constructor(helper: T, palletName: string) {
- super(helper);
-
- this.palletName = palletName;
- }
-
- async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: any) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, weightLimit], true);
- }
-
- async setSafeXcmVersion(signer: TSigner, version: number) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.forceDefaultXcmVersion`, [version], true);
- }
-
- async teleportAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number) {
- await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.teleportAssets`, [destination, beneficiary, assets, feeAssetItem], true);
- }
-
- async teleportNativeAsset(signer: TSigner, destinationParaId: number, targetAccount: Uint8Array, amount: bigint, xcmVersion = 3) {
- const destinationContent = {
- parents: 0,
- interior: {
- X1: {
- Parachain: destinationParaId,
- },
- },
- };
-
- const beneficiaryContent = {
- parents: 0,
- interior: {
- X1: {
- AccountId32: {
- network: 'Any',
- id: targetAccount,
- },
- },
- },
- };
-
- const assetsContent = [
- {
- id: {
- Concrete: {
- parents: 0,
- interior: 'Here',
- },
- },
- fun: {
- Fungible: amount,
- },
- },
- ];
-
- let destination;
- let beneficiary;
- let assets;
-
- if(xcmVersion == 2) {
- destination = {V1: destinationContent};
- beneficiary = {V1: beneficiaryContent};
- assets = {V1: assetsContent};
-
- } else if(xcmVersion == 3) {
- destination = {V2: destinationContent};
- beneficiary = {V2: beneficiaryContent};
- assets = {V2: assetsContent};
-
- } else {
- throw Error('Unknown XCM version: ' + xcmVersion);
- }
-
- const feeAssetItem = 0;
-
- await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);
- }
-
- async send(signer: IKeyringPair, destination: any, message: any) {
- await this.helper.executeExtrinsic(
- signer,
- `api.tx.${this.palletName}.send`,
- [
- destination,
- message,
- ],
- true,
- );
- }
-}
-
-export class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
- async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: any) {
- await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);
- }
-
- async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: any) {
- await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);
- }
-
- async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: any) {
- await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);
- }
-}
-
-
-
-export class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
- async accounts(address: string, currencyId: any) {
- const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;
- return BigInt(free);
- }
-}
-
-export class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {
- async create(signer: TSigner, assetId: number | bigint, admin: string, minimalBalance: bigint) {
- await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);
- }
-
- async forceCreate(signer: TSigner, assetId: number | bigint, admin: string, minimalBalance: bigint, isSufficient = true) {
- await this.helper.executeExtrinsic(signer, 'api.tx.assets.forceCreate', [assetId, admin, isSufficient, minimalBalance], true);
- }
-
- async setMetadata(signer: TSigner, assetId: number | bigint, name: string, symbol: string, decimals: number) {
- await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);
- }
-
- async mint(signer: TSigner, assetId: number | bigint, beneficiary: string, amount: bigint) {
- await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);
- }
-
- async assetInfo(assetId: number | bigint) {
- return (await this.helper.callRpc('api.query.assets.asset', [assetId])).toJSON();
- }
-
- async account(assetId: string | number | bigint, address: string) {
- const accountAsset = (
- await this.helper.callRpc('api.query.assets.account', [assetId, address])
- ).toJSON()! as any;
-
- if(accountAsset !== null) {
- return BigInt(accountAsset['balance']);
- } else {
- return null;
- }
- }
-}
-
-export class RelayHelper extends XcmChainHelper {
- balance: SubstrateBalanceGroup<RelayHelper>;
- xcm: XcmGroup<RelayHelper>;
-
- constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
- super(logger, options.helperBase ?? RelayHelper);
-
- this.balance = new SubstrateBalanceGroup(this);
- this.xcm = new XcmGroup(this, 'xcmPallet');
- }
-}
-
-export class WestmintHelper extends XcmChainHelper {
- balance: SubstrateBalanceGroup<WestmintHelper>;
- xcm: XcmGroup<WestmintHelper>;
- assets: AssetsGroup<WestmintHelper>;
- xTokens: XTokensGroup<WestmintHelper>;
-
- constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
- super(logger, options.helperBase ?? WestmintHelper);
-
- this.balance = new SubstrateBalanceGroup(this);
- this.xcm = new XcmGroup(this, 'polkadotXcm');
- this.assets = new AssetsGroup(this);
- this.xTokens = new XTokensGroup(this);
- }
-}
-
-export class MoonbeamHelper extends XcmChainHelper {
- balance: EthereumBalanceGroup<MoonbeamHelper>;
- assetManager: MoonbeamAssetManagerGroup;
- assets: AssetsGroup<MoonbeamHelper>;
- xTokens: XTokensGroup<MoonbeamHelper>;
- democracy: MoonbeamDemocracyGroup;
- collective: {
- council: MoonbeamCollectiveGroup,
- techCommittee: MoonbeamCollectiveGroup,
- };
-
- constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
- super(logger, options.helperBase ?? MoonbeamHelper);
-
- this.balance = new EthereumBalanceGroup(this);
- this.assetManager = new MoonbeamAssetManagerGroup(this);
- this.assets = new AssetsGroup(this);
- this.xTokens = new XTokensGroup(this);
- this.democracy = new MoonbeamDemocracyGroup(this, options);
- this.collective = {
- council: new MoonbeamCollectiveGroup(this, 'councilCollective'),
- techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),
- };
- }
-}
-
-export class AstarHelper extends XcmChainHelper {
- balance: SubstrateBalanceGroup<AstarHelper>;
- assets: AssetsGroup<AstarHelper>;
- xcm: XcmGroup<AstarHelper>;
-
- constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
- super(logger, options.helperBase ?? AstarHelper);
-
- this.balance = new SubstrateBalanceGroup(this);
- this.assets = new AssetsGroup(this);
- this.xcm = new XcmGroup(this, 'polkadotXcm');
- }
-}
-
-export class AcalaHelper extends XcmChainHelper {
- balance: SubstrateBalanceGroup<AcalaHelper>;
- assetRegistry: AcalaAssetRegistryGroup;
- xTokens: XTokensGroup<AcalaHelper>;
- tokens: TokensGroup<AcalaHelper>;
- xcm: XcmGroup<AcalaHelper>;
-
- constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
- super(logger, options.helperBase ?? AcalaHelper);
-
- this.balance = new SubstrateBalanceGroup(this);
- this.assetRegistry = new AcalaAssetRegistryGroup(this);
- this.xTokens = new XTokensGroup(this);
- this.tokens = new TokensGroup(this);
- this.xcm = new XcmGroup(this, 'polkadotXcm');
- }
-}
-
-export class PolkadexHelper extends XcmChainHelper {
- assets: AssetsGroup<PolkadexHelper>;
- balance: SubstrateBalanceGroup<PolkadexHelper>;
- xTokens: XTokensGroup<PolkadexHelper>;
- xcm: XcmGroup<PolkadexHelper>;
- xcmHelper: PolkadexXcmHelperGroup<PolkadexHelper>;
-
- constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
- super(logger, options.helperBase ?? PolkadexHelper);
-
- this.assets = new AssetsGroup(this);
- this.balance = new SubstrateBalanceGroup(this);
- this.xTokens = new XTokensGroup(this);
- this.xcm = new XcmGroup(this, 'polkadotXcm');
- this.xcmHelper = new PolkadexXcmHelperGroup(this);
- }
-}
-
-export class HydraDxHelper extends XcmChainHelper {
- balance: SubstrateBalanceGroup<HydraDxHelper>;
- xcm: XcmGroup<HydraDxHelper>;
- xTokens: XTokensGroup<HydraDxHelper>;
- democracy: DemocracyGroup<HydraDxHelper>;
- collective: {
- council: CollectiveGroup<HydraDxHelper>,
- techCommittee: CollectiveGroup<HydraDxHelper>,
- };
-
- constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
- super(logger, options.helperBase ?? HydraDxHelper);
- options.notePreimagePallet = options.notePreimagePallet ?? 'preimage';
-
- this.balance = new SubstrateBalanceGroup(this);
- this.xcm = new XcmGroup(this, 'polkadotXcm');
- this.xTokens = new XTokensGroup(this);
- this.democracy = new DemocracyGroup(this, options);
- this.collective = {
- council: new CollectiveGroup(this, 'council'),
- techCommittee: new CollectiveGroup(this, 'technicalCommittee'),
- };
- }
-}
-
js-packages/scripts/authorizeEnactUpgrade.tsdiffbeforeafterboth--- /dev/null
+++ b/js-packages/scripts/authorizeEnactUpgrade.ts
@@ -0,0 +1,19 @@
+import {readFile} from 'fs/promises';
+import {u8aToHex} from '@polkadot/util';
+import {usingPlaygrounds} from '@unique/test-utils/util.js';
+import {blake2AsHex} from '@polkadot/util-crypto';
+
+
+const codePath = process.argv[2];
+if(!codePath) throw new Error('missing code path argument');
+
+const code = await readFile(codePath);
+
+await usingPlaygrounds(async (helper, privateKey) => {
+ const alice = await privateKey('//Alice');
+ const hex = blake2AsHex(code);
+ await helper.getSudo().executeExtrinsic(alice, 'api.tx.parachainSystem.authorizeUpgrade', [hex, true]);
+ await helper.getSudo().executeExtrinsicUncheckedWeight(alice, 'api.tx.parachainSystem.enactAuthorizedUpgrade', [u8aToHex(code)]);
+});
+// We miss disconnect/unref somewhere.
+process.exit(0);
js-packages/scripts/benchmarks/mintFee/index.tsdiffbeforeafterboth--- a/js-packages/scripts/benchmarks/mintFee/index.ts
+++ b/js-packages/scripts/benchmarks/mintFee/index.ts
@@ -1,13 +1,13 @@
import {usingEthPlaygrounds} from '@unique/tests/eth/util/index.js';
import {EthUniqueHelper} from '@unique/tests/eth/util/playgrounds/unique.dev.js';
import {readFile} from 'fs/promises';
-import type {ICrossAccountId} from '@unique/playgrounds/types.js';
+import type {ICrossAccountId} from '@unique-nft/playgrounds/types.js';
import type {IKeyringPair} from '@polkadot/types/types';
-import {UniqueNFTCollection} from '@unique/playgrounds/unique.js';
+import {UniqueNFTCollection} from '@unique-nft/playgrounds/unique.js';
import {Contract} from 'web3-eth-contract';
import {createObjectCsvWriter} from 'csv-writer';
import {convertToTokens, createCollectionForBenchmarks, PERMISSIONS, PROPERTIES} from '../utils/common.js';
-import {makeNames} from '@unique/tests/util/index.js';
+import {makeNames} from '@unique/test-utils/util.js';
import type {ContractImports} from '@unique/tests/eth/util/playgrounds/types.js';
const {dirname} = makeNames(import.meta.url);
js-packages/scripts/benchmarks/nesting/index.tsdiffbeforeafterboth--- a/js-packages/scripts/benchmarks/nesting/index.ts
+++ b/js-packages/scripts/benchmarks/nesting/index.ts
@@ -4,7 +4,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {Contract} from 'web3-eth-contract';
import {convertToTokens} from '../utils/common.js';
-import {makeNames} from '@unique/tests/util/index.js';
+import {makeNames} from '@unique/test-utils/util.js';
import type {ContractImports} from '@unique/tests/eth/util/playgrounds/types.js';
import type {RMRKNestableMintable} from './ABIGEN/index.js';
js-packages/scripts/benchmarks/opsFee/index.tsdiffbeforeafterboth--- a/js-packages/scripts/benchmarks/opsFee/index.ts
+++ b/js-packages/scripts/benchmarks/opsFee/index.ts
@@ -3,13 +3,13 @@
import {readFile} from 'fs/promises';
import {CollectionLimitField, CreateCollectionData, TokenPermissionField} from '@unique/tests/eth/util/playgrounds/types.js';
import type {IKeyringPair} from '@polkadot/types/types';
-import {UniqueFTCollection, UniqueNFTCollection} from '@unique/playgrounds/unique.js';
+import {UniqueFTCollection, UniqueNFTCollection} from '@unique-nft/playgrounds/unique.js';
import {Contract} from 'web3-eth-contract';
import {createObjectCsvWriter} from 'csv-writer';
import {FunctionFeeVM} from '../utils/types.js';
import type {IFunctionFee} from '../utils/types.js';
import {convertToTokens, createCollectionForBenchmarks, PERMISSIONS, PROPERTIES, SUBS_PROPERTIES} from '../utils/common.js';
-import {makeNames} from '@unique/tests/util/index.js';
+import {makeNames} from '@unique/test-utils/util.js';
const {dirname} = makeNames(import.meta.url);
js-packages/scripts/benchmarks/utils/common.tsdiffbeforeafterboth--- a/js-packages/scripts/benchmarks/utils/common.ts
+++ b/js-packages/scripts/benchmarks/utils/common.ts
@@ -1,6 +1,6 @@
import {EthUniqueHelper} from '@unique/tests/eth/util/playgrounds/unique.dev.js';
-import {UniqueNFTCollection, UniqueRFTCollection} from '@unique/playgrounds/unique.js';
-import type {ITokenPropertyPermission, TCollectionMode} from '@unique/playgrounds/types.js';
+import {UniqueNFTCollection, UniqueRFTCollection} from '@unique-nft/playgrounds/unique.js';
+import type {ITokenPropertyPermission, TCollectionMode} from '@unique-nft/playgrounds/types.js';
import type {IKeyringPair} from '@polkadot/types/types';
export const PROPERTIES = Array(40)
js-packages/scripts/calibrateApply.tsdiffbeforeafterboth--- a/js-packages/scripts/calibrateApply.ts
+++ b/js-packages/scripts/calibrateApply.ts
@@ -1,6 +1,6 @@
import {readFile, writeFile} from 'fs/promises';
import path from 'path';
-import {makeNames, usingPlaygrounds} from '@unique/tests/util/index.js';
+import {makeNames, usingPlaygrounds} from '@unique/test-utils/util.js';
const {dirname} = makeNames(import.meta.url);
js-packages/scripts/createHrmp.tsdiffbeforeafterboth--- /dev/null
+++ b/js-packages/scripts/createHrmp.ts
@@ -0,0 +1,37 @@
+import {usingPlaygrounds} from '@unique/test-utils/util.js';
+import config from '../tests/config.js';
+
+const profile = process.argv[2];
+if(!profile) throw new Error('missing profile/relay argument');
+
+await usingPlaygrounds(async (helper, privateKey) => {
+ const bidirOpen = async (a: number, b: number) => {
+ console.log(`Opening ${a} <=> ${b}`);
+ await helper.getSudo().executeExtrinsic(alice, 'api.tx.hrmp.forceOpenHrmpChannel', [a, b, 8, 512]);
+ await helper.getSudo().executeExtrinsic(alice, 'api.tx.hrmp.forceOpenHrmpChannel', [b, a, 8, 512]);
+ };
+ const alice = await privateKey('//Alice');
+ switch (profile) {
+ case 'opal':
+ await bidirOpen(1001, 1002);
+ break;
+ case 'quartz':
+ await bidirOpen(1001, 1002);
+ await bidirOpen(1001, 1003);
+ await bidirOpen(1001, 1004);
+ await bidirOpen(1001, 1005);
+ break;
+ case 'unique':
+ await bidirOpen(1001, 1002);
+ await bidirOpen(1001, 1003);
+ await bidirOpen(1001, 1004);
+ await bidirOpen(1001, 1005);
+ await bidirOpen(1001, 1006);
+ await bidirOpen(1001, 1007);
+ break;
+ default:
+ throw new Error(`unknown hrmp config profile: ${profile}`);
+ }
+}, config.relayUrl);
+// We miss disconnect/unref somewhere.
+process.exit(0);
js-packages/scripts/generateEnv.tsdiffbeforeafterboth--- a/js-packages/scripts/generateEnv.ts
+++ b/js-packages/scripts/generateEnv.ts
@@ -1,7 +1,7 @@
import {ApiPromise, WsProvider} from '@polkadot/api';
import {readFile} from 'fs/promises';
import {join} from 'path';
-import {makeNames} from '@unique/tests/util/index.js';
+import {makeNames} from '@unique/test-utils/util.js';
const {dirname} = makeNames(import.meta.url);
js-packages/scripts/identitySetter.tsdiffbeforeafterboth--- /dev/null
+++ b/js-packages/scripts/identitySetter.ts
@@ -0,0 +1,229 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+//
+// Pulls identities and sub-identities from a chain and then makes a preimage to later force upload them into another.
+// Only changed or previously non-existent data are inserted.
+//
+// Usage: `yarn setIdentities [relay WS URL] [parachain WS URL] [user key]
+// Example: `yarn setIdentities wss://polkadot-rpc.dwellir.com ws://localhost:9944 escape pattern miracle train sudden cart adapt embark wedding alien lamp mesh`
+
+import {encodeAddress} from '@polkadot/keyring';
+import type {IKeyringPair} from '@polkadot/types/types';
+import {usingPlaygrounds, Pallets} from '@unique/test-utils/util.js';
+import {ChainHelperBase} from '@unique-nft/playgrounds/unique.js';
+
+const relayUrl = process.argv[2] ?? 'ws://localhost:9844';
+const paraUrl = process.argv[3] ?? 'ws://localhost:9944';
+const key = process.argv.length > 4 ? process.argv.slice(4).join(' ') : '//Alice';
+
+export function extractAccountId(key: any): string {
+ return (key as any).toHuman()[0];
+}
+
+export function extractIdentityInfo(value: any): object {
+ const heart = (value as any).unwrap();
+ const identity = heart.toJSON();
+ identity.judgements = heart.judgements.toHuman();
+ return identity;
+}
+
+export function extractIdentity(key: any, value: any): [string, any] {
+ return [extractAccountId(key), extractIdentityInfo(value)];
+}
+
+export async function getIdentities(helper: ChainHelperBase, noneCasePredicate?: (key: any, value: any) => void) {
+ const identities: [string, any][] = [];
+ for(const [key, v] of await helper.getApi().query.identity.identityOf.entries()) {
+ const value = v as any;
+ if(value.isNone) {
+ if(noneCasePredicate) noneCasePredicate(key, value);
+ continue;
+ }
+ identities.push(extractIdentity(key, value));
+ }
+ return identities;
+}
+
+// whether the existing chain data is more important than the coming
+function isCurrentChainDataPriority(helper: ChainHelperBase, currentChainId: number | undefined, relayChainId: number) {
+ if(!currentChainId) return false;
+ // information has come from local chain, and is automatically superior
+ if(currentChainId == helper.chain.getChainProperties().ss58Format) return true;
+ // the lower the id, the more important it is (Polkadot has ss58 prefix = 0, Kusama has ss58 prefix = 2)
+ return currentChainId < relayChainId;
+}
+
+// construct an object with all data necessary for insertion from storage query results
+export function constructSubInfo(identityAccount: string, subQuery: any, supers: any[], ss58?: number): [string, any] {
+ const deposit = subQuery.toJSON()[0];
+ const subIdentities = subQuery.toHuman()[1];
+ subIdentities.map((sub: string) => supers.find((sup: any) => sup[0] === sub));
+
+ return [
+ encodeAddress(identityAccount, ss58), [
+ deposit,
+ subIdentities.map((sub: string): [string, object] | null => {
+ const sup = supers.find((sup: any) => sup[0] === sub);
+ if(!sup) {
+ console.error(`Error: Could not find info on super for \nsub-identity account\t${sub} of \nsuper account \t\t${identityAccount}, skipping.`);
+ return null;
+ }
+ return [encodeAddress(sub, ss58), sup[1].toJSON()[1]];
+ }).filter((x: any) => x),
+ ],
+ ];
+}
+
+export async function getSubs(helper: ChainHelperBase) {
+ return (await helper.getApi().query.identity.subsOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]);
+}
+
+export async function getSupers(helper: ChainHelperBase) {
+ return (await helper.getApi().query.identity.superOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]);
+}
+
+async function uploadPreimage(helper: ChainHelperBase, preimageMaker: IKeyringPair, preimage: string) {
+ try {
+ await helper.executeExtrinsic(preimageMaker, 'api.tx.preimage.notePreimage', [preimage]);
+ } catch (e: any) {
+ if(e.message.includes('AlreadyNoted')) {
+ console.warn('Warning: The same preimage already exists on the chain. Nothing was uploaded.');
+ } else {
+ console.error(e);
+ }
+ }
+}
+
+// The utility for pulling identity and sub-identity data
+const forceInsertIdentities = async (): Promise<void> => {
+ let relaySS58Prefix = 0;
+ const identitiesOnRelay: any[] = [];
+ const subsOnRelay: any[] = [];
+ const identitiesToRemove: string[] = [];
+ await usingPlaygrounds(async helper => {
+ try {
+ relaySS58Prefix = helper.chain.getChainProperties().ss58Format;
+ // iterate over every identity
+ for(const [key, value] of await getIdentities(helper, (key, _value) => identitiesToRemove.push((key as any).toHuman()[0]))) {
+ // if any of the judgements resulted in a good confirmed outcome, keep this identity
+ let knownGood = false, reasonable = false;
+ for(const [_id, judgement] of value.judgements) {
+ if(judgement == 'KnownGood') knownGood = true;
+ if(judgement == 'Reasonable') reasonable = true;
+ }
+ if(!(reasonable || knownGood)) continue;
+ // replace the registrator id with the relay chain's ss58 format
+ value.judgements = [[helper.chain.getChainProperties().ss58Format, knownGood ? 'KnownGood' : 'Reasonable']];
+ identitiesOnRelay.push([key, value]);
+ }
+
+ const sublessIdentities = [...identitiesOnRelay];
+ const supersOfSubs = await getSupers(helper);
+
+ // iterate over every sub-identity
+ for(const [key, value] of await getSubs(helper)) {
+ // only get subs of the identities interesting to us
+ const identityIndex = sublessIdentities.findIndex((x: any) => x[0] == key);
+ if(identityIndex == -1) continue;
+ sublessIdentities.splice(identityIndex, 1);
+ subsOnRelay.push(constructSubInfo(key, value, supersOfSubs));
+ }
+
+ // mark the rest of sub-identities for deletion with empty arrays
+ /*for(const [account, _identity] of sublessIdentities) {
+ subsOnRelay.push([account, ['0', []]]);
+ }*/
+ } catch (error) {
+ console.error(error);
+ throw Error('Error during fetching identities');
+ }
+ }, relayUrl);
+
+ await usingPlaygrounds(async (helper, privateKey) => {
+ if(helper.fetchMissingPalletNames([Pallets.Identity]).length != 0) console.error('pallet-identity is not included in parachain.');
+ if(helper.fetchMissingPalletNames([Pallets.Preimage]).length != 0) console.error('pallet-preimage is not included in parachain.');
+
+ try {
+ const preimageMaker = await privateKey(key);
+ const ss58Format = helper.chain.getChainProperties().ss58Format;
+ const paraIdentities = await getIdentities(helper);
+ const identitiesToAdd: any[] = [];
+ const paraAccountsRegistrators: {[name: string]: number} = {};
+
+ // cross-reference every account for changes
+ for(const [key, value] of identitiesOnRelay) {
+ const encodedKey = encodeAddress(key, ss58Format);
+
+ // only update if the identity info does not exist or is changed
+ const identity = paraIdentities.find(i => i[0] === encodedKey);
+ if(identity) {
+ const registratorId = identity[1].judgements[0][0];
+ paraAccountsRegistrators[encodedKey] = registratorId;
+ if(isCurrentChainDataPriority(helper, registratorId, value.judgements[0][0])
+ || JSON.stringify(value.info) === JSON.stringify(identity[1].info)) {
+ continue;
+ }
+ }
+
+ identitiesToAdd.push([key, value]);
+ // exercise caution - in case we have an identity and the realy doesn't, it might mean one of two things:
+ // 1) it was deleted on the relay;
+ // 2) it is our own identity, we don't want to delete it.
+ // identitiesToRemove.push((key as any).toHuman()[0]);
+ }
+
+ if(identitiesToRemove.length != 0)
+ await uploadPreimage(
+ helper,
+ preimageMaker,
+ helper.constructApiCall('api.tx.identity.forceRemoveIdentities', [identitiesToRemove]).method.toHex(),
+ );
+ if(identitiesToAdd.length != 0)
+ await uploadPreimage(
+ helper,
+ preimageMaker,
+ helper.constructApiCall('api.tx.identity.forceInsertIdentities', [identitiesToAdd]).method.toHex(),
+ );
+
+ console.log(`Tried to push ${identitiesToAdd.length} identities`
+ + ` and found ${identitiesToRemove.length} identities for potential removal.`
+ + ` Currently there are ${(await helper.getApi().query.identity.identityOf.keys()).length} identities on the chain.`);
+
+ // fill sub-identities
+ const paraSubs = await getSubs(helper);
+ const supersOfSubs = await getSupers(helper);
+ const subsToUpdate: any[] = [];
+
+ for(const [key, value] of subsOnRelay) {
+ const encodedKey = encodeAddress(key, ss58Format);
+ const sub = paraSubs.find(i => i[0] === encodedKey);
+ if(sub) {
+ // only update if the sub-identity info does not exist or is changed
+ if(isCurrentChainDataPriority(helper, paraAccountsRegistrators[encodedKey], relaySS58Prefix)
+ || JSON.stringify(value) === JSON.stringify(constructSubInfo(sub[0], sub[1], supersOfSubs)[1])) {
+ continue;
+ }
+ } else if(value[1].length == 0)
+ continue;
+
+ subsToUpdate.push([key, value]);
+ }
+
+ if(subsToUpdate.length != 0)
+ await uploadPreimage(
+ helper,
+ preimageMaker,
+ helper.constructApiCall('api.tx.identity.forceSetSubs', [subsToUpdate]).method.toHex(),
+ );
+
+ console.log(`Also tried to push ${subsToUpdate.length} identities with their sub-identities.`
+ + ` Currently there are ${(await helper.getApi().query.identity.subsOf.keys()).length} identities with subs.`);
+ } catch (error) {
+ console.error(error);
+ throw Error('Error during setting identities');
+ }
+ }, paraUrl);
+};
+
+if(process.argv[1] === module.filename)
+ forceInsertIdentities().catch(() => process.exit(1));
js-packages/scripts/relayIdentitiesChecker.tsdiffbeforeafterboth--- /dev/null
+++ b/js-packages/scripts/relayIdentitiesChecker.ts
@@ -0,0 +1,114 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+//
+// Checks and reports the differences between identities and sub-identities on two chains.
+//
+// Usage: `yarn checkRelayIdentities [relay-1 WS URL] [relay-2 WS URL]`
+// Example: `yarn checkRelayIdentities wss://polkadot-rpc.dwellir.com wss://kusama-rpc.dwellir.com`
+
+import {encodeAddress} from '@polkadot/keyring';
+import {usingPlaygrounds} from '@unique/test-utils/util.js';
+import {getIdentities, getSubs, getSupers, constructSubInfo} from './identitySetter.js';
+
+const relay1Url = process.argv[2] ?? 'ws://localhost:9844';
+const relay2Url = process.argv[3] ?? 'ws://localhost:9844';
+
+async function pullIdentities(relayUrl: string): Promise<[any[], any[]]> {
+ const identities: any[] = [];
+ const subs: any[] = [];
+
+ await usingPlaygrounds(async helper => {
+ try {
+ // iterate over every identity
+ for(const [key, value] of await getIdentities(helper)) {
+ // if any of the judgements resulted in a good confirmed outcome, keep this identity
+ if(value.toHuman().judgements.filter((x: any) => x[1] == 'Reasonable' || x[1] == 'KnownGood').length == 0) continue;
+ identities.push([key, value]);
+ }
+
+ const supersOfSubs = await getSupers(helper);
+
+ // iterate over every sub-identity
+ for(const [key, value] of await getSubs(helper)) {
+ // only get subs of the identities interesting to us
+ if(identities.find((x: any) => x[0] == key) == -1) continue;
+ subs.push(constructSubInfo(key, value, supersOfSubs));
+ }
+ } catch (error) {
+ console.error(error);
+ throw Error(`Error during fetching identities on ${relayUrl}`);
+ }
+ }, relayUrl);
+
+ return [identities, subs];
+}
+
+// The utility for pulling identity and sub-identity data
+const checkRelayIdentities = async (): Promise<void> => {
+ const [identitiesOnRelay1, subIdentitiesOnRelay1] = await pullIdentities(relay1Url);
+ const [identitiesOnRelay2, subIdentitiesOnRelay2] = await pullIdentities(relay2Url);
+
+ console.log('identities counts:\t', identitiesOnRelay1.length, identitiesOnRelay2.length);
+ console.log('sub-identities counts:\t', subIdentitiesOnRelay1.length, subIdentitiesOnRelay2.length);
+ console.log();
+
+ try {
+ const matchingAddresses: string[] = [];
+ const inequalIdentities: {[name: string]: [any, any]} = {};
+
+ for(const [key1, value1] of identitiesOnRelay1) {
+ const address = encodeAddress(key1);
+ const identity2 = identitiesOnRelay2.find(([key2, _value2]) => address === encodeAddress(key2));
+ if(!identity2) continue;
+ matchingAddresses.push(address);
+
+ //const [[key2, value2]] = identitiesOnRelay2.splice(index2, 1);
+ const [_key2, value2] = identity2;
+ if(JSON.stringify(value1.info) === JSON.stringify(value2.info)) continue;
+ inequalIdentities[address] = [value1, value2];
+ }
+
+ /*for (const [v1, v2] of Object.values(inequalIdentities)) {
+ console.log(v1.toHuman().info);
+ console.log();
+ console.log(v2.toHuman().info);
+ await new Promise(resolve => setTimeout(resolve, 4000));
+ }*/
+
+ console.log(`Accounts with identities on both relays:\t${matchingAddresses.length}`);
+ console.log(`Sub-identities with conflicting information:\t${Object.entries(inequalIdentities).length}`);
+ console.log();
+
+ const inequalSubIdentities = [];
+ let matchesFound = 0;
+ for(const address of matchingAddresses) {
+ const sub1 = subIdentitiesOnRelay1.find(([key1, _value1]) => address === encodeAddress(key1));
+ if(!sub1) continue;
+ const sub2 = subIdentitiesOnRelay2.find(([key2, _value2]) => address === encodeAddress(key2));
+ if(!sub2) continue;
+
+ const [value1, value2] = [sub1[1], sub2[1]];
+ matchesFound++;
+
+ if(JSON.stringify(value1[1]) === JSON.stringify(value2[1])) {
+ continue;
+ }
+ inequalSubIdentities.push([value1, value2]);
+ }
+
+ /*for (const [v1, v2] of inequalSubIdentities) {
+ console.log(v1[1]);
+ console.log();
+ console.log(v2[1]);
+ await new Promise(resolve => setTimeout(resolve, 300));
+ }*/
+ console.log(`Accounts with sub-identities on both relays:\t${matchesFound}`);
+ console.log(`Of them, those with conflicting sub-identities:\t${inequalSubIdentities.length}`);
+ } catch (error) {
+ console.error(error);
+ throw Error('Error during comparison');
+ }
+};
+
+if(process.argv[1] === module.filename)
+ checkRelayIdentities().catch(() => process.exit(1));
js-packages/scripts/setCode.tsdiffbeforeafterboth--- /dev/null
+++ b/js-packages/scripts/setCode.ts
@@ -0,0 +1,15 @@
+import {readFile} from 'fs/promises';
+import {u8aToHex} from '@polkadot/util';
+import {usingPlaygrounds} from '@unique/test-utils/util.js';
+
+const codePath = process.argv[2];
+if(!codePath) throw new Error('missing code path argument');
+
+const code = await readFile(codePath);
+
+await usingPlaygrounds(async (helper, privateKey) => {
+ const alice = await privateKey('//Alice');
+ await helper.getSudo().executeExtrinsicUncheckedWeight(alice, 'api.tx.system.setCode', [u8aToHex(code)]);
+});
+// We miss disconnect/unref somewhere.
+process.exit(0);
js-packages/scripts/transfer.nload.tsdiffbeforeafterboth--- a/js-packages/scripts/transfer.nload.ts
+++ b/js-packages/scripts/transfer.nload.ts
@@ -17,8 +17,8 @@
/* eslint-disable @typescript-eslint/no-floating-promises */
import os from 'os';
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds} from '@unique/tests/util/index.js';
-import {UniqueHelper} from '@unique/playgrounds/unique.js';
+import {usingPlaygrounds} from '@unique/test-utils/util.js';
+import {UniqueHelper} from '@unique-nft/playgrounds/unique.js';
import * as notReallyCluster from 'cluster'; // https://github.com/nodejs/node/issues/42271#issuecomment-1063415346
const cluster = notReallyCluster as unknown as notReallyCluster.Cluster;
js-packages/test-utils/globalSetup.tsdiffbeforeafterboth--- /dev/null
+++ b/js-packages/test-utils/globalSetup.ts
@@ -0,0 +1,119 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+import {
+ usingPlaygrounds, Pallets, DONOR_FUNDING, MINIMUM_DONOR_FUND, LOCKING_PERIOD, UNLOCKING_PERIOD, makeNames,
+} from './util.js';
+import * as path from 'path';
+import {promises as fs} from 'fs';
+
+const {dirname} = makeNames(import.meta.url);
+
+// This function should be called before running test suites.
+const globalSetup = async (): Promise<void> => {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ try {
+ // 1. Wait node producing blocks
+ await helper.wait.newBlocks(1, 600_000);
+
+ // 2. Create donors for test files
+ await fundFilenamesWithRetries(3)
+ .then((result) => {
+ if(!result) throw Error('Some problems with fundFilenamesWithRetries');
+ });
+
+ // 3. Configure App Promotion
+ const missingPallets = helper.fetchMissingPalletNames([Pallets.AppPromotion]);
+ if(missingPallets.length === 0) {
+ const superuser = await privateKey('//Alice');
+ const palletAddress = helper.arrange.calculatePalletAddress('appstake');
+ const palletAdmin = await privateKey('//PromotionAdmin');
+ const api = helper.getApi();
+ await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})));
+ const nominal = helper.balance.getOneTokenNominal();
+ await helper.balance.transferToSubstrate(superuser, palletAdmin.address, 10000n * nominal);
+ await helper.balance.transferToSubstrate(superuser, palletAddress, 10000n * nominal);
+ await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [api.tx.configuration
+ .setAppPromotionConfigurationOverride({
+ recalculationInterval: LOCKING_PERIOD,
+ pendingInterval: UNLOCKING_PERIOD})], true);
+ }
+ } catch (error) {
+ console.error(error);
+ throw Error('Error during globalSetup');
+ }
+ });
+};
+
+async function getFiles(rootPath: string): Promise<string[]> {
+ const files = await fs.readdir(rootPath, {withFileTypes: true});
+ const filenames: string[] = [];
+ for(const entry of files) {
+ const res = path.resolve(rootPath, entry.name);
+ if(entry.isDirectory()) {
+ filenames.push(...await getFiles(res));
+ } else {
+ filenames.push(res);
+ }
+ }
+ return filenames;
+}
+
+const fundFilenames = async () => {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const oneToken = helper.balance.getOneTokenNominal();
+ const alice = await privateKey('//Alice');
+ const nonce = await helper.chain.getNonce(alice.address);
+ const filenames = await getFiles(path.resolve(dirname, '..'));
+
+ // batching is actually undesireable, it takes away the time while all the transactions actually succeed
+ const batchSize = 300;
+ let balanceGrantedCounter = 0;
+ for(let b = 0; b < filenames.length; b += batchSize) {
+ const tx: Promise<boolean>[] = [];
+ let batchBalanceGrantedCounter = 0;
+ for(let i = 0; batchBalanceGrantedCounter < batchSize && b + i < filenames.length; i++) {
+ const f = filenames[b + i];
+ if(!f.endsWith('.test.ts') && !f.endsWith('seqtest.ts') || f.includes('.outdated')) continue;
+ const account = await privateKey({filename: f, ignoreFundsPresence: true});
+ const aliceBalance = await helper.balance.getSubstrate(account.address);
+
+ if(aliceBalance < MINIMUM_DONOR_FUND * oneToken) {
+ tx.push(helper.executeExtrinsic(
+ alice,
+ 'api.tx.balances.transferKeepAlive',
+ [account.address, DONOR_FUNDING * oneToken],
+ true,
+ {nonce: nonce + balanceGrantedCounter++},
+ ).then(() => true).catch(() => {console.error(`Transaction to ${path.basename(f)} registered as failed. Strange.`); return false;}));
+ batchBalanceGrantedCounter++;
+ }
+ }
+
+ if(tx.length > 0) {
+ console.log(`Granting funds to ${batchBalanceGrantedCounter} filename accounts.`);
+ const result = await Promise.all(tx);
+ if(result && result.lastIndexOf(false) > -1) throw new Error('The transactions actually probably succeeded, should check the balances.');
+ }
+ }
+
+ if(balanceGrantedCounter == 0) console.log('No account needs additional funding.');
+ });
+};
+
+const fundFilenamesWithRetries = (retriesLeft: number): Promise<boolean> => {
+ if(retriesLeft <= 0) return Promise.resolve(false);
+ return fundFilenames()
+ .then(() => Promise.resolve(true))
+ .catch(e => {
+ console.error(e);
+ console.error(`Some transactions might have failed. ${retriesLeft > 1 ? 'Retrying...' : 'Something is wrong.'}\n`);
+ return fundFilenamesWithRetries(--retriesLeft);
+ });
+};
+
+globalSetup().catch(e => {
+ console.error('Setup error');
+ console.error(e);
+ process.exit(1);
+});
js-packages/test-utils/governance.tsdiffbeforeafterboth--- /dev/null
+++ b/js-packages/test-utils/governance.ts
@@ -0,0 +1,531 @@
+import {blake2AsHex} from '@polkadot/util-crypto';
+import type {PalletDemocracyConviction} from '@polkadot/types/lookup';
+import type {IPhasicEvent, TSigner} from '@unique-nft/playgrounds/types.js';
+import {HelperGroup, UniqueHelper} from '@unique-nft/playgrounds/unique.js';
+
+export class CollectiveGroup extends HelperGroup<UniqueHelper> {
+ /**
+ * Pallet name to make an API call to. Examples: 'council', 'technicalCommittee'
+ */
+ private collective: string;
+
+ constructor(helper: UniqueHelper, collective: string) {
+ super(helper);
+ this.collective = collective;
+ }
+
+ /**
+ * Check the result of a proposal execution for the success of the underlying proposed extrinsic.
+ * @param events events of the proposal execution
+ * @returns proposal hash
+ */
+ private checkExecutedEvent(events: IPhasicEvent[]): string {
+ const executionEvents = events.filter(x =>
+ x.event.section === this.collective && (x.event.method === 'Executed' || x.event.method === 'MemberExecuted'));
+
+ if(executionEvents.length != 1) {
+ if(events.filter(x => x.event.section === this.collective && x.event.method === 'Disapproved').length > 0)
+ throw new Error(`Disapproved by ${this.collective}`);
+ else
+ throw new Error(`Expected one 'Executed' or 'MemberExecuted' event for ${this.collective}`);
+ }
+
+ const result = (executionEvents[0].event.data as any).result;
+
+ if(result.isErr) {
+ if(result.asErr.isModule) {
+ const error = result.asErr.asModule;
+ const metaError = this.helper.getApi()?.registry.findMetaError(error);
+ throw new Error(`Proposal execution failed with ${metaError.section}.${metaError.name}`);
+ } else {
+ throw new Error('Proposal execution failed with ' + result.asErr.toHuman());
+ }
+ }
+
+ return (executionEvents[0].event.data as any).proposalHash;
+ }
+
+ /**
+ * Returns an array of members' addresses.
+ */
+ async getMembers() {
+ return (await this.helper.callRpc(`api.query.${this.collective}.members`, [])).toHuman();
+ }
+
+ /**
+ * Returns the optional address of the prime member of the collective.
+ */
+ async getPrimeMember() {
+ return (await this.helper.callRpc(`api.query.${this.collective}.prime`, [])).toHuman();
+ }
+
+ /**
+ * Returns an array of proposal hashes that are currently active for this collective.
+ */
+ async getProposals() {
+ return (await this.helper.callRpc(`api.query.${this.collective}.proposals`, [])).toHuman();
+ }
+
+ /**
+ * Returns the call originally encoded under the specified hash.
+ * @param hash h256-encoded proposal
+ * @returns the optional call that the proposal hash stands for.
+ */
+ async getProposalCallOf(hash: string) {
+ return (await this.helper.callRpc(`api.query.${this.collective}.proposalOf`, [hash])).toHuman();
+ }
+
+ /**
+ * Returns the total number of proposals so far.
+ */
+ async getTotalProposalsCount() {
+ return (await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, [])).toNumber();
+ }
+
+ /**
+ * Creates a new proposal up for voting. If the threshold is set to 1, the proposal will be executed immediately.
+ * @param signer keyring of the proposer
+ * @param proposal constructed call to be executed if the proposal is successful
+ * @param voteThreshold minimal number of votes for the proposal to be verified and executed
+ * @param lengthBound byte length of the encoded call
+ * @returns promise of extrinsic execution and its result
+ */
+ async propose(signer: TSigner, proposal: any, voteThreshold: number, lengthBound = 10000) {
+ return await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [voteThreshold, proposal, lengthBound]);
+ }
+
+ /**
+ * Casts a vote to either approve or reject a proposal.
+ * @param signer keyring of the voter
+ * @param proposalHash hash of the proposal to be voted for
+ * @param proposalIndex absolute index of the proposal used for absolutely nothing but throwing pointless errors
+ * @param approve aye or nay
+ * @returns promise of extrinsic execution and its result
+ */
+ vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {
+ return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve]);
+ }
+
+ /**
+ * Executes a call immediately as a member of the collective. Needed for the Member origin.
+ * @param signer keyring of the executor member
+ * @param proposal constructed call to be executed by the member
+ * @param lengthBound byte length of the encoded call
+ * @returns promise of extrinsic execution
+ */
+ async execute(signer: TSigner, proposal: any, lengthBound = 10000) {
+ const result = await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.execute`, [proposal, lengthBound]);
+ this.checkExecutedEvent(result.result.events);
+ return result;
+ }
+
+ /**
+ * Attempt to close and execute a proposal. Note that there must already be enough votes to meet the threshold set when proposing.
+ * @param signer keyring of the executor. Can be absolutely anyone.
+ * @param proposalHash hash of the proposal to close
+ * @param proposalIndex index of the proposal generated on its creation
+ * @param weightBound weight of the proposed call. Can be obtained by calling `paymentInfo()` on the call.
+ * @param lengthBound byte length of the encoded call
+ * @returns promise of extrinsic execution and its result
+ */
+ async close(
+ signer: TSigner,
+ proposalHash: string,
+ proposalIndex: number,
+ weightBound: [number, number] | any = [20_000_000_000, 1000_000],
+ lengthBound = 10_000,
+ ) {
+ const result = await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [
+ proposalHash,
+ proposalIndex,
+ weightBound,
+ lengthBound,
+ ]);
+ this.checkExecutedEvent(result.result.events);
+ return result;
+ }
+
+ /**
+ * Shut down a proposal, regardless of its current state.
+ * @param signer keyring of the disapprover. Must be root
+ * @param proposalHash hash of the proposal to close
+ * @returns promise of extrinsic execution and its result
+ */
+ disapproveProposal(signer: TSigner, proposalHash: string) {
+ return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.disapproveProposal`, [proposalHash]);
+ }
+}
+
+export class CollectiveMembershipGroup extends HelperGroup<UniqueHelper> {
+ /**
+ * Pallet name to make an API call to. Examples: 'councilMembership', 'technicalCommitteeMembership'
+ */
+ private membership: string;
+
+ constructor(helper: UniqueHelper, membership: string) {
+ super(helper);
+ this.membership = membership;
+ }
+
+ /**
+ * Returns an array of members' addresses according to the membership pallet's perception.
+ * Note that it does not recognize the original pallet's members set with `setMembers()`.
+ */
+ async getMembers() {
+ return (await this.helper.callRpc(`api.query.${this.membership}.members`, [])).toHuman();
+ }
+
+ /**
+ * Returns the optional address of the prime member of the collective.
+ */
+ async getPrimeMember() {
+ return (await this.helper.callRpc(`api.query.${this.membership}.prime`, [])).toHuman();
+ }
+
+ /**
+ * Add a member to the collective.
+ * @param signer keyring of the setter. Must be root
+ * @param member address of the member to add
+ * @returns promise of extrinsic execution and its result
+ */
+ addMember(signer: TSigner, member: string) {
+ return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.addMember`, [member]);
+ }
+
+ addMemberCall(member: string) {
+ return this.helper.constructApiCall(`api.tx.${this.membership}.addMember`, [member]);
+ }
+
+ /**
+ * Remove a member from the collective.
+ * @param signer keyring of the setter. Must be root
+ * @param member address of the member to remove
+ * @returns promise of extrinsic execution and its result
+ */
+ removeMember(signer: TSigner, member: string) {
+ return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.removeMember`, [member]);
+ }
+
+ removeMemberCall(member: string) {
+ return this.helper.constructApiCall(`api.tx.${this.membership}.removeMember`, [member]);
+ }
+
+ /**
+ * Set members of the collective to the given list of addresses.
+ * @param signer keyring of the setter. Must be root (for the direct call, bypassing a public motion)
+ * @param members addresses of the members to set
+ * @returns promise of extrinsic execution and its result
+ */
+ resetMembers(signer: TSigner, members: string[]) {
+ return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.resetMembers`, [members]);
+ }
+
+ /**
+ * Set the collective's prime member to the given address.
+ * @param signer keyring of the setter. Must be root (for the direct call, bypassing a public motion)
+ * @param prime address of the prime member of the collective
+ * @returns promise of extrinsic execution and its result
+ */
+ setPrime(signer: TSigner, prime: string) {
+ return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.setPrime`, [prime]);
+ }
+
+ setPrimeCall(member: string) {
+ return this.helper.constructApiCall(`api.tx.${this.membership}.setPrime`, [member]);
+ }
+
+ /**
+ * Remove the collective's prime member.
+ * @param signer keyring of the setter. Must be root (for the direct call, bypassing a public motion)
+ * @returns promise of extrinsic execution and its result
+ */
+ clearPrime(signer: TSigner) {
+ return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.clearPrime`, []);
+ }
+
+ clearPrimeCall() {
+ return this.helper.constructApiCall(`api.tx.${this.membership}.clearPrime`, []);
+ }
+}
+
+export class RankedCollectiveGroup extends HelperGroup<UniqueHelper> {
+ /**
+ * Pallet name to make an API call to. Examples: 'FellowshipCollective'
+ */
+ private collective: string;
+
+ constructor(helper: UniqueHelper, collective: string) {
+ super(helper);
+ this.collective = collective;
+ }
+
+ addMember(signer: TSigner, newMember: string) {
+ return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.addMember`, [newMember]);
+ }
+
+ addMemberCall(newMember: string) {
+ return this.helper.constructApiCall(`api.tx.${this.collective}.addMember`, [newMember]);
+ }
+
+ removeMember(signer: TSigner, member: string, minRank: number) {
+ return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.removeMember`, [member, minRank]);
+ }
+
+ removeMemberCall(newMember: string, minRank: number) {
+ return this.helper.constructApiCall(`api.tx.${this.collective}.removeMember`, [newMember, minRank]);
+ }
+
+ promote(signer: TSigner, member: string) {
+ return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.promoteMember`, [member]);
+ }
+
+ promoteCall(member: string) {
+ return this.helper.constructApiCall(`api.tx.${this.collective}.promoteMember`, [member]);
+ }
+
+ demote(signer: TSigner, member: string) {
+ return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.demoteMember`, [member]);
+ }
+
+ demoteCall(newMember: string) {
+ return this.helper.constructApiCall(`api.tx.${this.collective}.demoteMember`, [newMember]);
+ }
+
+ vote(signer: TSigner, pollIndex: number, aye: boolean) {
+ return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [pollIndex, aye]);
+ }
+
+ async getMembers() {
+ return (await this.helper.getApi().query.fellowshipCollective.members.keys())
+ .map((key) => key.args[0].toString());
+ }
+
+ async getMemberRank(member: string) {
+ return (await this.helper.callRpc('api.query.fellowshipCollective.members', [member])).toJSON().rank;
+ }
+}
+
+export class ReferendaGroup extends HelperGroup<UniqueHelper> {
+ /**
+ * Pallet name to make an API call to. Examples: 'FellowshipReferenda'
+ */
+ private referenda: string;
+
+ constructor(helper: UniqueHelper, referenda: string) {
+ super(helper);
+ this.referenda = referenda;
+ }
+
+ submit(
+ signer: TSigner,
+ proposalOrigin: string,
+ proposal: any,
+ enactmentMoment: any,
+ ) {
+ return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.submit`, [
+ {Origins: proposalOrigin},
+ proposal,
+ enactmentMoment,
+ ]);
+ }
+
+ placeDecisionDeposit(signer: TSigner, referendumIndex: number) {
+ return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.placeDecisionDeposit`, [referendumIndex]);
+ }
+
+ cancel(signer: TSigner, referendumIndex: number) {
+ return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.cancel`, [referendumIndex]);
+ }
+
+ cancelCall(referendumIndex: number) {
+ return this.helper.constructApiCall(`api.tx.${this.referenda}.cancel`, [referendumIndex]);
+ }
+
+ async referendumInfo(referendumIndex: number) {
+ return (await this.helper.callRpc(`api.query.${this.referenda}.referendumInfoFor`, [referendumIndex])).toJSON();
+ }
+
+ async enactmentEventId(referendumIndex: number) {
+ const api = await this.helper.getApi();
+
+ const bytes = api.createType('([u8;8], Text, u32)', ['assembly', 'enactment', referendumIndex]).toU8a();
+ return blake2AsHex(bytes, 256);
+ }
+}
+
+export interface IFellowshipGroup {
+ collective: RankedCollectiveGroup;
+ referenda: ReferendaGroup;
+}
+
+export interface ICollectiveGroup {
+ collective: CollectiveGroup;
+ membership: CollectiveMembershipGroup;
+}
+
+export class DemocracyGroup extends HelperGroup<UniqueHelper> {
+ // todo displace proposal into types?
+ propose(signer: TSigner, call: any, deposit: bigint) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.propose', [{Inline: call.method.toHex()}, deposit]);
+ }
+
+ proposeWithPreimage(signer: TSigner, preimage: string, deposit: bigint) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.propose', [{Legacy: preimage}, deposit]);
+ }
+
+ proposeCall(call: any, deposit: bigint) {
+ return this.helper.constructApiCall('api.tx.democracy.propose', [{Inline: call.method.toHex()}, deposit]);
+ }
+
+ second(signer: TSigner, proposalIndex: number) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.second', [proposalIndex]);
+ }
+
+ externalPropose(signer: TSigner, proposalCall: any) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalPropose', [{Inline: proposalCall.method.toHex()}]);
+ }
+
+ externalProposeMajority(signer: TSigner, proposalCall: any) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeMajority', [{Inline: proposalCall.method.toHex()}]);
+ }
+
+ externalProposeDefault(signer: TSigner, proposalCall: any) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeDefault', [{Inline: proposalCall.method.toHex()}]);
+ }
+
+ externalProposeDefaultWithPreimage(signer: TSigner, preimage: string) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeDefault', [{Legacy: preimage}]);
+ }
+
+ externalProposeCall(proposalCall: any) {
+ return this.helper.constructApiCall('api.tx.democracy.externalPropose', [{Inline: proposalCall.method.toHex()}]);
+ }
+
+ externalProposeMajorityCall(proposalCall: any) {
+ return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [{Inline: proposalCall.method.toHex()}]);
+ }
+
+ externalProposeDefaultCall(proposalCall: any) {
+ return this.helper.constructApiCall('api.tx.democracy.externalProposeDefault', [{Inline: proposalCall.method.toHex()}]);
+ }
+
+ externalProposeDefaultWithPreimageCall(preimage: string) {
+ return this.helper.constructApiCall('api.tx.democracy.externalProposeDefault', [{Legacy: preimage}]);
+ }
+
+ // ... and blacklist external proposal hash.
+ vetoExternal(signer: TSigner, proposalHash: string) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vetoExternal', [proposalHash]);
+ }
+
+ vetoExternalCall(proposalHash: string) {
+ return this.helper.constructApiCall('api.tx.democracy.vetoExternal', [proposalHash]);
+ }
+
+ blacklist(signer: TSigner, proposalHash: string, referendumIndex: number | null = null) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.blacklist', [proposalHash, referendumIndex]);
+ }
+
+ blacklistCall(proposalHash: string, referendumIndex: number | null = null) {
+ return this.helper.constructApiCall('api.tx.democracy.blacklist', [proposalHash, referendumIndex]);
+ }
+
+ // proposal. CancelProposalOrigin (root or all techcom)
+ cancelProposal(signer: TSigner, proposalIndex: number) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.cancelProposal', [proposalIndex]);
+ }
+
+ cancelProposalCall(proposalIndex: number) {
+ return this.helper.constructApiCall('api.tx.democracy.cancelProposal', [proposalIndex]);
+ }
+
+ clearPublicProposals(signer: TSigner) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.clearPublicProposals', []);
+ }
+
+ fastTrack(signer: TSigner, proposalHash: string, votingPeriod: number, delayPeriod: number) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);
+ }
+
+ fastTrackCall(proposalHash: string, votingPeriod: number, delayPeriod: number) {
+ return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);
+ }
+
+ // referendum. CancellationOrigin (TechCom member)
+ emergencyCancel(signer: TSigner, referendumIndex: number) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.emergencyCancel', [referendumIndex]);
+ }
+
+ emergencyCancelCall(referendumIndex: number) {
+ return this.helper.constructApiCall('api.tx.democracy.emergencyCancel', [referendumIndex]);
+ }
+
+ vote(signer: TSigner, referendumIndex: number, vote: any) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, vote]);
+ }
+
+ removeVote(signer: TSigner, referendumIndex: number, targetAccount?: string) {
+ if(targetAccount) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.removeOtherVote', [targetAccount, referendumIndex]);
+ } else {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.removeVote', [referendumIndex]);
+ }
+ }
+
+ unlock(signer: TSigner, targetAccount: string) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.unlock', [targetAccount]);
+ }
+
+ delegate(signer: TSigner, toAccount: string, conviction: PalletDemocracyConviction, balance: bigint) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.delegate', [toAccount, conviction, balance]);
+ }
+
+ undelegate(signer: TSigner) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.undelegate', []);
+ }
+
+ async referendumInfo(referendumIndex: number) {
+ return (await this.helper.callRpc('api.query.democracy.referendumInfoOf', [referendumIndex])).toJSON();
+ }
+
+ async publicProposals() {
+ return (await this.helper.callRpc('api.query.democracy.publicProps', [])).toJSON();
+ }
+
+ async findPublicProposal(proposalIndex: number) {
+ const proposalInfo = (await this.publicProposals()).find((proposalInfo: any[]) => proposalInfo[0] == proposalIndex);
+
+ return proposalInfo ? proposalInfo[1] : null;
+ }
+
+ async expectPublicProposal(proposalIndex: number) {
+ const proposal = await this.findPublicProposal(proposalIndex);
+
+ if(proposal) {
+ return proposal;
+ } else {
+ throw Error(`Proposal #${proposalIndex} is expected to exist`);
+ }
+ }
+
+ async getExternalProposal() {
+ return (await this.helper.callRpc('api.query.democracy.nextExternal', []));
+ }
+
+ async expectExternalProposal() {
+ const proposal = await this.getExternalProposal();
+
+ if(proposal) {
+ return proposal;
+ } else {
+ throw Error('An external proposal is expected to exist');
+ }
+ }
+
+ /* setMetadata? */
+
+ /* todo?
+ referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);
+ }*/
+}
js-packages/test-utils/index.tsdiffbeforeafterboth--- /dev/null
+++ b/js-packages/test-utils/index.ts
@@ -0,0 +1,1646 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+import '@unique-nft/opal-testnet-types/augment-api.js';
+import '@unique-nft/opal-testnet-types/augment-types.js';
+import '@unique-nft/opal-testnet-types/types-lookup.js';
+
+import {stringToU8a} from '@polkadot/util';
+import {blake2AsHex, encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto';
+import type {ChainHelperBaseConstructor, UniqueHelperConstructor} from '@unique-nft/playgrounds/unique.js';
+import {UniqueHelper, ChainHelperBase, HelperGroup} from '@unique-nft/playgrounds/unique.js';
+import {ApiPromise, Keyring, WsProvider} from '@polkadot/api';
+import * as defs from '@unique-nft/opal-testnet-types/definitions.js';
+import type {IKeyringPair} from '@polkadot/types/types';
+import type {EventRecord} from '@polkadot/types/interfaces';
+import type {ICrossAccountId, ILogger, IPovInfo, ISchedulerOptions, ITransactionResult, TSigner} from '@unique-nft/playgrounds/types.js';
+import type {FrameSystemEventRecord, StagingXcmV2TraitsError, StagingXcmV3TraitsOutcome} from '@polkadot/types/lookup';
+import type {SignerOptions, VoidFn} from '@polkadot/api/types';
+import {spawnSync} from 'child_process';
+import {AcalaHelper, AstarHelper, MoonbeamHelper, PolkadexHelper, RelayHelper, WestmintHelper, ForeignAssetsGroup, XcmGroup, XTokensGroup, TokensGroup, HydraDxHelper} from './xcm/index.js';
+import {CollectiveGroup, CollectiveMembershipGroup, DemocracyGroup, RankedCollectiveGroup, ReferendaGroup} from './governance.js';
+import type {ICollectiveGroup, IFellowshipGroup} from './governance.js';
+
+export class SilentLogger {
+ log(_msg: any, _level: any): void { }
+ level = {
+ ERROR: 'ERROR' as const,
+ WARNING: 'WARNING' as const,
+ INFO: 'INFO' as const,
+ };
+}
+
+export class SilentConsole {
+ // TODO: Remove, this is temporary: Filter unneeded API output
+ // (Jaco promised it will be removed in the next version)
+ consoleErr: any;
+ consoleLog: any;
+ consoleWarn: any;
+
+ constructor() {
+ this.consoleErr = console.error;
+ this.consoleLog = console.log;
+ this.consoleWarn = console.warn;
+ }
+
+ enable() {
+ const outFn = (printer: any) => (...args: any[]) => {
+ for(const arg of args) {
+ if(typeof arg !== 'string')
+ continue;
+ const skippedWarnings = ['1000:: Normal connection closure', 'Not decorating unknown runtime apis:', 'RPC methods not decorated:', 'Not decorating runtime apis', 'Bad input data provided to validate_transaction', 'account balance too low', '1006:: Abnormal Closure'];
+ const needToSkip = skippedWarnings.reduce((a, b) => a || arg.includes(b), false);
+ if(needToSkip || arg === 'Normal connection closure')
+ return;
+ }
+ printer(...args);
+ };
+
+ console.error = outFn(this.consoleErr.bind(console));
+ console.log = outFn(this.consoleLog.bind(console));
+ console.warn = outFn(this.consoleWarn.bind(console));
+ }
+
+ disable() {
+ console.error = this.consoleErr;
+ console.log = this.consoleLog;
+ console.warn = this.consoleWarn;
+ }
+}
+
+export interface IEventHelper {
+ section(): string;
+
+ method(): string;
+
+ wrapEvent(data: any[]): any;
+}
+
+// eslint-disable-next-line @typescript-eslint/naming-convention
+function EventHelper(section: string, method: string, wrapEvent: (data: any[]) => any) {
+ const helperClass = class implements IEventHelper {
+ wrapEvent: (data: any[]) => any;
+ _section: string;
+ _method: string;
+
+ constructor() {
+ this.wrapEvent = wrapEvent;
+ this._section = section;
+ this._method = method;
+ }
+
+ section(): string {
+ return this._section;
+ }
+
+ method(): string {
+ return this._method;
+ }
+
+ filter(txres: ITransactionResult) {
+ return txres.result.events.filter(e => e.event.section === section && e.event.method === method)
+ .map(e => this.wrapEvent(e.event.data));
+ }
+
+ find(txres: ITransactionResult) {
+ const e = txres.result.events.find(e => e.event.section === section && e.event.method === method);
+ return e ? this.wrapEvent(e.event.data) : null;
+ }
+
+ expect(txres: ITransactionResult) {
+ const e = this.find(txres);
+ if(e) {
+ return e;
+ } else {
+ throw Error(`Expected event ${section}.${method}`);
+ }
+ }
+ };
+
+ return helperClass;
+}
+
+function eventJsonData<T = any>(data: any[], index: number) {
+ return data[index].toJSON() as T;
+}
+
+function eventHumanData(data: any[], index: number) {
+ return data[index].toHuman();
+}
+
+function eventData<T = any>(data: any[], index: number) {
+ return data[index] as T;
+}
+
+// eslint-disable-next-line @typescript-eslint/naming-convention
+function EventSection(section: string) {
+ return class Section {
+ static section = section;
+
+ static Method(name: string, wrapEvent: (data: any[]) => any = () => {}) {
+ const helperClass = EventHelper(Section.section, name, wrapEvent);
+ return new helperClass();
+ }
+ };
+}
+
+function schedulerSection(schedulerInstance: string) {
+ return class extends EventSection(schedulerInstance) {
+ static Dispatched = this.Method('Dispatched', data => ({
+ task: eventJsonData(data, 0),
+ id: eventHumanData(data, 1),
+ result: data[2],
+ }));
+
+ static PriorityChanged = this.Method('PriorityChanged', data => ({
+ task: eventJsonData(data, 0),
+ priority: eventJsonData(data, 1),
+ }));
+ };
+}
+
+export class Event {
+ static Democracy = class extends EventSection('democracy') {
+ static Proposed = this.Method('Proposed', data => ({
+ proposalIndex: eventJsonData<number>(data, 0),
+ }));
+
+ static ExternalTabled = this.Method('ExternalTabled');
+
+ static Started = this.Method('Started', data => ({
+ referendumIndex: eventJsonData<number>(data, 0),
+ threshold: eventHumanData(data, 1),
+ }));
+
+ static Voted = this.Method('Voted', data => ({
+ voter: eventJsonData(data, 0),
+ referendumIndex: eventJsonData<number>(data, 1),
+ vote: eventJsonData(data, 2),
+ }));
+
+ static Passed = this.Method('Passed', data => ({
+ referendumIndex: eventJsonData<number>(data, 0),
+ }));
+
+ static ProposalCanceled = this.Method('ProposalCanceled', data => ({
+ propIndex: eventJsonData<number>(data, 0),
+ }));
+
+ static Cancelled = this.Method('Cancelled', data => ({
+ propIndex: eventJsonData<number>(data, 0),
+ }));
+
+ static Vetoed = this.Method('Vetoed', data => ({
+ who: eventHumanData(data, 0),
+ proposalHash: eventHumanData(data, 1),
+ until: eventJsonData<number>(data, 1),
+ }));
+ };
+
+ static Council = class extends EventSection('council') {
+ static Proposed = this.Method('Proposed', data => ({
+ account: eventHumanData(data, 0),
+ proposalIndex: eventJsonData<number>(data, 1),
+ proposalHash: eventHumanData(data, 2),
+ threshold: eventJsonData<number>(data, 3),
+ }));
+ static Closed = this.Method('Closed', data => ({
+ proposalHash: eventHumanData(data, 0),
+ yes: eventJsonData<number>(data, 1),
+ no: eventJsonData<number>(data, 2),
+ }));
+ static Executed = this.Method('Executed', data => ({
+ proposalHash: eventHumanData(data, 0),
+ }));
+ };
+
+ static TechnicalCommittee = class extends EventSection('technicalCommittee') {
+ static Proposed = this.Method('Proposed', data => ({
+ account: eventHumanData(data, 0),
+ proposalIndex: eventJsonData<number>(data, 1),
+ proposalHash: eventHumanData(data, 2),
+ threshold: eventJsonData<number>(data, 3),
+ }));
+ static Closed = this.Method('Closed', data => ({
+ proposalHash: eventHumanData(data, 0),
+ yes: eventJsonData<number>(data, 1),
+ no: eventJsonData<number>(data, 2),
+ }));
+ static Approved = this.Method('Approved', data => ({
+ proposalHash: eventHumanData(data, 0),
+ }));
+ static Executed = this.Method('Executed', data => ({
+ proposalHash: eventHumanData(data, 0),
+ result: eventHumanData(data, 1),
+ }));
+ };
+
+ static FellowshipReferenda = class extends EventSection('fellowshipReferenda') {
+ static Submitted = this.Method('Submitted', data => ({
+ referendumIndex: eventJsonData<number>(data, 0),
+ trackId: eventJsonData<number>(data, 1),
+ proposal: eventJsonData(data, 2),
+ }));
+
+ static Cancelled = this.Method('Cancelled', data => ({
+ index: eventJsonData<number>(data, 0),
+ tally: eventJsonData(data, 1),
+ }));
+ };
+
+ static UniqueScheduler = schedulerSection('uniqueScheduler');
+ static Scheduler = schedulerSection('scheduler');
+
+ static XcmpQueue = class extends EventSection('xcmpQueue') {
+ static XcmpMessageSent = this.Method('XcmpMessageSent', data => ({
+ messageHash: eventJsonData(data, 0),
+ }));
+
+ static Success = this.Method('Success', data => ({
+ messageHash: eventJsonData(data, 0),
+ }));
+
+ static Fail = this.Method('Fail', data => ({
+ messageHash: eventJsonData(data, 0),
+ outcome: eventData<StagingXcmV2TraitsError>(data, 2),
+ }));
+ };
+
+ static DmpQueue = class extends EventSection('dmpQueue') {
+ static ExecutedDownward = this.Method('ExecutedDownward', data => ({
+ outcome: eventData<StagingXcmV3TraitsOutcome>(data, 2),
+ }));
+ };
+}
+
+// eslint-disable-next-line @typescript-eslint/naming-convention
+export function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {
+ return class extends Base {
+ constructor(...args: any[]) {
+ super(...args);
+ }
+
+ override async executeExtrinsic(
+ sender: IKeyringPair,
+ extrinsic: string,
+ params: any[],
+ expectSuccess?: boolean,
+ options: Partial<SignerOptions> | null = null,
+ ): Promise<ITransactionResult> {
+ const call = this.constructApiCall(extrinsic, params);
+ const result = await super.executeExtrinsic(
+ sender,
+ 'api.tx.sudo.sudo',
+ [call],
+ expectSuccess,
+ options,
+ );
+
+ if(result.status === 'Fail') return result;
+
+ const data = (result.result.events.find(x => x.event.section == 'sudo' && x.event.method == 'Sudid')?.event.data as any).sudoResult;
+ if(data.isErr) {
+ if(data.asErr.isModule) {
+ const error = (result.result.events[1].event.data as any).sudoResult.asErr.asModule;
+ const metaError = super.getApi()?.registry.findMetaError(error);
+ throw new Error(`${metaError.section}.${metaError.name}`);
+ } else if(data.asErr.isToken) {
+ throw new Error(`Token: ${data.asErr.asToken}`);
+ }
+ // May be [object Object] in case of unhandled non-unit enum
+ throw new Error(`Misc: ${data.asErr.toHuman()}`);
+ }
+ return result;
+ }
+ override async executeExtrinsicUncheckedWeight(
+ sender: IKeyringPair,
+ extrinsic: string,
+ params: any[],
+ expectSuccess?: boolean,
+ options: Partial<SignerOptions> | null = null,
+ ): Promise<ITransactionResult> {
+ const call = this.constructApiCall(extrinsic, params);
+ const result = await super.executeExtrinsic(
+ sender,
+ 'api.tx.sudo.sudoUncheckedWeight',
+ [call, {refTime: 0, proofSize: 0}],
+ expectSuccess,
+ options,
+ );
+
+ if(result.status === 'Fail') return result;
+
+ const data = (result.result.events.find(x => x.event.section == 'sudo' && x.event.method == 'Sudid')?.event.data as any).sudoResult;
+ if(data.isErr) {
+ if(data.asErr.isModule) {
+ const error = (result.result.events[1].event.data as any).sudoResult.asErr.asModule;
+ const metaError = super.getApi()?.registry.findMetaError(error);
+ throw new Error(`${metaError.section}.${metaError.name}`);
+ } else if(data.asErr.isToken) {
+ throw new Error(`Token: ${data.asErr.asToken}`);
+ }
+ // May be [object Object] in case of unhandled non-unit enum
+ throw new Error(`Misc: ${data.asErr.toHuman()}`);
+ }
+ return result;
+ }
+ };
+}
+
+class SchedulerGroup extends HelperGroup<UniqueHelper> {
+ constructor(helper: UniqueHelper) {
+ super(helper);
+ }
+
+ cancelScheduled(signer: TSigner, scheduledId: string) {
+ return this.helper.executeExtrinsic(
+ signer,
+ 'api.tx.scheduler.cancelNamed',
+ [scheduledId],
+ true,
+ );
+ }
+
+ changePriority(signer: TSigner, scheduledId: string, priority: number) {
+ return this.helper.executeExtrinsic(
+ signer,
+ 'api.tx.scheduler.changeNamedPriority',
+ [scheduledId, priority],
+ true,
+ );
+ }
+
+ scheduleAt<T extends DevUniqueHelper>(
+ executionBlockNumber: number,
+ options: ISchedulerOptions = {},
+ ) {
+ return this.schedule<T>('schedule', executionBlockNumber, options);
+ }
+
+ scheduleAfter<T extends DevUniqueHelper>(
+ blocksBeforeExecution: number,
+ options: ISchedulerOptions = {},
+ ) {
+ return this.schedule<T>('scheduleAfter', blocksBeforeExecution, options);
+ }
+
+ schedule<T extends UniqueHelper>(
+ scheduleFn: 'schedule' | 'scheduleAfter',
+ blocksNum: number,
+ options: ISchedulerOptions = {},
+ ) {
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);
+ return this.helper.clone(ScheduledHelperType, {
+ scheduleFn,
+ blocksNum,
+ options,
+ }) as T;
+ }
+}
+
+class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {
+ //todo:collator documentation
+ addInvulnerable(signer: TSigner, address: string) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.addInvulnerable', [address]);
+ }
+
+ removeInvulnerable(signer: TSigner, address: string) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.removeInvulnerable', [address]);
+ }
+
+ async getInvulnerables(): Promise<string[]> {
+ return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());
+ }
+
+ /** and also total max invulnerables */
+ maxCollators(): number {
+ return (this.helper.getApi().consts.configuration.defaultCollatorSelectionMaxCollators.toJSON() as number);
+ }
+
+ async getDesiredCollators(): Promise<number> {
+ return (await this.helper.callRpc('api.query.configuration.collatorSelectionDesiredCollatorsOverride')).toNumber();
+ }
+
+ setLicenseBond(signer: TSigner, amount: bigint) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.configuration.setCollatorSelectionLicenseBond', [amount]);
+ }
+
+ async getLicenseBond(): Promise<bigint> {
+ return (await this.helper.callRpc('api.query.configuration.collatorSelectionLicenseBondOverride')).toBigInt();
+ }
+
+ obtainLicense(signer: TSigner) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.getLicense', []);
+ }
+
+ releaseLicense(signer: TSigner) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.releaseLicense', []);
+ }
+
+ forceReleaseLicense(signer: TSigner, released: string) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.forceReleaseLicense', [released]);
+ }
+
+ async hasLicense(address: string): Promise<bigint> {
+ return (await this.helper.callRpc('api.query.collatorSelection.licenseDepositOf', [address])).toBigInt();
+ }
+
+ onboard(signer: TSigner) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.onboard', []);
+ }
+
+ offboard(signer: TSigner) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.offboard', []);
+ }
+
+ async getCandidates(): Promise<string[]> {
+ return (await this.helper.callRpc('api.query.collatorSelection.candidates')).map((x: any) => x.toHuman());
+ }
+}
+
+export class DevUniqueHelper extends UniqueHelper {
+ /**
+ * Arrange methods for tests
+ */
+ arrange: ArrangeGroup;
+ wait: WaitGroup;
+ admin: AdminGroup;
+ session: SessionGroup;
+ testUtils: TestUtilGroup;
+ foreignAssets: ForeignAssetsGroup;
+ xcm: XcmGroup<DevUniqueHelper>;
+ xTokens: XTokensGroup<DevUniqueHelper>;
+ tokens: TokensGroup<DevUniqueHelper>;
+ scheduler: SchedulerGroup;
+ collatorSelection: CollatorSelectionGroup;
+ council: ICollectiveGroup;
+ technicalCommittee: ICollectiveGroup;
+ fellowship: IFellowshipGroup;
+ democracy: DemocracyGroup;
+
+ constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+ options.helperBase = options.helperBase ?? DevUniqueHelper;
+
+ super(logger, options);
+ this.arrange = new ArrangeGroup(this);
+ this.wait = new WaitGroup(this);
+ this.admin = new AdminGroup(this);
+ this.testUtils = new TestUtilGroup(this);
+ this.session = new SessionGroup(this);
+ this.foreignAssets = new ForeignAssetsGroup(this);
+ this.xcm = new XcmGroup(this, 'polkadotXcm');
+ this.xTokens = new XTokensGroup(this);
+ this.tokens = new TokensGroup(this);
+ this.scheduler = new SchedulerGroup(this);
+ this.collatorSelection = new CollatorSelectionGroup(this);
+ this.council = {
+ collective: new CollectiveGroup(this, 'council'),
+ membership: new CollectiveMembershipGroup(this, 'councilMembership'),
+ };
+ this.technicalCommittee = {
+ collective: new CollectiveGroup(this, 'technicalCommittee'),
+ membership: new CollectiveMembershipGroup(this, 'technicalCommitteeMembership'),
+ };
+ this.fellowship = {
+ collective: new RankedCollectiveGroup(this, 'fellowshipCollective'),
+ referenda: new ReferendaGroup(this, 'fellowshipReferenda'),
+ };
+ this.democracy = new DemocracyGroup(this);
+ }
+
+ override async connect(wsEndpoint: string, _listeners?: any): Promise<void> {
+ if(!wsEndpoint) throw new Error('wsEndpoint was not set');
+ const wsProvider = new WsProvider(wsEndpoint);
+ this.api = new ApiPromise({
+ provider: wsProvider,
+ signedExtensions: {
+ ContractHelpers: {
+ extrinsic: {},
+ payload: {},
+ },
+ CheckMaintenance: {
+ extrinsic: {},
+ payload: {},
+ },
+ DisableIdentityCalls: {
+ extrinsic: {},
+ payload: {},
+ },
+ FakeTransactionFinalizer: {
+ extrinsic: {},
+ payload: {},
+ },
+ },
+ rpc: {
+ unique: defs.unique.rpc,
+ appPromotion: defs.appPromotion.rpc,
+ povinfo: defs.povinfo.rpc,
+ eth: {
+ feeHistory: {
+ description: 'Dummy',
+ params: [],
+ type: 'u8',
+ },
+ maxPriorityFeePerGas: {
+ description: 'Dummy',
+ params: [],
+ type: 'u8',
+ },
+ },
+ },
+ });
+ await this.api.isReadyOrError;
+ this.network = await UniqueHelper.detectNetwork(this.api);
+ this.wsEndpoint = wsEndpoint;
+ }
+ getSudo<T extends DevUniqueHelper>() {
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const SudoHelperType = SudoHelper(this.helperBase);
+ return this.clone(SudoHelperType) as T;
+ }
+}
+
+export class DevRelayHelper extends RelayHelper {
+ wait: WaitGroup;
+
+ constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+ options.helperBase = options.helperBase ?? DevRelayHelper;
+
+ super(logger, options);
+ this.wait = new WaitGroup(this);
+ }
+
+ getSudo() {
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const SudoHelperType = SudoHelper(this.helperBase);
+ return this.clone(SudoHelperType) as DevRelayHelper;
+ }
+}
+
+export class DevWestmintHelper extends WestmintHelper {
+ wait: WaitGroup;
+
+ constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+ options.helperBase = options.helperBase ?? DevWestmintHelper;
+
+ super(logger, options);
+ this.wait = new WaitGroup(this);
+ }
+}
+
+export class DevStatemineHelper extends DevWestmintHelper {}
+
+export class DevStatemintHelper extends DevWestmintHelper {}
+
+export class DevMoonbeamHelper extends MoonbeamHelper {
+ account: MoonbeamAccountGroup;
+ wait: WaitGroup;
+ fastDemocracy: MoonbeamFastDemocracyGroup;
+
+ constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+ options.helperBase = options.helperBase ?? DevMoonbeamHelper;
+ options.notePreimagePallet = options.notePreimagePallet ?? 'preimage';
+
+ super(logger, options);
+ this.account = new MoonbeamAccountGroup(this);
+ this.wait = new WaitGroup(this);
+ this.fastDemocracy = new MoonbeamFastDemocracyGroup(this);
+ }
+}
+
+export class DevMoonriverHelper extends DevMoonbeamHelper {
+ constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+ options.notePreimagePallet = options.notePreimagePallet ?? 'preimage';
+ super(logger, options);
+ }
+}
+
+export class DevAstarHelper extends AstarHelper {
+ wait: WaitGroup;
+
+ constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+ options.helperBase = options.helperBase ?? DevAstarHelper;
+
+ super(logger, options);
+ this.wait = new WaitGroup(this);
+ }
+
+ getSudo<T extends AstarHelper>() {
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const SudoHelperType = SudoHelper(this.helperBase);
+ return this.clone(SudoHelperType) as T;
+ }
+}
+
+export class DevShidenHelper extends DevAstarHelper { }
+
+export class DevAcalaHelper extends AcalaHelper {
+ wait: WaitGroup;
+
+ constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+ options.helperBase = options.helperBase ?? DevAcalaHelper;
+
+ super(logger, options);
+ this.wait = new WaitGroup(this);
+ }
+ getSudo() {
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const SudoHelperType = SudoHelper(this.helperBase);
+ return this.clone(SudoHelperType) as DevAcalaHelper;
+ }
+}
+
+export class DevPolkadexHelper extends PolkadexHelper {
+ wait: WaitGroup;
+ constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+ options.helperBase = options.helperBase ?? PolkadexHelper;
+
+ super(logger, options);
+ this.wait = new WaitGroup(this);
+ }
+
+ getSudo() {
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const SudoHelperType = SudoHelper(this.helperBase);
+ return this.clone(SudoHelperType) as DevPolkadexHelper;
+ }
+}
+
+export class DevHydraDxHelper extends HydraDxHelper {
+ wait: WaitGroup;
+ fastDemocracy: HydraFastDemocracyGroup;
+
+ constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+ options.helperBase = options.helperBase ?? DevHydraDxHelper;
+
+ super(logger, options);
+
+ this.wait = new WaitGroup(this);
+ this.fastDemocracy = new HydraFastDemocracyGroup(this);
+ }
+}
+
+export class DevKaruraHelper extends DevAcalaHelper {}
+
+export class ArrangeGroup {
+ helper: DevUniqueHelper;
+
+ scheduledIdSlider = 0;
+
+ constructor(helper: DevUniqueHelper) {
+ this.helper = helper;
+ }
+
+ /**
+ * Generates accounts with the specified UNQ token balance
+ * @param balances balances for generated accounts. Each balance will be multiplied by the token nominal.
+ * @param donor donor account for balances
+ * @returns array of newly created accounts
+ * @example const [acc1, acc2, acc3] = await createAccounts([0n, 10n, 20n], donor);
+ */
+ createAccounts = async (balances: bigint[], donor: IKeyringPair): Promise<IKeyringPair[]> => {
+ let nonce = await this.helper.chain.getNonce(donor.address);
+ const wait = new WaitGroup(this.helper);
+ const ss58Format = this.helper.chain.getChainProperties().ss58Format;
+ const tokenNominal = this.helper.balance.getOneTokenNominal();
+ const transactions = [];
+ const accounts: IKeyringPair[] = [];
+ for(const balance of balances) {
+ const recipient = this.helper.util.fromSeed(mnemonicGenerate(), ss58Format);
+ accounts.push(recipient);
+ if(balance !== 0n) {
+ const tx = this.helper.constructApiCall('api.tx.balances.transferKeepAlive', [{Id: recipient.address}, balance * tokenNominal]);
+ transactions.push(this.helper.signTransaction(donor, tx, {nonce}, 'account generation'));
+ nonce++;
+ }
+ }
+
+ await Promise.all(transactions).catch(_e => {});
+
+ //#region TODO remove this region, when nonce problem will be solved
+ const checkBalances = async () => {
+ let isSuccess = true;
+ for(let i = 0; i < balances.length; i++) {
+ const balance = await this.helper.balance.getSubstrate(accounts[i].address);
+ if(balance !== balances[i] * tokenNominal) {
+ isSuccess = false;
+ break;
+ }
+ }
+ return isSuccess;
+ };
+
+ let accountsCreated = false;
+ const maxBlocksChecked = await this.helper.arrange.isDevNode() ? 50 : 5;
+ // checkBalances retry up to 5-50 blocks
+ for(let index = 0; index < maxBlocksChecked; index++) {
+ accountsCreated = await checkBalances();
+ if(accountsCreated) break;
+ await wait.newBlocks(1);
+ }
+
+ if(!accountsCreated) throw Error('Accounts generation failed');
+ //#endregion
+
+ return accounts;
+ };
+
+ // TODO combine this method and createAccounts into one
+ createCrowd = async (accountsToCreate: number, withBalance: bigint, donor: IKeyringPair): Promise<IKeyringPair[]> => {
+ const createAsManyAsCan = async () => {
+ let transactions: any = [];
+ const accounts: IKeyringPair[] = [];
+ let nonce = await this.helper.chain.getNonce(donor.address);
+ const tokenNominal = this.helper.balance.getOneTokenNominal();
+ const ss58Format = this.helper.chain.getChainProperties().ss58Format;
+ for(let i = 0; i < accountsToCreate; i++) {
+ if(i === 500) { // if there are too many accounts to create
+ await Promise.allSettled(transactions); // wait while first 500 (should be 100 for devnode) tx will be settled
+ transactions = []; //
+ nonce = await this.helper.chain.getNonce(donor.address); // update nonce
+ }
+ const recipient = this.helper.util.fromSeed(mnemonicGenerate(), ss58Format);
+ accounts.push(recipient);
+ if(withBalance !== 0n) {
+ const tx = this.helper.constructApiCall('api.tx.balances.transferKeepAlive', [{Id: recipient.address}, withBalance * tokenNominal]);
+ transactions.push(this.helper.signTransaction(donor, tx, {nonce}, 'account generation'));
+ nonce++;
+ }
+ }
+
+ const fullfilledAccounts = [];
+ await Promise.allSettled(transactions);
+ for(const account of accounts) {
+ const accountBalance = await this.helper.balance.getSubstrate(account.address);
+ if(accountBalance === withBalance * tokenNominal) {
+ fullfilledAccounts.push(account);
+ }
+ }
+ return fullfilledAccounts;
+ };
+
+
+ const crowd: IKeyringPair[] = [];
+ // do up to 5 retries
+ for(let index = 0; index < 5 && accountsToCreate !== 0; index++) {
+ const asManyAsCan = await createAsManyAsCan();
+ crowd.push(...asManyAsCan);
+ accountsToCreate -= asManyAsCan.length;
+ }
+
+ if(accountsToCreate !== 0) throw Error(`Crowd generation failed: ${accountsToCreate} accounts left`);
+
+ return crowd;
+ };
+
+ /**
+ * Generates one account with zero balance
+ * @returns the newly generated account
+ * @example const account = await helper.arrange.createEmptyAccount();
+ */
+ createEmptyAccount = (): IKeyringPair => {
+ const ss58Format = this.helper.chain.getChainProperties().ss58Format;
+ return this.helper.util.fromSeed(mnemonicGenerate(), ss58Format);
+ };
+
+ isDevNode = async () => {
+ let blockNumber = (await this.helper.callRpc('api.query.system.number')).toJSON();
+ if(blockNumber == 0) {
+ await this.helper.wait.newBlocks(1);
+ blockNumber = (await this.helper.callRpc('api.query.system.number')).toJSON();
+ }
+ const block2 = await this.helper.callRpc('api.rpc.chain.getBlock', [await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])]);
+ const block1 = await this.helper.callRpc('api.rpc.chain.getBlock', [await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber - 1])]);
+ const findCreationDate = (block: any) => {
+ const humanBlock = block.toHuman();
+ let date;
+ humanBlock.block.extrinsics.forEach((ext: any) => {
+ if(ext.method.section === 'timestamp') {
+ date = Number(ext.method.args.now.replaceAll(',', ''));
+ }
+ });
+ return date;
+ };
+ const block1date = await findCreationDate(block1);
+ const block2date = await findCreationDate(block2);
+ if(block2date! - block1date! < 9000) return true;
+ return false;
+ };
+
+ async calculcateFee(payer: ICrossAccountId, promise: () => Promise<any>): Promise<bigint> {
+ const address = 'Substrate' in payer ? payer.Substrate : this.helper.address.ethToSubstrate(payer.Ethereum);
+ let balance = await this.helper.balance.getSubstrate(address);
+
+ await promise();
+
+ balance -= await this.helper.balance.getSubstrate(address);
+
+ return balance;
+ }
+
+ async calculatePoVInfo(txs: any[]): Promise<IPovInfo> {
+ const rawPovInfo = await this.helper.callRpc('api.rpc.povinfo.estimateExtrinsicPoV', [txs]);
+
+ const kvJson: {[key: string]: string} = {};
+
+ for(const kv of rawPovInfo.keyValues) {
+ kvJson[kv.key.toHex()] = kv.value.toHex();
+ }
+
+ const kvStr = JSON.stringify(kvJson);
+
+ const chainql = spawnSync(
+ 'chainql',
+ [
+ `--tla-code=data=${kvStr}`,
+ '-e', `function(data) cql.dump(cql.chain("${this.helper.getEndpoint()}").latest._meta, data, {omit_empty:true})`,
+ ],
+ );
+
+ if(!chainql.stdout) {
+ throw Error('unable to get an output from the `chainql`');
+ }
+
+ return {
+ proofSize: rawPovInfo.proofSize.toNumber(),
+ compactProofSize: rawPovInfo.compactProofSize.toNumber(),
+ compressedProofSize: rawPovInfo.compressedProofSize.toNumber(),
+ results: rawPovInfo.results,
+ kv: JSON.parse(chainql.stdout.toString()),
+ };
+ }
+
+ calculatePalletAddress(palletId: any) {
+ const address = stringToU8a(('modl' + palletId).padEnd(32, '\0'));
+ return encodeAddress(address, this.helper.chain.getChainProperties().ss58Format);
+ }
+
+ makeScheduledIds(num: number): string[] {
+ function makeId(slider: number) {
+ const scheduledIdSize = 64;
+ const hexId = slider.toString(16);
+ const prefixSize = scheduledIdSize - hexId.length;
+
+ const scheduledId = '0x' + '0'.repeat(prefixSize) + hexId;
+
+ return scheduledId;
+ }
+
+ const ids = [];
+ for(let i = 0; i < num; i++) {
+ ids.push(makeId(this.scheduledIdSlider));
+ this.scheduledIdSlider += 1;
+ }
+
+ return ids;
+ }
+
+ makeScheduledId(): string {
+ return (this.makeScheduledIds(1))[0];
+ }
+
+ async captureEvents(eventSection: string, eventMethod: string): Promise<EventCapture> {
+ const capture = new EventCapture(this.helper, eventSection, eventMethod);
+ await capture.startCapture();
+
+ return capture;
+ }
+
+ makeXcmProgramWithdrawDeposit(beneficiary: Uint8Array, id: any, amount: bigint) {
+ return {
+ V2: [
+ {
+ WithdrawAsset: [
+ {
+ id,
+ fun: {
+ Fungible: amount,
+ },
+ },
+ ],
+ },
+ {
+ BuyExecution: {
+ fees: {
+ id,
+ fun: {
+ Fungible: amount,
+ },
+ },
+ weightLimit: 'Unlimited',
+ },
+ },
+ {
+ DepositAsset: {
+ assets: {
+ Wild: 'All',
+ },
+ maxAssets: 1,
+ beneficiary: {
+ parents: 0,
+ interior: {
+ X1: {
+ AccountId32: {
+ network: 'Any',
+ id: beneficiary,
+ },
+ },
+ },
+ },
+ },
+ },
+ ],
+ };
+ }
+
+ makeXcmProgramReserveAssetDeposited(beneficiary: Uint8Array, id: any, amount: bigint) {
+ return {
+ V2: [
+ {
+ ReserveAssetDeposited: [
+ {
+ id,
+ fun: {
+ Fungible: amount,
+ },
+ },
+ ],
+ },
+ {
+ BuyExecution: {
+ fees: {
+ id,
+ fun: {
+ Fungible: amount,
+ },
+ },
+ weightLimit: 'Unlimited',
+ },
+ },
+ {
+ DepositAsset: {
+ assets: {
+ Wild: 'All',
+ },
+ maxAssets: 1,
+ beneficiary: {
+ parents: 0,
+ interior: {
+ X1: {
+ AccountId32: {
+ network: 'Any',
+ id: beneficiary,
+ },
+ },
+ },
+ },
+ },
+ },
+ ],
+ };
+ }
+
+ makeUnpaidSudoTransactProgram(info: {weightMultiplier: number, call: string}) {
+ return {
+ V3: [
+ {
+ UnpaidExecution: {
+ weightLimit: 'Unlimited',
+ checkOrigin: null,
+ },
+ },
+ {
+ Transact: {
+ originKind: 'Superuser',
+ requireWeightAtMost: {
+ refTime: info.weightMultiplier * 200000000,
+ proofSize: info.weightMultiplier * 3000,
+ },
+ call: {
+ encoded: info.call,
+ },
+ },
+ },
+ ],
+ };
+ }
+}
+
+class MoonbeamAccountGroup {
+ helper: MoonbeamHelper;
+
+ keyring: Keyring;
+ _alithAccount: IKeyringPair;
+ _baltatharAccount: IKeyringPair;
+ _dorothyAccount: IKeyringPair;
+
+ constructor(helper: MoonbeamHelper) {
+ this.helper = helper;
+
+ this.keyring = new Keyring({type: 'ethereum'});
+ const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';
+ const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';
+ const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';
+
+ this._alithAccount = this.keyring.addFromUri(alithPrivateKey, undefined, 'ethereum');
+ this._baltatharAccount = this.keyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');
+ this._dorothyAccount = this.keyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');
+ }
+
+ alithAccount() {
+ return this._alithAccount;
+ }
+
+ baltatharAccount() {
+ return this._baltatharAccount;
+ }
+
+ dorothyAccount() {
+ return this._dorothyAccount;
+ }
+
+ create() {
+ return this.keyring.addFromUri(mnemonicGenerate());
+ }
+}
+
+class MoonbeamFastDemocracyGroup {
+ helper: DevMoonbeamHelper;
+
+ constructor(helper: DevMoonbeamHelper) {
+ this.helper = helper;
+ }
+
+ async executeProposal(proposalDesciption: string, encodedProposal: string) {
+ const proposalHash = blake2AsHex(encodedProposal);
+
+ const alithAccount = this.helper.account.alithAccount();
+ const baltatharAccount = this.helper.account.baltatharAccount();
+ const dorothyAccount = this.helper.account.dorothyAccount();
+
+ const councilVotingThreshold = 2;
+ const technicalCommitteeThreshold = 2;
+ const fastTrackVotingPeriod = 3;
+ const fastTrackDelayPeriod = 0;
+
+ console.log(`[democracy] executing '${proposalDesciption}' proposal`);
+
+ // >>> Propose external motion through council >>>
+ console.log('\t* Propose external motion through council.......');
+ const externalMotion = this.helper.democracy.externalProposeMajority({Inline: encodedProposal});
+ const encodedMotion = externalMotion?.method.toHex() || '';
+ const motionHash = blake2AsHex(encodedMotion);
+ console.log('\t* Motion hash is %s', motionHash);
+
+ await this.helper.collective.council.propose(
+ baltatharAccount,
+ councilVotingThreshold,
+ externalMotion,
+ externalMotion.encodedLength,
+ );
+
+ const councilProposalIdx = await this.helper.collective.council.proposalCount() - 1;
+ await this.helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);
+ await this.helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);
+
+ await this.helper.collective.council.close(
+ dorothyAccount,
+ motionHash,
+ councilProposalIdx,
+ {
+ refTime: 1_000_000_000,
+ proofSize: 1_000_000,
+ },
+ externalMotion.encodedLength,
+ );
+ console.log('\t* Propose external motion through council.......DONE');
+ // <<< Propose external motion through council <<<
+
+ // >>> Fast track proposal through technical committee >>>
+ console.log('\t* Fast track proposal through technical committee.......');
+ const fastTrack = this.helper.democracy.fastTrack(proposalHash, fastTrackVotingPeriod, fastTrackDelayPeriod);
+ const encodedFastTrack = fastTrack?.method.toHex() || '';
+ const fastTrackHash = blake2AsHex(encodedFastTrack);
+ console.log('\t* FastTrack hash is %s', fastTrackHash);
+
+ await this.helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);
+
+ const techProposalIdx = await this.helper.collective.techCommittee.proposalCount() - 1;
+ await this.helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);
+ await this.helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);
+
+ await this.helper.collective.techCommittee.close(
+ baltatharAccount,
+ fastTrackHash,
+ techProposalIdx,
+ {
+ refTime: 1_000_000_000,
+ proofSize: 1_000_000,
+ },
+ fastTrack.encodedLength,
+ );
+ console.log('\t* Fast track proposal through technical committee.......DONE');
+ // <<< Fast track proposal through technical committee <<<
+
+ const democracyStarted = await this.helper.wait.expectEvent(3, Event.Democracy.Started);
+ const referendumIndex = democracyStarted.referendumIndex;
+
+ // >>> Referendum voting >>>
+ console.log(`\t* Referendum #${referendumIndex} voting.......`);
+ await this.helper.democracy.referendumVote(dorothyAccount, referendumIndex, {
+ balance: 10_000_000_000_000_000_000n,
+ vote: {aye: true, conviction: 1},
+ });
+ console.log(`\t* Referendum #${referendumIndex} voting.......DONE`);
+ // <<< Referendum voting <<<
+
+ // Wait the proposal to pass
+ await this.helper.wait.expectEvent(3, Event.Democracy.Passed, event => event.referendumIndex == referendumIndex);
+
+ await this.helper.wait.newBlocks(1);
+
+ console.log(`[democracy] executing '${proposalDesciption}' proposal.......DONE`);
+ }
+}
+
+class HydraFastDemocracyGroup {
+ helper: DevHydraDxHelper;
+
+ constructor(helper: DevHydraDxHelper) {
+ this.helper = helper;
+ }
+
+ async executeProposal(proposalDesciption: string, encodedProposal: string) {
+ const proposalHash = blake2AsHex(encodedProposal);
+ const aliceAccount = this.helper.util.fromSeed('//Alice');
+ const bobAccount = this.helper.util.fromSeed('//Bob');
+ const eveAccount = this.helper.util.fromSeed('//Eve');
+
+ const councilVotingThreshold = 1;
+ const technicalCommitteeThreshold = 3;
+ const fastTrackVotingPeriod = 3;
+ const fastTrackDelayPeriod = 0;
+
+ console.log(`[democracy] executing '${proposalDesciption}' proposal`);
+
+ // >>> Propose external motion through council >>>
+ console.log('\t* Propose external motion through council.......');
+ const externalMotion = this.helper.democracy.externalProposeMajority({Inline: encodedProposal});
+ const encodedMotion = externalMotion?.method.toHex() || '';
+ const motionHash = blake2AsHex(encodedMotion);
+ console.log('\t* Motion hash is %s', motionHash);
+
+ await this.helper.collective.council.propose(
+ aliceAccount,
+ councilVotingThreshold,
+ externalMotion,
+ externalMotion.encodedLength,
+ );
+
+ console.log('\t* Propose external motion through council.......DONE');
+ // <<< Propose external motion through council <<<
+
+ // >>> Fast track proposal through technical committee >>>
+ console.log('\t* Fast track proposal through technical committee.......');
+ const fastTrack = this.helper.democracy.fastTrack(proposalHash, fastTrackVotingPeriod, fastTrackDelayPeriod);
+ const encodedFastTrack = fastTrack?.method.toHex() || '';
+ const fastTrackHash = blake2AsHex(encodedFastTrack);
+ console.log('\t* FastTrack hash is %s', fastTrackHash);
+
+ await this.helper.collective.techCommittee.propose(aliceAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);
+
+ const techProposalIdx = await this.helper.collective.techCommittee.proposalCount() - 1;
+ await this.helper.collective.techCommittee.vote(aliceAccount, fastTrackHash, techProposalIdx, true);
+ await this.helper.collective.techCommittee.vote(bobAccount, fastTrackHash, techProposalIdx, true);
+ await this.helper.collective.techCommittee.vote(eveAccount, fastTrackHash, techProposalIdx, true);
+
+ await this.helper.collective.techCommittee.close(
+ bobAccount,
+ fastTrackHash,
+ techProposalIdx,
+ {
+ refTime: 1_000_000_000,
+ proofSize: 1_000_000,
+ },
+ fastTrack.encodedLength,
+ );
+ console.log('\t* Fast track proposal through technical committee.......DONE');
+ // <<< Fast track proposal through technical committee <<<
+
+ const democracyStarted = await this.helper.wait.expectEvent(3, Event.Democracy.Started);
+ const referendumIndex = democracyStarted.referendumIndex;
+
+ // >>> Referendum voting >>>
+ console.log(`\t* Referendum #${referendumIndex} voting.......`);
+ await this.helper.democracy.referendumVote(eveAccount, referendumIndex, {
+ balance: 10_000_000_000_000_000_000n,
+ vote: {aye: true, conviction: 1},
+ });
+ console.log(`\t* Referendum #${referendumIndex} voting.......DONE`);
+ // <<< Referendum voting <<<
+
+ // Wait the proposal to pass
+ await this.helper.wait.expectEvent(3, Event.Democracy.Passed, event => event.referendumIndex == referendumIndex);
+
+ await this.helper.wait.newBlocks(1);
+
+ console.log(`[democracy] executing '${proposalDesciption}' proposal.......DONE`);
+ }
+}
+
+class WaitGroup {
+ helper: ChainHelperBase;
+
+ constructor(helper: ChainHelperBase) {
+ this.helper = helper;
+ }
+
+ sleep(milliseconds: number) {
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
+ }
+
+ private async waitWithTimeout(promise: Promise<any>, timeout: number) {
+ let isBlock = false;
+ promise.then(() => isBlock = true).catch(() => isBlock = true);
+ let totalTime = 0;
+ const step = 100;
+ while(!isBlock) {
+ await this.sleep(step);
+ totalTime += step;
+ if(totalTime >= timeout) throw Error('Blocks production failed');
+ }
+ return promise;
+ }
+
+ /**
+ * Launch some async operation, or throw an error after some time. Note that it will still continue executing after the timeout.
+ * @param promise async operation to race against the timeout
+ * @param timeoutMS time after which to time out
+ * @param timeoutError error message to throw
+ * @returns promise of the same type the operation had
+ */
+ withTimeout<T>(
+ promise: Promise<T>,
+ timeoutMS = 30000,
+ timeoutError = 'The operation has timed out!',
+ ): Promise<T> {
+ const timeout = new Promise<never>((_, reject) => {
+ setTimeout(() => {
+ reject(new Error(timeoutError));
+ }, timeoutMS);
+ });
+
+ return Promise.race<T>([promise, timeout]).catch(e => {throw new Error(e);});
+ }
+
+ /**
+ * Wait for specified number of blocks
+ * @param blocksCount number of blocks to wait
+ * @returns
+ */
+ async newBlocks(blocksCount = 1, timeout?: number): Promise<void> {
+ timeout = timeout ?? blocksCount * 60_000;
+ // eslint-disable-next-line no-async-promise-executor
+ const promise = new Promise<void>(async (resolve) => {
+ const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(() => {
+ if(blocksCount > 0) {
+ blocksCount--;
+ } else {
+ unsubscribe();
+ resolve();
+ }
+ });
+ });
+ await this.waitWithTimeout(promise, timeout);
+ return promise;
+ }
+
+ /**
+ * Wait for the specified number of sessions to pass.
+ * Only applicable if the Session pallet is turned on.
+ * @param sessionCount number of sessions to wait
+ * @param blockTimeout time in ms until panicking that the chain has stopped producing blocks
+ * @returns
+ */
+ async newSessions(sessionCount = 1, blockTimeout = 60000): Promise<void> {
+ console.log(`Waiting for ${sessionCount} new session${sessionCount > 1 ? 's' : ''}.`
+ + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.');
+
+ const expectedSessionIndex = await (this.helper as DevUniqueHelper).session.getIndex() + sessionCount;
+ let currentSessionIndex = -1;
+
+ while(currentSessionIndex < expectedSessionIndex) {
+ // eslint-disable-next-line no-async-promise-executor
+ currentSessionIndex = await this.withTimeout(new Promise(async (resolve) => {
+ await this.newBlocks(1);
+ const res = await (this.helper as DevUniqueHelper).session.getIndex();
+ resolve(res);
+ }), blockTimeout, 'The chain has stopped producing blocks!');
+ }
+ }
+
+ async forParachainBlockNumber(blockNumber: bigint | number, timeout?: number) {
+ timeout = timeout ?? 30 * 60 * 1000;
+ // eslint-disable-next-line no-async-promise-executor
+ const promise = new Promise<void>(async (resolve) => {
+ const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads((data: any) => {
+ if(data.number.toNumber() >= blockNumber) {
+ unsubscribe();
+ resolve();
+ }
+ });
+ });
+ await this.waitWithTimeout(promise, timeout);
+ return promise;
+ }
+
+ async forRelayBlockNumber(blockNumber: bigint | number, timeout?: number) {
+ timeout = timeout ?? 30 * 60 * 1000;
+ // eslint-disable-next-line no-async-promise-executor
+ const promise = new Promise<void>(async (resolve) => {
+ const unsubscribe = await this.helper.getApi().query.parachainSystem.validationData((data: any) => {
+ if(data.value.relayParentNumber.toNumber() >= blockNumber) {
+ // @ts-ignore
+ unsubscribe();
+ resolve();
+ }
+ });
+ });
+ await this.waitWithTimeout(promise, timeout);
+ return promise;
+ }
+
+ noScheduledTasks() {
+ const api = this.helper.getApi();
+
+ // eslint-disable-next-line no-async-promise-executor
+ const promise = new Promise<void>(async resolve => {
+ const unsubscribe = await api.rpc.chain.subscribeNewHeads(async () => {
+ const areThereScheduledTasks = await api.query.scheduler.lookup.entries();
+
+ if(areThereScheduledTasks.length == 0) {
+ unsubscribe();
+ resolve();
+ }
+ });
+ });
+
+ return promise;
+ }
+
+ parachainBlockMultiplesOf(val: bigint) {
+ // eslint-disable-next-line no-async-promise-executor
+ const promise = new Promise<void>(async resolve => {
+ const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads((data: any) => {
+ if(data.number.toBigInt() % val == 0n) {
+ console.log(`from waiter: ${data.number.toBigInt()}`);
+ unsubscribe();
+ resolve();
+ }
+ });
+ });
+ return promise;
+ }
+
+ event<T extends IEventHelper>(
+ maxBlocksToWait: number,
+ eventHelper: T,
+ filter: (_: any) => boolean = () => true,
+ ): any {
+ // eslint-disable-next-line no-async-promise-executor
+ const promise = new Promise<T | null>(async (resolve) => {
+ const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(async header => {
+ const blockNumber = header.number.toJSON();
+ const blockHash = header.hash;
+ const eventIdStr = `${eventHelper.section()}.${eventHelper.method()}`;
+ const waitLimitStr = `wait blocks remaining: ${maxBlocksToWait}`;
+
+ this.helper.logger.log(`[Block #${blockNumber}] Waiting for event \`${eventIdStr}\` (${waitLimitStr})`);
+
+ const apiAt = await this.helper.getApi().at(blockHash);
+ const eventRecords = (await apiAt.query.system.events()) as any;
+
+ const neededEvent = eventRecords.toArray()
+ .filter((r: FrameSystemEventRecord) => r.event.section == eventHelper.section() && r.event.method == eventHelper.method())
+ .map((r: FrameSystemEventRecord) => eventHelper.wrapEvent(r.event.data))
+ .find(filter);
+
+ if(neededEvent) {
+ unsubscribe();
+ resolve(neededEvent);
+ } else if(maxBlocksToWait > 0) {
+ maxBlocksToWait--;
+ } else {
+ this.helper.logger.log(`Eligible event \`${eventIdStr}\` is NOT found.
+ The wait lasted until block ${blockNumber} inclusive`);
+ unsubscribe();
+ resolve(null);
+ }
+ });
+ });
+ return promise;
+ }
+
+ async expectEvent<T extends IEventHelper>(
+ maxBlocksToWait: number,
+ eventHelper: T,
+ filter: (e: any) => boolean = () => true,
+ ) {
+ const e = await this.event(maxBlocksToWait, eventHelper, filter);
+ if(e == null) {
+ throw Error(`The event '${eventHelper.section()}.${eventHelper.method()}' is expected`);
+ } else {
+ return e;
+ }
+ }
+}
+
+class SessionGroup {
+ helper: ChainHelperBase;
+
+ constructor(helper: ChainHelperBase) {
+ this.helper = helper;
+ }
+
+ //todo:collator documentation
+ async getIndex(): Promise<number> {
+ return (await this.helper.callRpc('api.query.session.currentIndex', [])).toNumber();
+ }
+
+ newSessions(sessionCount = 1, blockTimeout = 24000): Promise<void> {
+ return (this.helper as DevUniqueHelper).wait.newSessions(sessionCount, blockTimeout);
+ }
+
+ setOwnKeys(signer: TSigner, key: string) {
+ return this.helper.executeExtrinsic(
+ signer,
+ 'api.tx.session.setKeys',
+ [key, '0x0'],
+ true,
+ );
+ }
+
+ setOwnKeysFromAddress(signer: TSigner) {
+ return this.setOwnKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex'));
+ }
+}
+
+class TestUtilGroup {
+ helper: DevUniqueHelper;
+
+ constructor(helper: DevUniqueHelper) {
+ this.helper = helper;
+ }
+
+ async enable(testUtilsPalletName: string) {
+ if(this.helper.fetchMissingPalletNames([testUtilsPalletName]).length != 0) {
+ return;
+ }
+
+ const signer = this.helper.util.fromSeed('//Alice');
+ await this.helper.getSudo<DevUniqueHelper>().executeExtrinsic(signer, 'api.tx.testUtils.enable', [], true);
+ }
+
+ async setTestValue(signer: TSigner, testVal: number) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.setTestValue', [testVal], true);
+ }
+
+ async incTestValue(signer: TSigner) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.incTestValue', [], true);
+ }
+
+ async setTestValueAndRollback(signer: TSigner, testVal: number) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.setTestValueAndRollback', [testVal], true);
+ }
+
+ async testValue(blockIdx?: number) {
+ const api = blockIdx
+ ? await this.helper.getApi().at(await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockIdx]))
+ : this.helper.getApi();
+
+ return (await api.query.testUtils.testValue()).toJSON();
+ }
+
+ async justTakeFee(signer: TSigner) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.justTakeFee', [], true);
+ }
+
+ async selfCancelingInc(signer: TSigner, scheduledId: string, maxTestVal: number) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.selfCancelingInc', [scheduledId, maxTestVal], true);
+ }
+}
+
+class EventCapture {
+ helper: DevUniqueHelper;
+ eventSection: string;
+ eventMethod: string;
+ events: EventRecord[] = [];
+ unsubscribe: VoidFn | null = null;
+
+ constructor(
+ helper: DevUniqueHelper,
+ eventSection: string,
+ eventMethod: string,
+ ) {
+ this.helper = helper;
+ this.eventSection = eventSection;
+ this.eventMethod = eventMethod;
+ }
+
+ async startCapture() {
+ this.stopCapture();
+ this.unsubscribe = (await this.helper.getApi().query.system.events((eventRecords: FrameSystemEventRecord[]) => {
+ const newEvents = eventRecords.filter(r => r.event.section == this.eventSection && r.event.method == this.eventMethod);
+
+ this.events.push(...newEvents);
+ })) as any;
+ }
+
+ stopCapture() {
+ if(this.unsubscribe !== null) {
+ this.unsubscribe();
+ }
+ }
+
+ extractCapturedEvents() {
+ return this.events;
+ }
+}
+
+class AdminGroup {
+ helper: UniqueHelper;
+
+ constructor(helper: UniqueHelper) {
+ this.helper = helper;
+ }
+
+ async payoutStakers(signer: IKeyringPair, stakersToPayout: number): Promise<{staker: string, stake: bigint, payout: bigint}[]> {
+ const payoutResult = await this.helper.executeExtrinsic(signer, 'api.tx.appPromotion.payoutStakers', [stakersToPayout], true);
+ return payoutResult.result.events.filter(e => e.event.method === 'StakingRecalculation').map(e => ({
+ staker: e.event.data[0].toString(),
+ stake: e.event.data[1].toBigInt(),
+ payout: e.event.data[2].toBigInt(),
+ }));
+ }
+}
+
+// eslint-disable-next-line @typescript-eslint/naming-convention
+function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {
+ return class extends Base {
+ scheduleFn: 'schedule' | 'scheduleAfter';
+ blocksNum: number;
+ options: ISchedulerOptions;
+
+ constructor(...args: any[]) {
+ const logger = args[0] as ILogger;
+ const options = args[1] as {
+ scheduleFn: 'schedule' | 'scheduleAfter',
+ blocksNum: number,
+ options: ISchedulerOptions
+ };
+
+ super(logger);
+
+ this.scheduleFn = options.scheduleFn;
+ this.blocksNum = options.blocksNum;
+ this.options = options.options;
+ }
+
+ override executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {
+ const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);
+
+ const mandatorySchedArgs = [
+ this.blocksNum,
+ this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,
+ this.options.priority ?? null,
+ scheduledTx,
+ ];
+
+ let schedArgs;
+ let scheduleFn;
+
+ if(this.options.scheduledId) {
+ schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs];
+
+ if(this.scheduleFn == 'schedule') {
+ scheduleFn = 'scheduleNamed';
+ } else if(this.scheduleFn == 'scheduleAfter') {
+ scheduleFn = 'scheduleNamedAfter';
+ }
+ } else {
+ schedArgs = mandatorySchedArgs;
+ scheduleFn = this.scheduleFn;
+ }
+
+ const extrinsic = 'api.tx.scheduler.' + scheduleFn;
+
+ return super.executeExtrinsic(
+ sender,
+ extrinsic as any,
+ schedArgs,
+ expectSuccess,
+ );
+ }
+ };
+}
js-packages/test-utils/package.jsondiffbeforeafterboth--- /dev/null
+++ b/js-packages/test-utils/package.json
@@ -0,0 +1,18 @@
+{
+ "author": "",
+ "license": "SEE LICENSE IN ../../../LICENSE",
+ "description": "Playground scripts",
+ "engines": {
+ "node": ">=16"
+ },
+ "name": "@unique/test-utils",
+ "type": "module",
+ "version": "1.0.0",
+ "main": "index.js",
+ "dependencies": {
+ "@polkadot/api": "10.10.1",
+ "@polkadot/util": "^12.5.1",
+ "@polkadot/util-crypto": "^12.5.1",
+ "@unique-nft/opal-testnet-types": "workspace:*"
+ }
+}
js-packages/test-utils/util.tsdiffbeforeafterboth--- /dev/null
+++ b/js-packages/test-utils/util.ts
@@ -0,0 +1,223 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+import * as path from 'path';
+import * as crypto from 'crypto';
+import type {IKeyringPair} from '@polkadot/types/types/interfaces';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import chaiSubset from 'chai-subset';
+import {Context} from 'mocha';
+import config from '../tests/config.js';
+import {ChainHelperBase} from '@unique-nft/playgrounds/unique.js';
+import type {ILogger} from '@unique-nft/playgrounds/types.js';
+import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper, DevAstarHelper, DevShidenHelper, DevPolkadexHelper, DevHydraDxHelper} from '@unique/test-utils';
+import {dirname} from 'path';
+import {fileURLToPath} from 'url';
+
+chai.config.truncateThreshold = 0;
+chai.use(chaiAsPromised);
+chai.use(chaiSubset);
+export const expect = chai.expect;
+
+const getTestHash = (filename: string) => crypto.createHash('md5').update(filename).digest('hex');
+
+export const getTestSeed = (filename: string) => `//Alice+${getTestHash(filename)}`;
+
+async function usingPlaygroundsGeneral<T extends ChainHelperBase, R = void>(
+ helperType: new (logger: ILogger) => T,
+ url: string,
+ code: (helper: T, privateKey: (seed: string | { filename?: string, url?: string, ignoreFundsPresence?: boolean }) => Promise<IKeyringPair>) => Promise<R>,
+): Promise<R> {
+ const silentConsole = new SilentConsole();
+ silentConsole.enable();
+
+ const helper = new helperType(new SilentLogger());
+ let result;
+ try {
+ await helper.connect(url);
+ const ss58Format = helper.chain.getChainProperties().ss58Format;
+ const privateKey = async (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => {
+ if(typeof seed === 'string') {
+ return helper.util.fromSeed(seed, ss58Format);
+ }
+ if(seed.url) {
+ const {filename} = makeNames(seed.url);
+ seed.filename = filename;
+ } else if(seed.filename) {
+ // Pass
+ } else {
+ throw new Error('no url nor filename set');
+ }
+ const actualSeed = getTestSeed(seed.filename);
+ let account = helper.util.fromSeed(actualSeed, ss58Format);
+ // here's to hoping that no
+ if(!seed.ignoreFundsPresence && ((helper as any)['balance'] == undefined || await (helper as any).balance.getSubstrate(account.address) < MINIMUM_DONOR_FUND)) {
+ console.warn(`${path.basename(seed.filename)}: Not enough funds present on the filename account. Using the default one as the donor instead.`);
+ account = helper.util.fromSeed('//Alice', ss58Format);
+ }
+ return account;
+ };
+ result = await code(helper, privateKey);
+ }
+ finally {
+ await helper.disconnect();
+ silentConsole.disable();
+ }
+ return result as any as R;
+}
+
+export const usingPlaygrounds = <R = void>(code: (helper: DevUniqueHelper, privateKey: (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => Promise<IKeyringPair>) => Promise<R>, url: string = config.substrateUrl) => usingPlaygroundsGeneral<DevUniqueHelper, R>(DevUniqueHelper, url, code);
+
+export const usingWestmintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevWestmintHelper>(DevWestmintHelper, url, code);
+
+export const usingStateminePlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevStatemineHelper>(DevWestmintHelper, url, code);
+
+export const usingStatemintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevStatemintHelper>(DevWestmintHelper, url, code);
+
+export const usingRelayPlaygrounds = (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevRelayHelper>(DevRelayHelper, url, code);
+
+export const usingAcalaPlaygrounds = (url: string, code: (helper: DevAcalaHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevAcalaHelper>(DevAcalaHelper, url, code);
+
+export const usingKaruraPlaygrounds = (url: string, code: (helper: DevKaruraHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevKaruraHelper>(DevAcalaHelper, url, code);
+
+export const usingMoonbeamPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevMoonbeamHelper>(DevMoonbeamHelper, url, code);
+
+export const usingMoonriverPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevMoonriverHelper>(DevMoonriverHelper, url, code);
+
+export const usingAstarPlaygrounds = (url: string, code: (helper: DevAstarHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevAstarHelper>(DevAstarHelper, url, code);
+
+export const usingShidenPlaygrounds = (url: string, code: (helper: DevShidenHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevShidenHelper>(DevShidenHelper, url, code);
+
+export const usingPolkadexPlaygrounds = (url: string, code: (helper: DevPolkadexHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevPolkadexHelper>(DevPolkadexHelper, url, code);
+
+export const usingHydraDxPlaygrounds = (url: string, code: (helper: DevHydraDxHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevHydraDxHelper>(DevHydraDxHelper, url, code);
+
+export const MINIMUM_DONOR_FUND = 4_000_000n;
+export const DONOR_FUNDING = 4_000_000n;
+
+// App-promotion periods:
+export const LOCKING_PERIOD = 12n; // 12 blocks of relay
+export const UNLOCKING_PERIOD = 6n; // 6 blocks of parachain
+
+// Native contracts
+export const COLLECTION_HELPER = '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f';
+export const CONTRACT_HELPER = '0x842899ECF380553E8a4de75bF534cdf6fBF64049';
+
+export enum Pallets {
+ Inflation = 'inflation',
+ ReFungible = 'refungible',
+ Fungible = 'fungible',
+ NFT = 'nonfungible',
+ Scheduler = 'scheduler',
+ UniqueScheduler = 'uniqueScheduler',
+ AppPromotion = 'apppromotion',
+ CollatorSelection = 'collatorselection',
+ Session = 'session',
+ Identity = 'identity',
+ Democracy = 'democracy',
+ Council = 'council',
+ //CouncilMembership = 'councilmembership',
+ TechnicalCommittee = 'technicalcommittee',
+ Fellowship = 'fellowshipcollective',
+ Preimage = 'preimage',
+ Maintenance = 'maintenance',
+ TestUtils = 'testutils',
+}
+
+export function requirePalletsOrSkip(test: Context, helper: DevUniqueHelper, requiredPallets: readonly string[]) {
+ const missingPallets = helper.fetchMissingPalletNames(requiredPallets);
+
+ if(missingPallets.length > 0) {
+ const skipMsg = `\tSkipping test '${test.test?.title}'.\n\tThe following pallets are missing:\n\t- ${missingPallets.join('\n\t- ')}`;
+ console.warn('\x1b[38:5:208m%s\x1b[0m', skipMsg);
+ test.skip();
+ }
+}
+
+export function itSub(name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: readonly string[] } = {}) {
+ (opts.only ? it.only :
+ opts.skip ? it.skip : it)(name, async function () {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ if(opts.requiredPallets) {
+ requirePalletsOrSkip(this, helper, opts.requiredPallets);
+ }
+
+ await cb({helper, privateKey});
+ });
+ });
+}
+export function itSubIfWithPallet(name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: readonly string[] } = {}) {
+ return itSub(name, cb, {requiredPallets: required, ...opts});
+}
+itSub.only = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSub(name, cb, {only: true});
+itSub.skip = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSub(name, cb, {skip: true});
+
+itSubIfWithPallet.only = (name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSubIfWithPallet(name, required, cb, {only: true});
+itSubIfWithPallet.skip = (name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSubIfWithPallet(name, required, cb, {skip: true});
+itSub.ifWithPallets = itSubIfWithPallet;
+
+export type SchedKind = 'anon' | 'named';
+
+export function itSched(
+ name: string,
+ cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any,
+ opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {},
+) {
+ itSub(name + ' (anonymous scheduling)', (apis) => cb('anon', apis), opts);
+ itSub(name + ' (named scheduling)', (apis) => cb('named', apis), opts);
+}
+itSched.only = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSched(name, cb, {only: true});
+itSched.skip = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSched(name, cb, {skip: true});
+itSched.ifWithPallets = itSchedIfWithPallets;
+
+function itSchedIfWithPallets(name: string, required: string[], cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
+ return itSched(name, cb, {requiredPallets: required, ...opts});
+}
+
+export function describeXCM(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) {
+ (process.env.RUN_XCM_TESTS && !opts.skip
+ ? describe
+ : describe.skip)(title, fn);
+}
+
+describeXCM.skip = (name: string, fn: (this: Mocha.Suite) => void) => describeXCM(name, fn, {skip: true});
+
+export function describeGov(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) {
+ (process.env.RUN_GOV_TESTS && !opts.skip
+ ? describe
+ : describe.skip)(title, fn);
+}
+
+describeGov.skip = (name: string, fn: (this: Mocha.Suite) => void) => describeGov(name, fn, {skip: true});
+
+export function sizeOfInt(i: number) {
+ if(i < 0 || i > 0xffffffff) throw new Error('out of range');
+ if(i < 0b11_1111) {
+ return 1;
+ } else if(i < 0b11_1111_1111_1111) {
+ return 2;
+ } else if(i < 0b11_1111_1111_1111_1111_1111_1111_1111) {
+ return 4;
+ } else {
+ return 5;
+ }
+}
+
+const UTF8_ENCODER = new TextEncoder();
+export function sizeOfEncodedStr(v: string) {
+ const encoded = UTF8_ENCODER.encode(v);
+ return sizeOfInt(encoded.length) + encoded.length;
+}
+
+export function sizeOfProperty(prop: {key: string, value: string}) {
+ return sizeOfEncodedStr(prop.key) + sizeOfEncodedStr(prop.value);
+}
+
+export function makeNames(url: string) {
+ const filename = fileURLToPath(url);
+ return {
+ filename,
+ dirname: dirname(filename),
+ };
+}
js-packages/test-utils/xcm/index.tsdiffbeforeafterboth--- /dev/null
+++ b/js-packages/test-utils/xcm/index.ts
@@ -0,0 +1,449 @@
+import {ApiPromise, WsProvider} from '@polkadot/api';
+import type {IKeyringPair} from '@polkadot/types/types';
+import {ChainHelperBase, EthereumBalanceGroup, HelperGroup, SubstrateBalanceGroup, UniqueHelper} from '@unique-nft/playgrounds/unique.js';
+import type {ILogger, TSigner} from '@unique-nft/playgrounds/types.js';
+import type {AcalaAssetMetadata, DemocracyStandardAccountVote, MoonbeamAssetInfo} from './types.js';
+
+
+export class XcmChainHelper extends ChainHelperBase {
+ override async connect(wsEndpoint: string, _listeners?: any): Promise<void> {
+ const wsProvider = new WsProvider(wsEndpoint);
+ this.api = new ApiPromise({
+ provider: wsProvider,
+ });
+ await this.api.isReadyOrError;
+ this.network = await UniqueHelper.detectNetwork(this.api);
+ }
+}
+
+class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {
+ async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);
+ }
+}
+
+class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {
+ makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {
+ const apiPrefix = 'api.tx.assetManager.';
+
+ const registerTx = this.helper.constructApiCall(
+ apiPrefix + 'registerForeignAsset',
+ [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],
+ );
+
+ const setUnitsTx = this.helper.constructApiCall(
+ apiPrefix + 'setAssetUnitsPerSecond',
+ [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],
+ );
+
+ const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);
+ const encodedProposal = batchCall?.method.toHex() || '';
+ return encodedProposal;
+ }
+
+ async assetTypeId(location: any) {
+ return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);
+ }
+}
+
+class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {
+ notePreimagePallet: string;
+
+ constructor(helper: MoonbeamHelper, options: { [key: string]: any } = {}) {
+ super(helper);
+ this.notePreimagePallet = options.notePreimagePallet;
+ }
+
+ async notePreimage(signer: TSigner, encodedProposal: string) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.notePreimagePallet}.notePreimage`, [encodedProposal], true);
+ }
+
+ externalProposeMajority(proposal: any) {
+ return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposal]);
+ }
+
+ fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {
+ return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);
+ }
+
+ async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);
+ }
+}
+
+class DemocracyGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+ notePreimagePallet: string;
+
+ constructor(helper: T, options: { [key: string]: any } = {}) {
+ super(helper);
+ this.notePreimagePallet = options.notePreimagePallet;
+ }
+
+ async notePreimage(signer: TSigner, encodedProposal: string) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.notePreimagePallet}.notePreimage`, [encodedProposal], true);
+ }
+
+ externalProposeMajority(proposal: any) {
+ return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposal]);
+ }
+
+ fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {
+ return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);
+ }
+
+ async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);
+ }
+}
+
+class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {
+ collective: string;
+
+ constructor(helper: MoonbeamHelper, collective: string) {
+ super(helper);
+
+ this.collective = collective;
+ }
+
+ async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);
+ }
+
+ async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);
+ }
+
+ async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: any, lengthBound: number) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);
+ }
+
+ async proposalCount() {
+ return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));
+ }
+}
+
+class CollectiveGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+ collective: string;
+
+ constructor(helper: T, palletName: string) {
+ super(helper);
+
+ this.collective = palletName;
+ }
+
+ async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);
+ }
+
+ async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);
+ }
+
+ async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: any, lengthBound: number) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);
+ }
+
+ async proposalCount() {
+ return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));
+ }
+}
+class PolkadexXcmHelperGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+ async whitelistToken(signer: TSigner, assetId: any) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.xcmHelper.whitelistToken', [assetId], true);
+ }
+}
+
+export class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {
+ async register(signer: TSigner, assetId: any, name: string, tokenPrefix: string, mode: 'NFT' | { Fungible: number }) {
+ await this.helper.executeExtrinsic(
+ signer,
+ 'api.tx.foreignAssets.forceRegisterForeignAsset',
+ [{V3: assetId}, this.helper.util.str2vec(name), tokenPrefix, mode],
+ true,
+ );
+ }
+
+ async foreignCollectionId(assetId: any) {
+ return (await this.helper.callRpc('api.query.foreignAssets.foreignAssetToCollection', [assetId])).toJSON();
+ }
+}
+
+export class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+ palletName: string;
+
+ constructor(helper: T, palletName: string) {
+ super(helper);
+
+ this.palletName = palletName;
+ }
+
+ async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: any) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, weightLimit], true);
+ }
+
+ async setSafeXcmVersion(signer: TSigner, version: number) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.forceDefaultXcmVersion`, [version], true);
+ }
+
+ async teleportAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number) {
+ await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.teleportAssets`, [destination, beneficiary, assets, feeAssetItem], true);
+ }
+
+ async teleportNativeAsset(signer: TSigner, destinationParaId: number, targetAccount: Uint8Array, amount: bigint, xcmVersion = 3) {
+ const destinationContent = {
+ parents: 0,
+ interior: {
+ X1: {
+ Parachain: destinationParaId,
+ },
+ },
+ };
+
+ const beneficiaryContent = {
+ parents: 0,
+ interior: {
+ X1: {
+ AccountId32: {
+ network: 'Any',
+ id: targetAccount,
+ },
+ },
+ },
+ };
+
+ const assetsContent = [
+ {
+ id: {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ fun: {
+ Fungible: amount,
+ },
+ },
+ ];
+
+ let destination;
+ let beneficiary;
+ let assets;
+
+ if(xcmVersion == 2) {
+ destination = {V1: destinationContent};
+ beneficiary = {V1: beneficiaryContent};
+ assets = {V1: assetsContent};
+
+ } else if(xcmVersion == 3) {
+ destination = {V2: destinationContent};
+ beneficiary = {V2: beneficiaryContent};
+ assets = {V2: assetsContent};
+
+ } else {
+ throw Error('Unknown XCM version: ' + xcmVersion);
+ }
+
+ const feeAssetItem = 0;
+
+ await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);
+ }
+
+ async send(signer: IKeyringPair, destination: any, message: any) {
+ await this.helper.executeExtrinsic(
+ signer,
+ `api.tx.${this.palletName}.send`,
+ [
+ destination,
+ message,
+ ],
+ true,
+ );
+ }
+}
+
+export class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+ async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: any) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);
+ }
+
+ async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: any) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);
+ }
+
+ async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: any) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);
+ }
+}
+
+
+
+export class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+ async accounts(address: string, currencyId: any) {
+ const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;
+ return BigInt(free);
+ }
+}
+
+export class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+ async create(signer: TSigner, assetId: number | bigint, admin: string, minimalBalance: bigint) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);
+ }
+
+ async forceCreate(signer: TSigner, assetId: number | bigint, admin: string, minimalBalance: bigint, isSufficient = true) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.assets.forceCreate', [assetId, admin, isSufficient, minimalBalance], true);
+ }
+
+ async setMetadata(signer: TSigner, assetId: number | bigint, name: string, symbol: string, decimals: number) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);
+ }
+
+ async mint(signer: TSigner, assetId: number | bigint, beneficiary: string, amount: bigint) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);
+ }
+
+ async assetInfo(assetId: number | bigint) {
+ return (await this.helper.callRpc('api.query.assets.asset', [assetId])).toJSON();
+ }
+
+ async account(assetId: string | number | bigint, address: string) {
+ const accountAsset = (
+ await this.helper.callRpc('api.query.assets.account', [assetId, address])
+ ).toJSON()! as any;
+
+ if(accountAsset !== null) {
+ return BigInt(accountAsset['balance']);
+ } else {
+ return null;
+ }
+ }
+}
+
+export class RelayHelper extends XcmChainHelper {
+ balance: SubstrateBalanceGroup<RelayHelper>;
+ xcm: XcmGroup<RelayHelper>;
+
+ constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
+ super(logger, options.helperBase ?? RelayHelper);
+
+ this.balance = new SubstrateBalanceGroup(this);
+ this.xcm = new XcmGroup(this, 'xcmPallet');
+ }
+}
+
+export class WestmintHelper extends XcmChainHelper {
+ balance: SubstrateBalanceGroup<WestmintHelper>;
+ xcm: XcmGroup<WestmintHelper>;
+ assets: AssetsGroup<WestmintHelper>;
+ xTokens: XTokensGroup<WestmintHelper>;
+
+ constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
+ super(logger, options.helperBase ?? WestmintHelper);
+
+ this.balance = new SubstrateBalanceGroup(this);
+ this.xcm = new XcmGroup(this, 'polkadotXcm');
+ this.assets = new AssetsGroup(this);
+ this.xTokens = new XTokensGroup(this);
+ }
+}
+
+export class MoonbeamHelper extends XcmChainHelper {
+ balance: EthereumBalanceGroup<MoonbeamHelper>;
+ assetManager: MoonbeamAssetManagerGroup;
+ assets: AssetsGroup<MoonbeamHelper>;
+ xTokens: XTokensGroup<MoonbeamHelper>;
+ democracy: MoonbeamDemocracyGroup;
+ collective: {
+ council: MoonbeamCollectiveGroup,
+ techCommittee: MoonbeamCollectiveGroup,
+ };
+
+ constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
+ super(logger, options.helperBase ?? MoonbeamHelper);
+
+ this.balance = new EthereumBalanceGroup(this);
+ this.assetManager = new MoonbeamAssetManagerGroup(this);
+ this.assets = new AssetsGroup(this);
+ this.xTokens = new XTokensGroup(this);
+ this.democracy = new MoonbeamDemocracyGroup(this, options);
+ this.collective = {
+ council: new MoonbeamCollectiveGroup(this, 'councilCollective'),
+ techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),
+ };
+ }
+}
+
+export class AstarHelper extends XcmChainHelper {
+ balance: SubstrateBalanceGroup<AstarHelper>;
+ assets: AssetsGroup<AstarHelper>;
+ xcm: XcmGroup<AstarHelper>;
+
+ constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
+ super(logger, options.helperBase ?? AstarHelper);
+
+ this.balance = new SubstrateBalanceGroup(this);
+ this.assets = new AssetsGroup(this);
+ this.xcm = new XcmGroup(this, 'polkadotXcm');
+ }
+}
+
+export class AcalaHelper extends XcmChainHelper {
+ balance: SubstrateBalanceGroup<AcalaHelper>;
+ assetRegistry: AcalaAssetRegistryGroup;
+ xTokens: XTokensGroup<AcalaHelper>;
+ tokens: TokensGroup<AcalaHelper>;
+ xcm: XcmGroup<AcalaHelper>;
+
+ constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
+ super(logger, options.helperBase ?? AcalaHelper);
+
+ this.balance = new SubstrateBalanceGroup(this);
+ this.assetRegistry = new AcalaAssetRegistryGroup(this);
+ this.xTokens = new XTokensGroup(this);
+ this.tokens = new TokensGroup(this);
+ this.xcm = new XcmGroup(this, 'polkadotXcm');
+ }
+}
+
+export class PolkadexHelper extends XcmChainHelper {
+ assets: AssetsGroup<PolkadexHelper>;
+ balance: SubstrateBalanceGroup<PolkadexHelper>;
+ xTokens: XTokensGroup<PolkadexHelper>;
+ xcm: XcmGroup<PolkadexHelper>;
+ xcmHelper: PolkadexXcmHelperGroup<PolkadexHelper>;
+
+ constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
+ super(logger, options.helperBase ?? PolkadexHelper);
+
+ this.assets = new AssetsGroup(this);
+ this.balance = new SubstrateBalanceGroup(this);
+ this.xTokens = new XTokensGroup(this);
+ this.xcm = new XcmGroup(this, 'polkadotXcm');
+ this.xcmHelper = new PolkadexXcmHelperGroup(this);
+ }
+}
+
+export class HydraDxHelper extends XcmChainHelper {
+ balance: SubstrateBalanceGroup<HydraDxHelper>;
+ xcm: XcmGroup<HydraDxHelper>;
+ xTokens: XTokensGroup<HydraDxHelper>;
+ democracy: DemocracyGroup<HydraDxHelper>;
+ collective: {
+ council: CollectiveGroup<HydraDxHelper>,
+ techCommittee: CollectiveGroup<HydraDxHelper>,
+ };
+
+ constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
+ super(logger, options.helperBase ?? HydraDxHelper);
+ options.notePreimagePallet = options.notePreimagePallet ?? 'preimage';
+
+ this.balance = new SubstrateBalanceGroup(this);
+ this.xcm = new XcmGroup(this, 'polkadotXcm');
+ this.xTokens = new XTokensGroup(this);
+ this.democracy = new DemocracyGroup(this, options);
+ this.collective = {
+ council: new CollectiveGroup(this, 'council'),
+ techCommittee: new CollectiveGroup(this, 'technicalCommittee'),
+ };
+ }
+}
+
js-packages/test-utils/xcm/types.tsdiffbeforeafterboth--- /dev/null
+++ b/js-packages/test-utils/xcm/types.ts
@@ -0,0 +1,29 @@
+export interface AcalaAssetMetadata {
+ name: string,
+ symbol: string,
+ decimals: number,
+ minimalBalance: bigint,
+}
+
+export interface MoonbeamAssetInfo {
+ location: any,
+ metadata: {
+ name: string,
+ symbol: string,
+ decimals: number,
+ isFrozen: boolean,
+ minimalBalance: bigint,
+ },
+ existentialDeposit: bigint,
+ isSufficient: boolean,
+ unitsPerSecond: bigint,
+ numAssetsWeightHint: number,
+}
+
+export interface DemocracyStandardAccountVote {
+ balance: bigint,
+ vote: {
+ aye: boolean,
+ conviction: number,
+ },
+}
js-packages/tests/addCollectionAdmin.test.tsdiffbeforeafterboth--- a/js-packages/tests/addCollectionAdmin.test.ts
+++ b/js-packages/tests/addCollectionAdmin.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+import {itSub, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique-nft/playgrounds/types.js';
describe('Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => {
let donor: IKeyringPair;
js-packages/tests/adminTransferAndBurn.test.tsdiffbeforeafterboth--- a/js-packages/tests/adminTransferAndBurn.test.ts
+++ b/js-packages/tests/adminTransferAndBurn.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub} from './util/index.js';
+import {usingPlaygrounds, expect, itSub} from '@unique/test-utils/util.js';
describe('Integration Test: ownerCanTransfer allows admins to use only transferFrom/burnFrom:', () => {
let alice: IKeyringPair;
js-packages/tests/allowLists.test.tsdiffbeforeafterboth--- a/js-packages/tests/allowLists.test.ts
+++ b/js-packages/tests/allowLists.test.ts
@@ -15,9 +15,9 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub} from './util/index.js';
-import type {ICollectionPermissions} from '@unique/playgrounds/types.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+import {usingPlaygrounds, expect, itSub} from '@unique/test-utils/util.js';
+import type {ICollectionPermissions} from '@unique-nft/playgrounds/types.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique-nft/playgrounds/types.js';
describe('Integration Test ext. Allow list tests', () => {
let alice: IKeyringPair;
js-packages/tests/apiConsts.test.tsdiffbeforeafterboth--- a/js-packages/tests/apiConsts.test.ts
+++ b/js-packages/tests/apiConsts.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import {ApiPromise} from '@polkadot/api';
-import {usingPlaygrounds, itSub, expect, COLLECTION_HELPER, CONTRACT_HELPER} from './util/index.js';
+import {usingPlaygrounds, itSub, expect, COLLECTION_HELPER, CONTRACT_HELPER} from '@unique/test-utils/util.js';
const MAX_COLLECTION_DESCRIPTION_LENGTH = 256n;
js-packages/tests/approve.test.tsdiffbeforeafterboth--- a/js-packages/tests/approve.test.ts
+++ b/js-packages/tests/approve.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, usingPlaygrounds} from './util/index.js';
-import {CrossAccountId} from '@unique/playgrounds/unique.js';
+import {expect, itSub, Pallets, usingPlaygrounds} from '@unique/test-utils/util.js';
+import {CrossAccountId} from '@unique-nft/playgrounds/unique.js';
js-packages/tests/burnItem.test.tsdiffbeforeafterboth--- a/js-packages/tests/burnItem.test.ts
+++ b/js-packages/tests/burnItem.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from './util/index.js';
+import {expect, itSub, usingPlaygrounds} from '@unique/test-utils/util.js';
describe('integration test: ext. burnItem():', () => {
js-packages/tests/change-collection-owner.test.tsdiffbeforeafterboth--- a/js-packages/tests/change-collection-owner.test.ts
+++ b/js-packages/tests/change-collection-owner.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+import {usingPlaygrounds, expect, itSub} from '@unique/test-utils/util.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique-nft/playgrounds/types.js';
describe('Integration Test changeCollectionOwner(collection_id, new_owner):', () => {
let alice: IKeyringPair;
js-packages/tests/confirmSponsorship.test.tsdiffbeforeafterboth--- a/js-packages/tests/confirmSponsorship.test.ts
+++ b/js-packages/tests/confirmSponsorship.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub, Pallets} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+import {usingPlaygrounds, expect, itSub, Pallets} from '@unique/test-utils/util.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique-nft/playgrounds/types.js';
async function setSponsorHelper(collection: any, signer: IKeyringPair, sponsorAddress: string) {
await collection.setSponsor(signer, sponsorAddress);
js-packages/tests/connection.test.tsdiffbeforeafterboth--- a/js-packages/tests/connection.test.ts
+++ b/js-packages/tests/connection.test.ts
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-import {itSub, expect, usingPlaygrounds} from './util/index.js';
+import {itSub, expect, usingPlaygrounds} from '@unique/test-utils/util.js';
describe('Connection smoke test', () => {
itSub('Connection can be established', async ({helper}) => {
js-packages/tests/createCollection.test.tsdiffbeforeafterboth--- a/js-packages/tests/createCollection.test.ts
+++ b/js-packages/tests/createCollection.test.ts
@@ -15,10 +15,10 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub, Pallets} from './util/index.js';
-import {CollectionFlag} from '@unique/playgrounds/types.js';
-import type {ICollectionCreationOptions, IProperty} from '@unique/playgrounds/types.js';
-import {UniqueHelper} from '@unique/playgrounds/unique.js';
+import {usingPlaygrounds, expect, itSub, Pallets} from '@unique/test-utils/util.js';
+import {CollectionFlag} from '@unique-nft/playgrounds/types.js';
+import type {ICollectionCreationOptions, IProperty} from '@unique-nft/playgrounds/types.js';
+import {UniqueHelper} from '@unique-nft/playgrounds/unique.js';
async function mintCollectionHelper(helper: UniqueHelper, signer: IKeyringPair, options: ICollectionCreationOptions, type?: 'nft' | 'fungible' | 'refungible') {
let collection;
js-packages/tests/createItem.test.tsdiffbeforeafterboth--- a/js-packages/tests/createItem.test.ts
+++ b/js-packages/tests/createItem.test.ts
@@ -15,9 +15,9 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub, Pallets} from './util/index.js';
-import type {IProperty, ICrossAccountId} from '@unique/playgrounds/types.js';
-import {UniqueHelper} from '@unique/playgrounds/unique.js';
+import {usingPlaygrounds, expect, itSub, Pallets} from '@unique/test-utils/util.js';
+import type {IProperty, ICrossAccountId} from '@unique-nft/playgrounds/types.js';
+import {UniqueHelper} from '@unique-nft/playgrounds/unique.js';
async function mintTokenHelper(helper: UniqueHelper, collection: any, signer: IKeyringPair, owner: ICrossAccountId, type: 'nft' | 'fungible' | 'refungible'='nft', properties?: IProperty[]) {
let token;
js-packages/tests/createMultipleItems.test.tsdiffbeforeafterboth--- a/js-packages/tests/createMultipleItems.test.ts
+++ b/js-packages/tests/createMultipleItems.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, Pallets, itSub} from './util/index.js';
+import {usingPlaygrounds, expect, Pallets, itSub} from '@unique/test-utils/util.js';
describe('Integration Test createMultipleItems(collection_id, owner, items_data):', () => {
let alice: IKeyringPair;
js-packages/tests/createMultipleItemsEx.test.tsdiffbeforeafterboth--- a/js-packages/tests/createMultipleItemsEx.test.ts
+++ b/js-packages/tests/createMultipleItemsEx.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, Pallets, itSub} from './util/index.js';
-import type {IProperty} from '@unique/playgrounds/types.js';
+import {usingPlaygrounds, expect, Pallets, itSub} from '@unique/test-utils/util.js';
+import type {IProperty} from '@unique-nft/playgrounds/types.js';
describe('Integration Test: createMultipleItemsEx', () => {
let alice: IKeyringPair;
js-packages/tests/creditFeesToTreasury.seqtest.tsdiffbeforeafterboth--- a/js-packages/tests/creditFeesToTreasury.seqtest.ts
+++ b/js-packages/tests/creditFeesToTreasury.seqtest.ts
@@ -16,7 +16,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {ApiPromise} from '@polkadot/api';
-import {usingPlaygrounds, expect, itSub} from './util/index.js';
+import {usingPlaygrounds, expect, itSub} from '@unique/test-utils/util.js';
import type {u32} from '@polkadot/types-codec';
const TREASURY = '5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z';
js-packages/tests/destroyCollection.test.tsdiffbeforeafterboth--- a/js-packages/tests/destroyCollection.test.ts
+++ b/js-packages/tests/destroyCollection.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, expect, usingPlaygrounds, Pallets} from './util/index.js';
+import {itSub, expect, usingPlaygrounds, Pallets} from '@unique/test-utils/util.js';
describe('integration test: ext. destroyCollection():', () => {
let alice: IKeyringPair;
js-packages/tests/enableDisableTransfer.test.tsdiffbeforeafterboth--- a/js-packages/tests/enableDisableTransfer.test.ts
+++ b/js-packages/tests/enableDisableTransfer.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
+import {itSub, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
describe('Enable/Disable Transfers', () => {
let alice: IKeyringPair;
js-packages/tests/eth/allowlist.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/allowlist.test.ts
+++ b/js-packages/tests/eth/allowlist.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {Pallets} from '../util/index.js';
+import {Pallets} from '@unique/test-utils/util.js';
import {itEth, usingEthPlaygrounds, expect, SponsoringMode} from './util/index.js';
import {CreateCollectionData} from './util/playgrounds/types.js';
js-packages/tests/eth/collectionAdmin.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/collectionAdmin.test.ts
+++ b/js-packages/tests/eth/collectionAdmin.test.ts
@@ -15,8 +15,8 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {expect} from 'chai';
-import {Pallets} from '../util/index.js';
-import type {IEthCrossAccountId} from '@unique/playgrounds/types.js';
+import {Pallets} from '@unique/test-utils/util.js';
+import type {IEthCrossAccountId} from '@unique-nft/playgrounds/types.js';
import {usingEthPlaygrounds, itEth} from './util/index.js';
import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
import {CreateCollectionData} from './util/playgrounds/types.js';
js-packages/tests/eth/collectionHelperAddress.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/collectionHelperAddress.test.ts
+++ b/js-packages/tests/eth/collectionHelperAddress.test.ts
@@ -16,7 +16,7 @@
import {itEth, usingEthPlaygrounds, expect} from './util/index.js';
import type {IKeyringPair} from '@polkadot/types/types';
-import {COLLECTION_HELPER, Pallets} from '../util/index.js';
+import {COLLECTION_HELPER, Pallets} from '@unique/test-utils/util.js';
describe('[eth]CollectionHelperAddress test: ERC20/ERC721 ', () => {
let donor: IKeyringPair;
js-packages/tests/eth/collectionLimits.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/collectionLimits.test.ts
+++ b/js-packages/tests/eth/collectionLimits.test.ts
@@ -1,5 +1,5 @@
import type {IKeyringPair} from '@polkadot/types/types';
-import {Pallets} from '../util/index.js';
+import {Pallets} from '@unique/test-utils/util.js';
import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
import {CollectionLimitField, CreateCollectionData} from './util/playgrounds/types.js';
js-packages/tests/eth/collectionProperties.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/collectionProperties.test.ts
+++ b/js-packages/tests/eth/collectionProperties.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import {itEth, usingEthPlaygrounds, expect} from './util/index.js';
-import {Pallets} from '../util/index.js';
-import type {IProperty, ITokenPropertyPermission} from '@unique/playgrounds/types.js';
+import {Pallets} from '@unique/test-utils/util.js';
+import type {IProperty, ITokenPropertyPermission} from '@unique-nft/playgrounds/types.js';
import type {IKeyringPair} from '@polkadot/types/types';
describe('EVM collection properties', () => {
js-packages/tests/eth/collectionSponsoring.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/collectionSponsoring.test.ts
+++ b/js-packages/tests/eth/collectionSponsoring.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../util/index.js';
+import {Pallets, requirePalletsOrSkip, usingPlaygrounds} from '@unique/test-utils/util.js';
import {itEth, expect} from './util/index.js';
import {CollectionLimitField, TokenPermissionField} from './util/playgrounds/types.js';
js-packages/tests/eth/contractSponsoring.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/contractSponsoring.test.ts
+++ b/js-packages/tests/eth/contractSponsoring.test.ts
@@ -17,7 +17,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
import {itEth, expect, SponsoringMode, usingEthPlaygrounds} from './util/index.js';
-import {usingPlaygrounds} from '../util/index.js';
+import {usingPlaygrounds} from '@unique/test-utils/util.js';
import type {CompiledContract} from './util/playgrounds/types.js';
describe('Sponsoring EVM contracts', () => {
js-packages/tests/eth/createCollection.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/createCollection.test.ts
+++ b/js-packages/tests/eth/createCollection.test.ts
@@ -16,11 +16,11 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {evmToAddress} from '@polkadot/util-crypto';
-import {Pallets, requirePalletsOrSkip} from '../util/index.js';
+import {Pallets, requirePalletsOrSkip} from '@unique/test-utils/util.js';
import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
import {CREATE_COLLECTION_DATA_DEFAULTS, CollectionLimitField, CollectionMode, CreateCollectionData, TokenPermissionField, emptyAddress} from './util/playgrounds/types.js';
-import {CollectionFlag} from '@unique/playgrounds/types.js';
-import type {IEthCrossAccountId, TCollectionMode} from '@unique/playgrounds/types.js';
+import {CollectionFlag} from '@unique-nft/playgrounds/types.js';
+import type {IEthCrossAccountId, TCollectionMode} from '@unique-nft/playgrounds/types.js';
const DECIMALS = 18;
const CREATE_COLLECTION_DATA_DEFAULTS_ARRAY = [
js-packages/tests/eth/createFTCollection.seqtest.tsdiffbeforeafterboth--- a/js-packages/tests/eth/createFTCollection.seqtest.ts
+++ b/js-packages/tests/eth/createFTCollection.seqtest.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {Pallets, requirePalletsOrSkip} from '../util/index.js';
+import {Pallets, requirePalletsOrSkip} from '@unique/test-utils/util.js';
import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
const DECIMALS = 18;
js-packages/tests/eth/createFTCollection.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/createFTCollection.test.ts
+++ b/js-packages/tests/eth/createFTCollection.test.ts
@@ -16,7 +16,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {evmToAddress} from '@polkadot/util-crypto';
-import {Pallets, requirePalletsOrSkip} from '../util/index.js';
+import {Pallets, requirePalletsOrSkip} from '@unique/test-utils/util.js';
import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
import {CollectionLimitField} from './util/playgrounds/types.js';
js-packages/tests/eth/createRFTCollection.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/createRFTCollection.test.ts
+++ b/js-packages/tests/eth/createRFTCollection.test.ts
@@ -16,7 +16,7 @@
import {evmToAddress} from '@polkadot/util-crypto';
import type {IKeyringPair} from '@polkadot/types/types';
-import {Pallets, requirePalletsOrSkip} from '../util/index.js';
+import {Pallets, requirePalletsOrSkip} from '@unique/test-utils/util.js';
import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
import {CollectionLimitField} from './util/playgrounds/types.js';
js-packages/tests/eth/crossTransfer.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/crossTransfer.test.ts
+++ b/js-packages/tests/eth/crossTransfer.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import {itEth, usingEthPlaygrounds} from './util/index.js';
-import {CrossAccountId} from '@unique/playgrounds/unique.js';
+import {CrossAccountId} from '@unique-nft/playgrounds/unique.js';
import type {IKeyringPair} from '@polkadot/types/types';
describe('Token transfer between substrate address and EVM address. Fungible', () => {
js-packages/tests/eth/destroyCollection.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/destroyCollection.test.ts
+++ b/js-packages/tests/eth/destroyCollection.test.ts
@@ -15,9 +15,9 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {Pallets} from '../util/index.js';
+import {Pallets} from '@unique/test-utils/util.js';
import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
-import type {TCollectionMode} from '@unique/playgrounds/types.js';
+import type {TCollectionMode} from '@unique-nft/playgrounds/types.js';
import {CreateCollectionData} from './util/playgrounds/types.js';
describe('Destroy Collection from EVM', function() {
js-packages/tests/eth/events.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/events.test.ts
+++ b/js-packages/tests/eth/events.test.ts
@@ -18,8 +18,8 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {itEth, usingEthPlaygrounds} from './util/index.js';
import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
-import type {IEvent, TCollectionMode} from '@unique/playgrounds/types.js';
-import {Pallets, requirePalletsOrSkip} from '../util/index.js';
+import type {IEvent, TCollectionMode} from '@unique-nft/playgrounds/types.js';
+import {Pallets, requirePalletsOrSkip} from '@unique/test-utils/util.js';
import {CollectionLimitField, TokenPermissionField, CreateCollectionData} from './util/playgrounds/types.js';
import type {NormalizedEvent} from './util/playgrounds/types.js';
js-packages/tests/eth/fractionalizer/fractionalizer.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/fractionalizer/fractionalizer.test.ts
+++ b/js-packages/tests/eth/fractionalizer/fractionalizer.test.ts
@@ -25,7 +25,7 @@
import {usingEthPlaygrounds, expect, itEth} from '../util/index.js';
import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js';
import type {CompiledContract} from '../util/playgrounds/types.js';
-import {requirePalletsOrSkip, Pallets, makeNames} from '../../util/index.js';
+import {requirePalletsOrSkip, Pallets, makeNames} from '@unique/test-utils/util.js';
const {dirname} = makeNames(import.meta.url);
js-packages/tests/eth/getCode.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/getCode.test.ts
+++ b/js-packages/tests/eth/getCode.test.ts
@@ -16,7 +16,7 @@
import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
import type {IKeyringPair} from '@polkadot/types/types';
-import {COLLECTION_HELPER, CONTRACT_HELPER} from '../util/index.js';
+import {COLLECTION_HELPER, CONTRACT_HELPER} from '@unique/test-utils/util.js';
describe('RPC eth_getCode', () => {
let donor: IKeyringPair;
js-packages/tests/eth/marketplace-v2/marketplace.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/marketplace-v2/marketplace.test.ts
+++ b/js-packages/tests/eth/marketplace-v2/marketplace.test.ts
@@ -19,8 +19,7 @@
import {readFile} from 'fs/promises';
import {SponsoringMode, itEth, usingEthPlaygrounds} from '../util/index.js';
import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js';
-import {makeNames} from '../../util/index.js';
-import {expect} from 'chai';
+import {makeNames, expect} from '@unique/test-utils/util.js';
const {dirname} = makeNames(import.meta.url);
js-packages/tests/eth/marketplace/marketplace.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/marketplace/marketplace.test.ts
+++ b/js-packages/tests/eth/marketplace/marketplace.test.ts
@@ -17,7 +17,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {readFile} from 'fs/promises';
import {itEth, usingEthPlaygrounds, expect, SponsoringMode} from '../util/index.js';
-import {makeNames} from '../../util/index.js';
+import {makeNames} from '@unique/test-utils/util.js';
const {dirname} = makeNames(import.meta.url);
js-packages/tests/eth/migration.seqtest.tsdiffbeforeafterboth--- a/js-packages/tests/eth/migration.seqtest.ts
+++ b/js-packages/tests/eth/migration.seqtest.ts
@@ -18,7 +18,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {Struct} from '@polkadot/types';
-import type {IEvent} from '@unique/playgrounds/types.js';
+import type {IEvent} from '@unique-nft/playgrounds/types.js';
import type {InterfaceTypes} from '@polkadot/types/types/registry';
import {ApiPromise} from '@polkadot/api';
js-packages/tests/eth/nativeFungible.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/nativeFungible.test.ts
+++ b/js-packages/tests/eth/nativeFungible.test.ts
@@ -16,7 +16,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
-import {UniqueHelper} from '@unique/playgrounds/unique.js';
+import {UniqueHelper} from '@unique-nft/playgrounds/unique.js';
describe('NativeFungible: ERC20 calls', () => {
let donor: IKeyringPair;
js-packages/tests/eth/nonFungible.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/nonFungible.test.ts
+++ b/js-packages/tests/eth/nonFungible.test.ts
@@ -18,7 +18,7 @@
import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
import type {IKeyringPair} from '@polkadot/types/types';
import {Contract} from 'web3-eth-contract';
-import type {ITokenPropertyPermission} from '@unique/playgrounds/types.js';
+import type {ITokenPropertyPermission} from '@unique-nft/playgrounds/types.js';
import {CREATE_COLLECTION_DATA_DEFAULTS, TokenPermissionField} from './util/playgrounds/types.js';
describe('Check ERC721 token URI for NFT', () => {
js-packages/tests/eth/payable.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/payable.test.ts
+++ b/js-packages/tests/eth/payable.test.ts
@@ -18,7 +18,7 @@
import {itEth, expect, usingEthPlaygrounds} from './util/index.js';
import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
-import {makeNames} from '../util/index.js';
+import {makeNames} from '@unique/test-utils/util.js';
const {dirname} = makeNames(import.meta.url);
js-packages/tests/eth/proxy/fungibleProxy.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/proxy/fungibleProxy.test.ts
+++ b/js-packages/tests/eth/proxy/fungibleProxy.test.ts
@@ -19,7 +19,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {itEth, usingEthPlaygrounds} from '../util/index.js';
import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js';
-import {makeNames} from '../../util/index.js';
+import {makeNames} from '@unique/test-utils/util.js';
const {dirname} = makeNames(import.meta.url);
js-packages/tests/eth/proxy/nonFungibleProxy.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/proxy/nonFungibleProxy.test.ts
+++ b/js-packages/tests/eth/proxy/nonFungibleProxy.test.ts
@@ -18,7 +18,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {itEth, usingEthPlaygrounds, expect} from '../util/index.js';
import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js';
-import {makeNames} from '../../util/index.js';
+import {makeNames} from '@unique/test-utils/util.js';
const {dirname} = makeNames(import.meta.url);
js-packages/tests/eth/reFungible.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/reFungible.test.ts
+++ b/js-packages/tests/eth/reFungible.test.ts
@@ -14,10 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-import {Pallets, requirePalletsOrSkip} from '../util/index.js';
+import {Pallets, requirePalletsOrSkip} from '@unique/test-utils/util.js';
import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
import type {IKeyringPair} from '@polkadot/types/types';
-import type {ITokenPropertyPermission} from '@unique/playgrounds/types.js';
+import type {ITokenPropertyPermission} from '@unique-nft/playgrounds/types.js';
import {CREATE_COLLECTION_DATA_DEFAULTS, TokenPermissionField} from './util/playgrounds/types.js';
describe('Refungible: Plain calls', () => {
js-packages/tests/eth/reFungibleToken.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/reFungibleToken.test.ts
+++ b/js-packages/tests/eth/reFungibleToken.test.ts
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-import {Pallets, requirePalletsOrSkip} from '../util/index.js';
+import {Pallets, requirePalletsOrSkip} from '@unique/test-utils/util.js';
import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
import type {IKeyringPair} from '@polkadot/types/types';
js-packages/tests/eth/scheduling.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/scheduling.test.ts
+++ b/js-packages/tests/eth/scheduling.test.ts
@@ -17,7 +17,7 @@
import {expect} from 'chai';
import {itSchedEth} from './util/index.js';
import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
-import {Pallets, usingPlaygrounds} from '../util/index.js';
+import {Pallets, usingPlaygrounds} from '@unique/test-utils/util.js';
describe('Scheduing EVM smart contracts', () => {
js-packages/tests/eth/sponsoring.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/sponsoring.test.ts
+++ b/js-packages/tests/eth/sponsoring.test.ts
@@ -16,7 +16,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {itEth, expect, SponsoringMode} from './util/index.js';
-import {usingPlaygrounds} from '../util/index.js';
+import {usingPlaygrounds} from '@unique/test-utils/util.js';
describe('EVM sponsoring', () => {
let donor: IKeyringPair;
js-packages/tests/eth/tokenProperties.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/tokenProperties.test.ts
+++ b/js-packages/tests/eth/tokenProperties.test.ts
@@ -17,9 +17,9 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {Contract} from 'web3-eth-contract';
import {itEth, usingEthPlaygrounds, expect} from './util/index.js';
-import type {ITokenPropertyPermission} from '@unique/playgrounds/types.js';
-import {Pallets} from '../util/index.js';
-import {UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection} from '@unique/playgrounds/unique.js';
+import type {ITokenPropertyPermission} from '@unique-nft/playgrounds/types.js';
+import {Pallets} from '@unique/test-utils/util.js';
+import {UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection} from '@unique-nft/playgrounds/unique.js';
import {CreateCollectionData, TokenPermissionField} from './util/playgrounds/types.js';
describe('EVM token properties', () => {
js-packages/tests/eth/tokens/callMethodsERC20.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/tokens/callMethodsERC20.test.ts
+++ b/js-packages/tests/eth/tokens/callMethodsERC20.test.ts
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-import {Pallets, requirePalletsOrSkip} from '../../util/index.js';
+import {Pallets, requirePalletsOrSkip} from '@unique/test-utils/util.js';
import {expect, itEth, usingEthPlaygrounds} from '../util/index.js';
import type {IKeyringPair} from '@polkadot/types/types';
import {CreateCollectionData} from '../util/playgrounds/types.js';
js-packages/tests/eth/tokens/callMethodsERC721.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/tokens/callMethodsERC721.test.ts
+++ b/js-packages/tests/eth/tokens/callMethodsERC721.test.ts
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-import {Pallets} from '../../util/index.js';
+import {Pallets} from '@unique/test-utils/util.js';
import {expect, itEth, usingEthPlaygrounds} from '../util/index.js';
import type {IKeyringPair} from '@polkadot/types/types';
import {CreateCollectionData} from '../util/playgrounds/types.js';
js-packages/tests/eth/tokens/minting.test.tsdiffbeforeafterboth--- a/js-packages/tests/eth/tokens/minting.test.ts
+++ b/js-packages/tests/eth/tokens/minting.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {Pallets} from '../../util/index.js';
+import {Pallets} from '@unique/test-utils/util.js';
import {expect, itEth, usingEthPlaygrounds} from '../util/index.js';
import {CreateCollectionData} from '../util/playgrounds/types.js';
js-packages/tests/eth/util/index.tsdiffbeforeafterboth--- a/js-packages/tests/eth/util/index.ts
+++ b/js-packages/tests/eth/util/index.ts
@@ -7,14 +7,13 @@
import config from '../../config.js';
import {EthUniqueHelper} from './playgrounds/unique.dev.js';
-import {SilentLogger, SilentConsole} from '@unique/playgrounds/unique.dev.js';
-import {makeNames} from '../../util/index.js';
-import type {SchedKind} from '../../util/index.js';
+import {SilentLogger, SilentConsole} from '@unique/test-utils';
+import type {SchedKind} from '@unique/test-utils/util.js';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import chaiLike from 'chai-like';
-import {getTestSeed, MINIMUM_DONOR_FUND, requirePalletsOrSkip} from '../../util/index.js';
+import {getTestSeed, MINIMUM_DONOR_FUND, requirePalletsOrSkip, makeNames} from '@unique/test-utils/util.js';
chai.use(chaiAsPromised);
chai.use(chaiLike);
js-packages/tests/eth/util/playgrounds/types.tsdiffbeforeafterboth--- a/js-packages/tests/eth/util/playgrounds/types.ts
+++ b/js-packages/tests/eth/util/playgrounds/types.ts
@@ -1,5 +1,5 @@
-import {CollectionFlag} from '@unique/playgrounds/types.js';
-import type {TCollectionMode} from '@unique/playgrounds/types.js';
+import {CollectionFlag} from '@unique-nft/playgrounds/types.js';
+import type {TCollectionMode} from '@unique-nft/playgrounds/types.js';
export interface ContractImports {
solPath: string;
js-packages/tests/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/js-packages/tests/eth/util/playgrounds/unique.dev.ts
+++ b/js-packages/tests/eth/util/playgrounds/unique.dev.ts
@@ -15,7 +15,7 @@
import {evmToAddress} from '@polkadot/util-crypto';
import type {IKeyringPair} from '@polkadot/types/types';
-import {ArrangeGroup, DevUniqueHelper} from '@unique/playgrounds/unique.dev.js';
+import {ArrangeGroup, DevUniqueHelper} from '@unique/test-utils/index.js';
import type {ContractImports, CompiledContract, CrossAddress, NormalizedEvent, EthProperty} from './types.js';
import {CollectionMode, CreateCollectionData} from './types.js';
@@ -32,7 +32,7 @@
import refungibleTokenAbi from '../../abi/reFungibleToken.json' assert {type: 'json'};
import refungibleTokenDeprecatedAbi from '../../abi/reFungibleTokenDeprecated.json' assert {type: 'json'};
import contractHelpersAbi from '../../abi/contractHelpers.json' assert {type: 'json'};
-import type {ICrossAccountId, TEthereumAccount, TCollectionMode} from '@unique/playgrounds/types.js';
+import type {ICrossAccountId, TEthereumAccount, TCollectionMode} from '@unique-nft/playgrounds/types.js';
class EthGroupBase {
helper: EthUniqueHelper;
js-packages/tests/fungible.test.tsdiffbeforeafterboth--- a/js-packages/tests/fungible.test.ts
+++ b/js-packages/tests/fungible.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect, requirePalletsOrSkip, Pallets} from './util/index.js';
+import {itSub, usingPlaygrounds, expect, requirePalletsOrSkip, Pallets} from '@unique/test-utils/util.js';
const U128_MAX = (1n << 128n) - 1n;
js-packages/tests/getPropertiesRpc.test.tsdiffbeforeafterboth--- a/js-packages/tests/getPropertiesRpc.test.ts
+++ b/js-packages/tests/getPropertiesRpc.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
-import {UniqueHelper, UniqueNFTCollection} from '@unique/playgrounds/unique.js';
+import {itSub, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
+import {UniqueHelper, UniqueNFTCollection} from '@unique-nft/playgrounds/unique.js';
const collectionProps = [
{key: 'col-0', value: 'col-0-value'},
js-packages/tests/inflation.seqtest.tsdiffbeforeafterboth--- a/js-packages/tests/inflation.seqtest.ts
+++ b/js-packages/tests/inflation.seqtest.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from './util/index.js';
+import {expect, itSub, usingPlaygrounds} from '@unique/test-utils/util.js';
const TREASURY = '5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z';
js-packages/tests/limits.test.tsdiffbeforeafterboth--- a/js-packages/tests/limits.test.ts
+++ b/js-packages/tests/limits.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util/index.js';
+import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from '@unique/test-utils/util.js';
describe('Number of tokens per address (NFT)', () => {
let alice: IKeyringPair;
js-packages/tests/maintenance.seqtest.tsdiffbeforeafterboth--- a/js-packages/tests/maintenance.seqtest.ts
+++ b/js-packages/tests/maintenance.seqtest.ts
@@ -16,7 +16,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {ApiPromise} from '@polkadot/api';
-import {expect, itSched, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util/index.js';
+import {expect, itSched, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from '@unique/test-utils/util.js';
import {itEth} from './eth/util/index.js';
import {main as correctState} from './migrations/correctStateAfterMaintenance.js';
import type {PalletBalancesIdAmount} from '@polkadot/types/lookup';
js-packages/tests/migrations/942057-appPromotion/index.tsdiffbeforeafterboth--- a/js-packages/tests/migrations/942057-appPromotion/index.ts
+++ b/js-packages/tests/migrations/942057-appPromotion/index.ts
@@ -1,4 +1,4 @@
-import type {Migration} from '../../util/frankensteinMigrate.js';
+import type {Migration} from '../index.js';
import {collectData} from './collectData.js';
import {migrateLockedToFreeze} from './lockedToFreeze.js';
js-packages/tests/migrations/942057-appPromotion/lockedToFreeze.tsdiffbeforeafterboth--- a/js-packages/tests/migrations/942057-appPromotion/lockedToFreeze.ts
+++ b/js-packages/tests/migrations/942057-appPromotion/lockedToFreeze.ts
@@ -1,6 +1,6 @@
// import { usingApi, privateKey, onlySign } from "./../../load/lib";
import * as fs from 'fs';
-import {usingPlaygrounds} from '../../util/index.js';
+import {usingPlaygrounds} from '@unique/test-utils/util.js';
import path, {dirname} from 'path';
import {isInteger, parse} from 'lossless-json';
import {fileURLToPath} from 'url';
js-packages/tests/migrations/correctStateAfterMaintenance.tsdiffbeforeafterboth--- a/js-packages/tests/migrations/correctStateAfterMaintenance.ts
+++ b/js-packages/tests/migrations/correctStateAfterMaintenance.ts
@@ -1,5 +1,5 @@
import config from '../config.js';
-import {usingPlaygrounds} from '../util/index.js';
+import {usingPlaygrounds} from '@unique/test-utils/util.js';
import type {u32} from '@polkadot/types-codec';
const WS_ENDPOINT = config.substrateUrl;
js-packages/tests/migrations/index.tsdiffbeforeafterboth--- /dev/null
+++ b/js-packages/tests/migrations/index.ts
@@ -0,0 +1,9 @@
+import {migration as locksToFreezesMigration} from './942057-appPromotion/index.js';
+export interface Migration {
+ before: () => Promise<void>,
+ after: () => Promise<void>,
+}
+
+export const migrations: {[key: string]: Migration} = {
+ 'v942057': locksToFreezesMigration,
+};
\ No newline at end of file
js-packages/tests/nativeFungible.test.tsdiffbeforeafterboth--- a/js-packages/tests/nativeFungible.test.ts
+++ b/js-packages/tests/nativeFungible.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from './util/index.js';
+import {expect, itSub, usingPlaygrounds} from '@unique/test-utils/util.js';
describe('Native fungible', () => {
let root: IKeyringPair;
js-packages/tests/nextSponsoring.test.tsdiffbeforeafterboth--- a/js-packages/tests/nextSponsoring.test.ts
+++ b/js-packages/tests/nextSponsoring.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, usingPlaygrounds} from './util/index.js';
+import {expect, itSub, Pallets, usingPlaygrounds} from '@unique/test-utils/util.js';
const SPONSORING_TIMEOUT = 5;
js-packages/tests/package.jsondiffbeforeafterboth--- a/js-packages/tests/package.json
+++ b/js-packages/tests/package.json
@@ -13,9 +13,9 @@
"mocha": "^10.1.0"
},
"scripts": {
- "setup": "yarn node --no-warnings=ExperimentalWarning --loader ts-node/esm ./util/globalSetup.ts",
- "setIdentities": "yarn node --no-warnings=ExperimentalWarning --loader ts-node/esm ./util/identitySetter.ts",
- "checkRelayIdentities": "yarn node --no-warnings=ExperimentalWarning --loader ts-node/esm ./util/relayIdentitiesChecker.ts",
+ "setup": "yarn node --no-warnings=ExperimentalWarning --loader ts-node/esm ../test-utils/globalSetup.ts",
+ "setIdentities": "yarn node --no-warnings=ExperimentalWarning --loader ts-node/esm ../scripts/identitySetter.ts",
+ "checkRelayIdentities": "yarn node --no-warnings=ExperimentalWarning --loader ts-node/esm ../scripts/relayIdentitiesChecker.ts",
"_test": "yarn setup && yarn mocha --timeout 9999999 --loader=ts-node/esm.mjs",
"_testParallel": "yarn setup && yarn mocha --timeout 9999999 --parallel --loader=ts-node/esm.mjs",
"test": "yarn _test './**/*.*test.ts'",
js-packages/tests/pallet-presence.test.tsdiffbeforeafterboth--- a/js-packages/tests/pallet-presence.test.ts
+++ b/js-packages/tests/pallet-presence.test.ts
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
+import {itSub, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
// Pallets that must always be present
const requiredPallets = [
js-packages/tests/performance.seq.test.tsdiffbeforeafterboth--- a/js-packages/tests/performance.seq.test.ts
+++ b/js-packages/tests/performance.seq.test.ts
@@ -16,9 +16,9 @@
import {ApiPromise} from '@polkadot/api';
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from './util/index.js';
-import type {ICrossAccountId, IProperty} from '@unique/playgrounds/types.js';
-import {UniqueHelper} from '@unique/playgrounds/unique.js';
+import {expect, itSub, usingPlaygrounds} from '@unique/test-utils/util.js';
+import type {ICrossAccountId, IProperty} from '@unique-nft/playgrounds/types.js';
+import {UniqueHelper} from '@unique-nft/playgrounds/unique.js';
describe('Performace tests', () => {
let alice: IKeyringPair;
js-packages/tests/refungible.test.tsdiffbeforeafterboth--- a/js-packages/tests/refungible.test.ts
+++ b/js-packages/tests/refungible.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from './util/index.js';
+import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
const MAX_REFUNGIBLE_PIECES = 1_000_000_000_000_000_000_000n;
js-packages/tests/removeCollectionAdmin.test.tsdiffbeforeafterboth--- a/js-packages/tests/removeCollectionAdmin.test.ts
+++ b/js-packages/tests/removeCollectionAdmin.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+import {itSub, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique-nft/playgrounds/types.js';
describe('Integration Test removeCollectionAdmin(collection_id, account_id):', () => {
let alice: IKeyringPair;
js-packages/tests/removeCollectionSponsor.test.tsdiffbeforeafterboth--- a/js-packages/tests/removeCollectionSponsor.test.ts
+++ b/js-packages/tests/removeCollectionSponsor.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+import {itSub, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique-nft/playgrounds/types.js';
describe('integration test: ext. removeCollectionSponsor():', () => {
let donor: IKeyringPair;
js-packages/tests/rpc.test.tsdiffbeforeafterboth--- a/js-packages/tests/rpc.test.ts
+++ b/js-packages/tests/rpc.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, itSub, expect} from './util/index.js';
-import {ICrossAccountId} from '@unique/playgrounds/types.js';
+import {usingPlaygrounds, itSub, expect} from '@unique/test-utils/util.js';
+import {ICrossAccountId} from '@unique-nft/playgrounds/types.js';
describe('integration test: RPC methods', () => {
let donor: IKeyringPair;
js-packages/tests/scheduler.seqtest.tsdiffbeforeafterboth--- a/js-packages/tests/scheduler.seqtest.ts
+++ b/js-packages/tests/scheduler.seqtest.ts
@@ -14,9 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-import {expect, itSched, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util/index.js';
+import {expect, itSched, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from '@unique/test-utils/util.js';
import type {IKeyringPair} from '@polkadot/types/types';
-import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js';
+import {DevUniqueHelper, Event} from '@unique/test-utils';
describe('Scheduling token and balance transfers', () => {
let superuser: IKeyringPair;
js-packages/tests/setCollectionLimits.test.tsdiffbeforeafterboth--- a/js-packages/tests/setCollectionLimits.test.ts
+++ b/js-packages/tests/setCollectionLimits.test.ts
@@ -16,8 +16,8 @@
// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+import {itSub, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique-nft/playgrounds/types.js';
const accountTokenOwnershipLimit = 0;
const sponsoredDataSize = 0;
js-packages/tests/setCollectionSponsor.test.tsdiffbeforeafterboth--- a/js-packages/tests/setCollectionSponsor.test.ts
+++ b/js-packages/tests/setCollectionSponsor.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect, Pallets} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+import {itSub, usingPlaygrounds, expect, Pallets} from '@unique/test-utils/util.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique-nft/playgrounds/types.js';
describe('integration test: ext. setCollectionSponsor():', () => {
let alice: IKeyringPair;
js-packages/tests/setPermissions.test.tsdiffbeforeafterboth--- a/js-packages/tests/setPermissions.test.ts
+++ b/js-packages/tests/setPermissions.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+import {itSub, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique-nft/playgrounds/types.js';
describe('Integration Test: Set Permissions', () => {
let alice: IKeyringPair;
js-packages/tests/sub/appPromotion/appPromotion.seqtest.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import type {IKeyringPair} from '@polkadot/types/types';18import {itSub, usingPlaygrounds, Pallets, requirePalletsOrSkip} from '../../util/index.js';19import {expect} from '../../eth/util/index.js';2021let superuser: IKeyringPair;22let donor: IKeyringPair;23let palletAdmin: IKeyringPair;2425describe('App promotion', () => {26 before(async function () {27 await usingPlaygrounds(async (helper, privateKey) => {28 requirePalletsOrSkip(this, helper, [Pallets.AppPromotion]);29 superuser = await privateKey('//Alice');30 donor = await privateKey({url: import.meta.url});31 palletAdmin = await privateKey('//PromotionAdmin');32 const api = helper.getApi();33 await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})));34 });35 });3637 after(async function () {38 await usingPlaygrounds(async (helper) => {39 if(helper.fetchMissingPalletNames([Pallets.AppPromotion]).length != 0) return;40 const api = helper.getApi();41 await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})));42 });43 });4445 describe('admin adress', () => {46 itSub('can be set by sudo only', async ({helper}) => {47 const api = helper.getApi();48 const [nonAdmin] = await helper.arrange.createAccounts([10n], donor);49 // nonAdmin can not set admin not from himself nor as a sudo50 await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address}))).to.be.rejected;51 await expect(helper.signTransaction(nonAdmin, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address})))).to.be.rejected;52 });5354 itSub('can be any valid CrossAccountId', async ({helper}) => {55 // We are not going to set an eth address as a sponsor,56 // but we do want to check, it doesn't break anything;57 const api = helper.getApi();58 const [account] = await helper.arrange.createAccounts([10n], donor);59 const ethAccount = helper.address.substrateToEth(account.address);60 // Alice sets Ethereum address as a sudo. Then Substrate address back...61 await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Ethereum: ethAccount})))).to.be.fulfilled;62 await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})))).to.be.fulfilled;6364 // ...It doesn't break anything;65 const collection = await helper.nft.mintCollection(account, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});66 await expect(helper.signTransaction(account, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected;67 });6869 itSub('can be reassigned', async ({helper}) => {70 const api = helper.getApi();71 const [oldAdmin, newAdmin, collectionOwner] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);72 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});7374 await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: oldAdmin.address})))).to.be.fulfilled;75 await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: newAdmin.address})))).to.be.fulfilled;76 await expect(helper.signTransaction(oldAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected;7778 await expect(helper.signTransaction(newAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.fulfilled;79 });80 });81});821// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import type {IKeyringPair} from '@polkadot/types/types';18import {itSub, usingPlaygrounds, Pallets, requirePalletsOrSkip} from '@unique/test-utils/util.js';19import {expect} from '../../eth/util/index.js';2021let superuser: IKeyringPair;22let donor: IKeyringPair;23let palletAdmin: IKeyringPair;2425describe('App promotion', () => {26 before(async function () {27 await usingPlaygrounds(async (helper, privateKey) => {28 requirePalletsOrSkip(this, helper, [Pallets.AppPromotion]);29 superuser = await privateKey('//Alice');30 donor = await privateKey({url: import.meta.url});31 palletAdmin = await privateKey('//PromotionAdmin');32 const api = helper.getApi();33 await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})));34 });35 });3637 after(async function () {38 await usingPlaygrounds(async (helper) => {39 if(helper.fetchMissingPalletNames([Pallets.AppPromotion]).length != 0) return;40 const api = helper.getApi();41 await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})));42 });43 });4445 describe('admin adress', () => {46 itSub('can be set by sudo only', async ({helper}) => {47 const api = helper.getApi();48 const [nonAdmin] = await helper.arrange.createAccounts([10n], donor);49 // nonAdmin can not set admin not from himself nor as a sudo50 await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address}))).to.be.rejected;51 await expect(helper.signTransaction(nonAdmin, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address})))).to.be.rejected;52 });5354 itSub('can be any valid CrossAccountId', async ({helper}) => {55 // We are not going to set an eth address as a sponsor,56 // but we do want to check, it doesn't break anything;57 const api = helper.getApi();58 const [account] = await helper.arrange.createAccounts([10n], donor);59 const ethAccount = helper.address.substrateToEth(account.address);60 // Alice sets Ethereum address as a sudo. Then Substrate address back...61 await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Ethereum: ethAccount})))).to.be.fulfilled;62 await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})))).to.be.fulfilled;6364 // ...It doesn't break anything;65 const collection = await helper.nft.mintCollection(account, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});66 await expect(helper.signTransaction(account, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected;67 });6869 itSub('can be reassigned', async ({helper}) => {70 const api = helper.getApi();71 const [oldAdmin, newAdmin, collectionOwner] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);72 const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});7374 await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: oldAdmin.address})))).to.be.fulfilled;75 await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: newAdmin.address})))).to.be.fulfilled;76 await expect(helper.signTransaction(oldAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected;7778 await expect(helper.signTransaction(newAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.fulfilled;79 });80 });81});82js-packages/tests/sub/appPromotion/appPromotion.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/appPromotion/appPromotion.test.ts
+++ b/js-packages/tests/sub/appPromotion/appPromotion.test.ts
@@ -17,8 +17,8 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {
itSub, usingPlaygrounds, Pallets, requirePalletsOrSkip, LOCKING_PERIOD, UNLOCKING_PERIOD,
-} from '../../util/index.js';
-import {DevUniqueHelper} from '@unique/playgrounds/unique.dev.js';
+} from '@unique/test-utils/util.js';
+import {DevUniqueHelper} from '@unique/test-utils';
import {itEth, expect, SponsoringMode} from '../../eth/util/index.js';
let donor: IKeyringPair;
js-packages/tests/sub/check-event/burnItemEvent.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/check-event/burnItemEvent.test.ts
+++ b/js-packages/tests/sub/check-event/burnItemEvent.test.ts
@@ -16,8 +16,8 @@
// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub} from '../../util/index.js';
-import type {IEvent} from '@unique/playgrounds/types.js';
+import {usingPlaygrounds, expect, itSub} from '@unique/test-utils/util.js';
+import type {IEvent} from '@unique-nft/playgrounds/types.js';
describe('Burn Item event ', () => {
js-packages/tests/sub/check-event/createCollectionEvent.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/check-event/createCollectionEvent.test.ts
+++ b/js-packages/tests/sub/check-event/createCollectionEvent.test.ts
@@ -16,8 +16,8 @@
// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, itSub, expect} from '../../util/index.js';
-import type {IEvent} from '@unique/playgrounds/types.js';
+import {usingPlaygrounds, itSub, expect} from '@unique/test-utils/util.js';
+import type {IEvent} from '@unique-nft/playgrounds/types.js';
describe('Create collection event ', () => {
let alice: IKeyringPair;
js-packages/tests/sub/check-event/createItemEvent.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/check-event/createItemEvent.test.ts
+++ b/js-packages/tests/sub/check-event/createItemEvent.test.ts
@@ -16,8 +16,8 @@
// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from '../../util/index.js';
-import type {IEvent} from '@unique/playgrounds/types.js';
+import {itSub, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
+import type {IEvent} from '@unique-nft/playgrounds/types.js';
describe('Create Item event ', () => {
let alice: IKeyringPair;
js-packages/tests/sub/check-event/createMultipleItemsEvent.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/check-event/createMultipleItemsEvent.test.ts
+++ b/js-packages/tests/sub/check-event/createMultipleItemsEvent.test.ts
@@ -16,8 +16,8 @@
// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, itSub, expect} from '../../util/index.js';
-import type {IEvent} from '@unique/playgrounds/types.js';
+import {usingPlaygrounds, itSub, expect} from '@unique/test-utils/util.js';
+import type {IEvent} from '@unique-nft/playgrounds/types.js';
describe('Create Multiple Items Event event ', () => {
let alice: IKeyringPair;
js-packages/tests/sub/check-event/destroyCollectionEvent.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/check-event/destroyCollectionEvent.test.ts
+++ b/js-packages/tests/sub/check-event/destroyCollectionEvent.test.ts
@@ -16,8 +16,8 @@
// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from '../../util/index.js';
-import type {IEvent} from '@unique/playgrounds/types.js';
+import {itSub, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
+import type {IEvent} from '@unique-nft/playgrounds/types.js';
describe('Destroy collection event ', () => {
let alice: IKeyringPair;
js-packages/tests/sub/check-event/transferEvent.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/check-event/transferEvent.test.ts
+++ b/js-packages/tests/sub/check-event/transferEvent.test.ts
@@ -16,8 +16,8 @@
// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub} from '../../util/index.js';
-import type {IEvent} from '@unique/playgrounds/types.js';
+import {usingPlaygrounds, expect, itSub} from '@unique/test-utils/util.js';
+import type {IEvent} from '@unique-nft/playgrounds/types.js';
describe('Transfer event ', () => {
let alice: IKeyringPair;
js-packages/tests/sub/check-event/transferFromEvent.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/check-event/transferFromEvent.test.ts
+++ b/js-packages/tests/sub/check-event/transferFromEvent.test.ts
@@ -16,8 +16,8 @@
// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub} from '../../util/index.js';
-import type {IEvent} from '@unique/playgrounds/types.js';
+import {usingPlaygrounds, expect, itSub} from '@unique/test-utils/util.js';
+import type {IEvent} from '@unique-nft/playgrounds/types.js';
describe('Transfer event ', () => {
let alice: IKeyringPair;
js-packages/tests/sub/collator-selection/collatorSelection.seqtest.tsdiffbeforeafterboth--- a/js-packages/tests/sub/collator-selection/collatorSelection.seqtest.ts
+++ b/js-packages/tests/sub/collator-selection/collatorSelection.seqtest.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '../../util/index.js';
+import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '@unique/test-utils/util.js';
async function nodeAddress(name: string) {
// eslint-disable-next-line require-await
js-packages/tests/sub/collator-selection/identity.seqtest.tsdiffbeforeafterboth--- a/js-packages/tests/sub/collator-selection/identity.seqtest.ts
+++ b/js-packages/tests/sub/collator-selection/identity.seqtest.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '../../util/index.js';
-import {UniqueHelper} from '@unique/playgrounds/unique.js';
+import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '@unique/test-utils/util.js';
+import {UniqueHelper} from '@unique-nft/playgrounds/unique.js';
async function getIdentities(helper: UniqueHelper) {
const identities: [string, any][] = [];
js-packages/tests/sub/governance/council.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/governance/council.test.ts
+++ b/js-packages/tests/sub/governance/council.test.ts
@@ -1,7 +1,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../../util/index.js';
-import {Event} from '@unique/playgrounds/unique.dev.js';
+import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '@unique/test-utils/util.js';
+import {Event} from '@unique/test-utils';
import {initCouncil, democracyLaunchPeriod, democracyVotingPeriod, democracyEnactmentPeriod, councilMotionDuration, democracyFastTrackVotingPeriod, fellowshipRankLimit, clearCouncil, clearTechComm, initTechComm, clearFellowship, dummyProposal, dummyProposalCall, initFellowship, defaultEnactmentMoment, fellowshipPropositionOrigin} from './util.js';
import type {ICounselors} from './util.js';
js-packages/tests/sub/governance/democracy.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/governance/democracy.test.ts
+++ b/js-packages/tests/sub/governance/democracy.test.ts
@@ -1,7 +1,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../../util/index.js';
+import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '@unique/test-utils/util.js';
import {clearFellowship, democracyLaunchPeriod, democracyTrackMinRank, dummyProposalCall, fellowshipConfirmPeriod, fellowshipMinEnactPeriod, fellowshipPreparePeriod, fellowshipPropositionOrigin, initFellowship, voteUnanimouslyInFellowship} from './util.js';
-import {Event} from '@unique/playgrounds/unique.dev.js';
+import {Event} from '@unique/test-utils';
describeGov('Governance: Democracy tests', () => {
let regularUser: IKeyringPair;
js-packages/tests/sub/governance/electsudo.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/governance/electsudo.test.ts
+++ b/js-packages/tests/sub/governance/electsudo.test.ts
@@ -1,6 +1,6 @@
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../../util/index.js';
-import {Event} from '@unique/playgrounds/unique.dev.js';
+import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '@unique/test-utils/util.js';
+import {Event} from '@unique/test-utils';
import {initCouncil, democracyLaunchPeriod, democracyVotingPeriod, democracyEnactmentPeriod, clearCouncil, clearTechComm, initTechComm, ITechComms} from './util.js';
import type {ICounselors} from './util.js';
js-packages/tests/sub/governance/fellowship.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/governance/fellowship.test.ts
+++ b/js-packages/tests/sub/governance/fellowship.test.ts
@@ -1,6 +1,6 @@
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../../util/index.js';
-import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js';
+import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '@unique/test-utils/util.js';
+import {DevUniqueHelper, Event} from '@unique/test-utils';
import {initCouncil, democracyLaunchPeriod, democracyVotingPeriod, democracyFastTrackVotingPeriod, fellowshipRankLimit, clearCouncil, clearTechComm, clearFellowship, defaultEnactmentMoment, dummyProposal, dummyProposalCall, fellowshipPropositionOrigin, initFellowship, initTechComm, voteUnanimouslyInFellowship, democracyTrackMinRank, fellowshipPreparePeriod, fellowshipConfirmPeriod, fellowshipMinEnactPeriod, democracyTrackId, hardResetFellowshipReferenda, hardResetDemocracy, hardResetGovScheduler} from './util.js';
import type {ICounselors, ITechComms} from './util.js';
js-packages/tests/sub/governance/init.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/governance/init.test.ts
+++ b/js-packages/tests/sub/governance/init.test.ts
@@ -1,6 +1,6 @@
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../../util/index.js';
-import {Event} from '@unique/playgrounds/unique.dev.js';
+import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '@unique/test-utils/util.js';
+import {Event} from '@unique/test-utils';
import {democracyLaunchPeriod, democracyVotingPeriod, democracyEnactmentPeriod, clearCouncil, clearTechComm, clearFellowship} from './util.js';
import type {ICounselors, ITechComms} from './util.js';
js-packages/tests/sub/governance/technicalCommittee.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/governance/technicalCommittee.test.ts
+++ b/js-packages/tests/sub/governance/technicalCommittee.test.ts
@@ -1,6 +1,6 @@
import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../../util/index.js';
-import {Event} from '@unique/playgrounds/unique.dev.js';
+import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '@unique/test-utils/util.js';
+import {Event} from '@unique/test-utils';
import {initCouncil, democracyLaunchPeriod, democracyFastTrackVotingPeriod, clearCouncil, clearTechComm, clearFellowship, defaultEnactmentMoment, dummyProposal, dummyProposalCall, fellowshipPropositionOrigin, initFellowship, initTechComm, hardResetFellowshipReferenda, hardResetDemocracy, hardResetGovScheduler} from './util.js';
import type {ITechComms} from './util.js';
js-packages/tests/sub/governance/util.tsdiffbeforeafterboth--- a/js-packages/tests/sub/governance/util.ts
+++ b/js-packages/tests/sub/governance/util.ts
@@ -1,9 +1,9 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {xxhashAsHex} from '@polkadot/util-crypto';
import type {u32} from '@polkadot/types-codec';
-import {usingPlaygrounds, expect} from '../../util/index.js';
-import {UniqueHelper} from '@unique/playgrounds/unique.js';
-import {DevUniqueHelper} from '@unique/playgrounds/unique.dev.js';
+import {usingPlaygrounds, expect} from '@unique/test-utils/util.js';
+import {UniqueHelper} from '@unique-nft/playgrounds/unique.js';
+import {DevUniqueHelper} from '@unique/test-utils';
export const democracyLaunchPeriod = 35;
export const democracyVotingPeriod = 35;
js-packages/tests/sub/nesting/admin.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/nesting/admin.test.ts
+++ b/js-packages/tests/sub/nesting/admin.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from '../../util/index.js';
-import {CrossAccountId} from '@unique/playgrounds/unique.js';
+import {expect, itSub, usingPlaygrounds} from '@unique/test-utils/util.js';
+import {CrossAccountId} from '@unique-nft/playgrounds/unique.js';
describe('Nesting by collection admin', () => {
let alice: IKeyringPair;
js-packages/tests/sub/nesting/collectionProperties.seqtest.tsdiffbeforeafterboth--- a/js-packages/tests/sub/nesting/collectionProperties.seqtest.ts
+++ b/js-packages/tests/sub/nesting/collectionProperties.seqtest.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../../util/index.js';
+import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '@unique/test-utils/util.js';
describe('Integration Test: Collection Properties with sudo', () => {
let superuser: IKeyringPair;
js-packages/tests/sub/nesting/collectionProperties.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/nesting/collectionProperties.test.ts
+++ b/js-packages/tests/sub/nesting/collectionProperties.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../../util/index.js';
+import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '@unique/test-utils/util.js';
describe('Integration Test: Collection Properties', () => {
let alice: IKeyringPair;
js-packages/tests/sub/nesting/common.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/nesting/common.test.ts
+++ b/js-packages/tests/sub/nesting/common.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, usingPlaygrounds} from '../../util/index.js';
-import {CrossAccountId, UniqueNFTCollection, UniqueRFTCollection} from '@unique/playgrounds/unique.js';
+import {expect, itSub, Pallets, usingPlaygrounds} from '@unique/test-utils/util.js';
+import {CrossAccountId, UniqueNFTCollection, UniqueRFTCollection} from '@unique-nft/playgrounds/unique.js';
let alice: IKeyringPair;
let bob: IKeyringPair;
js-packages/tests/sub/nesting/e2e.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/nesting/e2e.test.ts
+++ b/js-packages/tests/sub/nesting/e2e.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from '../../util/index.js';
-import {CrossAccountId} from '@unique/playgrounds/unique.js';
+import {expect, itSub, usingPlaygrounds} from '@unique/test-utils/util.js';
+import {CrossAccountId} from '@unique-nft/playgrounds/unique.js';
describe('Composite nesting tests', () => {
let alice: IKeyringPair;
js-packages/tests/sub/nesting/graphs.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/nesting/graphs.test.ts
+++ b/js-packages/tests/sub/nesting/graphs.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from '../../util/index.js';
-import {UniqueHelper, UniqueNFTCollection, UniqueNFToken} from '@unique/playgrounds/unique.js';
+import {expect, itSub, usingPlaygrounds} from '@unique/test-utils/util.js';
+import {UniqueHelper, UniqueNFTCollection, UniqueNFToken} from '@unique-nft/playgrounds/unique.js';
/**
* ```dot
js-packages/tests/sub/nesting/nesting.negative.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/nesting/nesting.negative.test.ts
+++ b/js-packages/tests/sub/nesting/nesting.negative.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, usingPlaygrounds} from '../../util/index.js';
-import {UniqueFTCollection, UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection, UniqueRFToken} from '@unique/playgrounds/unique.js';
+import {expect, itSub, Pallets, usingPlaygrounds} from '@unique/test-utils/util.js';
+import {UniqueFTCollection, UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection, UniqueRFToken} from '@unique-nft/playgrounds/unique.js';
import {itEth} from '../../eth/util/index.js';
let alice: IKeyringPair;
js-packages/tests/sub/nesting/propertyPermissions.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/nesting/propertyPermissions.test.ts
+++ b/js-packages/tests/sub/nesting/propertyPermissions.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, usingPlaygrounds, expect} from '../../util/index.js';
-import {UniqueNFTCollection, UniqueRFTCollection} from '@unique/playgrounds/unique.js';
+import {itSub, Pallets, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
+import {UniqueNFTCollection, UniqueRFTCollection} from '@unique-nft/playgrounds/unique.js';
describe('Integration Test: Access Rights to Token Properties', () => {
let alice: IKeyringPair;
js-packages/tests/sub/nesting/refungible.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/nesting/refungible.test.ts
+++ b/js-packages/tests/sub/nesting/refungible.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, usingPlaygrounds} from '../../util/index.js';
+import {expect, itSub, Pallets, usingPlaygrounds} from '@unique/test-utils/util.js';
describe('ReFungible-specific nesting tests', () => {
let alice: IKeyringPair;
js-packages/tests/sub/nesting/tokenProperties.seqtest.tsdiffbeforeafterboth--- a/js-packages/tests/sub/nesting/tokenProperties.seqtest.ts
+++ b/js-packages/tests/sub/nesting/tokenProperties.seqtest.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../../util/index.js';
+import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '@unique/test-utils/util.js';
describe('Integration Test: Token Properties with sudo', () => {
let superuser: IKeyringPair;
js-packages/tests/sub/nesting/tokenProperties.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/nesting/tokenProperties.test.ts
+++ b/js-packages/tests/sub/nesting/tokenProperties.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect, sizeOfProperty} from '../../util/index.js';
-import {UniqueHelper, UniqueNFToken, UniqueRFToken} from '@unique/playgrounds/unique.js';
+import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect, sizeOfProperty} from '@unique/test-utils/util.js';
+import {UniqueHelper, UniqueNFToken, UniqueRFToken} from '@unique-nft/playgrounds/unique.js';
describe('Integration Test: Token Properties', () => {
let alice: IKeyringPair; // collection owner
js-packages/tests/sub/nesting/unnest.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/nesting/unnest.test.ts
+++ b/js-packages/tests/sub/nesting/unnest.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, usingPlaygrounds} from '../../util/index.js';
-import {CrossAccountId, UniqueFTCollection, UniqueNFToken, UniqueRFToken} from '@unique/playgrounds/unique.js';
+import {expect, itSub, Pallets, usingPlaygrounds} from '@unique/test-utils/util.js';
+import {CrossAccountId, UniqueFTCollection, UniqueNFToken, UniqueRFToken} from '@unique-nft/playgrounds/unique.js';
describe('Integration Test: Unnesting', () => {
let alice: IKeyringPair;
js-packages/tests/sub/nesting/unnesting.negative.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/nesting/unnesting.negative.test.ts
+++ b/js-packages/tests/sub/nesting/unnesting.negative.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from '../../util/index.js';
-import {CrossAccountId} from '@unique/playgrounds/unique.js';
+import {expect, itSub, usingPlaygrounds} from '@unique/test-utils/util.js';
+import {CrossAccountId} from '@unique-nft/playgrounds/unique.js';
describe('Negative Test: Unnesting', () => {
let alice: IKeyringPair;
js-packages/tests/sub/refungible/burn.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/refungible/burn.test.ts
+++ b/js-packages/tests/sub/refungible/burn.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../../util/index.js';
+import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
describe('Refungible: burn', () => {
let donor: IKeyringPair;
js-packages/tests/sub/refungible/nesting.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/refungible/nesting.test.ts
+++ b/js-packages/tests/sub/refungible/nesting.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../../util/index.js';
+import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from '@unique/test-utils/util.js';
describe('Refungible nesting', () => {
let alice: IKeyringPair;
js-packages/tests/sub/refungible/repartition.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/refungible/repartition.test.ts
+++ b/js-packages/tests/sub/refungible/repartition.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../../util/index.js';
+import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
describe('integration test: Refungible functionality:', () => {
let donor: IKeyringPair;
js-packages/tests/sub/refungible/transfer.test.tsdiffbeforeafterboth--- a/js-packages/tests/sub/refungible/transfer.test.ts
+++ b/js-packages/tests/sub/refungible/transfer.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../../util/index.js';
+import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
describe('Refungible transfer tests', () => {
let donor: IKeyringPair;
js-packages/tests/transfer.test.tsdiffbeforeafterboth--- a/js-packages/tests/transfer.test.ts
+++ b/js-packages/tests/transfer.test.ts
@@ -16,8 +16,8 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {itEth, usingEthPlaygrounds} from './eth/util/index.js';
-import {itSub, Pallets, usingPlaygrounds, expect} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+import {itSub, Pallets, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique-nft/playgrounds/types.js';
describe('Integration Test Transfer(recipient, collection_id, item_id, value)', () => {
let donor: IKeyringPair;
js-packages/tests/transferFrom.test.tsdiffbeforeafterboth--- a/js-packages/tests/transferFrom.test.ts
+++ b/js-packages/tests/transferFrom.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, usingPlaygrounds, expect} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+import {itSub, Pallets, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique-nft/playgrounds/types.js';
describe('Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {
let alice: IKeyringPair;
js-packages/tests/tx-version-presence.test.tsdiffbeforeafterboth--- a/js-packages/tests/tx-version-presence.test.ts
+++ b/js-packages/tests/tx-version-presence.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import {Metadata} from '@polkadot/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
+import {itSub, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
let metadata: Metadata;
js-packages/tests/util/authorizeEnactUpgrade.tsdiffbeforeafterboth--- a/js-packages/tests/util/authorizeEnactUpgrade.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import {readFile} from 'fs/promises';
-import {u8aToHex} from '@polkadot/util';
-import {usingPlaygrounds} from './index.js';
-import {blake2AsHex} from '@polkadot/util-crypto';
-
-
-const codePath = process.argv[2];
-if(!codePath) throw new Error('missing code path argument');
-
-const code = await readFile(codePath);
-
-await usingPlaygrounds(async (helper, privateKey) => {
- const alice = await privateKey('//Alice');
- const hex = blake2AsHex(code);
- await helper.getSudo().executeExtrinsic(alice, 'api.tx.parachainSystem.authorizeUpgrade', [hex, true]);
- await helper.getSudo().executeExtrinsicUncheckedWeight(alice, 'api.tx.parachainSystem.enactAuthorizedUpgrade', [u8aToHex(code)]);
-});
-// We miss disconnect/unref somewhere.
-process.exit(0);
js-packages/tests/util/createHrmp.tsdiffbeforeafterboth--- a/js-packages/tests/util/createHrmp.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import {usingPlaygrounds} from './index.js';
-import config from '../config.js';
-
-const profile = process.argv[2];
-if(!profile) throw new Error('missing profile/relay argument');
-
-await usingPlaygrounds(async (helper, privateKey) => {
- const bidirOpen = async (a: number, b: number) => {
- console.log(`Opening ${a} <=> ${b}`);
- await helper.getSudo().executeExtrinsic(alice, 'api.tx.hrmp.forceOpenHrmpChannel', [a, b, 8, 512]);
- await helper.getSudo().executeExtrinsic(alice, 'api.tx.hrmp.forceOpenHrmpChannel', [b, a, 8, 512]);
- };
- const alice = await privateKey('//Alice');
- switch (profile) {
- case 'opal':
- await bidirOpen(1001, 1002);
- break;
- case 'quartz':
- await bidirOpen(1001, 1002);
- await bidirOpen(1001, 1003);
- await bidirOpen(1001, 1004);
- await bidirOpen(1001, 1005);
- break;
- case 'unique':
- await bidirOpen(1001, 1002);
- await bidirOpen(1001, 1003);
- await bidirOpen(1001, 1004);
- await bidirOpen(1001, 1005);
- await bidirOpen(1001, 1006);
- await bidirOpen(1001, 1007);
- break;
- default:
- throw new Error(`unknown hrmp config profile: ${profile}`);
- }
-}, config.relayUrl);
-// We miss disconnect/unref somewhere.
-process.exit(0);
js-packages/tests/util/frankensteinMigrate.tsdiffbeforeafterboth--- a/js-packages/tests/util/frankensteinMigrate.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import {migration as locksToFreezesMigration} from '../migrations/942057-appPromotion/index.js';
-export interface Migration {
- before: () => Promise<void>,
- after: () => Promise<void>,
-}
-
-export const migrations: {[key: string]: Migration} = {
- 'v942057': locksToFreezesMigration,
-};
js-packages/tests/util/globalSetup.tsdiffbeforeafterboth--- a/js-packages/tests/util/globalSetup.ts
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-import {
- usingPlaygrounds, Pallets, DONOR_FUNDING, MINIMUM_DONOR_FUND, LOCKING_PERIOD, UNLOCKING_PERIOD, makeNames,
-} from './index.js';
-import * as path from 'path';
-import {promises as fs} from 'fs';
-
-const {dirname} = makeNames(import.meta.url);
-
-// This function should be called before running test suites.
-const globalSetup = async (): Promise<void> => {
- await usingPlaygrounds(async (helper, privateKey) => {
- try {
- // 1. Wait node producing blocks
- await helper.wait.newBlocks(1, 600_000);
-
- // 2. Create donors for test files
- await fundFilenamesWithRetries(3)
- .then((result) => {
- if(!result) throw Error('Some problems with fundFilenamesWithRetries');
- });
-
- // 3. Configure App Promotion
- const missingPallets = helper.fetchMissingPalletNames([Pallets.AppPromotion]);
- if(missingPallets.length === 0) {
- const superuser = await privateKey('//Alice');
- const palletAddress = helper.arrange.calculatePalletAddress('appstake');
- const palletAdmin = await privateKey('//PromotionAdmin');
- const api = helper.getApi();
- await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})));
- const nominal = helper.balance.getOneTokenNominal();
- await helper.balance.transferToSubstrate(superuser, palletAdmin.address, 10000n * nominal);
- await helper.balance.transferToSubstrate(superuser, palletAddress, 10000n * nominal);
- await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [api.tx.configuration
- .setAppPromotionConfigurationOverride({
- recalculationInterval: LOCKING_PERIOD,
- pendingInterval: UNLOCKING_PERIOD})], true);
- }
- } catch (error) {
- console.error(error);
- throw Error('Error during globalSetup');
- }
- });
-};
-
-async function getFiles(rootPath: string): Promise<string[]> {
- const files = await fs.readdir(rootPath, {withFileTypes: true});
- const filenames: string[] = [];
- for(const entry of files) {
- const res = path.resolve(rootPath, entry.name);
- if(entry.isDirectory()) {
- filenames.push(...await getFiles(res));
- } else {
- filenames.push(res);
- }
- }
- return filenames;
-}
-
-const fundFilenames = async () => {
- await usingPlaygrounds(async (helper, privateKey) => {
- const oneToken = helper.balance.getOneTokenNominal();
- const alice = await privateKey('//Alice');
- const nonce = await helper.chain.getNonce(alice.address);
- const filenames = await getFiles(path.resolve(dirname, '..'));
-
- // batching is actually undesireable, it takes away the time while all the transactions actually succeed
- const batchSize = 300;
- let balanceGrantedCounter = 0;
- for(let b = 0; b < filenames.length; b += batchSize) {
- const tx: Promise<boolean>[] = [];
- let batchBalanceGrantedCounter = 0;
- for(let i = 0; batchBalanceGrantedCounter < batchSize && b + i < filenames.length; i++) {
- const f = filenames[b + i];
- if(!f.endsWith('.test.ts') && !f.endsWith('seqtest.ts') || f.includes('.outdated')) continue;
- const account = await privateKey({filename: f, ignoreFundsPresence: true});
- const aliceBalance = await helper.balance.getSubstrate(account.address);
-
- if(aliceBalance < MINIMUM_DONOR_FUND * oneToken) {
- tx.push(helper.executeExtrinsic(
- alice,
- 'api.tx.balances.transferKeepAlive',
- [account.address, DONOR_FUNDING * oneToken],
- true,
- {nonce: nonce + balanceGrantedCounter++},
- ).then(() => true).catch(() => {console.error(`Transaction to ${path.basename(f)} registered as failed. Strange.`); return false;}));
- batchBalanceGrantedCounter++;
- }
- }
-
- if(tx.length > 0) {
- console.log(`Granting funds to ${batchBalanceGrantedCounter} filename accounts.`);
- const result = await Promise.all(tx);
- if(result && result.lastIndexOf(false) > -1) throw new Error('The transactions actually probably succeeded, should check the balances.');
- }
- }
-
- if(balanceGrantedCounter == 0) console.log('No account needs additional funding.');
- });
-};
-
-const fundFilenamesWithRetries = (retriesLeft: number): Promise<boolean> => {
- if(retriesLeft <= 0) return Promise.resolve(false);
- return fundFilenames()
- .then(() => Promise.resolve(true))
- .catch(e => {
- console.error(e);
- console.error(`Some transactions might have failed. ${retriesLeft > 1 ? 'Retrying...' : 'Something is wrong.'}\n`);
- return fundFilenamesWithRetries(--retriesLeft);
- });
-};
-
-globalSetup().catch(e => {
- console.error('Setup error');
- console.error(e);
- process.exit(1);
-});
js-packages/tests/util/identitySetter.tsdiffbeforeafterboth--- a/js-packages/tests/util/identitySetter.ts
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-//
-// Pulls identities and sub-identities from a chain and then makes a preimage to later force upload them into another.
-// Only changed or previously non-existent data are inserted.
-//
-// Usage: `yarn setIdentities [relay WS URL] [parachain WS URL] [user key]
-// Example: `yarn setIdentities wss://polkadot-rpc.dwellir.com ws://localhost:9944 escape pattern miracle train sudden cart adapt embark wedding alien lamp mesh`
-
-import {encodeAddress} from '@polkadot/keyring';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, Pallets} from './index.js';
-import {ChainHelperBase} from '@unique/playgrounds/unique.js';
-
-const relayUrl = process.argv[2] ?? 'ws://localhost:9844';
-const paraUrl = process.argv[3] ?? 'ws://localhost:9944';
-const key = process.argv.length > 4 ? process.argv.slice(4).join(' ') : '//Alice';
-
-export function extractAccountId(key: any): string {
- return (key as any).toHuman()[0];
-}
-
-export function extractIdentityInfo(value: any): object {
- const heart = (value as any).unwrap();
- const identity = heart.toJSON();
- identity.judgements = heart.judgements.toHuman();
- return identity;
-}
-
-export function extractIdentity(key: any, value: any): [string, any] {
- return [extractAccountId(key), extractIdentityInfo(value)];
-}
-
-export async function getIdentities(helper: ChainHelperBase, noneCasePredicate?: (key: any, value: any) => void) {
- const identities: [string, any][] = [];
- for(const [key, v] of await helper.getApi().query.identity.identityOf.entries()) {
- const value = v as any;
- if(value.isNone) {
- if(noneCasePredicate) noneCasePredicate(key, value);
- continue;
- }
- identities.push(extractIdentity(key, value));
- }
- return identities;
-}
-
-// whether the existing chain data is more important than the coming
-function isCurrentChainDataPriority(helper: ChainHelperBase, currentChainId: number | undefined, relayChainId: number) {
- if(!currentChainId) return false;
- // information has come from local chain, and is automatically superior
- if(currentChainId == helper.chain.getChainProperties().ss58Format) return true;
- // the lower the id, the more important it is (Polkadot has ss58 prefix = 0, Kusama has ss58 prefix = 2)
- return currentChainId < relayChainId;
-}
-
-// construct an object with all data necessary for insertion from storage query results
-export function constructSubInfo(identityAccount: string, subQuery: any, supers: any[], ss58?: number): [string, any] {
- const deposit = subQuery.toJSON()[0];
- const subIdentities = subQuery.toHuman()[1];
- subIdentities.map((sub: string) => supers.find((sup: any) => sup[0] === sub));
-
- return [
- encodeAddress(identityAccount, ss58), [
- deposit,
- subIdentities.map((sub: string): [string, object] | null => {
- const sup = supers.find((sup: any) => sup[0] === sub);
- if(!sup) {
- console.error(`Error: Could not find info on super for \nsub-identity account\t${sub} of \nsuper account \t\t${identityAccount}, skipping.`);
- return null;
- }
- return [encodeAddress(sub, ss58), sup[1].toJSON()[1]];
- }).filter((x: any) => x),
- ],
- ];
-}
-
-export async function getSubs(helper: ChainHelperBase) {
- return (await helper.getApi().query.identity.subsOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]);
-}
-
-export async function getSupers(helper: ChainHelperBase) {
- return (await helper.getApi().query.identity.superOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]);
-}
-
-async function uploadPreimage(helper: ChainHelperBase, preimageMaker: IKeyringPair, preimage: string) {
- try {
- await helper.executeExtrinsic(preimageMaker, 'api.tx.preimage.notePreimage', [preimage]);
- } catch (e: any) {
- if(e.message.includes('AlreadyNoted')) {
- console.warn('Warning: The same preimage already exists on the chain. Nothing was uploaded.');
- } else {
- console.error(e);
- }
- }
-}
-
-// The utility for pulling identity and sub-identity data
-const forceInsertIdentities = async (): Promise<void> => {
- let relaySS58Prefix = 0;
- const identitiesOnRelay: any[] = [];
- const subsOnRelay: any[] = [];
- const identitiesToRemove: string[] = [];
- await usingPlaygrounds(async helper => {
- try {
- relaySS58Prefix = helper.chain.getChainProperties().ss58Format;
- // iterate over every identity
- for(const [key, value] of await getIdentities(helper, (key, _value) => identitiesToRemove.push((key as any).toHuman()[0]))) {
- // if any of the judgements resulted in a good confirmed outcome, keep this identity
- let knownGood = false, reasonable = false;
- for(const [_id, judgement] of value.judgements) {
- if(judgement == 'KnownGood') knownGood = true;
- if(judgement == 'Reasonable') reasonable = true;
- }
- if(!(reasonable || knownGood)) continue;
- // replace the registrator id with the relay chain's ss58 format
- value.judgements = [[helper.chain.getChainProperties().ss58Format, knownGood ? 'KnownGood' : 'Reasonable']];
- identitiesOnRelay.push([key, value]);
- }
-
- const sublessIdentities = [...identitiesOnRelay];
- const supersOfSubs = await getSupers(helper);
-
- // iterate over every sub-identity
- for(const [key, value] of await getSubs(helper)) {
- // only get subs of the identities interesting to us
- const identityIndex = sublessIdentities.findIndex((x: any) => x[0] == key);
- if(identityIndex == -1) continue;
- sublessIdentities.splice(identityIndex, 1);
- subsOnRelay.push(constructSubInfo(key, value, supersOfSubs));
- }
-
- // mark the rest of sub-identities for deletion with empty arrays
- /*for(const [account, _identity] of sublessIdentities) {
- subsOnRelay.push([account, ['0', []]]);
- }*/
- } catch (error) {
- console.error(error);
- throw Error('Error during fetching identities');
- }
- }, relayUrl);
-
- await usingPlaygrounds(async (helper, privateKey) => {
- if(helper.fetchMissingPalletNames([Pallets.Identity]).length != 0) console.error('pallet-identity is not included in parachain.');
- if(helper.fetchMissingPalletNames([Pallets.Preimage]).length != 0) console.error('pallet-preimage is not included in parachain.');
-
- try {
- const preimageMaker = await privateKey(key);
- const ss58Format = helper.chain.getChainProperties().ss58Format;
- const paraIdentities = await getIdentities(helper);
- const identitiesToAdd: any[] = [];
- const paraAccountsRegistrators: {[name: string]: number} = {};
-
- // cross-reference every account for changes
- for(const [key, value] of identitiesOnRelay) {
- const encodedKey = encodeAddress(key, ss58Format);
-
- // only update if the identity info does not exist or is changed
- const identity = paraIdentities.find(i => i[0] === encodedKey);
- if(identity) {
- const registratorId = identity[1].judgements[0][0];
- paraAccountsRegistrators[encodedKey] = registratorId;
- if(isCurrentChainDataPriority(helper, registratorId, value.judgements[0][0])
- || JSON.stringify(value.info) === JSON.stringify(identity[1].info)) {
- continue;
- }
- }
-
- identitiesToAdd.push([key, value]);
- // exercise caution - in case we have an identity and the realy doesn't, it might mean one of two things:
- // 1) it was deleted on the relay;
- // 2) it is our own identity, we don't want to delete it.
- // identitiesToRemove.push((key as any).toHuman()[0]);
- }
-
- if(identitiesToRemove.length != 0)
- await uploadPreimage(
- helper,
- preimageMaker,
- helper.constructApiCall('api.tx.identity.forceRemoveIdentities', [identitiesToRemove]).method.toHex(),
- );
- if(identitiesToAdd.length != 0)
- await uploadPreimage(
- helper,
- preimageMaker,
- helper.constructApiCall('api.tx.identity.forceInsertIdentities', [identitiesToAdd]).method.toHex(),
- );
-
- console.log(`Tried to push ${identitiesToAdd.length} identities`
- + ` and found ${identitiesToRemove.length} identities for potential removal.`
- + ` Currently there are ${(await helper.getApi().query.identity.identityOf.keys()).length} identities on the chain.`);
-
- // fill sub-identities
- const paraSubs = await getSubs(helper);
- const supersOfSubs = await getSupers(helper);
- const subsToUpdate: any[] = [];
-
- for(const [key, value] of subsOnRelay) {
- const encodedKey = encodeAddress(key, ss58Format);
- const sub = paraSubs.find(i => i[0] === encodedKey);
- if(sub) {
- // only update if the sub-identity info does not exist or is changed
- if(isCurrentChainDataPriority(helper, paraAccountsRegistrators[encodedKey], relaySS58Prefix)
- || JSON.stringify(value) === JSON.stringify(constructSubInfo(sub[0], sub[1], supersOfSubs)[1])) {
- continue;
- }
- } else if(value[1].length == 0)
- continue;
-
- subsToUpdate.push([key, value]);
- }
-
- if(subsToUpdate.length != 0)
- await uploadPreimage(
- helper,
- preimageMaker,
- helper.constructApiCall('api.tx.identity.forceSetSubs', [subsToUpdate]).method.toHex(),
- );
-
- console.log(`Also tried to push ${subsToUpdate.length} identities with their sub-identities.`
- + ` Currently there are ${(await helper.getApi().query.identity.subsOf.keys()).length} identities with subs.`);
- } catch (error) {
- console.error(error);
- throw Error('Error during setting identities');
- }
- }, paraUrl);
-};
-
-if(process.argv[1] === module.filename)
- forceInsertIdentities().catch(() => process.exit(1));
js-packages/tests/util/index.tsdiffbeforeafterboth--- a/js-packages/tests/util/index.ts
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-import * as path from 'path';
-import * as crypto from 'crypto';
-import type {IKeyringPair} from '@polkadot/types/types/interfaces';
-import chai from 'chai';
-import chaiAsPromised from 'chai-as-promised';
-import chaiSubset from 'chai-subset';
-import {Context} from 'mocha';
-import config from '../config.js';
-import {ChainHelperBase} from '@unique/playgrounds/unique.js';
-import type {ILogger} from '@unique/playgrounds/types.js';
-import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper, DevAstarHelper, DevShidenHelper, DevPolkadexHelper, DevHydraDxHelper} from '@unique/playgrounds/unique.dev.js';
-import {dirname} from 'path';
-import {fileURLToPath} from 'url';
-
-chai.config.truncateThreshold = 0;
-chai.use(chaiAsPromised);
-chai.use(chaiSubset);
-export const expect = chai.expect;
-
-const getTestHash = (filename: string) => crypto.createHash('md5').update(filename).digest('hex');
-
-export const getTestSeed = (filename: string) => `//Alice+${getTestHash(filename)}`;
-
-async function usingPlaygroundsGeneral<T extends ChainHelperBase, R = void>(
- helperType: new (logger: ILogger) => T,
- url: string,
- code: (helper: T, privateKey: (seed: string | { filename?: string, url?: string, ignoreFundsPresence?: boolean }) => Promise<IKeyringPair>) => Promise<R>,
-): Promise<R> {
- const silentConsole = new SilentConsole();
- silentConsole.enable();
-
- const helper = new helperType(new SilentLogger());
- let result;
- try {
- await helper.connect(url);
- const ss58Format = helper.chain.getChainProperties().ss58Format;
- const privateKey = async (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => {
- if(typeof seed === 'string') {
- return helper.util.fromSeed(seed, ss58Format);
- }
- if(seed.url) {
- const {filename} = makeNames(seed.url);
- seed.filename = filename;
- } else if(seed.filename) {
- // Pass
- } else {
- throw new Error('no url nor filename set');
- }
- const actualSeed = getTestSeed(seed.filename);
- let account = helper.util.fromSeed(actualSeed, ss58Format);
- // here's to hoping that no
- if(!seed.ignoreFundsPresence && ((helper as any)['balance'] == undefined || await (helper as any).balance.getSubstrate(account.address) < MINIMUM_DONOR_FUND)) {
- console.warn(`${path.basename(seed.filename)}: Not enough funds present on the filename account. Using the default one as the donor instead.`);
- account = helper.util.fromSeed('//Alice', ss58Format);
- }
- return account;
- };
- result = await code(helper, privateKey);
- }
- finally {
- await helper.disconnect();
- silentConsole.disable();
- }
- return result as any as R;
-}
-
-export const usingPlaygrounds = <R = void>(code: (helper: DevUniqueHelper, privateKey: (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => Promise<IKeyringPair>) => Promise<R>, url: string = config.substrateUrl) => usingPlaygroundsGeneral<DevUniqueHelper, R>(DevUniqueHelper, url, code);
-
-export const usingWestmintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevWestmintHelper>(DevWestmintHelper, url, code);
-
-export const usingStateminePlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevStatemineHelper>(DevWestmintHelper, url, code);
-
-export const usingStatemintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevStatemintHelper>(DevWestmintHelper, url, code);
-
-export const usingRelayPlaygrounds = (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevRelayHelper>(DevRelayHelper, url, code);
-
-export const usingAcalaPlaygrounds = (url: string, code: (helper: DevAcalaHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevAcalaHelper>(DevAcalaHelper, url, code);
-
-export const usingKaruraPlaygrounds = (url: string, code: (helper: DevKaruraHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevKaruraHelper>(DevAcalaHelper, url, code);
-
-export const usingMoonbeamPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevMoonbeamHelper>(DevMoonbeamHelper, url, code);
-
-export const usingMoonriverPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevMoonriverHelper>(DevMoonriverHelper, url, code);
-
-export const usingAstarPlaygrounds = (url: string, code: (helper: DevAstarHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevAstarHelper>(DevAstarHelper, url, code);
-
-export const usingShidenPlaygrounds = (url: string, code: (helper: DevShidenHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevShidenHelper>(DevShidenHelper, url, code);
-
-export const usingPolkadexPlaygrounds = (url: string, code: (helper: DevPolkadexHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevPolkadexHelper>(DevPolkadexHelper, url, code);
-
-export const usingHydraDxPlaygrounds = (url: string, code: (helper: DevHydraDxHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevHydraDxHelper>(DevHydraDxHelper, url, code);
-
-export const MINIMUM_DONOR_FUND = 4_000_000n;
-export const DONOR_FUNDING = 4_000_000n;
-
-// App-promotion periods:
-export const LOCKING_PERIOD = 12n; // 12 blocks of relay
-export const UNLOCKING_PERIOD = 6n; // 6 blocks of parachain
-
-// Native contracts
-export const COLLECTION_HELPER = '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f';
-export const CONTRACT_HELPER = '0x842899ECF380553E8a4de75bF534cdf6fBF64049';
-
-export enum Pallets {
- Inflation = 'inflation',
- ReFungible = 'refungible',
- Fungible = 'fungible',
- NFT = 'nonfungible',
- Scheduler = 'scheduler',
- UniqueScheduler = 'uniqueScheduler',
- AppPromotion = 'apppromotion',
- CollatorSelection = 'collatorselection',
- Session = 'session',
- Identity = 'identity',
- Democracy = 'democracy',
- Council = 'council',
- //CouncilMembership = 'councilmembership',
- TechnicalCommittee = 'technicalcommittee',
- Fellowship = 'fellowshipcollective',
- Preimage = 'preimage',
- Maintenance = 'maintenance',
- TestUtils = 'testutils',
-}
-
-export function requirePalletsOrSkip(test: Context, helper: DevUniqueHelper, requiredPallets: readonly string[]) {
- const missingPallets = helper.fetchMissingPalletNames(requiredPallets);
-
- if(missingPallets.length > 0) {
- const skipMsg = `\tSkipping test '${test.test?.title}'.\n\tThe following pallets are missing:\n\t- ${missingPallets.join('\n\t- ')}`;
- console.warn('\x1b[38:5:208m%s\x1b[0m', skipMsg);
- test.skip();
- }
-}
-
-export function itSub(name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: readonly string[] } = {}) {
- (opts.only ? it.only :
- opts.skip ? it.skip : it)(name, async function () {
- await usingPlaygrounds(async (helper, privateKey) => {
- if(opts.requiredPallets) {
- requirePalletsOrSkip(this, helper, opts.requiredPallets);
- }
-
- await cb({helper, privateKey});
- });
- });
-}
-export function itSubIfWithPallet(name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: readonly string[] } = {}) {
- return itSub(name, cb, {requiredPallets: required, ...opts});
-}
-itSub.only = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSub(name, cb, {only: true});
-itSub.skip = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSub(name, cb, {skip: true});
-
-itSubIfWithPallet.only = (name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSubIfWithPallet(name, required, cb, {only: true});
-itSubIfWithPallet.skip = (name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSubIfWithPallet(name, required, cb, {skip: true});
-itSub.ifWithPallets = itSubIfWithPallet;
-
-export type SchedKind = 'anon' | 'named';
-
-export function itSched(
- name: string,
- cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any,
- opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {},
-) {
- itSub(name + ' (anonymous scheduling)', (apis) => cb('anon', apis), opts);
- itSub(name + ' (named scheduling)', (apis) => cb('named', apis), opts);
-}
-itSched.only = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSched(name, cb, {only: true});
-itSched.skip = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSched(name, cb, {skip: true});
-itSched.ifWithPallets = itSchedIfWithPallets;
-
-function itSchedIfWithPallets(name: string, required: string[], cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
- return itSched(name, cb, {requiredPallets: required, ...opts});
-}
-
-export function describeXCM(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) {
- (process.env.RUN_XCM_TESTS && !opts.skip
- ? describe
- : describe.skip)(title, fn);
-}
-
-describeXCM.skip = (name: string, fn: (this: Mocha.Suite) => void) => describeXCM(name, fn, {skip: true});
-
-export function describeGov(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) {
- (process.env.RUN_GOV_TESTS && !opts.skip
- ? describe
- : describe.skip)(title, fn);
-}
-
-describeGov.skip = (name: string, fn: (this: Mocha.Suite) => void) => describeGov(name, fn, {skip: true});
-
-export function sizeOfInt(i: number) {
- if(i < 0 || i > 0xffffffff) throw new Error('out of range');
- if(i < 0b11_1111) {
- return 1;
- } else if(i < 0b11_1111_1111_1111) {
- return 2;
- } else if(i < 0b11_1111_1111_1111_1111_1111_1111_1111) {
- return 4;
- } else {
- return 5;
- }
-}
-
-const UTF8_ENCODER = new TextEncoder();
-export function sizeOfEncodedStr(v: string) {
- const encoded = UTF8_ENCODER.encode(v);
- return sizeOfInt(encoded.length) + encoded.length;
-}
-
-export function sizeOfProperty(prop: {key: string, value: string}) {
- return sizeOfEncodedStr(prop.key) + sizeOfEncodedStr(prop.value);
-}
-
-export function makeNames(url: string) {
- const filename = fileURLToPath(url);
- return {
- filename,
- dirname: dirname(filename),
- };
-}
js-packages/tests/util/relayIdentitiesChecker.tsdiffbeforeafterboth--- a/js-packages/tests/util/relayIdentitiesChecker.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-//
-// Checks and reports the differences between identities and sub-identities on two chains.
-//
-// Usage: `yarn checkRelayIdentities [relay-1 WS URL] [relay-2 WS URL]`
-// Example: `yarn checkRelayIdentities wss://polkadot-rpc.dwellir.com wss://kusama-rpc.dwellir.com`
-
-import {encodeAddress} from '@polkadot/keyring';
-import {usingPlaygrounds} from './index.js';
-import {getIdentities, getSubs, getSupers, constructSubInfo} from './identitySetter.js';
-
-const relay1Url = process.argv[2] ?? 'ws://localhost:9844';
-const relay2Url = process.argv[3] ?? 'ws://localhost:9844';
-
-async function pullIdentities(relayUrl: string): Promise<[any[], any[]]> {
- const identities: any[] = [];
- const subs: any[] = [];
-
- await usingPlaygrounds(async helper => {
- try {
- // iterate over every identity
- for(const [key, value] of await getIdentities(helper)) {
- // if any of the judgements resulted in a good confirmed outcome, keep this identity
- if(value.toHuman().judgements.filter((x: any) => x[1] == 'Reasonable' || x[1] == 'KnownGood').length == 0) continue;
- identities.push([key, value]);
- }
-
- const supersOfSubs = await getSupers(helper);
-
- // iterate over every sub-identity
- for(const [key, value] of await getSubs(helper)) {
- // only get subs of the identities interesting to us
- if(identities.find((x: any) => x[0] == key) == -1) continue;
- subs.push(constructSubInfo(key, value, supersOfSubs));
- }
- } catch (error) {
- console.error(error);
- throw Error(`Error during fetching identities on ${relayUrl}`);
- }
- }, relayUrl);
-
- return [identities, subs];
-}
-
-// The utility for pulling identity and sub-identity data
-const checkRelayIdentities = async (): Promise<void> => {
- const [identitiesOnRelay1, subIdentitiesOnRelay1] = await pullIdentities(relay1Url);
- const [identitiesOnRelay2, subIdentitiesOnRelay2] = await pullIdentities(relay2Url);
-
- console.log('identities counts:\t', identitiesOnRelay1.length, identitiesOnRelay2.length);
- console.log('sub-identities counts:\t', subIdentitiesOnRelay1.length, subIdentitiesOnRelay2.length);
- console.log();
-
- try {
- const matchingAddresses: string[] = [];
- const inequalIdentities: {[name: string]: [any, any]} = {};
-
- for(const [key1, value1] of identitiesOnRelay1) {
- const address = encodeAddress(key1);
- const identity2 = identitiesOnRelay2.find(([key2, _value2]) => address === encodeAddress(key2));
- if(!identity2) continue;
- matchingAddresses.push(address);
-
- //const [[key2, value2]] = identitiesOnRelay2.splice(index2, 1);
- const [_key2, value2] = identity2;
- if(JSON.stringify(value1.info) === JSON.stringify(value2.info)) continue;
- inequalIdentities[address] = [value1, value2];
- }
-
- /*for (const [v1, v2] of Object.values(inequalIdentities)) {
- console.log(v1.toHuman().info);
- console.log();
- console.log(v2.toHuman().info);
- await new Promise(resolve => setTimeout(resolve, 4000));
- }*/
-
- console.log(`Accounts with identities on both relays:\t${matchingAddresses.length}`);
- console.log(`Sub-identities with conflicting information:\t${Object.entries(inequalIdentities).length}`);
- console.log();
-
- const inequalSubIdentities = [];
- let matchesFound = 0;
- for(const address of matchingAddresses) {
- const sub1 = subIdentitiesOnRelay1.find(([key1, _value1]) => address === encodeAddress(key1));
- if(!sub1) continue;
- const sub2 = subIdentitiesOnRelay2.find(([key2, _value2]) => address === encodeAddress(key2));
- if(!sub2) continue;
-
- const [value1, value2] = [sub1[1], sub2[1]];
- matchesFound++;
-
- if(JSON.stringify(value1[1]) === JSON.stringify(value2[1])) {
- continue;
- }
- inequalSubIdentities.push([value1, value2]);
- }
-
- /*for (const [v1, v2] of inequalSubIdentities) {
- console.log(v1[1]);
- console.log();
- console.log(v2[1]);
- await new Promise(resolve => setTimeout(resolve, 300));
- }*/
- console.log(`Accounts with sub-identities on both relays:\t${matchesFound}`);
- console.log(`Of them, those with conflicting sub-identities:\t${inequalSubIdentities.length}`);
- } catch (error) {
- console.error(error);
- throw Error('Error during comparison');
- }
-};
-
-if(process.argv[1] === module.filename)
- checkRelayIdentities().catch(() => process.exit(1));
js-packages/tests/util/setCode.tsdiffbeforeafterboth--- a/js-packages/tests/util/setCode.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import {readFile} from 'fs/promises';
-import {u8aToHex} from '@polkadot/util';
-import {usingPlaygrounds} from './index.js';
-
-const codePath = process.argv[2];
-if(!codePath) throw new Error('missing code path argument');
-
-const code = await readFile(codePath);
-
-await usingPlaygrounds(async (helper, privateKey) => {
- const alice = await privateKey('//Alice');
- await helper.getSudo().executeExtrinsicUncheckedWeight(alice, 'api.tx.system.setCode', [u8aToHex(code)]);
-});
-// We miss disconnect/unref somewhere.
-process.exit(0);
js-packages/tests/vesting.test.tsdiffbeforeafterboth--- a/js-packages/tests/vesting.test.ts
+++ b/js-packages/tests/vesting.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
+import {itSub, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
describe('Vesting', () => {
let donor: IKeyringPair;
js-packages/tests/xcm/lowLevelXcmQuartz.test.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/lowLevelXcmQuartz.test.ts
+++ b/js-packages/tests/xcm/lowLevelXcmQuartz.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingMoonriverPlaygrounds, usingShidenPlaygrounds, usingRelayPlaygrounds} from '../util/index.js';
+import {itSub, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingMoonriverPlaygrounds, usingShidenPlaygrounds} from '@unique/test-utils/util.js';
import {QUARTZ_CHAIN, QTZ_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, shidenUrl, SAFE_XCM_VERSION, XcmTestHelper, TRANSFER_AMOUNT, SENDER_BUDGET, relayUrl} from './xcm.types.js';
import {hexToString} from '@polkadot/util';
js-packages/tests/xcm/lowLevelXcmUnique.test.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/lowLevelXcmUnique.test.ts
+++ b/js-packages/tests/xcm/lowLevelXcmUnique.test.ts
@@ -16,7 +16,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import config from '../config.js';
-import {itSub, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds, usingHydraDxPlaygrounds} from '../util/index.js';
+import {itSub, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds, usingHydraDxPlaygrounds} from '@unique/test-utils/util.js';
import {nToBigInt} from '@polkadot/util';
import {hexToString} from '@polkadot/util';
import {ASTAR_DECIMALS, SAFE_XCM_VERSION, SENDER_BUDGET, UNIQUE_CHAIN, UNQ_DECIMALS, XcmTestHelper, acalaUrl, astarUrl, hydraDxUrl, moonbeamUrl, polkadexUrl, uniqueAssetId} from './xcm.types.js';
js-packages/tests/xcm/xcm.types.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/xcm.types.ts
+++ b/js-packages/tests/xcm/xcm.types.ts
@@ -1,7 +1,6 @@
import type {IKeyringPair} from '@polkadot/types/types';
-import {hexToString} from '@polkadot/util';
-import {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingHydraDxPlaygrounds, usingKaruraPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds, usingShidenPlaygrounds} from '../util/index.js';
-import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js';
+import {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingHydraDxPlaygrounds, usingKaruraPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds, usingShidenPlaygrounds} from '@unique/test-utils/util.js';
+import {DevUniqueHelper, Event} from '@unique/test-utils';
import config from '../config.js';
export const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);
js-packages/tests/xcm/xcmOpal.test.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/xcmOpal.test.ts
+++ b/js-packages/tests/xcm/xcmOpal.test.ts
@@ -16,7 +16,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import config from '../config.js';
-import {itSub, expect, describeXCM, usingPlaygrounds, usingWestmintPlaygrounds, usingRelayPlaygrounds} from '../util/index.js';
+import {itSub, expect, describeXCM, usingPlaygrounds, usingWestmintPlaygrounds, usingRelayPlaygrounds} from '@unique/test-utils/util.js';
import {XcmTestHelper} from './xcm.types.js';
const STATEMINE_CHAIN = +(process.env.RELAY_WESTMINT_ID || 1000);
js-packages/tests/xcm/xcmQuartz.test.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/xcmQuartz.test.ts
+++ b/js-packages/tests/xcm/xcmQuartz.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '../util/index.js';
-import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js';
+import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '@unique/test-utils/util.js';
+import {DevUniqueHelper, Event} from '@unique/test-utils';
import {STATEMINE_CHAIN, QUARTZ_CHAIN, KARURA_CHAIN, MOONRIVER_CHAIN, SHIDEN_CHAIN, STATEMINE_DECIMALS, KARURA_DECIMALS, QTZ_DECIMALS, RELAY_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, relayUrl, shidenUrl, statemineUrl} from './xcm.types.js';
import {hexToString} from '@polkadot/util';
import {XcmTestHelper} from './xcm.types.js';
js-packages/tests/xcm/xcmUnique.test.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/xcmUnique.test.ts
+++ b/js-packages/tests/xcm/xcmUnique.test.ts
@@ -16,8 +16,8 @@
import type {IKeyringPair} from '@polkadot/types/types';
import config from '../config.js';
-import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds} from '../util/index.js';
-import {Event} from '@unique/playgrounds/unique.dev.js';
+import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds} from '@unique/test-utils/util.js';
+import {Event} from '@unique/test-utils';
import {hexToString, nToBigInt} from '@polkadot/util';
import {ACALA_CHAIN, ASTAR_CHAIN, MOONBEAM_CHAIN, POLKADEX_CHAIN, SAFE_XCM_VERSION, STATEMINT_CHAIN, UNIQUE_CHAIN, expectFailedToTransact, expectUntrustedReserveLocationFail, uniqueAssetId, uniqueVersionedMultilocation} from './xcm.types.js';
import {XcmTestHelper} from './xcm.types.js';
js-packages/types/package.jsondiffbeforeafterboth--- a/js-packages/types/package.json
+++ b/js-packages/types/package.json
@@ -5,7 +5,7 @@
"engines": {
"node": ">=16"
},
- "name": "@unique/opal-types",
+ "name": "@unique-nft/opal-testnet-types",
"type": "module",
"version": "1.0.0",
"main": "index.js",
js-packages/yarn.lockdiffbeforeafterboth--- a/js-packages/yarn.lock
+++ b/js-packages/yarn.lock
@@ -1242,22 +1242,21 @@
languageName: node
linkType: hard
-"@unique/opal-types@workspace:*, @unique/opal-types@workspace:types":
+"@unique-nft/opal-testnet-types@workspace:*, @unique-nft/opal-testnet-types@workspace:types":
version: 0.0.0-use.local
- resolution: "@unique/opal-types@workspace:types"
+ resolution: "@unique-nft/opal-testnet-types@workspace:types"
dependencies:
"@polkadot/typegen": ^10.10.1
languageName: unknown
linkType: soft
-"@unique/playgrounds@workspace:*, @unique/playgrounds@workspace:playgrounds":
+"@unique-nft/playgrounds@workspace:*, @unique-nft/playgrounds@workspace:playgrounds":
version: 0.0.0-use.local
- resolution: "@unique/playgrounds@workspace:playgrounds"
+ resolution: "@unique-nft/playgrounds@workspace:playgrounds"
dependencies:
"@polkadot/api": 10.10.1
"@polkadot/util": ^12.5.1
"@polkadot/util-crypto": ^12.5.1
- "@unique/opal-types": "workspace:*"
languageName: unknown
linkType: soft
@@ -1267,6 +1266,17 @@
languageName: unknown
linkType: soft
+"@unique/test-utils@workspace:*, @unique/test-utils@workspace:test-utils":
+ version: 0.0.0-use.local
+ resolution: "@unique/test-utils@workspace:test-utils"
+ dependencies:
+ "@polkadot/api": 10.10.1
+ "@polkadot/util": ^12.5.1
+ "@polkadot/util-crypto": ^12.5.1
+ "@unique-nft/opal-testnet-types": "workspace:*"
+ languageName: unknown
+ linkType: soft
+
"@unique/tests@workspace:tests":
version: 0.0.0-use.local
resolution: "@unique/tests@workspace:tests"
@@ -6001,8 +6011,9 @@
"@types/node": ^20.8.10
"@typescript-eslint/eslint-plugin": ^6.10.0
"@typescript-eslint/parser": ^6.10.0
- "@unique/opal-types": "workspace:*"
- "@unique/playgrounds": "workspace:*"
+ "@unique-nft/opal-testnet-types": "workspace:*"
+ "@unique-nft/playgrounds": "workspace:*"
+ "@unique/test-utils": "workspace:*"
chai: ^4.3.10
chai-as-promised: ^7.1.1
chai-like: ^1.1.1