difftreelog
refactor js-packages structure
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.tsdiffbeforeafterboth--- a/js-packages/tests/sub/appPromotion/appPromotion.seqtest.ts
+++ b/js-packages/tests/sub/appPromotion/appPromotion.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, usingPlaygrounds, Pallets, requirePalletsOrSkip} from '../../util/index.js';
+import {itSub, usingPlaygrounds, Pallets, requirePalletsOrSkip} from '@unique/test-utils/util.js';
import {expect} from '../../eth/util/index.js';
let superuser: IKeyringPair;
js-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.tsdiffbeforeafterboth1import type {IKeyringPair} from '@polkadot/types/types';2import {hexToString} from '@polkadot/util';3import {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingHydraDxPlaygrounds, usingKaruraPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds, usingShidenPlaygrounds} from '../util/index.js';4import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js';5import config from '../config.js';67export const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);8export const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000);9export const ACALA_CHAIN = +(process.env.RELAY_ACALA_ID || 2000);10export const MOONBEAM_CHAIN = +(process.env.RELAY_MOONBEAM_ID || 2004);11export const ASTAR_CHAIN = +(process.env.RELAY_ASTAR_ID || 2006);12export const POLKADEX_CHAIN = +(process.env.RELAY_POLKADEX_ID || 2040);13export const HYDRADX_CHAIN = +(process.env.RELAY_HYDRADX_ID || 2034);1415export const QUARTZ_CHAIN = +(process.env.RELAY_QUARTZ_ID || 2095);16export const STATEMINE_CHAIN = +(process.env.RELAY_STATEMINE_ID || 1000);17export const KARURA_CHAIN = +(process.env.RELAY_KARURA_ID || 2000);18export const MOONRIVER_CHAIN = +(process.env.RELAY_MOONRIVER_ID || 2023);19export const SHIDEN_CHAIN = +(process.env.RELAY_SHIDEN_ID || 2007);2021export const relayUrl = config.relayUrl;22export const statemintUrl = config.statemintUrl;23export const statemineUrl = config.statemineUrl;2425export const acalaUrl = config.acalaUrl;26export const moonbeamUrl = config.moonbeamUrl;27export const astarUrl = config.astarUrl;28export const polkadexUrl = config.polkadexUrl;29export const hydraDxUrl = config.hydraDxUrl;3031export const karuraUrl = config.karuraUrl;32export const moonriverUrl = config.moonriverUrl;33export const shidenUrl = config.shidenUrl;3435export const SAFE_XCM_VERSION = 3;363738export const RELAY_DECIMALS = 12;39export const STATEMINE_DECIMALS = 12;40export const KARURA_DECIMALS = 12;41export const SHIDEN_DECIMALS = 18n;42export const QTZ_DECIMALS = 18n;4344export const ASTAR_DECIMALS = 18n;45export const UNQ_DECIMALS = 18n;4647export const maxWaitBlocks = 6;4849export const uniqueMultilocation = {50 parents: 1,51 interior: {52 X1: {53 Parachain: UNIQUE_CHAIN,54 },55 },56};57export const uniqueVersionedMultilocation = {58 V3: uniqueMultilocation,59};6061export const uniqueAssetId = {62 Concrete: uniqueMultilocation,63};6465export const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {66 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash67 && event.outcome.isFailedToTransactAsset);68};69export const expectUntrustedReserveLocationFail = async (helper: DevUniqueHelper, messageSent: any) => {70 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash71 && event.outcome.isUntrustedReserveLocation);72};7374export const expectDownwardXcmNoPermission = async (helper: DevUniqueHelper) => {75 // The correct messageHash for downward messages can't be reliably obtained76 await helper.wait.expectEvent(maxWaitBlocks, Event.DmpQueue.ExecutedDownward, event => event.outcome.asIncomplete[1].isNoPermission);77};7879export const expectDownwardXcmComplete = async (helper: DevUniqueHelper) => {80 // The correct messageHash for downward messages can't be reliably obtained81 await helper.wait.expectEvent(maxWaitBlocks, Event.DmpQueue.ExecutedDownward, event => event.outcome.isComplete);82};8384export const NETWORKS = {85 acala: usingAcalaPlaygrounds,86 astar: usingAstarPlaygrounds,87 polkadex: usingPolkadexPlaygrounds,88 moonbeam: usingMoonbeamPlaygrounds,89 moonriver: usingMoonriverPlaygrounds,90 karura: usingKaruraPlaygrounds,91 shiden: usingShidenPlaygrounds,92 hydraDx: usingHydraDxPlaygrounds,93} as const;94type NetworkNames = keyof typeof NETWORKS;9596type NativeRuntime = 'opal' | 'quartz' | 'unique';9798export function mapToChainId(networkName: keyof typeof NETWORKS): number {99 switch (networkName) {100 case 'acala':101 return ACALA_CHAIN;102 case 'astar':103 return ASTAR_CHAIN;104 case 'moonbeam':105 return MOONBEAM_CHAIN;106 case 'polkadex':107 return POLKADEX_CHAIN;108 case 'moonriver':109 return MOONRIVER_CHAIN;110 case 'karura':111 return KARURA_CHAIN;112 case 'shiden':113 return SHIDEN_CHAIN;114 case 'hydraDx':115 return HYDRADX_CHAIN;116 }117}118119export function mapToChainUrl(networkName: NetworkNames): string {120 switch (networkName) {121 case 'acala':122 return acalaUrl;123 case 'astar':124 return astarUrl;125 case 'moonbeam':126 return moonbeamUrl;127 case 'polkadex':128 return polkadexUrl;129 case 'moonriver':130 return moonriverUrl;131 case 'karura':132 return karuraUrl;133 case 'shiden':134 return shidenUrl;135 case 'hydraDx':136 return hydraDxUrl;137 }138}139140export function getDevPlayground(name: NetworkNames) {141 return NETWORKS[name];142}143144export const TRANSFER_AMOUNT = 2000000_000_000_000_000_000_000n;145export const SENDER_BUDGET = 2n * TRANSFER_AMOUNT;146export const SENDBACK_AMOUNT = TRANSFER_AMOUNT / 2n;147export const STAYED_ON_TARGET_CHAIN = TRANSFER_AMOUNT - SENDBACK_AMOUNT;148export const TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT = 100_000_000_000n;149150export class XcmTestHelper {151 private _balanceUniqueTokenInit: bigint = 0n;152 private _balanceUniqueTokenMiddle: bigint = 0n;153 private _balanceUniqueTokenFinal: bigint = 0n;154 private _unqFees: bigint = 0n;155 private _nativeRuntime: NativeRuntime;156157 constructor(runtime: NativeRuntime) {158 this._nativeRuntime = runtime;159 }160161 private _getNativeId() {162 switch (this._nativeRuntime) {163 case 'opal':164 // To-Do165 return 1001;166 case 'quartz':167 return QUARTZ_CHAIN;168 case 'unique':169 return UNIQUE_CHAIN;170 }171 }172173 private _isAddress20FormatFor(network: NetworkNames) {174 switch (network) {175 case 'moonbeam':176 case 'moonriver':177 return true;178 default:179 return false;180 }181 }182183 private _runtimeVersionedMultilocation() {184 return {185 V3: {186 parents: 1,187 interior: {188 X1: {189 Parachain: this._getNativeId(),190 },191 },192 },193 };194 }195196 private _uniqueChainMultilocationForRelay() {197 return {198 V3: {199 parents: 0,200 interior: {201 X1: {Parachain: this._getNativeId()},202 },203 },204 };205 }206207 async sendUnqTo(208 networkName: keyof typeof NETWORKS,209 randomAccount: IKeyringPair,210 randomAccountOnTargetChain = randomAccount,211 ) {212 const networkUrl = mapToChainUrl(networkName);213 const targetPlayground = getDevPlayground(networkName);214 await usingPlaygrounds(async (helper) => {215 this._balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);216 const destination = {217 V2: {218 parents: 1,219 interior: {220 X1: {221 Parachain: mapToChainId(networkName),222 },223 },224 },225 };226227 const beneficiary = {228 V2: {229 parents: 0,230 interior: {231 X1: (232 this._isAddress20FormatFor(networkName) ?233 {234 AccountKey20: {235 network: 'Any',236 key: randomAccountOnTargetChain.address,237 },238 }239 :240 {241 AccountId32: {242 network: 'Any',243 id: randomAccountOnTargetChain.addressRaw,244 },245 }246 ),247 },248 },249 };250251 const assets = {252 V2: [253 {254 id: {255 Concrete: {256 parents: 0,257 interior: 'Here',258 },259 },260 fun: {261 Fungible: TRANSFER_AMOUNT,262 },263 },264 ],265 };266 const feeAssetItem = 0;267268 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');269 const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);270 this._balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);271272 this._unqFees = this._balanceUniqueTokenInit - this._balanceUniqueTokenMiddle - TRANSFER_AMOUNT;273 console.log('[%s -> %s] transaction fees: %s', this._nativeRuntime, networkName, helper.util.bigIntToDecimals(this._unqFees));274 expect(this._unqFees > 0n, 'Negative fees, looks like nothing was transferred').to.be.true;275276 await targetPlayground(networkUrl, async (helper) => {277 /*278 Since only the parachain part of the Polkadex279 infrastructure is launched (without their280 solochain validators), processing incoming281 assets will lead to an error.282 This error indicates that the Polkadex chain283 received a message from the Unique network,284 since the hash is being checked to ensure285 it matches what was sent.286 */287 if(networkName == 'polkadex' || networkName =='hydraDx') {288 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);289 } else {290 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == messageSent.messageHash);291 }292 });293294 });295 }296297 async sendUnqBack(298 networkName: keyof typeof NETWORKS,299 sudoer: IKeyringPair,300 randomAccountOnUnq: IKeyringPair,301 ) {302 const networkUrl = mapToChainUrl(networkName);303304 const targetPlayground = getDevPlayground(networkName);305 await usingPlaygrounds(async (helper) => {306307 const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(308 randomAccountOnUnq.addressRaw,309 {310 Concrete: {311 parents: 1,312 interior: {313 X1: {Parachain: this._getNativeId()},314 },315 },316 },317 SENDBACK_AMOUNT,318 );319320 let xcmProgramSent: any;321322323 await targetPlayground(networkUrl, async (helper) => {324 if('getSudo' in helper) {325 await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), xcmProgram);326 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);327 } else if('fastDemocracy' in helper) {328 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), xcmProgram]);329 // Needed to bypass the call filter.330 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);331 await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall);332 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);333 }334 });335336 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);337338 this._balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountOnUnq.address);339340 expect(this._balanceUniqueTokenFinal).to.be.equal(this._balanceUniqueTokenInit - this._unqFees - STAYED_ON_TARGET_CHAIN);341342 });343 }344345 async sendOnlyOwnedBalance(346 networkName: keyof typeof NETWORKS,347 sudoer: IKeyringPair,348 ) {349 const networkUrl = mapToChainUrl(networkName);350 const targetPlayground = getDevPlayground(networkName);351352 const targetChainBalance = 10000n * (10n ** UNQ_DECIMALS);353354 await usingPlaygrounds(async (helper) => {355 const targetChainSovereignAccount = helper.address.paraSiblingSovereignAccount(mapToChainId(networkName));356 await helper.getSudo().balance.setBalanceSubstrate(sudoer, targetChainSovereignAccount, targetChainBalance);357 const moreThanTargetChainHas = 2n * targetChainBalance;358359 const targetAccount = helper.arrange.createEmptyAccount();360361 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(362 targetAccount.addressRaw,363 {364 Concrete: {365 parents: 0,366 interior: 'Here',367 },368 },369 moreThanTargetChainHas,370 );371372 let maliciousXcmProgramSent: any;373374375 await targetPlayground(networkUrl, async (helper) => {376 if('getSudo' in helper) {377 await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgram);378 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);379 } else if('fastDemocracy' in helper) {380 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgram]);381 // Needed to bypass the call filter.382 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);383 await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall);384 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);385 }386 });387388 await expectFailedToTransact(helper, maliciousXcmProgramSent);389390 const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);391 expect(targetAccountBalance).to.be.equal(0n);392 });393 }394395 async rejectReserveTransferUNQfrom(networkName: keyof typeof NETWORKS, sudoer: IKeyringPair) {396 const networkUrl = mapToChainUrl(networkName);397 const targetPlayground = getDevPlayground(networkName);398399 await usingPlaygrounds(async (helper) => {400 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);401 const targetAccount = helper.arrange.createEmptyAccount();402403 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(404 targetAccount.addressRaw,405 {406 Concrete: {407 parents: 1,408 interior: {409 X1: {410 Parachain: this._getNativeId(),411 },412 },413 },414 },415 testAmount,416 );417418 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(419 targetAccount.addressRaw,420 {421 Concrete: {422 parents: 0,423 interior: 'Here',424 },425 },426 testAmount,427 );428429 let maliciousXcmProgramFullIdSent: any;430 let maliciousXcmProgramHereIdSent: any;431 const maxWaitBlocks = 3;432433 // Try to trick Unique using full UNQ identification434 await targetPlayground(networkUrl, async (helper) => {435 if('getSudo' in helper) {436 await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId);437 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);438 }439 // Moonbeam case440 else if('fastDemocracy' in helper) {441 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId]);442 // Needed to bypass the call filter.443 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);444 await helper.fastDemocracy.executeProposal(`${networkName} try to act like a reserve location for UNQ using path asset identification`,batchCall);445446 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);447 }448 });449450451 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);452453 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);454 expect(accountBalance).to.be.equal(0n);455456 // Try to trick Unique using shortened UNQ identification457 await targetPlayground(networkUrl, async (helper) => {458 if('getSudo' in helper) {459 await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgramHereId);460 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);461 }462 else if('fastDemocracy' in helper) {463 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramHereId]);464 // Needed to bypass the call filter.465 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);466 await helper.fastDemocracy.executeProposal(`${networkName} try to act like a reserve location for UNQ using "here" asset identification`, batchCall);467468 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);469 }470 });471472 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);473474 accountBalance = await helper.balance.getSubstrate(targetAccount.address);475 expect(accountBalance).to.be.equal(0n);476 });477 }478479 async rejectNativeTokensFrom(networkName: keyof typeof NETWORKS, sudoerOnTargetChain: IKeyringPair) {480 const networkUrl = mapToChainUrl(networkName);481 const targetPlayground = getDevPlayground(networkName);482 let messageSent: any;483484 await usingPlaygrounds(async (helper) => {485 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(486 helper.arrange.createEmptyAccount().addressRaw,487 {488 Concrete: {489 parents: 1,490 interior: {491 X1: {492 Parachain: mapToChainId(networkName),493 },494 },495 },496 },497 TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT,498 );499 await targetPlayground(networkUrl, async (helper) => {500 if('getSudo' in helper) {501 await helper.getSudo().xcm.send(sudoerOnTargetChain, this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId);502 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);503 } else if('fastDemocracy' in helper) {504 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId]);505 // Needed to bypass the call filter.506 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);507 await helper.fastDemocracy.executeProposal(`${networkName} sending native tokens to the Unique via fast democracy`, batchCall);508509 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);510 }511 });512 await expectFailedToTransact(helper, messageSent);513 });514 }515516 async registerRelayNativeTokenOnUnique(alice: IKeyringPair) {517 return await usingPlaygrounds(async (helper) => {518 const relayLocation = {519 parents: 1,520 interior: 'Here',521 };522 const relayAssetId = {Concrete: relayLocation};523524 const relayCollectionId = await helper.foreignAssets.foreignCollectionId(relayAssetId);525 if(relayCollectionId == null) {526 const name = 'Relay Tokens';527 const tokenPrefix = 'xDOT';528 const decimals = 10;529 await helper.getSudo().foreignAssets.register(alice, relayAssetId, name, tokenPrefix, {Fungible: decimals});530531 return await helper.foreignAssets.foreignCollectionId(relayAssetId);532 } else {533 console.log('Relay foreign collection is already registered');534 return relayCollectionId;535 }536 });537 }538}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