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.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import type {IKeyringPair} from '@polkadot/types/types';18import {itEth, usingEthPlaygrounds} from './eth/util/index.js';19import {itSub, Pallets, usingPlaygrounds, expect} from './util/index.js';20import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';2122describe('Integration Test Transfer(recipient, collection_id, item_id, value)', () => {23 let donor: IKeyringPair;24 let alice: IKeyringPair;25 let bob: IKeyringPair;2627 before(async () => {28 await usingPlaygrounds(async (helper, privateKey) => {29 donor = await privateKey({url: import.meta.url});30 [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);31 });32 });3334 itSub('Balance transfers and check balance', async ({helper}) => {35 const alicesBalanceBefore = await helper.balance.getSubstrate(alice.address);36 const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);3738 expect(await helper.balance.transferToSubstrate(alice, bob.address, 1n)).to.be.true;3940 const alicesBalanceAfter = await helper.balance.getSubstrate(alice.address);41 const bobsBalanceAfter = await helper.balance.getSubstrate(bob.address);4243 expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true;44 expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true;45 });4647 itSub('Inability to pay fees error message is correct', async ({helper}) => {48 const [zero] = await helper.arrange.createAccounts([0n], donor);4950 // console.error = () => {};51 // The following operation throws an error into the console and the logs. Pay it no heed as long as the test succeeds.52 await expect(helper.balance.transferToSubstrate(zero, donor.address, 1n))53 .to.be.rejectedWith('Inability to pay some fees , e.g. account balance too low');54 });5556 itSub('[nft] User can transfer owned token', async ({helper}) => {57 const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-1-NFT', description: '', tokenPrefix: 'T'});58 const nft = await collection.mintToken(alice);5960 await nft.transfer(alice, {Substrate: bob.address});61 expect(await nft.getOwner()).to.be.deep.equal({Substrate: bob.address});62 });6364 itSub('[fungible] User can transfer owned token', async ({helper}) => {65 const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-1-FT', description: '', tokenPrefix: 'T'});66 await collection.mint(alice, 10n);6768 await collection.transfer(alice, {Substrate: bob.address}, 9n);69 expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(9n);70 expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n);71 });7273 itSub.ifWithPallets('[refungible] User can transfer owned token', [Pallets.ReFungible], async ({helper}) => {74 const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-1-RFT', description: '', tokenPrefix: 'T'});75 const rft = await collection.mintToken(alice, 10n);7677 await rft.transfer(alice, {Substrate: bob.address}, 9n);78 expect(await rft.getBalance({Substrate: bob.address})).to.be.equal(9n);79 expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(1n);80 });8182 itSub('[nft] Collection admin can transfer owned token', async ({helper}) => {83 const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-2-NFT', description: '', tokenPrefix: 'T'});84 await collection.addAdmin(alice, {Substrate: bob.address});8586 const nft = await collection.mintToken(bob, {Substrate: bob.address});87 await nft.transfer(bob, {Substrate: alice.address});8889 expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address});90 });9192 itSub('[fungible] Collection admin can transfer owned token', async ({helper}) => {93 const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-2-FT', description: '', tokenPrefix: 'T'});94 await collection.addAdmin(alice, {Substrate: bob.address});9596 await collection.mint(bob, 10n, {Substrate: bob.address});97 await collection.transfer(bob, {Substrate: alice.address}, 1n);9899 expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(9n);100 expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n);101 });102103 itSub.ifWithPallets('[refungible] Collection admin can transfer owned token', [Pallets.ReFungible], async ({helper}) => {104 const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-2-RFT', description: '', tokenPrefix: 'T'});105 await collection.addAdmin(alice, {Substrate: bob.address});106107 const rft = await collection.mintToken(bob, 10n, {Substrate: bob.address});108 await rft.transfer(bob, {Substrate: alice.address}, 1n);109110 expect(await rft.getBalance({Substrate: bob.address})).to.be.equal(9n);111 expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(1n);112 });113});114115describe('Negative Integration Test Transfer(recipient, collection_id, item_id, value)', () => {116 let alice: IKeyringPair;117 let bob: IKeyringPair;118119 before(async () => {120 await usingPlaygrounds(async (helper, privateKey) => {121 const donor = await privateKey({url: import.meta.url});122 [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);123 });124 });125126127 itSub('[nft] Transfer with not existed collection_id', async ({helper}) => {128 await expect(helper.nft.transferToken(alice, NON_EXISTENT_COLLECTION_ID, 1, {Substrate: bob.address}))129 .to.be.rejectedWith(/common\.CollectionNotFound/);130 });131132 itSub('[fungible] Transfer with not existed collection_id', async ({helper}) => {133 await expect(helper.ft.transfer(alice, NON_EXISTENT_COLLECTION_ID, {Substrate: bob.address}))134 .to.be.rejectedWith(/common\.CollectionNotFound/);135 });136137 itSub.ifWithPallets('[refungible] Transfer with not existed collection_id', [Pallets.ReFungible], async ({helper}) => {138 await expect(helper.rft.transferToken(alice, NON_EXISTENT_COLLECTION_ID, 1, {Substrate: bob.address}))139 .to.be.rejectedWith(/common\.CollectionNotFound/);140 });141142 itSub('[nft] Transfer with deleted collection_id', async ({helper}) => {143 const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-1-NFT', description: '', tokenPrefix: 'T'});144 const nft = await collection.mintToken(alice);145146 await nft.burn(alice);147 await collection.burn(alice);148149 await expect(nft.transfer(alice, {Substrate: bob.address}))150 .to.be.rejectedWith(/common\.CollectionNotFound/);151 });152153 itSub('[fungible] Transfer with deleted collection_id', async ({helper}) => {154 const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-1-FT', description: '', tokenPrefix: 'T'});155 await collection.mint(alice, 10n);156157 await collection.burnTokens(alice, 10n);158 await collection.burn(alice);159160 await expect(collection.transfer(alice, {Substrate: bob.address}))161 .to.be.rejectedWith(/common\.CollectionNotFound/);162 });163164 itSub.ifWithPallets('[refungible] Transfer with deleted collection_id', [Pallets.ReFungible], async ({helper}) => {165 const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-Neg-1-RFT', description: '', tokenPrefix: 'T'});166 const rft = await collection.mintToken(alice, 10n);167168 await rft.burn(alice, 10n);169 await collection.burn(alice);170171 await expect(rft.transfer(alice, {Substrate: bob.address}))172 .to.be.rejectedWith(/common\.CollectionNotFound/);173 });174175 itSub('[nft] Transfer with not existed item_id', async ({helper}) => {176 const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-2-NFT', description: '', tokenPrefix: 'T'});177 await expect(collection.transferToken(alice, 1, {Substrate: bob.address}))178 .to.be.rejectedWith(/common\.TokenNotFound/);179 });180181 itSub('[fungible] Transfer with not existed item_id', async ({helper}) => {182 const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-2-FT', description: '', tokenPrefix: 'T'});183 await expect(collection.transfer(alice, {Substrate: bob.address}))184 .to.be.rejectedWith(/common\.TokenValueTooLow/);185 });186187 itSub.ifWithPallets('[refungible] Transfer with not existed item_id', [Pallets.ReFungible], async ({helper}) => {188 const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-Neg-2-RFT', description: '', tokenPrefix: 'T'});189 await expect(collection.transferToken(alice, 1, {Substrate: bob.address}))190 .to.be.rejectedWith(/common\.TokenValueTooLow/);191 });192193 itSub('Zero transfer NFT', async ({helper}) => {194 const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-3-NFT', description: '', tokenPrefix: 'T'});195 const tokenAlice = await collection.mintToken(alice, {Substrate: alice.address});196 const tokenBob = await collection.mintToken(alice, {Substrate: bob.address});197 // 1. Zero transfer of own tokens allowed:198 await helper.executeExtrinsic(alice, 'api.tx.unique.transfer', [{Substrate: bob.address}, collection.collectionId, tokenAlice.tokenId, 0]);199 // 2. Zero transfer of non-owned tokens not allowed:200 await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transfer', [{Substrate: alice.address}, collection.collectionId, tokenBob.tokenId, 0])).to.be.rejectedWith('common.NoPermission');201 // 3. Zero transfer of non-existing tokens not allowed:202 await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transfer', [{Substrate: alice.address}, collection.collectionId, 10, 0])).to.be.rejectedWith('common.TokenNotFound');203 expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: alice.address});204 expect(await tokenBob.getOwner()).to.deep.eq({Substrate: bob.address});205 // 4. Storage is not corrupted:206 await tokenAlice.transfer(alice, {Substrate: bob.address});207 await tokenBob.transfer(bob, {Substrate: alice.address});208 expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: bob.address});209 expect(await tokenBob.getOwner()).to.deep.eq({Substrate: alice.address});210 });211212 itSub('[nft] Transfer with deleted item_id', async ({helper}) => {213 const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-3-NFT', description: '', tokenPrefix: 'T'});214 const nft = await collection.mintToken(alice);215216 await nft.burn(alice);217218 await expect(nft.transfer(alice, {Substrate: bob.address}))219 .to.be.rejectedWith(/common\.TokenNotFound/);220 });221222 itSub('[fungible] Transfer with deleted item_id', async ({helper}) => {223 const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-3-FT', description: '', tokenPrefix: 'T'});224 await collection.mint(alice, 10n);225226 await collection.burnTokens(alice, 10n);227228 await expect(collection.transfer(alice, {Substrate: bob.address}))229 .to.be.rejectedWith(/common\.TokenValueTooLow/);230 });231232 itSub.ifWithPallets('[refungible] Transfer with deleted item_id', [Pallets.ReFungible], async ({helper}) => {233 const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-Neg-3-RFT', description: '', tokenPrefix: 'T'});234 const rft = await collection.mintToken(alice, 10n);235236 await rft.burn(alice, 10n);237238 await expect(rft.transfer(alice, {Substrate: bob.address}))239 .to.be.rejectedWith(/common\.TokenValueTooLow/);240 });241242 itSub('[nft] Transfer with recipient that is not owner', async ({helper}) => {243 const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-4-NFT', description: '', tokenPrefix: 'T'});244 const nft = await collection.mintToken(alice);245246 await expect(nft.transfer(bob, {Substrate: bob.address}))247 .to.be.rejectedWith(/common\.NoPermission/);248 expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address});249 });250251 itSub('[fungible] Transfer with recipient that is not owner', async ({helper}) => {252 const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-4-FT', description: '', tokenPrefix: 'T'});253 await collection.mint(alice, 10n);254255 await expect(collection.transfer(bob, {Substrate: bob.address}, 9n))256 .to.be.rejectedWith(/common\.TokenValueTooLow/);257 expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(0n);258 expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(10n);259 });260261 itSub.ifWithPallets('[refungible] Transfer with recipient that is not owner', [Pallets.ReFungible], async ({helper}) => {262 const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-1-RFT', description: '', tokenPrefix: 'T'});263 const rft = await collection.mintToken(alice, 10n);264265 await expect(rft.transfer(bob, {Substrate: bob.address}, 9n))266 .to.be.rejectedWith(/common\.TokenValueTooLow/);267 expect(await rft.getBalance({Substrate: bob.address})).to.be.equal(0n);268 expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(10n);269 });270});271272describe('Transfers to self (potentially over substrate-evm boundary)', () => {273 let donor: IKeyringPair;274275 before(async function() {276 await usingEthPlaygrounds(async (_, privateKey) => {277 donor = await privateKey({url: import.meta.url});278 });279 });280281 itEth('Transfers to self. In case of same frontend', async ({helper}) => {282 const [owner] = await helper.arrange.createAccounts([10n], donor);283 const collection = await helper.ft.mintCollection(owner, {});284 await collection.mint(owner, 100n);285286 const ownerProxy = helper.address.substrateToEth(owner.address);287288 // transfer to own proxy289 await collection.transfer(owner, {Ethereum: ownerProxy}, 10n);290 expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(90n);291 expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(10n);292293 // transfer-from own proxy to own proxy again294 await collection.transferFrom(owner, {Ethereum: ownerProxy}, {Ethereum: ownerProxy}, 5n);295 expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(90n);296 expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(10n);297 });298299 itEth('Transfers to self. In case of substrate-evm boundary', async ({helper}) => {300 const [owner] = await helper.arrange.createAccounts([10n], donor);301 const collection = await helper.ft.mintCollection(owner, {});302 await collection.mint(owner, 100n);303304 const ownerProxy = helper.address.substrateToEth(owner.address);305306 // transfer to own proxy307 await collection.transfer(owner, {Ethereum: ownerProxy}, 10n);308 expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(90n);309 expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(10n);310311 // transfer-from own proxy to self312 await collection.transferFrom(owner, {Ethereum: ownerProxy}, {Substrate: owner.address}, 5n);313 expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(95n);314 expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(5n);315 });316317 itEth('Transfers to self. In case of inside substrate-evm', async ({helper}) => {318 const [owner] = await helper.arrange.createAccounts([10n], donor);319 const collection = await helper.ft.mintCollection(owner, {});320 await collection.mint(owner, 100n);321322 // transfer to self again323 await collection.transfer(owner, {Substrate: owner.address}, 10n);324 expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(100n);325326 // transfer-from self to self again327 await collection.transferFrom(owner, {Substrate: owner.address}, {Substrate: owner.address}, 5n);328 expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(100n);329 });330331 itEth('Transfers to self. In case of inside substrate-evm when not enought "Fungibles"', async ({helper}) => {332 const [owner] = await helper.arrange.createAccounts([10n], donor);333 const collection = await helper.ft.mintCollection(owner, {});334 await collection.mint(owner, 10n);335336 // transfer to self again337 await expect(collection.transfer(owner, {Substrate: owner.address}, 11n))338 .to.be.rejectedWith(/common\.TokenValueTooLow/);339340 // transfer-from self to self again341 await expect(collection.transferFrom(owner, {Substrate: owner.address}, {Substrate: owner.address}, 12n))342 .to.be.rejectedWith(/common\.TokenValueTooLow/);343 expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(10n);344 });345});js-packages/tests/transferFrom.test.tsdiffbeforeafterboth--- a/js-packages/tests/transferFrom.test.ts
+++ b/js-packages/tests/transferFrom.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, usingPlaygrounds, expect} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+import {itSub, Pallets, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique-nft/playgrounds/types.js';
describe('Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {
let alice: IKeyringPair;
js-packages/tests/tx-version-presence.test.tsdiffbeforeafterboth--- a/js-packages/tests/tx-version-presence.test.ts
+++ b/js-packages/tests/tx-version-presence.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import {Metadata} from '@polkadot/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
+import {itSub, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
let metadata: Metadata;
js-packages/tests/util/authorizeEnactUpgrade.tsdiffbeforeafterboth--- a/js-packages/tests/util/authorizeEnactUpgrade.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import {readFile} from 'fs/promises';
-import {u8aToHex} from '@polkadot/util';
-import {usingPlaygrounds} from './index.js';
-import {blake2AsHex} from '@polkadot/util-crypto';
-
-
-const codePath = process.argv[2];
-if(!codePath) throw new Error('missing code path argument');
-
-const code = await readFile(codePath);
-
-await usingPlaygrounds(async (helper, privateKey) => {
- const alice = await privateKey('//Alice');
- const hex = blake2AsHex(code);
- await helper.getSudo().executeExtrinsic(alice, 'api.tx.parachainSystem.authorizeUpgrade', [hex, true]);
- await helper.getSudo().executeExtrinsicUncheckedWeight(alice, 'api.tx.parachainSystem.enactAuthorizedUpgrade', [u8aToHex(code)]);
-});
-// We miss disconnect/unref somewhere.
-process.exit(0);
js-packages/tests/util/createHrmp.tsdiffbeforeafterboth--- a/js-packages/tests/util/createHrmp.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import {usingPlaygrounds} from './index.js';
-import config from '../config.js';
-
-const profile = process.argv[2];
-if(!profile) throw new Error('missing profile/relay argument');
-
-await usingPlaygrounds(async (helper, privateKey) => {
- const bidirOpen = async (a: number, b: number) => {
- console.log(`Opening ${a} <=> ${b}`);
- await helper.getSudo().executeExtrinsic(alice, 'api.tx.hrmp.forceOpenHrmpChannel', [a, b, 8, 512]);
- await helper.getSudo().executeExtrinsic(alice, 'api.tx.hrmp.forceOpenHrmpChannel', [b, a, 8, 512]);
- };
- const alice = await privateKey('//Alice');
- switch (profile) {
- case 'opal':
- await bidirOpen(1001, 1002);
- break;
- case 'quartz':
- await bidirOpen(1001, 1002);
- await bidirOpen(1001, 1003);
- await bidirOpen(1001, 1004);
- await bidirOpen(1001, 1005);
- break;
- case 'unique':
- await bidirOpen(1001, 1002);
- await bidirOpen(1001, 1003);
- await bidirOpen(1001, 1004);
- await bidirOpen(1001, 1005);
- await bidirOpen(1001, 1006);
- await bidirOpen(1001, 1007);
- break;
- default:
- throw new Error(`unknown hrmp config profile: ${profile}`);
- }
-}, config.relayUrl);
-// We miss disconnect/unref somewhere.
-process.exit(0);
js-packages/tests/util/frankensteinMigrate.tsdiffbeforeafterboth--- a/js-packages/tests/util/frankensteinMigrate.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import {migration as locksToFreezesMigration} from '../migrations/942057-appPromotion/index.js';
-export interface Migration {
- before: () => Promise<void>,
- after: () => Promise<void>,
-}
-
-export const migrations: {[key: string]: Migration} = {
- 'v942057': locksToFreezesMigration,
-};
js-packages/tests/util/globalSetup.tsdiffbeforeafterboth--- a/js-packages/tests/util/globalSetup.ts
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-import {
- usingPlaygrounds, Pallets, DONOR_FUNDING, MINIMUM_DONOR_FUND, LOCKING_PERIOD, UNLOCKING_PERIOD, makeNames,
-} from './index.js';
-import * as path from 'path';
-import {promises as fs} from 'fs';
-
-const {dirname} = makeNames(import.meta.url);
-
-// This function should be called before running test suites.
-const globalSetup = async (): Promise<void> => {
- await usingPlaygrounds(async (helper, privateKey) => {
- try {
- // 1. Wait node producing blocks
- await helper.wait.newBlocks(1, 600_000);
-
- // 2. Create donors for test files
- await fundFilenamesWithRetries(3)
- .then((result) => {
- if(!result) throw Error('Some problems with fundFilenamesWithRetries');
- });
-
- // 3. Configure App Promotion
- const missingPallets = helper.fetchMissingPalletNames([Pallets.AppPromotion]);
- if(missingPallets.length === 0) {
- const superuser = await privateKey('//Alice');
- const palletAddress = helper.arrange.calculatePalletAddress('appstake');
- const palletAdmin = await privateKey('//PromotionAdmin');
- const api = helper.getApi();
- await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})));
- const nominal = helper.balance.getOneTokenNominal();
- await helper.balance.transferToSubstrate(superuser, palletAdmin.address, 10000n * nominal);
- await helper.balance.transferToSubstrate(superuser, palletAddress, 10000n * nominal);
- await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [api.tx.configuration
- .setAppPromotionConfigurationOverride({
- recalculationInterval: LOCKING_PERIOD,
- pendingInterval: UNLOCKING_PERIOD})], true);
- }
- } catch (error) {
- console.error(error);
- throw Error('Error during globalSetup');
- }
- });
-};
-
-async function getFiles(rootPath: string): Promise<string[]> {
- const files = await fs.readdir(rootPath, {withFileTypes: true});
- const filenames: string[] = [];
- for(const entry of files) {
- const res = path.resolve(rootPath, entry.name);
- if(entry.isDirectory()) {
- filenames.push(...await getFiles(res));
- } else {
- filenames.push(res);
- }
- }
- return filenames;
-}
-
-const fundFilenames = async () => {
- await usingPlaygrounds(async (helper, privateKey) => {
- const oneToken = helper.balance.getOneTokenNominal();
- const alice = await privateKey('//Alice');
- const nonce = await helper.chain.getNonce(alice.address);
- const filenames = await getFiles(path.resolve(dirname, '..'));
-
- // batching is actually undesireable, it takes away the time while all the transactions actually succeed
- const batchSize = 300;
- let balanceGrantedCounter = 0;
- for(let b = 0; b < filenames.length; b += batchSize) {
- const tx: Promise<boolean>[] = [];
- let batchBalanceGrantedCounter = 0;
- for(let i = 0; batchBalanceGrantedCounter < batchSize && b + i < filenames.length; i++) {
- const f = filenames[b + i];
- if(!f.endsWith('.test.ts') && !f.endsWith('seqtest.ts') || f.includes('.outdated')) continue;
- const account = await privateKey({filename: f, ignoreFundsPresence: true});
- const aliceBalance = await helper.balance.getSubstrate(account.address);
-
- if(aliceBalance < MINIMUM_DONOR_FUND * oneToken) {
- tx.push(helper.executeExtrinsic(
- alice,
- 'api.tx.balances.transferKeepAlive',
- [account.address, DONOR_FUNDING * oneToken],
- true,
- {nonce: nonce + balanceGrantedCounter++},
- ).then(() => true).catch(() => {console.error(`Transaction to ${path.basename(f)} registered as failed. Strange.`); return false;}));
- batchBalanceGrantedCounter++;
- }
- }
-
- if(tx.length > 0) {
- console.log(`Granting funds to ${batchBalanceGrantedCounter} filename accounts.`);
- const result = await Promise.all(tx);
- if(result && result.lastIndexOf(false) > -1) throw new Error('The transactions actually probably succeeded, should check the balances.');
- }
- }
-
- if(balanceGrantedCounter == 0) console.log('No account needs additional funding.');
- });
-};
-
-const fundFilenamesWithRetries = (retriesLeft: number): Promise<boolean> => {
- if(retriesLeft <= 0) return Promise.resolve(false);
- return fundFilenames()
- .then(() => Promise.resolve(true))
- .catch(e => {
- console.error(e);
- console.error(`Some transactions might have failed. ${retriesLeft > 1 ? 'Retrying...' : 'Something is wrong.'}\n`);
- return fundFilenamesWithRetries(--retriesLeft);
- });
-};
-
-globalSetup().catch(e => {
- console.error('Setup error');
- console.error(e);
- process.exit(1);
-});
js-packages/tests/util/identitySetter.tsdiffbeforeafterboth--- a/js-packages/tests/util/identitySetter.ts
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-//
-// Pulls identities and sub-identities from a chain and then makes a preimage to later force upload them into another.
-// Only changed or previously non-existent data are inserted.
-//
-// Usage: `yarn setIdentities [relay WS URL] [parachain WS URL] [user key]
-// Example: `yarn setIdentities wss://polkadot-rpc.dwellir.com ws://localhost:9944 escape pattern miracle train sudden cart adapt embark wedding alien lamp mesh`
-
-import {encodeAddress} from '@polkadot/keyring';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, Pallets} from './index.js';
-import {ChainHelperBase} from '@unique/playgrounds/unique.js';
-
-const relayUrl = process.argv[2] ?? 'ws://localhost:9844';
-const paraUrl = process.argv[3] ?? 'ws://localhost:9944';
-const key = process.argv.length > 4 ? process.argv.slice(4).join(' ') : '//Alice';
-
-export function extractAccountId(key: any): string {
- return (key as any).toHuman()[0];
-}
-
-export function extractIdentityInfo(value: any): object {
- const heart = (value as any).unwrap();
- const identity = heart.toJSON();
- identity.judgements = heart.judgements.toHuman();
- return identity;
-}
-
-export function extractIdentity(key: any, value: any): [string, any] {
- return [extractAccountId(key), extractIdentityInfo(value)];
-}
-
-export async function getIdentities(helper: ChainHelperBase, noneCasePredicate?: (key: any, value: any) => void) {
- const identities: [string, any][] = [];
- for(const [key, v] of await helper.getApi().query.identity.identityOf.entries()) {
- const value = v as any;
- if(value.isNone) {
- if(noneCasePredicate) noneCasePredicate(key, value);
- continue;
- }
- identities.push(extractIdentity(key, value));
- }
- return identities;
-}
-
-// whether the existing chain data is more important than the coming
-function isCurrentChainDataPriority(helper: ChainHelperBase, currentChainId: number | undefined, relayChainId: number) {
- if(!currentChainId) return false;
- // information has come from local chain, and is automatically superior
- if(currentChainId == helper.chain.getChainProperties().ss58Format) return true;
- // the lower the id, the more important it is (Polkadot has ss58 prefix = 0, Kusama has ss58 prefix = 2)
- return currentChainId < relayChainId;
-}
-
-// construct an object with all data necessary for insertion from storage query results
-export function constructSubInfo(identityAccount: string, subQuery: any, supers: any[], ss58?: number): [string, any] {
- const deposit = subQuery.toJSON()[0];
- const subIdentities = subQuery.toHuman()[1];
- subIdentities.map((sub: string) => supers.find((sup: any) => sup[0] === sub));
-
- return [
- encodeAddress(identityAccount, ss58), [
- deposit,
- subIdentities.map((sub: string): [string, object] | null => {
- const sup = supers.find((sup: any) => sup[0] === sub);
- if(!sup) {
- console.error(`Error: Could not find info on super for \nsub-identity account\t${sub} of \nsuper account \t\t${identityAccount}, skipping.`);
- return null;
- }
- return [encodeAddress(sub, ss58), sup[1].toJSON()[1]];
- }).filter((x: any) => x),
- ],
- ];
-}
-
-export async function getSubs(helper: ChainHelperBase) {
- return (await helper.getApi().query.identity.subsOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]);
-}
-
-export async function getSupers(helper: ChainHelperBase) {
- return (await helper.getApi().query.identity.superOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]);
-}
-
-async function uploadPreimage(helper: ChainHelperBase, preimageMaker: IKeyringPair, preimage: string) {
- try {
- await helper.executeExtrinsic(preimageMaker, 'api.tx.preimage.notePreimage', [preimage]);
- } catch (e: any) {
- if(e.message.includes('AlreadyNoted')) {
- console.warn('Warning: The same preimage already exists on the chain. Nothing was uploaded.');
- } else {
- console.error(e);
- }
- }
-}
-
-// The utility for pulling identity and sub-identity data
-const forceInsertIdentities = async (): Promise<void> => {
- let relaySS58Prefix = 0;
- const identitiesOnRelay: any[] = [];
- const subsOnRelay: any[] = [];
- const identitiesToRemove: string[] = [];
- await usingPlaygrounds(async helper => {
- try {
- relaySS58Prefix = helper.chain.getChainProperties().ss58Format;
- // iterate over every identity
- for(const [key, value] of await getIdentities(helper, (key, _value) => identitiesToRemove.push((key as any).toHuman()[0]))) {
- // if any of the judgements resulted in a good confirmed outcome, keep this identity
- let knownGood = false, reasonable = false;
- for(const [_id, judgement] of value.judgements) {
- if(judgement == 'KnownGood') knownGood = true;
- if(judgement == 'Reasonable') reasonable = true;
- }
- if(!(reasonable || knownGood)) continue;
- // replace the registrator id with the relay chain's ss58 format
- value.judgements = [[helper.chain.getChainProperties().ss58Format, knownGood ? 'KnownGood' : 'Reasonable']];
- identitiesOnRelay.push([key, value]);
- }
-
- const sublessIdentities = [...identitiesOnRelay];
- const supersOfSubs = await getSupers(helper);
-
- // iterate over every sub-identity
- for(const [key, value] of await getSubs(helper)) {
- // only get subs of the identities interesting to us
- const identityIndex = sublessIdentities.findIndex((x: any) => x[0] == key);
- if(identityIndex == -1) continue;
- sublessIdentities.splice(identityIndex, 1);
- subsOnRelay.push(constructSubInfo(key, value, supersOfSubs));
- }
-
- // mark the rest of sub-identities for deletion with empty arrays
- /*for(const [account, _identity] of sublessIdentities) {
- subsOnRelay.push([account, ['0', []]]);
- }*/
- } catch (error) {
- console.error(error);
- throw Error('Error during fetching identities');
- }
- }, relayUrl);
-
- await usingPlaygrounds(async (helper, privateKey) => {
- if(helper.fetchMissingPalletNames([Pallets.Identity]).length != 0) console.error('pallet-identity is not included in parachain.');
- if(helper.fetchMissingPalletNames([Pallets.Preimage]).length != 0) console.error('pallet-preimage is not included in parachain.');
-
- try {
- const preimageMaker = await privateKey(key);
- const ss58Format = helper.chain.getChainProperties().ss58Format;
- const paraIdentities = await getIdentities(helper);
- const identitiesToAdd: any[] = [];
- const paraAccountsRegistrators: {[name: string]: number} = {};
-
- // cross-reference every account for changes
- for(const [key, value] of identitiesOnRelay) {
- const encodedKey = encodeAddress(key, ss58Format);
-
- // only update if the identity info does not exist or is changed
- const identity = paraIdentities.find(i => i[0] === encodedKey);
- if(identity) {
- const registratorId = identity[1].judgements[0][0];
- paraAccountsRegistrators[encodedKey] = registratorId;
- if(isCurrentChainDataPriority(helper, registratorId, value.judgements[0][0])
- || JSON.stringify(value.info) === JSON.stringify(identity[1].info)) {
- continue;
- }
- }
-
- identitiesToAdd.push([key, value]);
- // exercise caution - in case we have an identity and the realy doesn't, it might mean one of two things:
- // 1) it was deleted on the relay;
- // 2) it is our own identity, we don't want to delete it.
- // identitiesToRemove.push((key as any).toHuman()[0]);
- }
-
- if(identitiesToRemove.length != 0)
- await uploadPreimage(
- helper,
- preimageMaker,
- helper.constructApiCall('api.tx.identity.forceRemoveIdentities', [identitiesToRemove]).method.toHex(),
- );
- if(identitiesToAdd.length != 0)
- await uploadPreimage(
- helper,
- preimageMaker,
- helper.constructApiCall('api.tx.identity.forceInsertIdentities', [identitiesToAdd]).method.toHex(),
- );
-
- console.log(`Tried to push ${identitiesToAdd.length} identities`
- + ` and found ${identitiesToRemove.length} identities for potential removal.`
- + ` Currently there are ${(await helper.getApi().query.identity.identityOf.keys()).length} identities on the chain.`);
-
- // fill sub-identities
- const paraSubs = await getSubs(helper);
- const supersOfSubs = await getSupers(helper);
- const subsToUpdate: any[] = [];
-
- for(const [key, value] of subsOnRelay) {
- const encodedKey = encodeAddress(key, ss58Format);
- const sub = paraSubs.find(i => i[0] === encodedKey);
- if(sub) {
- // only update if the sub-identity info does not exist or is changed
- if(isCurrentChainDataPriority(helper, paraAccountsRegistrators[encodedKey], relaySS58Prefix)
- || JSON.stringify(value) === JSON.stringify(constructSubInfo(sub[0], sub[1], supersOfSubs)[1])) {
- continue;
- }
- } else if(value[1].length == 0)
- continue;
-
- subsToUpdate.push([key, value]);
- }
-
- if(subsToUpdate.length != 0)
- await uploadPreimage(
- helper,
- preimageMaker,
- helper.constructApiCall('api.tx.identity.forceSetSubs', [subsToUpdate]).method.toHex(),
- );
-
- console.log(`Also tried to push ${subsToUpdate.length} identities with their sub-identities.`
- + ` Currently there are ${(await helper.getApi().query.identity.subsOf.keys()).length} identities with subs.`);
- } catch (error) {
- console.error(error);
- throw Error('Error during setting identities');
- }
- }, paraUrl);
-};
-
-if(process.argv[1] === module.filename)
- forceInsertIdentities().catch(() => process.exit(1));
js-packages/tests/util/index.tsdiffbeforeafterboth--- a/js-packages/tests/util/index.ts
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-import * as path from 'path';
-import * as crypto from 'crypto';
-import type {IKeyringPair} from '@polkadot/types/types/interfaces';
-import chai from 'chai';
-import chaiAsPromised from 'chai-as-promised';
-import chaiSubset from 'chai-subset';
-import {Context} from 'mocha';
-import config from '../config.js';
-import {ChainHelperBase} from '@unique/playgrounds/unique.js';
-import type {ILogger} from '@unique/playgrounds/types.js';
-import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper, DevAstarHelper, DevShidenHelper, DevPolkadexHelper, DevHydraDxHelper} from '@unique/playgrounds/unique.dev.js';
-import {dirname} from 'path';
-import {fileURLToPath} from 'url';
-
-chai.config.truncateThreshold = 0;
-chai.use(chaiAsPromised);
-chai.use(chaiSubset);
-export const expect = chai.expect;
-
-const getTestHash = (filename: string) => crypto.createHash('md5').update(filename).digest('hex');
-
-export const getTestSeed = (filename: string) => `//Alice+${getTestHash(filename)}`;
-
-async function usingPlaygroundsGeneral<T extends ChainHelperBase, R = void>(
- helperType: new (logger: ILogger) => T,
- url: string,
- code: (helper: T, privateKey: (seed: string | { filename?: string, url?: string, ignoreFundsPresence?: boolean }) => Promise<IKeyringPair>) => Promise<R>,
-): Promise<R> {
- const silentConsole = new SilentConsole();
- silentConsole.enable();
-
- const helper = new helperType(new SilentLogger());
- let result;
- try {
- await helper.connect(url);
- const ss58Format = helper.chain.getChainProperties().ss58Format;
- const privateKey = async (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => {
- if(typeof seed === 'string') {
- return helper.util.fromSeed(seed, ss58Format);
- }
- if(seed.url) {
- const {filename} = makeNames(seed.url);
- seed.filename = filename;
- } else if(seed.filename) {
- // Pass
- } else {
- throw new Error('no url nor filename set');
- }
- const actualSeed = getTestSeed(seed.filename);
- let account = helper.util.fromSeed(actualSeed, ss58Format);
- // here's to hoping that no
- if(!seed.ignoreFundsPresence && ((helper as any)['balance'] == undefined || await (helper as any).balance.getSubstrate(account.address) < MINIMUM_DONOR_FUND)) {
- console.warn(`${path.basename(seed.filename)}: Not enough funds present on the filename account. Using the default one as the donor instead.`);
- account = helper.util.fromSeed('//Alice', ss58Format);
- }
- return account;
- };
- result = await code(helper, privateKey);
- }
- finally {
- await helper.disconnect();
- silentConsole.disable();
- }
- return result as any as R;
-}
-
-export const usingPlaygrounds = <R = void>(code: (helper: DevUniqueHelper, privateKey: (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => Promise<IKeyringPair>) => Promise<R>, url: string = config.substrateUrl) => usingPlaygroundsGeneral<DevUniqueHelper, R>(DevUniqueHelper, url, code);
-
-export const usingWestmintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevWestmintHelper>(DevWestmintHelper, url, code);
-
-export const usingStateminePlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevStatemineHelper>(DevWestmintHelper, url, code);
-
-export const usingStatemintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevStatemintHelper>(DevWestmintHelper, url, code);
-
-export const usingRelayPlaygrounds = (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevRelayHelper>(DevRelayHelper, url, code);
-
-export const usingAcalaPlaygrounds = (url: string, code: (helper: DevAcalaHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevAcalaHelper>(DevAcalaHelper, url, code);
-
-export const usingKaruraPlaygrounds = (url: string, code: (helper: DevKaruraHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevKaruraHelper>(DevAcalaHelper, url, code);
-
-export const usingMoonbeamPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevMoonbeamHelper>(DevMoonbeamHelper, url, code);
-
-export const usingMoonriverPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevMoonriverHelper>(DevMoonriverHelper, url, code);
-
-export const usingAstarPlaygrounds = (url: string, code: (helper: DevAstarHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevAstarHelper>(DevAstarHelper, url, code);
-
-export const usingShidenPlaygrounds = (url: string, code: (helper: DevShidenHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevShidenHelper>(DevShidenHelper, url, code);
-
-export const usingPolkadexPlaygrounds = (url: string, code: (helper: DevPolkadexHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevPolkadexHelper>(DevPolkadexHelper, url, code);
-
-export const usingHydraDxPlaygrounds = (url: string, code: (helper: DevHydraDxHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevHydraDxHelper>(DevHydraDxHelper, url, code);
-
-export const MINIMUM_DONOR_FUND = 4_000_000n;
-export const DONOR_FUNDING = 4_000_000n;
-
-// App-promotion periods:
-export const LOCKING_PERIOD = 12n; // 12 blocks of relay
-export const UNLOCKING_PERIOD = 6n; // 6 blocks of parachain
-
-// Native contracts
-export const COLLECTION_HELPER = '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f';
-export const CONTRACT_HELPER = '0x842899ECF380553E8a4de75bF534cdf6fBF64049';
-
-export enum Pallets {
- Inflation = 'inflation',
- ReFungible = 'refungible',
- Fungible = 'fungible',
- NFT = 'nonfungible',
- Scheduler = 'scheduler',
- UniqueScheduler = 'uniqueScheduler',
- AppPromotion = 'apppromotion',
- CollatorSelection = 'collatorselection',
- Session = 'session',
- Identity = 'identity',
- Democracy = 'democracy',
- Council = 'council',
- //CouncilMembership = 'councilmembership',
- TechnicalCommittee = 'technicalcommittee',
- Fellowship = 'fellowshipcollective',
- Preimage = 'preimage',
- Maintenance = 'maintenance',
- TestUtils = 'testutils',
-}
-
-export function requirePalletsOrSkip(test: Context, helper: DevUniqueHelper, requiredPallets: readonly string[]) {
- const missingPallets = helper.fetchMissingPalletNames(requiredPallets);
-
- if(missingPallets.length > 0) {
- const skipMsg = `\tSkipping test '${test.test?.title}'.\n\tThe following pallets are missing:\n\t- ${missingPallets.join('\n\t- ')}`;
- console.warn('\x1b[38:5:208m%s\x1b[0m', skipMsg);
- test.skip();
- }
-}
-
-export function itSub(name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: readonly string[] } = {}) {
- (opts.only ? it.only :
- opts.skip ? it.skip : it)(name, async function () {
- await usingPlaygrounds(async (helper, privateKey) => {
- if(opts.requiredPallets) {
- requirePalletsOrSkip(this, helper, opts.requiredPallets);
- }
-
- await cb({helper, privateKey});
- });
- });
-}
-export function itSubIfWithPallet(name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: readonly string[] } = {}) {
- return itSub(name, cb, {requiredPallets: required, ...opts});
-}
-itSub.only = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSub(name, cb, {only: true});
-itSub.skip = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSub(name, cb, {skip: true});
-
-itSubIfWithPallet.only = (name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSubIfWithPallet(name, required, cb, {only: true});
-itSubIfWithPallet.skip = (name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSubIfWithPallet(name, required, cb, {skip: true});
-itSub.ifWithPallets = itSubIfWithPallet;
-
-export type SchedKind = 'anon' | 'named';
-
-export function itSched(
- name: string,
- cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any,
- opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {},
-) {
- itSub(name + ' (anonymous scheduling)', (apis) => cb('anon', apis), opts);
- itSub(name + ' (named scheduling)', (apis) => cb('named', apis), opts);
-}
-itSched.only = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSched(name, cb, {only: true});
-itSched.skip = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSched(name, cb, {skip: true});
-itSched.ifWithPallets = itSchedIfWithPallets;
-
-function itSchedIfWithPallets(name: string, required: string[], cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
- return itSched(name, cb, {requiredPallets: required, ...opts});
-}
-
-export function describeXCM(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) {
- (process.env.RUN_XCM_TESTS && !opts.skip
- ? describe
- : describe.skip)(title, fn);
-}
-
-describeXCM.skip = (name: string, fn: (this: Mocha.Suite) => void) => describeXCM(name, fn, {skip: true});
-
-export function describeGov(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) {
- (process.env.RUN_GOV_TESTS && !opts.skip
- ? describe
- : describe.skip)(title, fn);
-}
-
-describeGov.skip = (name: string, fn: (this: Mocha.Suite) => void) => describeGov(name, fn, {skip: true});
-
-export function sizeOfInt(i: number) {
- if(i < 0 || i > 0xffffffff) throw new Error('out of range');
- if(i < 0b11_1111) {
- return 1;
- } else if(i < 0b11_1111_1111_1111) {
- return 2;
- } else if(i < 0b11_1111_1111_1111_1111_1111_1111_1111) {
- return 4;
- } else {
- return 5;
- }
-}
-
-const UTF8_ENCODER = new TextEncoder();
-export function sizeOfEncodedStr(v: string) {
- const encoded = UTF8_ENCODER.encode(v);
- return sizeOfInt(encoded.length) + encoded.length;
-}
-
-export function sizeOfProperty(prop: {key: string, value: string}) {
- return sizeOfEncodedStr(prop.key) + sizeOfEncodedStr(prop.value);
-}
-
-export function makeNames(url: string) {
- const filename = fileURLToPath(url);
- return {
- filename,
- dirname: dirname(filename),
- };
-}
js-packages/tests/util/relayIdentitiesChecker.tsdiffbeforeafterboth--- a/js-packages/tests/util/relayIdentitiesChecker.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-//
-// Checks and reports the differences between identities and sub-identities on two chains.
-//
-// Usage: `yarn checkRelayIdentities [relay-1 WS URL] [relay-2 WS URL]`
-// Example: `yarn checkRelayIdentities wss://polkadot-rpc.dwellir.com wss://kusama-rpc.dwellir.com`
-
-import {encodeAddress} from '@polkadot/keyring';
-import {usingPlaygrounds} from './index.js';
-import {getIdentities, getSubs, getSupers, constructSubInfo} from './identitySetter.js';
-
-const relay1Url = process.argv[2] ?? 'ws://localhost:9844';
-const relay2Url = process.argv[3] ?? 'ws://localhost:9844';
-
-async function pullIdentities(relayUrl: string): Promise<[any[], any[]]> {
- const identities: any[] = [];
- const subs: any[] = [];
-
- await usingPlaygrounds(async helper => {
- try {
- // iterate over every identity
- for(const [key, value] of await getIdentities(helper)) {
- // if any of the judgements resulted in a good confirmed outcome, keep this identity
- if(value.toHuman().judgements.filter((x: any) => x[1] == 'Reasonable' || x[1] == 'KnownGood').length == 0) continue;
- identities.push([key, value]);
- }
-
- const supersOfSubs = await getSupers(helper);
-
- // iterate over every sub-identity
- for(const [key, value] of await getSubs(helper)) {
- // only get subs of the identities interesting to us
- if(identities.find((x: any) => x[0] == key) == -1) continue;
- subs.push(constructSubInfo(key, value, supersOfSubs));
- }
- } catch (error) {
- console.error(error);
- throw Error(`Error during fetching identities on ${relayUrl}`);
- }
- }, relayUrl);
-
- return [identities, subs];
-}
-
-// The utility for pulling identity and sub-identity data
-const checkRelayIdentities = async (): Promise<void> => {
- const [identitiesOnRelay1, subIdentitiesOnRelay1] = await pullIdentities(relay1Url);
- const [identitiesOnRelay2, subIdentitiesOnRelay2] = await pullIdentities(relay2Url);
-
- console.log('identities counts:\t', identitiesOnRelay1.length, identitiesOnRelay2.length);
- console.log('sub-identities counts:\t', subIdentitiesOnRelay1.length, subIdentitiesOnRelay2.length);
- console.log();
-
- try {
- const matchingAddresses: string[] = [];
- const inequalIdentities: {[name: string]: [any, any]} = {};
-
- for(const [key1, value1] of identitiesOnRelay1) {
- const address = encodeAddress(key1);
- const identity2 = identitiesOnRelay2.find(([key2, _value2]) => address === encodeAddress(key2));
- if(!identity2) continue;
- matchingAddresses.push(address);
-
- //const [[key2, value2]] = identitiesOnRelay2.splice(index2, 1);
- const [_key2, value2] = identity2;
- if(JSON.stringify(value1.info) === JSON.stringify(value2.info)) continue;
- inequalIdentities[address] = [value1, value2];
- }
-
- /*for (const [v1, v2] of Object.values(inequalIdentities)) {
- console.log(v1.toHuman().info);
- console.log();
- console.log(v2.toHuman().info);
- await new Promise(resolve => setTimeout(resolve, 4000));
- }*/
-
- console.log(`Accounts with identities on both relays:\t${matchingAddresses.length}`);
- console.log(`Sub-identities with conflicting information:\t${Object.entries(inequalIdentities).length}`);
- console.log();
-
- const inequalSubIdentities = [];
- let matchesFound = 0;
- for(const address of matchingAddresses) {
- const sub1 = subIdentitiesOnRelay1.find(([key1, _value1]) => address === encodeAddress(key1));
- if(!sub1) continue;
- const sub2 = subIdentitiesOnRelay2.find(([key2, _value2]) => address === encodeAddress(key2));
- if(!sub2) continue;
-
- const [value1, value2] = [sub1[1], sub2[1]];
- matchesFound++;
-
- if(JSON.stringify(value1[1]) === JSON.stringify(value2[1])) {
- continue;
- }
- inequalSubIdentities.push([value1, value2]);
- }
-
- /*for (const [v1, v2] of inequalSubIdentities) {
- console.log(v1[1]);
- console.log();
- console.log(v2[1]);
- await new Promise(resolve => setTimeout(resolve, 300));
- }*/
- console.log(`Accounts with sub-identities on both relays:\t${matchesFound}`);
- console.log(`Of them, those with conflicting sub-identities:\t${inequalSubIdentities.length}`);
- } catch (error) {
- console.error(error);
- throw Error('Error during comparison');
- }
-};
-
-if(process.argv[1] === module.filename)
- checkRelayIdentities().catch(() => process.exit(1));
js-packages/tests/util/setCode.tsdiffbeforeafterboth--- a/js-packages/tests/util/setCode.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import {readFile} from 'fs/promises';
-import {u8aToHex} from '@polkadot/util';
-import {usingPlaygrounds} from './index.js';
-
-const codePath = process.argv[2];
-if(!codePath) throw new Error('missing code path argument');
-
-const code = await readFile(codePath);
-
-await usingPlaygrounds(async (helper, privateKey) => {
- const alice = await privateKey('//Alice');
- await helper.getSudo().executeExtrinsicUncheckedWeight(alice, 'api.tx.system.setCode', [u8aToHex(code)]);
-});
-// We miss disconnect/unref somewhere.
-process.exit(0);
js-packages/tests/vesting.test.tsdiffbeforeafterboth--- a/js-packages/tests/vesting.test.ts
+++ b/js-packages/tests/vesting.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
+import {itSub, usingPlaygrounds, expect} from '@unique/test-utils/util.js';
describe('Vesting', () => {
let donor: IKeyringPair;
js-packages/tests/xcm/lowLevelXcmQuartz.test.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/lowLevelXcmQuartz.test.ts
+++ b/js-packages/tests/xcm/lowLevelXcmQuartz.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingMoonriverPlaygrounds, usingShidenPlaygrounds, usingRelayPlaygrounds} from '../util/index.js';
+import {itSub, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingMoonriverPlaygrounds, usingShidenPlaygrounds} from '@unique/test-utils/util.js';
import {QUARTZ_CHAIN, QTZ_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, shidenUrl, SAFE_XCM_VERSION, XcmTestHelper, TRANSFER_AMOUNT, SENDER_BUDGET, relayUrl} from './xcm.types.js';
import {hexToString} from '@polkadot/util';
js-packages/tests/xcm/lowLevelXcmUnique.test.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/lowLevelXcmUnique.test.ts
+++ b/js-packages/tests/xcm/lowLevelXcmUnique.test.ts
@@ -16,7 +16,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import config from '../config.js';
-import {itSub, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds, usingHydraDxPlaygrounds} from '../util/index.js';
+import {itSub, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds, usingHydraDxPlaygrounds} from '@unique/test-utils/util.js';
import {nToBigInt} from '@polkadot/util';
import {hexToString} from '@polkadot/util';
import {ASTAR_DECIMALS, SAFE_XCM_VERSION, SENDER_BUDGET, UNIQUE_CHAIN, UNQ_DECIMALS, XcmTestHelper, acalaUrl, astarUrl, hydraDxUrl, moonbeamUrl, polkadexUrl, uniqueAssetId} from './xcm.types.js';
js-packages/tests/xcm/xcm.types.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/xcm.types.ts
+++ b/js-packages/tests/xcm/xcm.types.ts
@@ -1,7 +1,6 @@
import type {IKeyringPair} from '@polkadot/types/types';
-import {hexToString} from '@polkadot/util';
-import {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingHydraDxPlaygrounds, usingKaruraPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds, usingShidenPlaygrounds} from '../util/index.js';
-import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js';
+import {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingHydraDxPlaygrounds, usingKaruraPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds, usingShidenPlaygrounds} from '@unique/test-utils/util.js';
+import {DevUniqueHelper, Event} from '@unique/test-utils';
import config from '../config.js';
export const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);
js-packages/tests/xcm/xcmOpal.test.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/xcmOpal.test.ts
+++ b/js-packages/tests/xcm/xcmOpal.test.ts
@@ -16,7 +16,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import config from '../config.js';
-import {itSub, expect, describeXCM, usingPlaygrounds, usingWestmintPlaygrounds, usingRelayPlaygrounds} from '../util/index.js';
+import {itSub, expect, describeXCM, usingPlaygrounds, usingWestmintPlaygrounds, usingRelayPlaygrounds} from '@unique/test-utils/util.js';
import {XcmTestHelper} from './xcm.types.js';
const STATEMINE_CHAIN = +(process.env.RELAY_WESTMINT_ID || 1000);
js-packages/tests/xcm/xcmQuartz.test.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/xcmQuartz.test.ts
+++ b/js-packages/tests/xcm/xcmQuartz.test.ts
@@ -15,8 +15,8 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '../util/index.js';
-import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js';
+import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '@unique/test-utils/util.js';
+import {DevUniqueHelper, Event} from '@unique/test-utils';
import {STATEMINE_CHAIN, QUARTZ_CHAIN, KARURA_CHAIN, MOONRIVER_CHAIN, SHIDEN_CHAIN, STATEMINE_DECIMALS, KARURA_DECIMALS, QTZ_DECIMALS, RELAY_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, relayUrl, shidenUrl, statemineUrl} from './xcm.types.js';
import {hexToString} from '@polkadot/util';
import {XcmTestHelper} from './xcm.types.js';
js-packages/tests/xcm/xcmUnique.test.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/xcmUnique.test.ts
+++ b/js-packages/tests/xcm/xcmUnique.test.ts
@@ -16,8 +16,8 @@
import type {IKeyringPair} from '@polkadot/types/types';
import config from '../config.js';
-import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds} from '../util/index.js';
-import {Event} from '@unique/playgrounds/unique.dev.js';
+import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds} from '@unique/test-utils/util.js';
+import {Event} from '@unique/test-utils';
import {hexToString, nToBigInt} from '@polkadot/util';
import {ACALA_CHAIN, ASTAR_CHAIN, MOONBEAM_CHAIN, POLKADEX_CHAIN, SAFE_XCM_VERSION, STATEMINT_CHAIN, UNIQUE_CHAIN, expectFailedToTransact, expectUntrustedReserveLocationFail, uniqueAssetId, uniqueVersionedMultilocation} from './xcm.types.js';
import {XcmTestHelper} from './xcm.types.js';
js-packages/types/package.jsondiffbeforeafterboth--- a/js-packages/types/package.json
+++ b/js-packages/types/package.json
@@ -5,7 +5,7 @@
"engines": {
"node": ">=16"
},
- "name": "@unique/opal-types",
+ "name": "@unique-nft/opal-testnet-types",
"type": "module",
"version": "1.0.0",
"main": "index.js",
js-packages/yarn.lockdiffbeforeafterboth--- a/js-packages/yarn.lock
+++ b/js-packages/yarn.lock
@@ -1242,22 +1242,21 @@
languageName: node
linkType: hard
-"@unique/opal-types@workspace:*, @unique/opal-types@workspace:types":
+"@unique-nft/opal-testnet-types@workspace:*, @unique-nft/opal-testnet-types@workspace:types":
version: 0.0.0-use.local
- resolution: "@unique/opal-types@workspace:types"
+ resolution: "@unique-nft/opal-testnet-types@workspace:types"
dependencies:
"@polkadot/typegen": ^10.10.1
languageName: unknown
linkType: soft
-"@unique/playgrounds@workspace:*, @unique/playgrounds@workspace:playgrounds":
+"@unique-nft/playgrounds@workspace:*, @unique-nft/playgrounds@workspace:playgrounds":
version: 0.0.0-use.local
- resolution: "@unique/playgrounds@workspace:playgrounds"
+ resolution: "@unique-nft/playgrounds@workspace:playgrounds"
dependencies:
"@polkadot/api": 10.10.1
"@polkadot/util": ^12.5.1
"@polkadot/util-crypto": ^12.5.1
- "@unique/opal-types": "workspace:*"
languageName: unknown
linkType: soft
@@ -1267,6 +1266,17 @@
languageName: unknown
linkType: soft
+"@unique/test-utils@workspace:*, @unique/test-utils@workspace:test-utils":
+ version: 0.0.0-use.local
+ resolution: "@unique/test-utils@workspace:test-utils"
+ dependencies:
+ "@polkadot/api": 10.10.1
+ "@polkadot/util": ^12.5.1
+ "@polkadot/util-crypto": ^12.5.1
+ "@unique-nft/opal-testnet-types": "workspace:*"
+ languageName: unknown
+ linkType: soft
+
"@unique/tests@workspace:tests":
version: 0.0.0-use.local
resolution: "@unique/tests@workspace:tests"
@@ -6001,8 +6011,9 @@
"@types/node": ^20.8.10
"@typescript-eslint/eslint-plugin": ^6.10.0
"@typescript-eslint/parser": ^6.10.0
- "@unique/opal-types": "workspace:*"
- "@unique/playgrounds": "workspace:*"
+ "@unique-nft/opal-testnet-types": "workspace:*"
+ "@unique-nft/playgrounds": "workspace:*"
+ "@unique/test-utils": "workspace:*"
chai: ^4.3.10
chai-as-promised: ^7.1.1
chai-like: ^1.1.1